diff --git a/pom.xml b/pom.xml
index 0381d87..76927a9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -42,6 +42,33 @@
webdrivermanager
5.9.2
+
+
+ org.mockito
+ mockito-core
+ 5.5.0
+ test
+
+
+ org.mockito
+ mockito-junit-jupiter
+ 5.5.0
+ test
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 3.1.2
+
+ -Dnet.bytebuddy.experimental=true
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/java/cz/czechitas/automation/ElementFinder.java b/src/main/java/cz/czechitas/automation/ElementFinder.java
index 5e7ab47..1322fc5 100644
--- a/src/main/java/cz/czechitas/automation/ElementFinder.java
+++ b/src/main/java/cz/czechitas/automation/ElementFinder.java
@@ -15,7 +15,7 @@
* @since 1.0.0
*/
@ParametersAreNonnullByDefault
-public final class ElementFinder {
+public class ElementFinder implements ElementFinderInterface {
private final WebDriver driver;
@@ -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)));
}
/**
diff --git a/src/main/java/cz/czechitas/automation/ElementFinderInterface.java b/src/main/java/cz/czechitas/automation/ElementFinderInterface.java
new file mode 100644
index 0000000..bce5cf9
--- /dev/null
+++ b/src/main/java/cz/czechitas/automation/ElementFinderInterface.java
@@ -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);
+}
+
diff --git a/src/test/java/cz/czechitas/automation/ExampleTest.java b/src/test/java/cz/czechitas/automation/ExampleTest.java
index 57dc150..2a5f4d9 100644
--- a/src/test/java/cz/czechitas/automation/ExampleTest.java
+++ b/src/test/java/cz/czechitas/automation/ExampleTest.java
@@ -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) {
diff --git a/src/test/java/cz/czechitas/automation/TestRunner.java b/src/test/java/cz/czechitas/automation/TestRunner.java
index fa47d94..b580907 100644
--- a/src/test/java/cz/czechitas/automation/TestRunner.java
+++ b/src/test/java/cz/czechitas/automation/TestRunner.java
@@ -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
*
@@ -16,6 +20,7 @@
class TestRunner {
private final WebDriver webDriver;
+ private final String baseUrl;
protected final SeleniumActionFacade browser;
protected final AssertionFacade asserter;
@@ -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
@@ -44,4 +61,4 @@ void tearDown() {
throw new RuntimeException(e);
}
}
-}
+}
\ No newline at end of file
diff --git a/src/test/java/cz/czechitas/automation/assertion/ApplicationAssertion.java b/src/test/java/cz/czechitas/automation/assertion/ApplicationAssertion.java
index 87ea7c7..155392c 100644
--- a/src/test/java/cz/czechitas/automation/assertion/ApplicationAssertion.java
+++ b/src/test/java/cz/czechitas/automation/assertion/ApplicationAssertion.java
@@ -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;
@@ -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() {
diff --git a/src/test/java/cz/czechitas/automation/assertion/ApplicationAssertionTest.java b/src/test/java/cz/czechitas/automation/assertion/ApplicationAssertionTest.java
new file mode 100644
index 0000000..bd1c5e8
--- /dev/null
+++ b/src/test/java/cz/czechitas/automation/assertion/ApplicationAssertionTest.java
@@ -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);
+ }
+}
diff --git a/src/test/java/cz/czechitas/automation/assertion/ApplicationDetailAssertion.java b/src/test/java/cz/czechitas/automation/assertion/ApplicationDetailAssertion.java
index b9c72a3..2e96235 100644
--- a/src/test/java/cz/czechitas/automation/assertion/ApplicationDetailAssertion.java
+++ b/src/test/java/cz/czechitas/automation/assertion/ApplicationDetailAssertion.java
@@ -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;
@@ -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);
}
diff --git a/src/test/java/cz/czechitas/automation/assertion/AssertionFacade.java b/src/test/java/cz/czechitas/automation/assertion/AssertionFacade.java
index ea49f74..762b5d7 100644
--- a/src/test/java/cz/czechitas/automation/assertion/AssertionFacade.java
+++ b/src/test/java/cz/czechitas/automation/assertion/AssertionFacade.java
@@ -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;
@@ -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;
@@ -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");
}
diff --git a/src/test/resources/test.properties b/src/test/resources/test.properties
new file mode 100644
index 0000000..4b9ac2f
--- /dev/null
+++ b/src/test/resources/test.properties
@@ -0,0 +1 @@
+app.url=https://team8-2022brno.herokuapp.com/
\ No newline at end of file