Skip to content
This repository has been archived by the owner on Jan 28, 2025. It is now read-only.

Commit

Permalink
Change from htmlunit to selenium
Browse files Browse the repository at this point in the history
I found that htmlunit could not load certain javascript (HtmlUnit/htmlunit#232), so I changed to using selenium to perform E2E testing.
  • Loading branch information
PENEKhun committed Sep 10, 2024
1 parent 37330c1 commit fec279d
Show file tree
Hide file tree
Showing 6 changed files with 253 additions and 143 deletions.
2 changes: 2 additions & 0 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ jsoupVersion=1.18.1
htmlunitVersion=4.4.0
flywayVersion=10.17.1
classgraphVersion=4.8.175
seleniumVersion=4.24.0
webdrivermanagerVersion=5.9.2
# For deploy to maven central
SONATYPE_HOST=CENTRAL_PORTAL
SONATYPE_CONNECT_TIMEOUT_SECONDS=300
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
<html layout:decorate="~{/templates/layout/default_layout.html}"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
>
xmlns:th="http://www.thymeleaf.org">

<head>
<title>configuration</title>
Expand Down Expand Up @@ -97,7 +97,7 @@ <h1 class="mt-4">Error Trace</h1>

<div class="mb-3">
<label for="searchInput">Search</label>
<input type="text" class="form-control" id="searchInput" placeholder="Search for exceptions...">
<input type="search" class="form-control" id="searchInput" placeholder="Search for exceptions...">
</div>

<div class="accordion" id="exceptionAccordion">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@ dependencies {
testImplementation "org.springframework.security:spring-security-test:${springVersion}"

testImplementation "org.springframework.boot:spring-boot-starter-test:${springbootVersion}"
testImplementation group: 'org.htmlunit', name: 'htmlunit', version: '4.4.0'
testImplementation "org.seleniumhq.selenium:htmlunit3-driver:4.23.0"

implementation "org.seleniumhq.selenium:selenium-java:${seleniumVersion}"
implementation "org.seleniumhq.selenium:selenium-chrome-driver:${seleniumVersion}"
implementation "io.github.bonigarcia:webdrivermanager:${webdrivermanagerVersion}"
}

test {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,157 +16,136 @@

package org.easypeelsecurity.agenttests;


import static org.assertj.core.api.Assertions.assertThat;
import static org.openqa.selenium.By.cssSelector;
import static org.openqa.selenium.By.xpath;

import java.util.List;

import java.io.IOException;

import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.server.LocalServerPort;

import org.htmlunit.WebClient;
import org.htmlunit.html.HtmlButton;
import org.htmlunit.html.HtmlForm;
import org.htmlunit.html.HtmlPage;
import org.htmlunit.html.HtmlPasswordInput;
import org.htmlunit.html.HtmlTable;
import org.htmlunit.html.HtmlTableRow;
import org.htmlunit.html.HtmlTextInput;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junitpioneer.jupiter.RetryingTest;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.ExpectedConditions;

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class AgentTests {
final String AGENT_USERNAME = "username";
final String AGENT_PASSWORD = "password";

@LocalServerPort
private int port;

private WebClient webClient;

@BeforeEach
public void setUp() {
webClient = new WebClient();
webClient.getOptions().setThrowExceptionOnScriptError(false);
webClient.getOptions().setCssEnabled(false);
webClient.getOptions().setJavaScriptEnabled(false);
}
import support.SeleniumTestSupport;

@AfterEach
public void tearDown() {
if (webClient != null) {
webClient.close();
}
}
class AgentTests extends SeleniumTestSupport {

@Test
@DisplayName("Redirect to the login page when accessing the home PATH")
void should_redirect_to_loginPage() throws IOException {
HtmlPage page = webClient.getPage("http://localhost:" + port + "/springdog/");
assertThat(page.getUrl()).hasToString(getAgentPath("/login"));
void should_redirect_to_loginPage() {
accessPage("/");
assertThat(currentUrl()).isEqualTo(getAgentPath("/login"));
}


@Test
@DisplayName("Redirect to home page after successful login")
void login_success() throws IOException {
AgentTestHelper agent = new AgentTestHelper();
HtmlPage resultPage = agent.login(AGENT_USERNAME, AGENT_PASSWORD);

assertThat(resultPage.getUrl()).hasToString(getAgentPath(""));
assertThat(resultPage.asXml()).contains("Dashboard", "Total Endpoints");
void login_success() throws InterruptedException {
withLogin();
assertThat(currentUrl()).isEqualTo(getAgentPath(""));
assertThat(pageSource()).contains("Dashboard", "Total Endpoints");
}

@Test
@DisplayName("Display error message when login fails")
void login_fail() throws IOException {
AgentTestHelper agent = new AgentTestHelper();
HtmlPage resultPage = agent.login("wrong_username", "wrong_password");
void login_fail() throws InterruptedException {
withWrongLogin();

assertThat(resultPage.getUrl()).hasToString(getAgentPath("/login?error"));
assertThat(resultPage.asXml()).contains("Login Failed.");
assertThat(currentUrl()).isEqualTo(getAgentPath("/login?error"));
assertThat(pageSource()).contains("Login Failed.");
}

@Test
void homePage() throws IOException {
AgentTestHelper agent = new AgentTestHelper();
agent.withLogin();
void homePage() throws InterruptedException {
withLogin();

HtmlPage page = webClient.getPage(getAgentPath("/"));
assertThat(page.asXml()).contains("Dashboard", "Total Endpoints");
accessPage("/");
assertThat(pageSource()).contains("Dashboard", "Total Endpoints");
}

@Test
void ratelimit_detail() throws IOException {
AgentTestHelper agent = new AgentTestHelper();
agent.withLogin();

HtmlPage page = webClient.getPage(getAgentPath("/rate-limit"));
HtmlTable table = page.getFirstByXPath("//table[@id='table']");
HtmlTableRow row = table.getRow(1); // Skip the header row
HtmlButton analyzeBtn = row.getCell(4).getFirstByXPath(".//button[@title='Analyze']");
HtmlPage detailPage = analyzeBtn.click();

// then
assertThat(detailPage.asNormalizedText()).contains("""
Path Method Signature HTTP Method Status Actions
/api/hello java.lang.String org.easypeelsecurity.agenttests.ExampleController.hello() GET NOT_CONFIGURED""");
@RetryingTest(5)
void ratelimit_detail() throws InterruptedException {
withLogin();

accessPage("/rate-limit");
WebElement analyzeBtn = accessPageUntilXPath("/rate-limit", "(//button[@title='Analyze'])[1]");
analyzeBtn.click();

String pageText = getDriver().findElement(By.tagName("body")).getText();
assertThat(pageText).contains(
"Endpoint Information\nPath: /api/hello\nEndpoint Method Signature: java.lang.String org.easypeelsecurity.agenttests.ExampleController.hello()\nHTTP Method: GET\nPattern Path: false\nRule Status: NOT_CONFIGURED\nIP Based Rule: false\nPermanent Ban Rule: false\nRequest Limit Count: 0\nTime Limit (seconds): 0\nBan Time (seconds): 0\n");
}

@Test
void ratelimit_read() throws IOException {
AgentTestHelper agent = new AgentTestHelper();
agent.withLogin();
@RetryingTest(5)
void ratelimit_read() throws InterruptedException {
withLogin();

HtmlPage page = webClient.getPage(getAgentPath("/rate-limit"));
HtmlTable table = page.getFirstByXPath("//table[@id='table']");
HtmlTableRow row = table.getRow(1); // Skip the header row
WebElement table =
accessPageUntil("/rate-limit", ExpectedConditions.presenceOfElementLocated(By.id("table")));
WebElement row = table.findElements(By.tagName("tr")).get(1);

// check row
assertThat(row.getCell(0).getTextContent()).isEqualTo("/api/hello");
assertThat(row.getCell(1).getTextContent()).isEqualTo(
List<WebElement> cells = row.findElements(By.tagName("td"));
assertThat(cells.get(0).getText()).isEqualTo("/api/hello");
assertThat(cells.get(1).getText()).isEqualTo(
"java.lang.String org.easypeelsecurity.agenttests.ExampleController.hello()");
assertThat(row.getCell(2).getTextContent()).isEqualTo("GET");
assertThat(row.getCell(3).getTextContent()).contains("NOT_CONFIGURED");
HtmlButton configureButton = row.getCell(4).getFirstByXPath(".//button[@title='Configure']");
assertThat(configureButton).isNotNull();
HtmlButton analyzeButton = row.getCell(4).getFirstByXPath(".//button[@title='Analyze']");
assertThat(analyzeButton).isNotNull();
assertThat(cells.get(2).getText()).isEqualTo("GET");
assertThat(cells.get(3).getText()).contains("NOT_CONFIGURED");
assertThat(cells.get(4).findElement(xpath(".//button[@title='Configure']"))).isNotNull();
assertThat(cells.get(4).findElement(xpath(".//button[@title='Analyze']"))).isNotNull();
}

class AgentTestHelper {
HtmlPage login(String username, String password) throws IOException {
HtmlPage loginPage = webClient.getPage(getAgentPath("login"));

// Get the form
HtmlForm loginForm = loginPage.getForms().get(0);
assertThat(loginForm).isNotNull();

// Fill in the form
HtmlTextInput usernameInput = loginForm.getInputByName("username");
HtmlPasswordInput passwordInput = loginForm.getInputByName("password");
HtmlButton submitButton = loginForm.getFirstByXPath("//button[@type='submit']");
usernameInput.type(username);
passwordInput.type(password);

// Submit
return submitButton.click();
}

void withLogin() throws IOException {
login(AGENT_USERNAME, AGENT_PASSWORD);
}
@Test
@RetryingTest(5)
void errorTraceConfigurationSearchTest() throws InterruptedException {
withLogin();

WebElement searchInput = accessPageUntil("/error-tracing/configuration",
ExpectedConditions.visibilityOfElementLocated(By.id("searchInput")));
assertThat(searchInput).isNotNull();

performSearch("java.io.ObjectStrea", "<mark>java.io.ObjectStrea</mark>mException",
"java.lang.MatchException");
performSearch("MatchException", "java.lang.<mark>MatchException</mark>", "java.io.ObjectStreamException");
performSearch("nullpointer", "java.lang.<mark>NullPointer</mark>Exception",
"java.io.ObjectStreamException");
}

private void performSearch(String searchTerm, String expectedResult, String notExpectedResult) {
WebElement searchInput = getDriver().findElement(By.id("searchInput"));
searchInput.clear();
searchInput.sendKeys(searchTerm);

getWait().until(ExpectedConditions.attributeToBe(searchInput, "value", searchTerm));
List<String> visibleItems = getDriver().findElements(
cssSelector("li.list-group-item.exception-item:not([style*='display: none']) div label"))
.stream()
.map(element -> element.getAttribute("innerHTML"))
.toList();

assertThat(visibleItems).isNotEmpty();
assertThat(visibleItems).contains(expectedResult);
assertThat(visibleItems).doesNotContain(notExpectedResult);
}

private String getAgentPath(String path) {
if (path.startsWith("/")) {
path = path.substring(1);
}
return "http://localhost:" + port + "/springdog/" + path;
@Test
@RetryingTest(5)
void errorTraceConfigurationEnableTest() throws InterruptedException {
withLogin();

WebElement accordionButton =
accessPageUntilCssSelector("/error-tracing/configuration", "h2[id^='heading'] button[type='button']");
accordionButton.click();

getWait().until(
ExpectedConditions.visibilityOfElementLocated(cssSelector("li.list-group-item.exception-item")));
WebElement exceptionClasses =
getDriver().findElements(cssSelector("li.list-group-item.exception-item")).getFirst();
WebElement checkbox = exceptionClasses.findElement(xpath(".//input[@type='checkbox']"));
checkbox.click();
assertThat(checkbox.isSelected()).isFalse();
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -18,35 +18,18 @@

import static org.assertj.core.api.Assertions.assertThat;

import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.server.LocalServerPort;

import org.htmlunit.WebClient;
import org.htmlunit.html.HtmlPage;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class ExampleControllerTest {

@LocalServerPort
private int port;
import support.SeleniumTestSupport;

private WebClient webClient;

@BeforeEach
public void setUp() {
webClient = new WebClient();
webClient.getOptions().setThrowExceptionOnScriptError(false);
webClient.getOptions().setCssEnabled(false);
webClient.getOptions().setJavaScriptEnabled(false);
}
class ExampleControllerTest extends SeleniumTestSupport {

@Test
@DisplayName("Should return hello world")
void shouldReturnHelloWorld() throws Exception {
HtmlPage page = webClient.getPage("http://localhost:" + port + "/api/hello");
assertThat(page.getBody().asNormalizedText()).isEqualTo("Hello, World!");
void shouldReturnHelloWorld() {
accessNotSpringdogPage("/api/hello");

assertThat(pageSource()).contains("Hello, World!");
}
}
Loading

0 comments on commit fec279d

Please sign in to comment.