Skip to content
Open
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
27 changes: 27 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,33 @@
<artifactId>webdrivermanager</artifactId>
<version>5.9.2</version>
</dependency>
<!-- Mockito for unit testing (upgrade to newer 5.x) -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>5.5.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>5.5.0</version>
<scope>test</scope>
</dependency>
<!-- No mockito-inline here; ElementFinder is non-final so mockito-core can mock it -->
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.1.2</version>
<configuration>
<argLine>-Dnet.bytebuddy.experimental=true</argLine>
</configuration>
</plugin>
</plugins>
</build>

</project>
4 changes: 2 additions & 2 deletions src/main/java/cz/czechitas/automation/ElementFinder.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
* @since 1.0.0
*/
@ParametersAreNonnullByDefault
public final class ElementFinder {
public class ElementFinder implements ElementFinderInterface {

private final WebDriver driver;

Expand All @@ -32,7 +32,7 @@ public ElementFinder(WebDriver driver)
*/
@Nonnull
public WebElement findByXPath(String xpathExpression) {
return driver.findElement(By.xpath(Objects.requireNonNull(xpathExpression)));
return driver.findElement(By.xpath(Objects.requireNonNull(xpathExpression)));
}

/**
Expand Down
20 changes: 20 additions & 0 deletions src/main/java/cz/czechitas/automation/ElementFinderInterface.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package cz.czechitas.automation;

import org.openqa.selenium.WebElement;

import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault;

/**
* Used for mocking in testing
*/
@ParametersAreNonnullByDefault
public interface ElementFinderInterface {

@Nonnull
WebElement findByXPath(String xpathExpression);

@Nonnull
WebElement findByCssSelector(String cssSelector);
}

2 changes: 1 addition & 1 deletion src/test/java/cz/czechitas/automation/ExampleTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ void successfulLoginTest() {
asserter.checkIsLoggedIn();
}

// paramertized test - find out what is wrong with this test
// parametrized test - find out what is wrong with this test
@ParameterizedTest()
@ValueSource(strings = {"123456789", "ASDFBVC", "123"})
void icoFieldTest(String icoValue) {
Expand Down
21 changes: 19 additions & 2 deletions src/test/java/cz/czechitas/automation/TestRunner.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
import org.openqa.selenium.WebDriver;
import cz.czechitas.automation.assertion.AssertionFacade;

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

/**
* Base test runner class for low code automation on the {@code https://czechitas-app.kutac.cz/} page
*
Expand All @@ -16,6 +20,7 @@
class TestRunner {

private final WebDriver webDriver;
private final String baseUrl;

protected final SeleniumActionFacade browser;
protected final AssertionFacade asserter;
Expand All @@ -28,11 +33,23 @@ public TestRunner() {
this.browser = new SeleniumActionFacade(webDriver);
this.asserter = new AssertionFacade(webDriver);
this.screenshotExtension = new ScreenshotOnFailExtension(webDriver);

Properties props = new Properties();
try (InputStream in = getClass().getClassLoader().getResourceAsStream("test.properties")) {
if (in != null) {
props.load(in);
}
} catch (IOException e) {
throw new RuntimeException("Failed to load test.properties", e);
}

// System property overrides file property; fallback to a sensible default
this.baseUrl = System.getProperty("app.url", props.getProperty("app.url", "https://team8-2022brno.herokuapp.com/"));
}

@BeforeEach
void setUp() {
webDriver.get("https://team8-2022brno.herokuapp.com/");
webDriver.get(baseUrl);
}

@AfterEach
Expand All @@ -44,4 +61,4 @@ void tearDown() {
throw new RuntimeException(e);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package cz.czechitas.automation.assertion;

import cz.czechitas.automation.ElementFinder;
import cz.czechitas.automation.ElementFinderInterface;

import javax.annotation.ParametersAreNonnullByDefault;
import java.util.Objects;
Expand All @@ -16,15 +16,39 @@
@ParametersAreNonnullByDefault
public final class ApplicationAssertion {

private final ElementFinder elementFinder;
private final ElementFinderInterface elementFinder;

ApplicationAssertion(ElementFinder elementFinder) {
ApplicationAssertion(ElementFinderInterface elementFinder) {
this.elementFinder = Objects.requireNonNull(elementFinder);
}

public void checkColumnExists(String columnName) {
var column = elementFinder.findByXPath("//table[@id='DataTables_Table_0']/thead/tr");
assertThat(column.getText()).contains(columnName);
public void checkColumnExists(String... columnNames) {
Objects.requireNonNull(columnNames);
for (String columnName : columnNames) {
Objects.requireNonNull(columnName);
String xpath = "//table[@id='DataTables_Table_0']/thead/tr/th[normalize-space(.) = " + xpathLiteral(columnName) + "]";
var column = elementFinder.findByXPath(xpath);
assertThat(column.getText()).contains(columnName);
}
}

private static String xpathLiteral(String s) {
if (!s.contains("'")) {
return "'" + s + "'";
}
if (!s.contains("\"" ) ) {
return "\"" + s + "\"";
}
String[] parts = s.split("'");
StringBuilder sb = new StringBuilder("concat(");
for (int i = 0; i < parts.length; i++) {
if (i > 0) {
sb.append(", \"'\", ");
}
sb.append("'").append(parts[i]).append("'");
}
sb.append(")");
return sb.toString();
}

public void checkApplicationsTableIsEmpty() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package cz.czechitas.automation.assertion;

import cz.czechitas.automation.ElementFinderInterface;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.openqa.selenium.WebElement;

import javax.annotation.ParametersAreNonnullByDefault;

import static org.assertj.core.api.Assertions.assertThatCode;
import static org.mockito.Mockito.*;

@ParametersAreNonnullByDefault
@ExtendWith(MockitoExtension.class)
class ApplicationAssertionTest {

@Mock
ElementFinderInterface elementFinder;

private static WebElement mockElementWithText(String text) {
WebElement el = mock(WebElement.class);
when(el.getText()).thenReturn(text);
return el;
}

// Re-implementation of production xpathLiteral for building expected xpath in tests
private static String xpathLiteralForTest(String s) {
if (!s.contains("'")) {
return "'" + s + "'";
}
if (!s.contains("\"")) {
return "\"" + s + "\"";
}
String[] parts = s.split("'");
StringBuilder sb = new StringBuilder("concat(");
for (int i = 0; i < parts.length; i++) {
if (i > 0) {
sb.append(", \"'\", ");
}
sb.append("'").append(parts[i]).append("'");
}
sb.append(")");
return sb.toString();
}

@Test
void checkColumnExists_singleColumn() {
String column = "Name";
String expectedXpath = "//table[@id='DataTables_Table_0']/thead/tr/th[normalize-space(.) = " + xpathLiteralForTest(column) + "]";
doReturn(mockElementWithText(column)).when(elementFinder).findByXPath(expectedXpath);

ApplicationAssertion assertion = new ApplicationAssertion(elementFinder);

assertThatCode(() -> assertion.checkColumnExists(column)).doesNotThrowAnyException();
verify(elementFinder).findByXPath(expectedXpath);
}

@Test
void checkColumnExists_multipleColumns() {
String col1 = "Name";
String col2 = "Email";

String xpath1 = "//table[@id='DataTables_Table_0']/thead/tr/th[normalize-space(.) = " + xpathLiteralForTest(col1) + "]";
String xpath2 = "//table[@id='DataTables_Table_0']/thead/tr/th[normalize-space(.) = " + xpathLiteralForTest(col2) + "]";

doReturn(mockElementWithText(col1)).when(elementFinder).findByXPath(xpath1);
doReturn(mockElementWithText(col2)).when(elementFinder).findByXPath(xpath2);

ApplicationAssertion assertion = new ApplicationAssertion(elementFinder);

assertThatCode(() -> assertion.checkColumnExists(col1, col2)).doesNotThrowAnyException();
verify(elementFinder).findByXPath(xpath1);
verify(elementFinder).findByXPath(xpath2);
}

@Test
void checkColumnExists_columnWithBothQuotes() {
String col = "O'Hara \"Test\"";
String expectedXpath = "//table[@id='DataTables_Table_0']/thead/tr/th[normalize-space(.) = " + xpathLiteralForTest(col) + "]";
doReturn(mockElementWithText(col)).when(elementFinder).findByXPath(expectedXpath);

ApplicationAssertion assertion = new ApplicationAssertion(elementFinder);

assertThatCode(() -> assertion.checkColumnExists(col)).doesNotThrowAnyException();
verify(elementFinder).findByXPath(expectedXpath);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package cz.czechitas.automation.assertion;

import cz.czechitas.automation.ElementFinder;
import cz.czechitas.automation.ElementFinderInterface;

import javax.annotation.ParametersAreNonnullByDefault;
import java.util.Objects;
Expand All @@ -16,9 +16,9 @@
@ParametersAreNonnullByDefault
public final class ApplicationDetailAssertion {

private final ElementFinder elementFinder;
private final ElementFinderInterface elementFinder;

public ApplicationDetailAssertion(ElementFinder elementFinder)
public ApplicationDetailAssertion(ElementFinderInterface elementFinder)
{
this.elementFinder = Objects.requireNonNull(elementFinder);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package cz.czechitas.automation.assertion;

import cz.czechitas.automation.ElementFinder;
import cz.czechitas.automation.ElementFinderInterface;
import org.openqa.selenium.WebDriver;

import javax.annotation.ParametersAreNonnullByDefault;
Expand All @@ -16,7 +17,7 @@
@ParametersAreNonnullByDefault
public final class AssertionFacade {

private final ElementFinder elementFinder;
private final ElementFinderInterface elementFinder;
public final ApplicationAssertion applicationSection;
public final ApplicationDetailAssertion applicationDetailAction;

Expand All @@ -37,12 +38,12 @@ public void checkIsLoggedIn() {
assertThat(loggedInText.getText()).isEqualTo("Přihlášen");
}

public void checkProgrammingSectionPresense() {
public void checkProgrammingSectionPresence() {
var programmingText = elementFinder.findByCssSelector(".main_content .card-img-overlay");
assertThat(programmingText.getText().trim()).isEqualTo("Programování");
}

public void checkRegistrationButtonPresense() {
public void checkRegistrationButtonPresence() {
var registerButton = elementFinder.findByCssSelector(".btn-secondary");
assertThat(registerButton.getText().trim()).isEqualTo("Zaregistrujte se");
}
Expand Down
1 change: 1 addition & 0 deletions src/test/resources/test.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
app.url=https://team8-2022brno.herokuapp.com/