Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#1503] feat(IT): Gravitino web e2e test framework #1689

Merged
merged 3 commits into from
Jan 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ jsqlparser = "4.2"
mysql = "8.0.23"
postgresql = "42.6.0"
immutables-value = "2.10.0"
selenium = "3.141.59"
rauschig = "1.2.0"

protobuf-plugin = "0.9.2"
spotless-plugin = '6.11.0'
Expand Down Expand Up @@ -137,6 +139,8 @@ mysql-driver = { group = "mysql", name = "mysql-connector-java", version.ref = "
postgresql-driver = { group = "org.postgresql", name = "postgresql", version.ref = "postgresql" }
immutables-value = { module = "org.immutables:value", version.ref = "immutables-value" }
commons-cli = { group = "commons-cli", name = "commons-cli", version.ref = "commons-cli" }
selenium = { group = "org.seleniumhq.selenium", name = "selenium-java", version.ref = "selenium" }
rauschig = { group = "org.rauschig", name = "jarchivelib", version.ref = "rauschig" }

[bundles]
log4j = ["slf4j-api", "log4j-slf4j2-impl", "log4j-api", "log4j-core", "log4j-12-api"]
Expand Down
4 changes: 4 additions & 0 deletions integration-test/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@ dependencies {
testImplementation(libs.mysql.driver)
testImplementation(libs.postgresql.driver)
implementation(libs.commons.cli)
testImplementation(libs.selenium)
testImplementation(libs.rauschig)
}

/* Optimizing integration test execution conditions */
Expand Down Expand Up @@ -269,6 +271,7 @@ tasks.test {

// Default use MiniGravitino to run integration tests
environment("GRAVITINO_ROOT_DIR", rootDir.path)
environment("IT_PROJECT_DIR", buildDir.path)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need this environment? can we refer it from GRAVITINO_ROOT_DIR?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoid splice integration-test module path, and hard code System.getenv("GRAVITINO_ROOT_DIR") + '\' + 'integration-test'

environment("HADOOP_USER_NAME", "datastrato")
environment("HADOOP_HOME", "/tmp")
environment("PROJECT_VERSION", version)
Expand Down Expand Up @@ -296,6 +299,7 @@ tasks.test {
} else if (testMode == "embedded") {
environment("GRAVITINO_HOME", rootDir.path)
environment("GRAVITINO_TEST", "true")
environment("GRAVITINO_WAR", rootDir.path + "/web/dist/")
systemProperty("testMode", "embedded")
} else {
throw GradleException("Gravitino integration tests only support [-PtestMode=embedded] or [-PtestMode=deploy] mode!")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
* Copyright 2024 Datastrato Pvt Ltd.
* This software is licensed under the Apache License version 2.
*/
package com.datastrato.gravitino.integration.test.web.ui;

import com.datastrato.gravitino.integration.test.web.ui.utils.AbstractWebIT;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

public class MetalakePageTest extends AbstractWebIT {
@Test
public void homePage() {
String title = driver.getTitle();
Assertions.assertEquals("Gravitino", title);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright 2024 Datastrato Pvt Ltd.
* This software is licensed under the Apache License version 2.
*/
package com.datastrato.gravitino.integration.test.web.ui.utils;

import com.datastrato.gravitino.integration.test.util.AbstractIT;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.openqa.selenium.WebDriver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

// AbstractWebIT provides a WebDriver instance for WEB UI tests.
public class AbstractWebIT extends AbstractIT {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please add some information about it and how we can use it.

Furthermore, add a description of the content subclass that extends it needs to add.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DONE

protected static final Logger LOG = LoggerFactory.getLogger(AbstractWebIT.class);
protected static WebDriver driver;
protected static final long MAX_IMPLICIT_WAIT = 30;

@BeforeAll
public static void startUp() {
driver = WebDriverManager.getWebDriver(getGravitinoServerPort());
}

@AfterAll
public static void tearDown() {
driver.quit();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
/*
* Copyright 2024 Datastrato Pvt Ltd.
* This software is licensed under the Apache License version 2.
*/
package com.datastrato.gravitino.integration.test.web.ui.utils;

import com.datastrato.gravitino.integration.test.util.ITUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.SystemUtils;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.rauschig.jarchivelib.ArchiveFormat;
import org.rauschig.jarchivelib.Archiver;
import org.rauschig.jarchivelib.ArchiverFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

// ChromeWebDriverProvider provides a ChromeDriver instance for WEB UI tests.
public class ChromeWebDriverProvider implements WebDriverProvider {
protected static final Logger LOG = LoggerFactory.getLogger(ChromeWebDriverProvider.class);
private final String chromeDriverBinName;
private final String chromeBinName;
private static final String downLoadDir =
ITUtils.joinPath(System.getenv("IT_PROJECT_DIR"), "chrome");

public ChromeWebDriverProvider() {
if (SystemUtils.IS_OS_MAC_OSX) {
this.chromeDriverBinName = ITUtils.joinPath("chromedriver_mac64", "chromedriver");
this.chromeBinName = ITUtils.joinPath("chrome-mac", "Chromium.app");
} else if (SystemUtils.IS_OS_LINUX) {
this.chromeDriverBinName = ITUtils.joinPath("chromedriver_linux64", "chromedriver");
this.chromeBinName = ITUtils.joinPath("chrome-linux", "chrome");
} else if (SystemUtils.IS_OS_WINDOWS) {
this.chromeDriverBinName = ITUtils.joinPath("chromedriver_win32", "chromedriver.exe");
this.chromeBinName = ITUtils.joinPath("chrome-win", "chrome.exe");
} else {
throw new RuntimeException("Unsupported OS : " + SystemUtils.OS_NAME);
}
}

@Override
public void downloadWebDriver() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why aren't these downloads implemented with a separate script?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think better use JAVA method to download web browser driver.

// Chrome release list in here:
// https://commondatastorage.googleapis.com/chromium-browser-snapshots/index.html
String chromeDownloadURL = "", chromeDriverDownloadURL = "";
String chromeZipFile = "", chromeDriverZipFile = "";
if (SystemUtils.IS_OS_LINUX) {
chromeZipFile = "chrome-linux.zip";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think making chromeDownloadURL and chromeDriverDownloadURL a constant value can be better.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I accepted your suggestion.

chromeDownloadURL =
"https://www.googleapis.com/download/storage/v1/b/chromium-browser-snapshots/o/Linux_x64%2F1000022%2Fchrome-linux.zip?generation=1651778257041732&alt=media";

chromeDriverZipFile = "chromedriver_linux64.zip";
chromeDriverDownloadURL =
"https://www.googleapis.com/download/storage/v1/b/chromium-browser-snapshots/o/Linux_x64%2F1000022%2Fchromedriver_linux64.zip?generation=1651778262235204&alt=media";
} else if (SystemUtils.IS_OS_MAC_OSX) {
chromeZipFile = "chrome-mac.zip";
chromeDownloadURL =
"https://www.googleapis.com/download/storage/v1/b/chromium-browser-snapshots/o/Mac%2F1000022%2Fchrome-mac.zip?generation=1651779420087881&alt=media";

chromeDriverZipFile = "chromedriver_mac64.zip";
chromeDriverDownloadURL =
"https://www.googleapis.com/download/storage/v1/b/chromium-browser-snapshots/o/Mac%2F1000022%2Fchromedriver_mac64.zip?generation=1651779426705083&alt=media";
} else if (SystemUtils.IS_OS_WINDOWS) {
chromeZipFile = "chrome-win.zip";
chromeDownloadURL =
"https://www.googleapis.com/download/storage/v1/b/chromium-browser-snapshots/o/Win_x64%2F1000027%2Fchrome-win.zip?generation=1651780728332948&alt=media";

chromeDriverZipFile = "chromedriver_win32.zip";
chromeDriverDownloadURL =
"https://www.googleapis.com/download/storage/v1/b/chromium-browser-snapshots/o/Win_x64%2F1000027%2Fchromedriver_win32.zip?generation=1651780916599219&alt=media";
}

downloadZipFile(chromeDriverDownloadURL, chromeDriverZipFile, chromeDriverBinName);
downloadZipFile(chromeDownloadURL, chromeZipFile, chromeBinName);

LOG.info("Download the chromeDriver to " + downLoadDir + " successfully.");
}

@Override
public WebDriver createWebDriver() {
System.setProperty(
"webdriver.chrome.driver", ITUtils.joinPath(downLoadDir, chromeDriverBinName));
ChromeOptions chromeOptions = new ChromeOptions();
if (SystemUtils.IS_OS_MAC_OSX) {
chromeOptions.setBinary(
ITUtils.joinPath(downLoadDir, chromeBinName, "Contents", "MacOS", "Chromium"));
} else {
chromeOptions.setBinary(ITUtils.joinPath(downLoadDir, chromeBinName));
chromeOptions.addArguments("--headless");
}

return new ChromeDriver(chromeOptions);
}

private void downloadZipFile(String url, String zipFileName, String fileName) {
File targetFile = new File(downLoadDir, fileName);
if (targetFile.exists()) {
LOG.info("The file " + targetFile.getAbsolutePath() + " already exists, skip download.");
return;
}

try {
LOG.info("Download the zip file from " + url + " to " + downLoadDir);
File chromeDriverZip = new File(ChromeWebDriverProvider.downLoadDir, zipFileName);
FileUtils.copyURLToFile(new URL(url), chromeDriverZip);

LOG.info("Extract the zip file from " + chromeDriverZip.getAbsolutePath());
Archiver archiver = ArchiverFactory.createArchiver(ArchiveFormat.ZIP);
archiver.extract(new File(downLoadDir, zipFileName), new File(downLoadDir));
} catch (IOException e) {
LOG.error(
"Download of: " + url + ", failed in path " + ChromeWebDriverProvider.downLoadDir, e);
throw new RuntimeException(e);
}
LOG.info("Download the zip file from " + url + " to " + downLoadDir + " successfully.");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* Copyright 2024 Datastrato Pvt Ltd.
* This software is licensed under the Apache License version 2.
*/
package com.datastrato.gravitino.integration.test.web.ui.utils;

import java.util.concurrent.TimeUnit;
import org.openqa.selenium.By;
import org.openqa.selenium.Dimension;
import org.openqa.selenium.TimeoutException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.support.ui.ExpectedCondition;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

// WebDriverManager manage the multiple web drivers to run the WEB UI tests.
public class WebDriverManager {
public static final Logger LOG = LoggerFactory.getLogger(WebDriverManager.class);

public static WebDriver getWebDriver(int port) {
String url = String.format("http://127.0.0.1:%d", port);

WebDriverProvider provide = new ChromeWebDriverProvider();
WebDriver driver = generateWebDriver(provide);
driver.manage().timeouts().implicitlyWait(AbstractWebIT.MAX_IMPLICIT_WAIT, TimeUnit.SECONDS);
driver.get(url);

// wait for webpage load compiled.
long start = System.currentTimeMillis();
boolean loaded = false;
while (System.currentTimeMillis() - start < 60 * 1000) {
try {
(new WebDriverWait(driver, 10))
.until(
new ExpectedCondition<Boolean>() {
@Override
public Boolean apply(WebDriver d) {
String gravitinoVersion = d.findElement(By.id("gravitino_version")).getText();
String projectVersion = System.getenv("PROJECT_VERSION");
Copy link
Contributor

@diqiu50 diqiu50 Jan 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PROJECT_VERSION should retrieved by gravitino server api

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But I worry Gravitino server failed to start.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the Gravitino server failed to start. The tester also failed. So it does't matter

return projectVersion.equalsIgnoreCase(gravitinoVersion);
}
});
loaded = true;
break;
} catch (TimeoutException e) {
LOG.info("Exception in WebDriverManager while WebDriverWait ", e);
driver.navigate().to(url);
}
}

if (!loaded) {
throw new RuntimeException("Webpage not loaded in 60 seconds.");
}
Dimension d = new Dimension(1440, 1080);
driver.manage().window().setSize(d);

return driver;
}

private static WebDriver generateWebDriver(WebDriverProvider provide) {
provide.downloadWebDriver();
WebDriver driver = provide.createWebDriver();
return driver;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
* Copyright 2024 Datastrato Pvt Ltd.
* This software is licensed under the Apache License version 2.
*/
package com.datastrato.gravitino.integration.test.web.ui.utils;

import org.openqa.selenium.WebDriver;

// WebDriverProvider is an interface for WEB Driver provider.
public interface WebDriverProvider {

/** Download the browser web driver. */
public void downloadWebDriver();

/** create a new browser web driver */
public WebDriver createWebDriver();
}
6 changes: 5 additions & 1 deletion web/lib/layout/VersionView.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@ import { useAppSelector } from '@/lib/hooks/useStore'
const VersionView = () => {
const store = useAppSelector(state => state.sys)

return <Typography variant='subtitle2'>{store.version}</Typography>
return (
<Typography variant='subtitle2' id='gravitino_version'>
{store.version}
</Typography>
)
}

export default VersionView
Loading