Repository: provectus/kafka-ui
Issue: [e2e] TopicTests.messageCopyFromTopicProfile : Message copy from topic profile
Labels: scope/QA
scope/AQA

Autotest implementation for:
https://app.qase.io/project/KAFKAUI?view=1&suite=5&previewMode=side&case=21

Description:
Checking possibility to copy message within Topic/Messages

Preconditions:
Login to Kafka-ui application

Open the 'Local' section

Select the 'Topics'

Open the Topic profile which has messages

Turn to Messages tab

Steps:
1 Hover on Message  ER: Menu icon should display
2 Click on menu icon for a Message
3 Select 'Copy to clipboard'

Expected result:
1 Success message should appear
2 The message content should be copied and possible to paste (For Manual testting only)



Issue comments:

Pull request review comments:

Patch that resolved the issue:
diff --git a/kafka-ui-e2e-checks/src/main/java/com/provectus/kafka/ui/pages/topic/TopicDetails.java b/kafka-ui-e2e-checks/src/main/java/com/provectus/kafka/ui/pages/topic/TopicDetails.java
index c9469fab735..61a04115fb4 100644
--- a/kafka-ui-e2e-checks/src/main/java/com/provectus/kafka/ui/pages/topic/TopicDetails.java
+++ b/kafka-ui-e2e-checks/src/main/java/com/provectus/kafka/ui/pages/topic/TopicDetails.java
@@ -1,159 +1,230 @@
 package com.provectus.kafka.ui.pages.topic;
 
 import static com.codeborne.selenide.Selenide.$;
+import static com.codeborne.selenide.Selenide.$$x;
 import static com.codeborne.selenide.Selenide.$x;
 
+import com.codeborne.selenide.CollectionCondition;
 import com.codeborne.selenide.Condition;
+import com.codeborne.selenide.ElementsCollection;
 import com.codeborne.selenide.SelenideElement;
 import com.provectus.kafka.ui.pages.BasePage;
 import com.provectus.kafka.ui.utilities.WaitUtils;
 import io.qameta.allure.Step;
-import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.List;
 import lombok.experimental.ExtensionMethod;
 import org.openqa.selenium.By;
 
 @ExtensionMethod({WaitUtils.class})
 public class TopicDetails extends BasePage {
 
-    protected SelenideElement clearMessagesBtn = $x(("//div[contains(text(), 'Clear messages')]"));
-    protected SelenideElement overviewTab = $x("//a[contains(text(),'Overview')]");
-    protected SelenideElement messagesTab = $x("//a[contains(text(),'Messages')]");
-    protected SelenideElement editSettingsMenu = $x("//li[@role][contains(text(),'Edit settings')]");
-    protected SelenideElement removeTopicBtn = $x("//ul[@role='menu']//div[contains(text(),'Remove Topic')]");
-    protected SelenideElement confirmBtn = $x("//div[@role='dialog']//button[contains(text(),'Confirm')]");
-    protected SelenideElement produceMessageBtn = $x("//div//button[text()='Produce Message']");
-    protected SelenideElement contentMessageTab = $x("//html//div[@id='root']/div/main//table//p");
-    protected SelenideElement cleanUpPolicyField = $x("//div[contains(text(),'Clean Up Policy')]/../span/*");
-    protected SelenideElement partitionsField = $x("//div[contains(text(),'Partitions')]/../span");
-    protected String consumerIdLocator = "//a[@title='%s']";
-    protected String topicHeaderLocator = "//h1[contains(text(),'%s')]";
-
-    @Step
-    public TopicDetails waitUntilScreenReady() {
-        waitUntilSpinnerDisappear();
-        Arrays.asList(overviewTab,messagesTab).forEach(element -> element.shouldBe(Condition.visible));
-        return this;
+  protected SelenideElement clearMessagesBtn = $x(("//div[contains(text(), 'Clear messages')]"));
+  protected SelenideElement messageAmountCell = $x("//tbody/tr/td[5]");
+  protected SelenideElement overviewTab = $x("//a[contains(text(),'Overview')]");
+  protected SelenideElement messagesTab = $x("//a[contains(text(),'Messages')]");
+  protected SelenideElement editSettingsMenu = $x("//li[@role][contains(text(),'Edit settings')]");
+  protected SelenideElement removeTopicBtn = $x("//ul[@role='menu']//div[contains(text(),'Remove Topic')]");
+  protected SelenideElement confirmBtn = $x("//div[@role='dialog']//button[contains(text(),'Confirm')]");
+  protected SelenideElement produceMessageBtn = $x("//div//button[text()='Produce Message']");
+  protected SelenideElement contentMessageTab = $x("//html//div[@id='root']/div/main//table//p");
+  protected SelenideElement cleanUpPolicyField = $x("//div[contains(text(),'Clean Up Policy')]/../span/*");
+  protected SelenideElement partitionsField = $x("//div[contains(text(),'Partitions')]/../span");
+  protected ElementsCollection messageGridItems = $$x("//tbody//tr");
+  protected String consumerIdLocator = "//a[@title='%s']";
+  protected String topicHeaderLocator = "//h1[contains(text(),'%s')]";
+
+  @Step
+  public TopicDetails waitUntilScreenReady() {
+    waitUntilSpinnerDisappear();
+    dotMenuBtn.shouldBe(Condition.visible);
+    return this;
+  }
+
+  @Step
+  public TopicDetails openDetailsTab(TopicMenu menu) {
+    $(By.linkText(menu.toString())).shouldBe(Condition.visible).click();
+    waitUntilSpinnerDisappear();
+    return this;
+  }
+
+  @Step
+  public TopicDetails openDotMenu() {
+    clickByJavaScript(dotMenuBtn);
+    return this;
+  }
+
+  @Step
+  public boolean isAlertWithMessageVisible(AlertHeader header, String message) {
+    return isAlertVisible(header, message);
+  }
+
+  @Step
+  public TopicDetails clickEditSettingsMenu() {
+    editSettingsMenu.shouldBe(Condition.visible).click();
+    return this;
+  }
+
+  @Step
+  public TopicDetails clickClearMessagesMenu() {
+    clearMessagesBtn.shouldBe(Condition.visible).click();
+    return this;
+  }
+
+  @Step
+  public String getCleanUpPolicy() {
+    return cleanUpPolicyField.getText();
+  }
+
+  @Step
+  public String getPartitions() {
+    return partitionsField.getText();
+  }
+
+  @Step
+  public boolean isTopicHeaderVisible(String topicName) {
+    return isVisible($x(String.format(topicHeaderLocator, topicName)));
+  }
+
+  @Step
+  public TopicDetails clickDeleteTopicMenu() {
+    removeTopicBtn.shouldBe(Condition.visible).click();
+    return this;
+  }
+
+  @Step
+  public TopicDetails clickConfirmDeleteBtn() {
+    confirmBtn.shouldBe(Condition.enabled).click();
+    confirmBtn.shouldBe(Condition.disappear);
+    return this;
+  }
+
+  @Step
+  public TopicDetails clickProduceMessageBtn() {
+    clickByJavaScript(produceMessageBtn);
+    return this;
+  }
+
+  @Step
+  public TopicDetails openConsumerGroup(String consumerId) {
+    $x(String.format(consumerIdLocator, consumerId)).click();
+    return this;
+  }
+
+  @Step
+  public boolean isKeyMessageVisible(String keyMessage) {
+    return keyMessage.equals($("td[title]").getText());
+  }
+
+  @Step
+  public boolean isContentMessageVisible(String contentMessage) {
+    return contentMessage.matches(contentMessageTab.getText().trim());
+  }
+
+  @Step
+  public int getMessageCountAmount() {
+    return Integer.parseInt(messageAmountCell.getText().trim());
+  }
+
+  private List<TopicDetails.MessageGridItem> initItems() {
+    List<TopicDetails.MessageGridItem> gridItemList = new ArrayList<>();
+    messageGridItems.shouldHave(CollectionCondition.sizeGreaterThan(0))
+        .forEach(item -> gridItemList.add(new TopicDetails.MessageGridItem(item)));
+    return gridItemList;
+  }
+
+  @Step
+  public TopicDetails.MessageGridItem getMessage(int offset) {
+    return initItems().stream()
+        .filter(e -> e.getOffset() == offset)
+        .findFirst().orElse(null);
+  }
+
+  @Step
+  public TopicDetails.MessageGridItem getRandomMessage() {
+    return getMessage(initItems().size() - 1);
+  }
+
+  public static class MessageGridItem extends BasePage {
+
+    private final SelenideElement element;
+
+    private MessageGridItem(SelenideElement element) {
+      this.element = element;
     }
 
     @Step
-    public TopicDetails openDetailsTab(TopicMenu menu) {
-        $(By.linkText(menu.getValue())).shouldBe(Condition.visible).click();
-        return this;
+    public MessageGridItem clickExpand() {
+      clickByJavaScript(element.$x("./td[1]/span"));
+      return this;
     }
 
-    @Step
-    public TopicDetails openDotMenu() {
-        clickByJavaScript(dotMenuBtn);
-        return this;
+    private SelenideElement getOffsetElm() {
+      return element.$x("./td[2]");
     }
 
     @Step
-    public TopicDetails clickEditSettingsMenu() {
-        editSettingsMenu.shouldBe(Condition.visible).click();
-        return this;
+    public int getOffset() {
+      return Integer.parseInt(getOffsetElm().getText().trim());
     }
 
     @Step
-    public TopicDetails clickClearMessagesMenu() {
-        clearMessagesBtn.shouldBe(Condition.visible).click();
-        return this;
+    public int getPartition() {
+      return Integer.parseInt(element.$x("./td[3]").getText().trim());
     }
 
     @Step
-    public String getCleanUpPolicy(){
-      return cleanUpPolicyField.getText();
+    public String getTimestamp() {
+      return element.$x("./td[4]/div").getText().trim();
     }
 
     @Step
-    public String getPartitions(){
-        return partitionsField.getText();
+    public String getKey() {
+      return element.$x("./td[5]").getText().trim();
     }
 
     @Step
-    public boolean isTopicHeaderVisible(String topicName) {
-        return isVisible($x(String.format(topicHeaderLocator,topicName)));
+    public String getValue() {
+      return element.$x("./td[6]/span/p").getText().trim();
     }
 
     @Step
-    public TopicDetails clickDeleteTopicMenu() {
-        removeTopicBtn.shouldBe(Condition.visible).click();
-        return this;
+    public MessageGridItem openDotMenu() {
+      getOffsetElm().hover();
+      element.$x("./td[7]/div/button[@aria-label='Dropdown Toggle']")
+          .shouldBe(Condition.visible).click();
+      return this;
     }
 
     @Step
-    public TopicDetails clickConfirmDeleteBtn() {
-        confirmBtn.shouldBe(Condition.enabled).click();
-        confirmBtn.shouldBe(Condition.disappear);
-        return this;
+    public MessageGridItem clickCopyToClipBoard() {
+      clickByJavaScript(element.$x("./td[7]//li[text() = 'Copy to clipboard']")
+          .shouldBe(Condition.visible));
+      return this;
     }
 
     @Step
-    public TopicDetails clickProduceMessageBtn() {
-        clickByJavaScript(produceMessageBtn);
-        return this;
+    public MessageGridItem clickSaveAsFile() {
+      clickByJavaScript(element.$x("./td[7]//li[text() = 'Save as a file']")
+          .shouldBe(Condition.visible));
+      return this;
     }
+  }
 
-    @Step
-    public TopicDetails openConsumerGroup(String consumerId) {
-        $x(String.format(consumerIdLocator, consumerId)).click();
-        return this;
-    }
+  public enum TopicMenu {
+    OVERVIEW("Overview"),
+    MESSAGES("Messages"),
+    CONSUMERS("Consumers"),
+    SETTINGS("Settings");
 
-    @Step
-    public boolean isKeyMessageVisible(String keyMessage) {
-        return keyMessage.equals($("td[title]").getText());
-    }
+    private final String value;
 
-    @Step
-    public boolean isContentMessageVisible(String contentMessage) {
-        return contentMessage.matches(contentMessageTab.getText().trim());
-    }
-
-    @Step
-    public String MessageCountAmount() {
-        return $(By.xpath("//table[@class=\"sc-hiSbEG cvnuic\"]/tbody/tr/td[5]")).getText();
+    TopicMenu(String value) {
+      this.value = value;
     }
 
-    private enum DotMenuHeaderItems {
-        EDIT_SETTINGS("Edit settings"),
-        CLEAR_MESSAGES("Clear messages"),
-        REMOVE_TOPIC("Remove topic");
-
-        private final String value;
-
-        DotMenuHeaderItems(String value) {
-            this.value = value;
-        }
-
-        public String getValue() {
-            return value;
-        }
-
-        @Override
-        public String toString() {
-            return "DotMenuHeaderItems{" + "value='" + value + '\'' + '}';
-        }
-    }
-
-    public enum TopicMenu {
-        OVERVIEW("Overview"),
-        MESSAGES("Messages"),
-        CONSUMERS("Consumers"),
-        SETTINGS("Settings");
-
-        private final String value;
-
-        TopicMenu(String value) {
-            this.value = value;
-        }
-
-        public String getValue() {
-            return value;
-        }
-
-        @Override
-        public String toString() {
-            return "TopicMenu{" + "value='" + value + '\'' + '}';
-        }
+    public String toString() {
+      return value;
     }
+  }
 }


Test patch:
diff --git a/kafka-ui-e2e-checks/src/test/java/com/provectus/kafka/ui/tests/TopicTests.java b/kafka-ui-e2e-checks/src/test/java/com/provectus/kafka/ui/tests/TopicTests.java
index aaa7a6fe8b6..dc7a3b130a8 100644
--- a/kafka-ui-e2e-checks/src/test/java/com/provectus/kafka/ui/tests/TopicTests.java
+++ b/kafka-ui-e2e-checks/src/test/java/com/provectus/kafka/ui/tests/TopicTests.java
@@ -1,5 +1,6 @@
 package com.provectus.kafka.ui.tests;
 
+import static com.provectus.kafka.ui.pages.BasePage.AlertHeader.SUCCESS;
 import static com.provectus.kafka.ui.pages.NaviSideBar.SideMenuOption.TOPICS;
 import static com.provectus.kafka.ui.pages.topic.enums.CleanupPolicyValue.COMPACT;
 import static com.provectus.kafka.ui.pages.topic.enums.CleanupPolicyValue.DELETE;
@@ -12,6 +13,7 @@
 
 import com.provectus.kafka.ui.base.BaseTest;
 import com.provectus.kafka.ui.models.Topic;
+import com.provectus.kafka.ui.pages.BasePage;
 import com.provectus.kafka.ui.pages.topic.TopicDetails;
 import com.provectus.kafka.ui.utilities.qaseIoUtils.annotations.AutomationStatus;
 import com.provectus.kafka.ui.utilities.qaseIoUtils.annotations.Suite;
@@ -98,7 +100,8 @@ public void createTopic() {
         TOPIC_LIST.add(TOPIC_TO_CREATE);
     }
 
-    @Disabled("https://github.com/provectus/kafka-ui/issues/2625")
+    @Disabled()
+    @Issue("https://github.com/provectus/kafka-ui/issues/2625")
     @DisplayName("should update a topic")
     @Suite(suiteId = SUITE_ID, title = SUITE_TITLE)
     @AutomationStatus(status = Status.AUTOMATED)
@@ -193,37 +196,37 @@ void produceMessage() {
         softly.assertAll();
     }
 
-    @Issue("Uncomment last assertion after bug https://github.com/provectus/kafka-ui/issues/2778 fix")
+    @Disabled
+    @Issue("https://github.com/provectus/kafka-ui/issues/2778")
     @DisplayName("clear message")
     @Suite(suiteId = SUITE_ID, title = SUITE_TITLE)
     @AutomationStatus(status = Status.AUTOMATED)
     @CaseId(19)
     @Test
     void clearMessage() {
-        naviSideBar
-                .openSideMenu(TOPICS);
-        topicsList
-                .waitUntilScreenReady()
-                .openTopic(TOPIC_FOR_MESSAGES.getName());
-        topicDetails
-                .waitUntilScreenReady()
-                .openDetailsTab(TopicDetails.TopicMenu.OVERVIEW)
-                .clickProduceMessageBtn();
-        produceMessagePanel
-                .waitUntilScreenReady()
-                .setContentFiled(TOPIC_FOR_MESSAGES.getMessageContent())
-                .setKeyField(TOPIC_FOR_MESSAGES.getMessageKey())
-                .submitProduceMessage();
-        topicDetails
-                .waitUntilScreenReady();
-        String messageAmount = topicDetails.MessageCountAmount();
-        assertThat(messageAmount)
-                .withFailMessage("message amount not equals").isEqualTo(topicDetails.MessageCountAmount());
-        topicDetails
-                .openDotMenu()
-                .clickClearMessagesMenu();
-//        assertThat(Integer.toString(Integer.valueOf(messageAmount)-1))
-//                .withFailMessage("message amount not decrease by one").isEqualTo(topicDetails.MessageCountAmount());
+      naviSideBar
+          .openSideMenu(TOPICS);
+      topicsList
+          .waitUntilScreenReady()
+          .openTopic(TOPIC_FOR_MESSAGES.getName());
+      topicDetails
+          .waitUntilScreenReady()
+          .openDetailsTab(TopicDetails.TopicMenu.OVERVIEW)
+          .clickProduceMessageBtn();
+      int messageAmount = topicDetails.getMessageCountAmount();
+      produceMessagePanel
+          .waitUntilScreenReady()
+          .setContentFiled(TOPIC_FOR_MESSAGES.getMessageContent())
+          .setKeyField(TOPIC_FOR_MESSAGES.getMessageKey())
+          .submitProduceMessage();
+      topicDetails
+          .waitUntilScreenReady();
+      Assertions.assertEquals(messageAmount + 1, topicDetails.getMessageCountAmount(), "getMessageCountAmount()");
+      topicDetails
+          .openDotMenu()
+          .clickClearMessagesMenu()
+          .waitUntilScreenReady();
+      Assertions.assertEquals(0, topicDetails.getMessageCountAmount(), "getMessageCountAmount()");
     }
 
     @DisplayName("Redirect to consumer from topic profile")
@@ -295,6 +298,30 @@ void checkCustomParametersWithinCreateNewTopic() {
         .as("isValidationMessageCustomParameterValueVisible()").isTrue();
   }
 
+  @Disabled
+  @Issue("https://github.com/provectus/kafka-ui/issues/2819")
+  @DisplayName("Message copy from topic profile")
+  @Suite(suiteId = SUITE_ID, title = SUITE_TITLE)
+  @AutomationStatus(status = Status.AUTOMATED)
+  @CaseId(21)
+  @Test
+  void copyMessageFromTopicProfile() {
+    String topicName = "_schemas";
+    naviSideBar
+        .openSideMenu(TOPICS);
+    topicsList
+        .waitUntilScreenReady()
+        .openTopic(topicName);
+    topicDetails
+        .waitUntilScreenReady()
+        .openDetailsTab(TopicDetails.TopicMenu.MESSAGES)
+        .getRandomMessage()
+        .openDotMenu()
+        .clickCopyToClipBoard();
+    Assertions.assertTrue(topicDetails.isAlertWithMessageVisible(SUCCESS, "Copied successfully!"),
+        "isAlertWithMessageVisible()");
+  }
+
     @AfterAll
     public void afterAll() {
         TOPIC_LIST.forEach(topic -> apiHelper.deleteTopic(CLUSTER_NAME, topic.getName()));