Преглед изворни кода

[e2e] Add Local browser VM option (#3203)

* test commit

* fix BaseTest

* upd global

* upd global

* upd global

* add local browser VM option

* fix TopicsList column header locator
Vlad Senyuta пре 2 година
родитељ
комит
8f6cbffa0d

+ 4 - 17
kafka-ui-e2e-checks/README.md

@@ -6,7 +6,6 @@ This repository is for E2E UI automation.
 
 - [Prerequisites](#prerequisites)
 - [How to install](#how-to-install)
-- [Environment variables](#environment-variables)
 - [How to run checks](#how-to-run-checks)
 - [Reporting](#reporting)
 - [Environments setup](#environments-setup)
@@ -27,22 +26,6 @@ git clone https://github.com/provectus/kafka-ui.git
 cd  kafka-ui-e2e-checks
 docker pull selenoid/vnc:chrome_86.0  
 ```
-### Environment variables
-
-|Name               	                |   Default   | Description
-|---------------------------------------|-------------|---------------------
-|`USE_LOCAL_BROWSER`                    |  `true`     | clear reports dir on startup
-|`CLEAR_REPORTS_DIR`                    |  `true`     | clear reports dir on startup
-|`SHOULD_START_SELENOID`                |  `false`    | starts selenoid container on startup
-|`SELENOID_URL`                         |  `http://localhost:4444/wd/hub`    | URL of remote selenoid instance
-|`BASE_URL`                             |  `http://192.168.1.2:8080/`    | base url for selenide configuration
-|`PIXELS_THRESHOLD`                     |  `200`    | Amount of pixels, that should be different to fail screenshot check
-|`SCREENSHOTS_FOLDER`                   |  `screenshots/`    | folder for keeping reference screenshots
-|`DIFF_SCREENSHOTS_FOLDER`              |  `build/__diff__/`    | folder for keeping  screenshots diffs
-|`ACTUAL_SCREENSHOTS_FOLDER`            |  `build/__actual__/`   | folder for keeping  actual screenshots(during checks)
-|`SHOULD_SAVE_SCREENSHOTS_IF_NOT_EXIST` |  `true`    | folder for keeping  actual screenshots(during checks)
-|`TURN_OFF_SCREENSHOTS`                 |  `false`    | If true, `compareScreenshots` will not fail on different screenshots. Useful for functional debugging on local machine, while preserving golden screenshots made in selenoid
-
 ### How to run checks
 
 1. Run `kafka-ui`: 
@@ -54,6 +37,10 @@ docker-compose -f documentation/compose/e2e-tests.yaml up -d
 ```
 ./mvnw -DQASEIO_API_TOKEN='%s' -pl '!kafka-ui-api' test -Pprod
 ```
+3. To run tests on your local Chrome browser just add next VM option to the Run Configuration
+```
+-Dbrowser=local
+```
 
 ### Reporting
 

+ 1 - 1
kafka-ui-e2e-checks/src/main/java/com/provectus/kafka/ui/pages/topic/TopicsList.java

@@ -15,7 +15,7 @@ import java.util.stream.Stream;
 
 public class TopicsList extends BasePage {
 
-    protected SelenideElement topicListHeader = $x("//*[text()='Topics']");
+    protected SelenideElement topicListHeader = $x("//h1[text()='Topics']");
     protected SelenideElement addTopicBtn = $x("//button[normalize-space(text()) ='Add a Topic']");
     protected SelenideElement searchField = $x("//input[@placeholder='Search by Topic Name']");
     protected SelenideElement showInternalRadioBtn = $x("//input[@name='ShowInternalTopics']");

+ 20 - 26
kafka-ui-e2e-checks/src/main/java/com/provectus/kafka/ui/services/ApiService.java

@@ -1,5 +1,9 @@
 package com.provectus.kafka.ui.services;
 
+import static com.codeborne.selenide.Selenide.sleep;
+import static com.provectus.kafka.ui.settings.BaseSource.BASE_LOCAL_URL;
+import static com.provectus.kafka.ui.utilities.FileUtils.fileToString;
+
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.provectus.kafka.ui.api.ApiClient;
 import com.provectus.kafka.ui.api.api.KafkaConnectApi;
@@ -13,58 +17,48 @@ import com.provectus.kafka.ui.api.model.TopicCreation;
 import com.provectus.kafka.ui.models.Connector;
 import com.provectus.kafka.ui.models.Schema;
 import com.provectus.kafka.ui.models.Topic;
-import com.provectus.kafka.ui.settings.BaseSource;
+import java.util.HashMap;
+import java.util.Map;
 import lombok.SneakyThrows;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.web.reactive.function.client.WebClientResponseException;
 
-import java.util.HashMap;
-import java.util.Map;
-
-import static com.codeborne.selenide.Selenide.sleep;
-import static com.provectus.kafka.ui.utilities.FileUtils.fileToString;
-
 
 @Slf4j
 public class ApiService {
 
-    int partitions = 1;
-    int replicationFactor = 1;
-    String baseURL = BaseSource.BASE_API_URL;
-
-
     @SneakyThrows
     private TopicsApi topicApi() {
-        return new TopicsApi(new ApiClient().setBasePath(baseURL));
+      return new TopicsApi(new ApiClient().setBasePath(BASE_LOCAL_URL));
     }
 
     @SneakyThrows
     private SchemasApi schemaApi() {
-        return new SchemasApi(new ApiClient().setBasePath(baseURL));
+      return new SchemasApi(new ApiClient().setBasePath(BASE_LOCAL_URL));
     }
 
     @SneakyThrows
     private KafkaConnectApi connectorApi() {
-        return new KafkaConnectApi(new ApiClient().setBasePath(baseURL));
+      return new KafkaConnectApi(new ApiClient().setBasePath(BASE_LOCAL_URL));
     }
 
     @SneakyThrows
     private MessagesApi messageApi() {
-        return new MessagesApi(new ApiClient().setBasePath(baseURL));
+      return new MessagesApi(new ApiClient().setBasePath(BASE_LOCAL_URL));
     }
 
     @SneakyThrows
     public void createTopic(String clusterName, String topicName) {
-        TopicCreation topic = new TopicCreation();
-        topic.setName(topicName);
-        topic.setPartitions(partitions);
-        topic.setReplicationFactor(replicationFactor);
-        try {
-            topicApi().createTopic(clusterName, topic).block();
-            sleep(2000);
-        } catch (WebClientResponseException ex) {
-            ex.printStackTrace();
-        }
+      TopicCreation topic = new TopicCreation();
+      topic.setName(topicName);
+      topic.setPartitions(1);
+      topic.setReplicationFactor(1);
+      try {
+        topicApi().createTopic(clusterName, topic).block();
+        sleep(2000);
+      } catch (WebClientResponseException ex) {
+        ex.printStackTrace();
+      }
     }
 
     public void deleteTopic(String clusterName, String topicName) {

+ 14 - 2
kafka-ui-e2e-checks/src/main/java/com/provectus/kafka/ui/settings/BaseSource.java

@@ -1,8 +1,20 @@
 package com.provectus.kafka.ui.settings;
 
+import com.provectus.kafka.ui.settings.configs.Config;
+import org.aeonbits.owner.ConfigFactory;
+
 public abstract class BaseSource {
 
-  public static String BASE_API_URL = System.getProperty("BASE_URL", "http://localhost:8080");
-  public static String BASE_WEB_URL = System.getProperty("BASE_DOCKER_URL", "http://host.testcontainers.internal:8080");
+  private static Config config;
+  public static final String BASE_CONTAINER_URL = "http://host.testcontainers.internal:8080";
+  public static final String BASE_LOCAL_URL = "http://localhost:8080";
   public static final String CLUSTER_NAME = "local";
+  public static final String BROWSER = config().browser();
+
+  private static Config config() {
+    if (config == null) {
+      config = ConfigFactory.create(Config.class, System.getProperties());
+    }
+    return config;
+  }
 }

+ 4 - 0
kafka-ui-e2e-checks/src/main/java/com/provectus/kafka/ui/settings/configs/Config.java

@@ -0,0 +1,4 @@
+package com.provectus.kafka.ui.settings.configs;
+
+public interface Config extends Profiles {
+}

+ 13 - 0
kafka-ui-e2e-checks/src/main/java/com/provectus/kafka/ui/settings/configs/Profiles.java

@@ -0,0 +1,13 @@
+package com.provectus.kafka.ui.settings.configs;
+
+import org.aeonbits.owner.Config;
+
+public interface Profiles extends Config {
+
+  String CONTAINER = "container";
+  String LOCAL = "local";
+
+  @Key("browser")
+  @DefaultValue(CONTAINER)
+  String browser();
+}

+ 67 - 0
kafka-ui-e2e-checks/src/main/java/com/provectus/kafka/ui/settings/drivers/LocalWebDriver.java

@@ -0,0 +1,67 @@
+package com.provectus.kafka.ui.settings.drivers;
+
+import static com.codeborne.selenide.Selenide.clearBrowserCookies;
+import static com.codeborne.selenide.Selenide.clearBrowserLocalStorage;
+import static com.codeborne.selenide.Selenide.open;
+import static com.codeborne.selenide.Selenide.refresh;
+
+import com.codeborne.selenide.Configuration;
+import com.codeborne.selenide.WebDriverRunner;
+import com.codeborne.selenide.logevents.SelenideLogger;
+import io.qameta.allure.Step;
+import io.qameta.allure.selenide.AllureSelenide;
+import org.openqa.selenium.chrome.ChromeOptions;
+
+public abstract class LocalWebDriver {
+
+  private static org.openqa.selenium.WebDriver getWebDriver() {
+    try {
+      return WebDriverRunner.getWebDriver();
+    } catch (IllegalStateException ex) {
+      Configuration.headless = false;
+      Configuration.browser = "chrome";
+      Configuration.browserSize = "1920x1080";
+      /**screenshots and savePageSource config is needed for local debug
+       * optionally can be set as 'false' to not duplicate Allure report
+       */
+      Configuration.screenshots = true;
+      Configuration.savePageSource = true;
+      Configuration.pageLoadTimeout = 120000;
+      Configuration.browserCapabilities = new ChromeOptions()
+          .addArguments("--lang=en_US");
+      open();
+      return WebDriverRunner.getWebDriver();
+    }
+  }
+
+  @Step
+  public static void openUrl(String url) {
+    if (!getWebDriver().getCurrentUrl().equals(url)) {
+      getWebDriver().get(url);
+    }
+  }
+
+  @Step
+  public static void browserInit() {
+    getWebDriver();
+  }
+
+  @Step
+  public static void browserClear() {
+    clearBrowserLocalStorage();
+    clearBrowserCookies();
+    refresh();
+  }
+
+  @Step
+  public static void browserQuit() {
+    getWebDriver().quit();
+  }
+
+  @Step
+  public static void loggerSetup() {
+    SelenideLogger.addListener("AllureSelenide", new AllureSelenide()
+        .screenshots(true)
+        .savePageSource(false));
+  }
+}

+ 55 - 40
kafka-ui-e2e-checks/src/test/java/com/provectus/kafka/ui/base/BaseTest.java

@@ -1,19 +1,22 @@
 package com.provectus.kafka.ui.base;
 
-import static com.codeborne.selenide.Selenide.clearBrowserCookies;
-import static com.codeborne.selenide.Selenide.clearBrowserLocalStorage;
-import static com.codeborne.selenide.Selenide.refresh;
 import static com.provectus.kafka.ui.pages.NaviSideBar.SideMenuOption.TOPICS;
-import static com.provectus.kafka.ui.settings.BaseSource.BASE_WEB_URL;
+import static com.provectus.kafka.ui.settings.BaseSource.BASE_CONTAINER_URL;
+import static com.provectus.kafka.ui.settings.BaseSource.BASE_LOCAL_URL;
+import static com.provectus.kafka.ui.settings.BaseSource.BROWSER;
+import static com.provectus.kafka.ui.settings.configs.Profiles.CONTAINER;
+import static com.provectus.kafka.ui.settings.configs.Profiles.LOCAL;
+import static com.provectus.kafka.ui.settings.drivers.LocalWebDriver.browserClear;
+import static com.provectus.kafka.ui.settings.drivers.LocalWebDriver.browserQuit;
+import static com.provectus.kafka.ui.settings.drivers.LocalWebDriver.loggerSetup;
+import static com.provectus.kafka.ui.settings.drivers.LocalWebDriver.openUrl;
 
 import com.codeborne.selenide.Condition;
 import com.codeborne.selenide.Selenide;
 import com.codeborne.selenide.SelenideElement;
 import com.codeborne.selenide.WebDriverRunner;
 import com.provectus.kafka.ui.utilities.qaseIoUtils.DisplayNameGenerator;
-import io.qameta.allure.Allure;
 import io.qase.api.annotation.Step;
-import java.io.ByteArrayInputStream;
 import java.util.List;
 import lombok.extern.slf4j.Slf4j;
 import org.assertj.core.api.SoftAssertions;
@@ -23,8 +26,6 @@ import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.DisplayNameGeneration;
 import org.openqa.selenium.Dimension;
-import org.openqa.selenium.OutputType;
-import org.openqa.selenium.TakesScreenshot;
 import org.openqa.selenium.chrome.ChromeOptions;
 import org.openqa.selenium.remote.RemoteWebDriver;
 import org.testcontainers.Testcontainers;
@@ -46,53 +47,67 @@ public abstract class BaseTest extends Facade {
 
   @BeforeAll
   public static void start() {
-    DockerImageName image = isARM64()
-        ? DockerImageName.parse(SELENIARM_STANDALONE_CHROMIUM).asCompatibleSubstituteFor(SELENIUM_IMAGE_NAME)
-        : DockerImageName.parse(SELENIUM_IMAGE_NAME);
-    log.info("Using [{}] as image name for chrome", image.getUnversionedPart());
-    webDriverContainer = new BrowserWebDriverContainer<>(image)
-        .withEnv("JAVA_OPTS", "-Dwebdriver.chrome.whitelistedIps=")
-        .withCapabilities(new ChromeOptions()
-            .addArguments("--disable-dev-shm-usage")
-            .addArguments("--disable-gpu")
-            .addArguments("--no-sandbox")
-            .addArguments("--verbose")
-        )
-        .withLogConsumer(new Slf4jLogConsumer(log).withPrefix("[CHROME]: "));
-    try {
-      Testcontainers.exposeHostPorts(8080);
-      log.info("Starting browser container");
-      webDriverContainer.start();
-    } catch (Throwable e) {
-      log.error("Couldn't start a container", e);
+    switch (BROWSER) {
+      case (CONTAINER) -> {
+        DockerImageName image = isARM64()
+            ? DockerImageName.parse(SELENIARM_STANDALONE_CHROMIUM).asCompatibleSubstituteFor(SELENIUM_IMAGE_NAME)
+            : DockerImageName.parse(SELENIUM_IMAGE_NAME);
+        log.info("Using [{}] as image name for chrome", image.getUnversionedPart());
+        webDriverContainer = new BrowserWebDriverContainer<>(image)
+            .withEnv("JAVA_OPTS", "-Dwebdriver.chrome.whitelistedIps=")
+            .withCapabilities(new ChromeOptions()
+                .addArguments("--disable-dev-shm-usage")
+                .addArguments("--disable-gpu")
+                .addArguments("--no-sandbox")
+                .addArguments("--verbose")
+            )
+            .withLogConsumer(new Slf4jLogConsumer(log).withPrefix("[CHROME]: "));
+        try {
+          Testcontainers.exposeHostPorts(8080);
+          log.info("Starting browser container");
+          webDriverContainer.start();
+        } catch (Throwable e) {
+          log.error("Couldn't start a container", e);
+        }
+      }
+      case (LOCAL) -> loggerSetup();
+      default -> throw new IllegalStateException("Unexpected value: " + BROWSER);
     }
   }
 
   @AfterAll
   public static void tearDown() {
-    if (webDriverContainer.isRunning()) {
-      webDriverContainer.close();
-      webDriverContainer.stop();
+    switch (BROWSER) {
+      case (CONTAINER) -> {
+        if (webDriverContainer.isRunning()) {
+          webDriverContainer.close();
+          webDriverContainer.stop();
+        }
+      }
+      case (LOCAL) -> browserQuit();
+      default -> throw new IllegalStateException("Unexpected value: " + BROWSER);
     }
   }
 
   @BeforeEach
   public void beforeMethod() {
-    RemoteWebDriver remoteWebDriver = webDriverContainer.getWebDriver();
-    WebDriverRunner.setWebDriver(remoteWebDriver);
-    remoteWebDriver.manage()
-        .window().setSize(new Dimension(1440, 1024));
-    Selenide.open(BASE_WEB_URL);
+    switch (BROWSER) {
+      case (CONTAINER) -> {
+        RemoteWebDriver remoteWebDriver = webDriverContainer.getWebDriver();
+        WebDriverRunner.setWebDriver(remoteWebDriver);
+        remoteWebDriver.manage()
+            .window().setSize(new Dimension(1440, 1024));
+        Selenide.open(BASE_CONTAINER_URL);
+      }
+      case (LOCAL) -> openUrl(BASE_LOCAL_URL);
+      default -> throw new IllegalStateException("Unexpected value: " + BROWSER);
+    }
     naviSideBar.waitUntilScreenReady();
   }
 
   @AfterEach
   public void afterMethod() {
-    Allure.addAttachment("Screenshot", new ByteArrayInputStream(((TakesScreenshot)
-        webDriverContainer.getWebDriver()).getScreenshotAs(OutputType.BYTES)));
-    clearBrowserLocalStorage();
-    clearBrowserCookies();
-    refresh();
+    browserClear();
   }
 
   @Step