diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index bc31861..26dd72b 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -39,11 +39,18 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v4 + + # Setup correct java version + - name: Setup Java JDK + uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: 17 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v1 + uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -54,7 +61,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v1 + uses: github/codeql-action/autobuild@v3 # âšī¸ Command-line programs to run using the OS shell. # đ https://git.io/JvXDl @@ -68,4 +75,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 + uses: github/codeql-action/analyze@v3 diff --git a/.github/workflows/default.yml b/.github/workflows/default.yml index 4121aad..c6100c3 100644 --- a/.github/workflows/default.yml +++ b/.github/workflows/default.yml @@ -13,28 +13,22 @@ jobs: strategy: matrix: os: [ ubuntu-latest,macos-latest,windows-latest ] - java: [ 8.0.x, 9.0.x, 10.0.x, 11.0.x, 12.0.x, 13.0.x, 14.0.x, 15.0.x, 16.0.x, 17.0.x ] + java: [ '17', '21' ] steps: - name: Checkout Code - uses: actions/checkout@v1 + uses: actions/checkout@v4 - name: Set up JDK ${{ matrix.java }} - uses: actions/setup-java@v1 + uses: actions/setup-java@v4 with: + distribution: 'zulu' java-version: ${{ matrix.java }} + cache: 'maven' - - name: Cache .m2 - uses: actions/cache@v1 - with: - path: ~/.m2/repository - key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} - restore-keys: | - ${{ runner.os }}-maven - name: Prepare mvnw run: chmod +x ./mvnw - name: Build run: ./mvnw clean package - diff --git a/.github/workflows/jacoco.yml b/.github/workflows/jacoco.yml index c9fee89..b053dde 100644 --- a/.github/workflows/jacoco.yml +++ b/.github/workflows/jacoco.yml @@ -13,20 +13,15 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v1 + uses: actions/checkout@v4 - - name: Set up JDK 9 - uses: actions/setup-java@v1 + - name: Set up JDK 17 + uses: actions/setup-java@v4 with: - java-version: 9 + distribution: 'zulu' + java-version: 17 + cache: 'maven' - - name: Cache .m2 - uses: actions/cache@v1 - with: - path: ~/.m2/repository - key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} - restore-keys: | - ${{ runner.os }}-maven - name: Prepare mvnw run: chmod +x ./mvnw @@ -37,5 +32,5 @@ jobs: - name: Upload coverage to Codecov uses: codecov/codecov-action@v2 with: - token: ${{} - files: target/site/jacoco-aggregate/jacoco.xml + token: ${{secrets.CODECOV_TOKEN}} + files: target/site/jacoco-aggregate/jacoco.xml \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6f3ffe4..4615faf 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -11,15 +11,7 @@ jobs: steps: - name: Checkout Code - uses: actions/checkout@v1 - - - name: Cache .m2 - uses: actions/cache@v1 - with: - path: ~/.m2/repository - key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} - restore-keys: | - ${{ runner.os }}-maven + uses: actions/checkout@v4 # Get GPG private key into GPG - name: Import GPG Owner Trust @@ -28,10 +20,12 @@ jobs: run: echo ${{ secrets.GPG_SECRET_KEYS }} | base64 --decode | gpg --import --no-tty --batch --yes # Setup JDK and Maven - - name: Set up JDK 9 - uses: actions/setup-java@v1 + - name: Set up JDK 17 + uses: actions/setup-java@v4 with: - java-version: 9 + distribution: 'zulu' + java-version: 17 + cache: 'maven' server-id: sonatype-nexus-staging server-username: OSS_CENTRAL_USERNAME # env variable for Maven Central server-password: OSS_CENTRAL_PASSWORD # env variable for Maven Central @@ -43,7 +37,7 @@ jobs: run: ./mvnw clean package - name: Deploy a new version to central - run: ./mvnw clean install javadoc:jar source:jar deploy -B -DskipTests -Prelease -Dgpg.keyname=${{} -Dgpg.passphrase="${{}" + run: ./mvnw clean install javadoc:jar source:jar deploy -B -DskipTests -Prelease -Dgpg.keyname=${{secrets.GPG_KEYNAME}} -Dgpg.passphrase="${{secrets.GPG_PASSPHRASE}}" env: OSS_CENTRAL_USERNAME: ${{ secrets.SONATYPE_USERNAME }} - OSS_CENTRAL_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} + OSS_CENTRAL_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} \ No newline at end of file diff --git a/README.md b/README.md index 73b1b10..04c3068 100644 --- a/README.md +++ b/README.md @@ -1,46 +1,254 @@ -# ${rootArtifactId} Annotation Processor +# pogen4selenium - a page object generator 4 selenium + +[![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.toolisticon.pogen4selenium/pogen4selenium/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.toolisticon.pogen4selenium/pogen4selenium) -[![Maven Central](https://maven-badges.herokuapp.com/maven-central/${groupId}/${rootArtifactId}/badge.svg)](https://maven-badges.herokuapp.com/maven-central/${groupId}/${rootArtifactId}) -[![Build Status](https://travis-ci.org/toolisticon/${rootArtifactId}.svg?branch=master)](https://travis-ci.org/toolisticon/${rootArtifactId}) -[![codecov](https://codecov.io/gh/toolisticon/${rootArtifactId}/branch/master/graph/badge.svg)](https://codecov.io/gh/toolisticon/${rootArtifactId}) # Why you should use this project? +Page Objects are a good way to produce a maintainable and reusable codebase to implement automated ui tests with selenium. +Nevertheless those page objects can contain a lot of (selenium related) boilerplate code that is mostly reused via copy and paste. +This project provides processors to generate page object implementations by processing annotations placed on interfaces. +By doing that it drastically increases readability of page objects and reduces time for development. + + # Features -Annotation processor that -... +- generates page object class implementations based on annotated interfaces +- generates extraction data class implementations base on annotated interfaces. +- actions like clicking of elements or writing to input fields can be configured via annotations +- the api enforces creation of a fluent api that improves writing of tests. Doing assertions or executing of custom code is also embedded into this fluent api + +# Restrictions + +The project is still in development, so it currently just supports a few Actions like clicking or writing to input fields. Other Actions will be added in the near future. + +Please create an issue, if you need specific action to be implemented. Usually it will be included shortly after ;) # How does it work? -Just add the ${rootArtifactId} annotation processor dependency to your dependencies +## Project Setup -```xml` +The api lib must be bound as a dependency - for example in maven: +```xml - - - ${groupId} - ${rootArtifactId}-processor - 0.1.0 - provided - + + + io.toolisticon.pogen4selenium + pogen4selenium-api + 0.1.0 + provided + + -`` +``` + +Additionally, you need to declare the annotation processor path in your compiler plugin: + +```xml + + maven-compiler-plugin + + + + + + io.toolisticon.pogen4selenium + pogen4selenium-processor + 0.1.0 + + + + + + +``` + +## Documentation + +### Defining Page Objects +The page object interfaces must be annotated with the *PageObject* annotation and must extend the *PageObjectParent* interface. + +It's possible to add String constants annotated with the *PageObjectElement* annotation for all web elements which are related with the page object. The constant values must be unique inside the interface and will be used in the generated class as variable names for the corresponding elements. The constant names must have "_ID" as suffix. + +Then it's possible to add methods to the interface to declare actions. Those methods must return another page object interface. + +It's also possible to add methods to extract data. Those methods must be annotated with the *ExtractData* annotation and must return an instance or List of a data extraction type (see next section) + +You are able to use default methods to use custom code. + +Our [example website](pogen4selenium-example/src/test/resources/TestPage.html) is just a simple html file. This example demonstrates how to setup the page object: -## Preconditions +```java +@PageObject +public interface TestPagePageObject extends PageObjectParent{ + + static final String DATA_EXTRACTION_FROM_TABLE_XPATH = "//table//tr[contains(@class,'data')]"; + + @PageObjectElement(elementVariableName=TestPagePageObject.INPUT_FIELD_ID, by = By.ID, value="" ) + static final String INPUT_FIELD_ID = "searchField"; + + @PageObjectElement(elementVariableName=TestPagePageObject.COUNTER_INCREMENT_BUTTON_ID, by = By.XPATH, value="//fieldset[@name='counter']/input[@type='button']" ) + static final String COUNTER_INCREMENT_BUTTON_ID = "counterIncrementButton"; + + @ActionMoveToAndClick(COUNTER_INCREMENT_BUTTON_ID) + TestPagePageObject clickCounterIncrementButton(); + + + @ExtractData(by = io.toolisticon.pogen4selenium.api.By.XPATH, value = DATA_EXTRACTION_FROM_TABLE_XPATH) + List getTableEntries(); + + @ExtractData(by = io.toolisticon.pogen4selenium.api.By.XPATH, value = DATA_EXTRACTION_FROM_TABLE_XPATH) + TestPageTableEntry getFirstTableEntry(); + + default String getCounter() { + return getDriver().findElement(org.openqa.selenium.By.xpath("//fieldset[@name='counter']/span[@id='counter']")).getText(); + } + + + // Custom entry point for starting your tests + public static TestPagePageObject init(WebDriver driver) { + driver.get("http://localhost:9090/start"); + return new TestPagePageObjectImpl(driver); + } + +} +``` + + +### Defining Interfaces For Data Extraction + +Interfaces for data extraction must be annotated with the *DataToExtract* annotation. +The methods for reading extracted data values must be annotated with the *DataToExtractValue* annotation. Like with selenium it's possible to use different mechanics to locate the web elements to read the values from. Please make sure to use to prefix xpath expressions with "./" to locate elements relative to the Root element for extraction defined in the page objects extraction method. + +Extracting table data for our small [example](pogen4selenium-example/src/test/resources/TestPage.html): + +```java +@DataObject +public interface TestPageTableEntry { + + @ExtractDataValue(by = By.XPATH, value = "./td[1]") + String name(); + + @ExtractDataValue(by = By.XPATH, value = "./td[2]") + String age(); + + @ExtractDataValue(by = By.XPATH, value = "./td[3]/a", kind = Kind.ATTRIBUTE, name = "href") + String link(); + + @ExtractDataValue(by = By.XPATH, value = "./td[3]/a", kind = Kind.TEXT) + String linkText(); + +} +``` + + +### Writing tests +Writing tests is easy. The fluent api provides a *doAssertions* method that allows you to inline custom assertions done with your favorite unit testing tool. + +```java +public class TestPageTest { + + + private WebDriver webDriver; + private JettyServer jettyServer; + + @Before + public void init() throws Exception{ + + jettyServer = new JettyServer(); + jettyServer.start(); + + webDriver = new EdgeDriver(); + webDriver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10)); + } + + @After + public void cleanup() throws Exception{ + webDriver.quit(); + jettyServer.stop(); + } + + @Test + public void extractDatasetsTest() { + TestPagePageObject.init(webDriver) + .doAssertions(e -> { + + // Do assertions here + List results = e.getTableEntries(); + + MatcherAssert.assertThat(results, Matchers.hasSize(2)); + + MatcherAssert.assertThat(results.getFirst().name(), Matchers.is("Max")); + MatcherAssert.assertThat(results.getFirst().age(), Matchers.is("9")); + MatcherAssert.assertThat(results.getFirst().link(), Matchers.is("https://de.wikipedia.org/wiki/Max_und_Moritz")); + MatcherAssert.assertThat(results.getFirst().linkText(), Matchers.is("Max und Moritz Wikipedia")); + + + MatcherAssert.assertThat(results.get(1).name(), Matchers.is("Moritz")); + MatcherAssert.assertThat(results.get(1).age(), Matchers.is("10")); + MatcherAssert.assertThat(results.get(1).link(), Matchers.is("https://de.wikipedia.org/wiki/Wilhelm_Busch")); + MatcherAssert.assertThat(results.get(1).linkText(), Matchers.is("Wilhelm Busch Wikipedia")); + + + }); + } + + @Test + public void extractFirstDatasetTest() { + TestPagePageObject.init(webDriver) + .doAssertions(e -> { + + // Do assertions here + TestPageTableEntry result = e.getFirstTableEntry(); + + + MatcherAssert.assertThat(result.name(), Matchers.is("Max")); + MatcherAssert.assertThat(result.age(), Matchers.is("9")); + MatcherAssert.assertThat(result.link(), Matchers.is("https://de.wikipedia.org/wiki/Max_und_Moritz")); + MatcherAssert.assertThat(result.linkText(), Matchers.is("Max und Moritz Wikipedia")); + + + + }); + } + + @Test + public void incrementCounterTest() { + TestPagePageObject.init(webDriver) + .doAssertions(e -> { + MatcherAssert.assertThat(e.getCounter(), Matchers.is("1")); + }) + .clickCounterIncrementButton() + .doAssertions(e -> { + MatcherAssert.assertThat(e.getCounter(), Matchers.is("2")); + }) + .clickCounterIncrementButton() + .clickCounterIncrementButton() + .doAssertions(e -> { + MatcherAssert.assertThat(e.getCounter(), Matchers.is("4")); + }); + } + + +} + +``` + ## Example + +Please check the our [example](pogen4selenium-example/) # Contributing We welcome any kind of suggestions and pull requests. -## Building and developing the ${rootArtifactId} annotation processor +## Building and developing the pogen4selenium annotation processor -The ${rootArtifactId} is built using Maven. -A simple import of the pom in your IDE should get you up and running. To build the ${rootArtifactId} on the commandline, just run `mvn` or `mvn clean install` +The pogen4selenium is built using Maven. +A simple import of the pom in your IDE should get you up and running. To build the pogen4selenium on the commandline, just run `mvn` or `mvn clean install` ## Requirements diff --git a/pogen4selenium-api/pom.xml b/pogen4selenium-api/pom.xml index e899177..4876167 100644 --- a/pogen4selenium-api/pom.xml +++ b/pogen4selenium-api/pom.xml @@ -9,7 +9,7 @@ io.toolisticon.pogen4selenium pogen4selenium - 0.0.1 + 0.1.0 pogen4selenium-api diff --git a/pogen4selenium-api/src/main/java/io/toolisticon/pogen4selenium/api/By.java b/pogen4selenium-api/src/main/java/io/toolisticon/pogen4selenium/api/By.java index ba78a43..35af24e 100644 --- a/pogen4selenium-api/src/main/java/io/toolisticon/pogen4selenium/api/By.java +++ b/pogen4selenium-api/src/main/java/io/toolisticon/pogen4selenium/api/By.java @@ -1,6 +1,17 @@ package io.toolisticon.pogen4selenium.api; public enum By { - ID, - XPATH + ID("id"), + XPATH("xpath"); + + private final String correspondingByMethodName; + + By(String correspondingByMethodName) { + this.correspondingByMethodName = correspondingByMethodName; + } + + public String getCorrespondingByMethodName() { + return correspondingByMethodName; + } + } diff --git a/pogen4selenium-api/src/main/java/io/toolisticon/pogen4selenium/api/ReadValue.java b/pogen4selenium-api/src/main/java/io/toolisticon/pogen4selenium/api/DataObject.java similarity index 67% rename from pogen4selenium-api/src/main/java/io/toolisticon/pogen4selenium/api/ReadValue.java rename to pogen4selenium-api/src/main/java/io/toolisticon/pogen4selenium/api/DataObject.java index f6d758c..e74c117 100644 --- a/pogen4selenium-api/src/main/java/io/toolisticon/pogen4selenium/api/ReadValue.java +++ b/pogen4selenium-api/src/main/java/io/toolisticon/pogen4selenium/api/DataObject.java @@ -1,12 +1,14 @@ package io.toolisticon.pogen4selenium.api; +import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) -public @interface ReadValue { - String value(); +@Target(value = {ElementType.TYPE}) +@Documented +public @interface DataObject { + } diff --git a/pogen4selenium-api/src/main/java/io/toolisticon/pogen4selenium/api/ExtractData.java b/pogen4selenium-api/src/main/java/io/toolisticon/pogen4selenium/api/ExtractData.java new file mode 100644 index 0000000..c6accd6 --- /dev/null +++ b/pogen4selenium-api/src/main/java/io/toolisticon/pogen4selenium/api/ExtractData.java @@ -0,0 +1,18 @@ +package io.toolisticon.pogen4selenium.api; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation to extract data relative to passed element located by the annotation. + * This annotation must be used inside a {@link PageObject} annotated interface. + * Method must have a non void return type annotated with {@link DataObject} annotation or a list of it. + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface ExtractData { + By by() default By.XPATH; + String value(); +} diff --git a/pogen4selenium-api/src/main/java/io/toolisticon/pogen4selenium/api/ExtractDataValue.java b/pogen4selenium-api/src/main/java/io/toolisticon/pogen4selenium/api/ExtractDataValue.java new file mode 100644 index 0000000..cd21154 --- /dev/null +++ b/pogen4selenium-api/src/main/java/io/toolisticon/pogen4selenium/api/ExtractDataValue.java @@ -0,0 +1,39 @@ +package io.toolisticon.pogen4selenium.api; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * This annotation can be used inside interfaces annotated with either {@link PageObject} or {@link DataObject}. + * It allows extraction of single data values as String. + * So annotated method must have return type of String. + */ + +@Documented +@Retention(RUNTIME) +@Target(METHOD) +public @interface ExtractDataValue { + + By by() default By.XPATH; + + /** The locator string used together with locator configured in by. Be sure to use './/', if your relative xpath locator string starts with '//', otherwise the whole document will be scanned. */ + String value(); + + Kind kind() default Kind.TEXT; + + /** attribute or property name. */ + String name() default ""; + + enum Kind{ + TEXT, + ATTRIBUTE, + CSS_VALUE, + TAG_NAME, + ACCESSIBLE_NAME; + } + +} diff --git a/pogen4selenium-api/src/main/java/io/toolisticon/pogen4selenium/runtime/DataObjectParentImpl.java b/pogen4selenium-api/src/main/java/io/toolisticon/pogen4selenium/runtime/DataObjectParentImpl.java new file mode 100644 index 0000000..99b2a09 --- /dev/null +++ b/pogen4selenium-api/src/main/java/io/toolisticon/pogen4selenium/runtime/DataObjectParentImpl.java @@ -0,0 +1,72 @@ +package io.toolisticon.pogen4selenium.runtime; + +import org.openqa.selenium.WebElement; + +import io.toolisticon.pogen4selenium.api.By; +import io.toolisticon.pogen4selenium.api.ExtractDataValue; + +public class DataObjectParentImpl { + + private final WebElement relativeParentWebElement; + + protected DataObjectParentImpl(WebElement relativeParentWebElement) { + this.relativeParentWebElement = relativeParentWebElement; + } + + protected WebElement getRelativeParentWebElement() { + return this.relativeParentWebElement; + } + + protected WebElement getValueWebElement(By by, String locatorString) { + + org.openqa.selenium.By relativeLocator = null; + switch(by) { + case ID : + { + relativeLocator = org.openqa.selenium.By.id(locatorString); + break; + } + case XPATH : + { + relativeLocator = org.openqa.selenium.By.xpath(locatorString); + break; + } + } + + if (relativeLocator != null) { + return relativeParentWebElement.findElement(relativeLocator); + } + + return null; + } + + protected String getValue(By by, String locatorString, ExtractDataValue.Kind kind, String name) { + WebElement valueWebElement = this.getValueWebElement(by, locatorString); + + if (valueWebElement != null) { + + switch (kind) { + case ATTRIBUTE: { + return valueWebElement.getAttribute(name); + } + case CSS_VALUE: { + return valueWebElement.getCssValue(name); + } + case TAG_NAME: { + return valueWebElement.getTagName(); + } + case ACCESSIBLE_NAME: { + return valueWebElement.getAccessibleName(); + } + case TEXT: + default: + return valueWebElement.getText(); + } + + + } + + return null; + } + +} diff --git a/pogen4selenium-example/pom.xml b/pogen4selenium-example/pom.xml index 8f46495..afe9ebf 100644 --- a/pogen4selenium-example/pom.xml +++ b/pogen4selenium-example/pom.xml @@ -9,7 +9,7 @@ io.toolisticon.pogen4selenium pogen4selenium - 0.0.1 + 0.1.0 pogen4selenium-example @@ -33,7 +33,7 @@ io.toolisticon.pogen4selenium pogen4selenium-api - 0.0.1 + 0.1.0 @@ -47,6 +47,20 @@ compile + + org.eclipse.jetty + jetty-server + 9.4.3.v20170317 + test + + + + org.eclipse.jetty + jetty-servlet + 9.4.3.v20170317 + test + + diff --git a/pogen4selenium-example/src/main/java/io/toolisticon/pogen4selenium/example/GoogleHomepage.java b/pogen4selenium-example/src/main/java/io/toolisticon/pogen4selenium/example/GoogleHomepage.java deleted file mode 100644 index dfda12c..0000000 --- a/pogen4selenium-example/src/main/java/io/toolisticon/pogen4selenium/example/GoogleHomepage.java +++ /dev/null @@ -1,39 +0,0 @@ -package io.toolisticon.pogen4selenium.example; - -import org.openqa.selenium.WebDriver; - -import io.toolisticon.pogen4selenium.api.ActionClick; -import io.toolisticon.pogen4selenium.api.ActionMoveToAndClick; -import io.toolisticon.pogen4selenium.api.ActionWrite; -import io.toolisticon.pogen4selenium.api.PageObject; -import io.toolisticon.pogen4selenium.api.PageObjectElement; -import io.toolisticon.pogen4selenium.api.PageObjectParent; - -@PageObject -public interface GoogleHomepage extends PageObjectParent{ - - @PageObjectElement(elementVariableName=GoogleHomepage.SEARCH_FIELD_ID, value="//textarea[@name='q']" ) - static final String SEARCH_FIELD_ID = "searchField"; - - @PageObjectElement(elementVariableName=GoogleHomepage.SEARCH_BUTTON_ID, value="//input[@name='btnK']" ) - static final String SEARCH_BUTTON_ID = "searchButton"; - - @PageObjectElement(elementVariableName=GoogleHomepage.ACCEPT_L2AGL_BUTTON_ID, value="//button[@id='L2AGLb']" ) - static final String ACCEPT_L2AGL_BUTTON_ID = "acceptL2aglButton"; - - @ActionMoveToAndClick(ACCEPT_L2AGL_BUTTON_ID) - GoogleHomepage acceptL2Agl(); - - GoogleHomepage enterSearchString(@ActionWrite(SEARCH_FIELD_ID) String searchString); - - @ActionClick(SEARCH_BUTTON_ID) - GoogleHomepage clickSearchButton(); - - - public static GoogleHomepage init(WebDriver driver) { - driver.get("https://www.google.de"); - return new GoogleHomepageImpl(driver); - } - - -} diff --git a/pogen4selenium-example/src/main/java/io/toolisticon/pogen4selenium/example/TestPagePageObject.java b/pogen4selenium-example/src/main/java/io/toolisticon/pogen4selenium/example/TestPagePageObject.java new file mode 100644 index 0000000..39f0076 --- /dev/null +++ b/pogen4selenium-example/src/main/java/io/toolisticon/pogen4selenium/example/TestPagePageObject.java @@ -0,0 +1,46 @@ +package io.toolisticon.pogen4selenium.example; + +import java.util.List; + +import org.openqa.selenium.WebDriver; + +import io.toolisticon.pogen4selenium.api.ActionMoveToAndClick; +import io.toolisticon.pogen4selenium.api.By; +import io.toolisticon.pogen4selenium.api.ExtractData; +import io.toolisticon.pogen4selenium.api.PageObject; +import io.toolisticon.pogen4selenium.api.PageObjectElement; +import io.toolisticon.pogen4selenium.api.PageObjectParent; + +@PageObject +public interface TestPagePageObject extends PageObjectParent{ + + static final String DATA_EXTRACTION_FROM_TABLE_XPATH = "//table//tr[contains(@class,'data')]"; + + @PageObjectElement(elementVariableName=TestPagePageObject.INPUT_FIELD_ID, by = By.ID, value="" ) + static final String INPUT_FIELD_ID = "searchField"; + + @PageObjectElement(elementVariableName=TestPagePageObject.COUNTER_INCREMENT_BUTTON_ID, by = By.XPATH, value="//fieldset[@name='counter']/input[@type='button']" ) + static final String COUNTER_INCREMENT_BUTTON_ID = "counterIncrementButton"; + + @ActionMoveToAndClick(COUNTER_INCREMENT_BUTTON_ID) + TestPagePageObject clickCounterIncrementButton(); + + + @ExtractData(by = io.toolisticon.pogen4selenium.api.By.XPATH, value = DATA_EXTRACTION_FROM_TABLE_XPATH) + List getTableEntries(); + + @ExtractData(by = io.toolisticon.pogen4selenium.api.By.XPATH, value = DATA_EXTRACTION_FROM_TABLE_XPATH) + TestPageTableEntry getFirstTableEntry(); + + default String getCounter() { + return getDriver().findElement(org.openqa.selenium.By.xpath("//fieldset[@name='counter']/span[@id='counter']")).getText(); + } + + + // Custom entry point for starting your tests + public static TestPagePageObject init(WebDriver driver) { + driver.get("http://localhost:9090/start"); + return new TestPagePageObjectImpl(driver); + } + +} diff --git a/pogen4selenium-example/src/main/java/io/toolisticon/pogen4selenium/example/TestPageTableEntry.java b/pogen4selenium-example/src/main/java/io/toolisticon/pogen4selenium/example/TestPageTableEntry.java new file mode 100644 index 0000000..2583406 --- /dev/null +++ b/pogen4selenium-example/src/main/java/io/toolisticon/pogen4selenium/example/TestPageTableEntry.java @@ -0,0 +1,23 @@ +package io.toolisticon.pogen4selenium.example; + +import io.toolisticon.pogen4selenium.api.By; +import io.toolisticon.pogen4selenium.api.DataObject; +import io.toolisticon.pogen4selenium.api.ExtractDataValue; +import io.toolisticon.pogen4selenium.api.ExtractDataValue.Kind; + +@DataObject +public interface TestPageTableEntry { + + @ExtractDataValue(by = By.XPATH, value = "./td[1]") + String name(); + + @ExtractDataValue(by = By.XPATH, value = "./td[2]") + String age(); + + @ExtractDataValue(by = By.XPATH, value = "./td[3]/a", kind = Kind.ATTRIBUTE, name = "href") + String link(); + + @ExtractDataValue(by = By.XPATH, value = "./td[3]/a", kind = Kind.TEXT) + String linkText(); + +} diff --git a/pogen4selenium-example/src/test/java/io/toolisticon/pogen4selenium/example/GoogleSearchTest.java b/pogen4selenium-example/src/test/java/io/toolisticon/pogen4selenium/example/GoogleSearchTest.java deleted file mode 100644 index 43ee442..0000000 --- a/pogen4selenium-example/src/test/java/io/toolisticon/pogen4selenium/example/GoogleSearchTest.java +++ /dev/null @@ -1,42 +0,0 @@ -package io.toolisticon.pogen4selenium.example; - -import java.time.Duration; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.chrome.ChromeDriver; - -public class GoogleSearchTest { - - - private WebDriver webDriver; - - - @Before - public void init() { - webDriver = new ChromeDriver(); - webDriver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10)); - } - - @After - public void cleanup() { - webDriver.close(); - } - - @Test - public void testGoogleSearch() { - GoogleHomepage.init(webDriver) - .acceptL2Agl() - .enterSearchString("toolisticon aptk") - .clickSearchButton() - .doAssertions(e -> { - - // Do assertions here - - }) - ; - } - -} diff --git a/pogen4selenium-example/src/test/java/io/toolisticon/pogen4selenium/example/JettyServer.java b/pogen4selenium-example/src/test/java/io/toolisticon/pogen4selenium/example/JettyServer.java new file mode 100644 index 0000000..794a31b --- /dev/null +++ b/pogen4selenium-example/src/test/java/io/toolisticon/pogen4selenium/example/JettyServer.java @@ -0,0 +1,74 @@ +package io.toolisticon.pogen4selenium.example; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.stream.Collectors; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.eclipse.jetty.server.Connector; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.servlet.ServletHandler; + +public class JettyServer { + + private Server server; + + public void start() throws Exception { + server = new Server(); + ServerConnector connector = new ServerConnector(server); + connector.setPort(9090); + server.setConnectors(new Connector[] {connector}); + + ServletHandler servletHandler = new ServletHandler(); + server.setHandler(servletHandler); + + servletHandler.addServletWithMapping(TestPage.class, "/start"); + + server.start(); + } + + public static class TestPage extends HttpServlet { + + private static final long serialVersionUID = 547644172712833066L; + + final String content; + + public TestPage (){ + String tmpContent = null; + try { + tmpContent = new String(getClass().getResourceAsStream("/TestPage.html").readAllBytes()); + } catch (IOException e) { + e.printStackTrace(); + tmpContent = e.toString(); + } + content = tmpContent; + } + + + + protected void doGet( + HttpServletRequest request, + HttpServletResponse response) + throws ServletException, IOException { + + response.setContentType("text/html"); + response.setStatus(HttpServletResponse.SC_OK); + response.getWriter().println(content); + } + + + + } + + public void stop() throws Exception { + server.stop(); + } + +} diff --git a/pogen4selenium-example/src/test/java/io/toolisticon/pogen4selenium/example/TestPageTest.java b/pogen4selenium-example/src/test/java/io/toolisticon/pogen4selenium/example/TestPageTest.java new file mode 100644 index 0000000..ec918b9 --- /dev/null +++ b/pogen4selenium-example/src/test/java/io/toolisticon/pogen4selenium/example/TestPageTest.java @@ -0,0 +1,100 @@ +package io.toolisticon.pogen4selenium.example; + +import java.time.Duration; +import java.util.List; + +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.edge.EdgeDriver; + +public class TestPageTest { + + + private WebDriver webDriver; + private JettyServer jettyServer; + + @Before + public void init() throws Exception{ + + jettyServer = new JettyServer(); + jettyServer.start(); + + webDriver = new EdgeDriver(); + webDriver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10)); + } + + @After + public void cleanup() throws Exception{ + webDriver.quit(); + jettyServer.stop(); + } + + @Test + public void extractDatasetsTest() { + TestPagePageObject.init(webDriver) + .doAssertions(e -> { + + // Do assertions here + List results = e.getTableEntries(); + + MatcherAssert.assertThat(results, Matchers.hasSize(2)); + + MatcherAssert.assertThat(results.getFirst().name(), Matchers.is("Max")); + MatcherAssert.assertThat(results.getFirst().age(), Matchers.is("9")); + MatcherAssert.assertThat(results.getFirst().link(), Matchers.is("https://de.wikipedia.org/wiki/Max_und_Moritz")); + MatcherAssert.assertThat(results.getFirst().linkText(), Matchers.is("Max und Moritz Wikipedia")); + + + MatcherAssert.assertThat(results.get(1).name(), Matchers.is("Moritz")); + MatcherAssert.assertThat(results.get(1).age(), Matchers.is("10")); + MatcherAssert.assertThat(results.get(1).link(), Matchers.is("https://de.wikipedia.org/wiki/Wilhelm_Busch")); + MatcherAssert.assertThat(results.get(1).linkText(), Matchers.is("Wilhelm Busch Wikipedia")); + + + }) + ; + } + + @Test + public void extractFirstDatasetTest() { + TestPagePageObject.init(webDriver) + .doAssertions(e -> { + + // Do assertions here + TestPageTableEntry result = e.getFirstTableEntry(); + + + MatcherAssert.assertThat(result.name(), Matchers.is("Max")); + MatcherAssert.assertThat(result.age(), Matchers.is("9")); + MatcherAssert.assertThat(result.link(), Matchers.is("https://de.wikipedia.org/wiki/Max_und_Moritz")); + MatcherAssert.assertThat(result.linkText(), Matchers.is("Max und Moritz Wikipedia")); + + + + }) + ; + } + + @Test + public void incrementCounterTest() { + TestPagePageObject.init(webDriver) + .doAssertions(e -> { + MatcherAssert.assertThat(e.getCounter(), Matchers.is("1")); + }).clickCounterIncrementButton() + .doAssertions(e -> { + MatcherAssert.assertThat(e.getCounter(), Matchers.is("2")); + }) + .clickCounterIncrementButton() + .clickCounterIncrementButton() + .doAssertions(e -> { + MatcherAssert.assertThat(e.getCounter(), Matchers.is("4")); + }) + ; + } + + +} diff --git a/pogen4selenium-example/src/test/resources/TestPage.html b/pogen4selenium-example/src/test/resources/TestPage.html new file mode 100644 index 0000000..4a41440 --- /dev/null +++ b/pogen4selenium-example/src/test/resources/TestPage.html @@ -0,0 +1,50 @@ + + + + + + POGEN4SELENIUM TEST PAGE + + + + Write and Read to/from input field + Input field : + + + + + + Click Element and Read single value + : 1 + + + + Data Extraction + + + NAME + AGE + LINK + + + Max + 9 + Max und Moritz Wikipedia + + + Moritz + 10 + Wilhelm Busch Wikipedia + + + + + \ No newline at end of file diff --git a/pogen4selenium-integrationTest/pom.xml b/pogen4selenium-integrationTest/pom.xml index 03c7a18..c5b6565 100644 --- a/pogen4selenium-integrationTest/pom.xml +++ b/pogen4selenium-integrationTest/pom.xml @@ -9,7 +9,7 @@ io.toolisticon.pogen4selenium pogen4selenium - 0.0.1 + 0.1.0 pogen4selenium-integrationTest diff --git a/pogen4selenium-processor/pom.xml b/pogen4selenium-processor/pom.xml index 556b33e..f956a77 100644 --- a/pogen4selenium-processor/pom.xml +++ b/pogen4selenium-processor/pom.xml @@ -9,7 +9,7 @@ io.toolisticon.pogen4selenium pogen4selenium - 0.0.1 + 0.1.0 pogen4selenium-processor diff --git a/pogen4selenium-processor/src/main/java/io/toolisticon/pogen4selenium/processor/datatoextract/DataObjectProcessor.java b/pogen4selenium-processor/src/main/java/io/toolisticon/pogen4selenium/processor/datatoextract/DataObjectProcessor.java new file mode 100644 index 0000000..0598da9 --- /dev/null +++ b/pogen4selenium-processor/src/main/java/io/toolisticon/pogen4selenium/processor/datatoextract/DataObjectProcessor.java @@ -0,0 +1,160 @@ +package io.toolisticon.pogen4selenium.processor.datatoextract; + +import java.io.IOException; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import javax.annotation.processing.Processor; +import javax.annotation.processing.RoundEnvironment; +import javax.lang.model.element.Element; +import javax.lang.model.element.Modifier; +import javax.lang.model.element.TypeElement; + +import io.toolisticon.aptk.compilermessage.api.DeclareCompilerMessage; +import io.toolisticon.aptk.compilermessage.api.DeclareCompilerMessageCodePrefix; +import io.toolisticon.aptk.tools.AbstractAnnotationProcessor; +import io.toolisticon.aptk.tools.FilerUtils; +import io.toolisticon.aptk.tools.InterfaceUtils; +import io.toolisticon.aptk.tools.corematcher.AptkCoreMatchers; +import io.toolisticon.aptk.tools.fluentvalidator.FluentElementValidator; +import io.toolisticon.aptk.tools.generators.SimpleJavaWriter; +import io.toolisticon.aptk.tools.wrapper.ElementWrapper; +import io.toolisticon.aptk.tools.wrapper.TypeElementWrapper; +import io.toolisticon.pogen4selenium.api.DataObject; +import io.toolisticon.pogen4selenium.runtime.DataObjectParentImpl; +import io.toolisticon.spiap.api.SpiService; + +/** + * Annotation Processor for {@link io.toolisticon.pogen4selenium.api.PageObject}. + * + * This demo processor does some validations and creates a class. + */ + +@SpiService(Processor.class) +@DeclareCompilerMessageCodePrefix("DataObject") +public class DataObjectProcessor extends AbstractAnnotationProcessor { + + private final static Set SUPPORTED_ANNOTATIONS = createSupportedAnnotationSet(DataObject.class); + + @Override + public Set getSupportedAnnotationTypes() { + return SUPPORTED_ANNOTATIONS; + } + + @Override + public boolean processAnnotations(Set extends TypeElement> annotations, RoundEnvironment roundEnv) { + + if (!roundEnv.processingOver()) { + // process Services annotation + for (Element element : roundEnv.getElementsAnnotatedWith(DataObject.class)) { + + TypeElementWrapper wrappedTypeElement = TypeElementWrapper.wrap((TypeElement) element); + DataObjectWrapper annotation = DataObjectWrapper.wrap(wrappedTypeElement.unwrap()); + + if (validateUsage(wrappedTypeElement, annotation)) { + processAnnotation(wrappedTypeElement, annotation); + } + + } + + } else { + + // ProcessingOver round + + } + return false; + + } + + void processAnnotation(TypeElementWrapper wrappedTypeElement, DataObjectWrapper annotation) { + + createClass(wrappedTypeElement, annotation); + + } + + boolean validateUsage(TypeElementWrapper wrappedTypeElement, DataObjectWrapper annotation) { + + boolean result = true; + + // First make sure that the annotation is placed on interface and interface extends PageObjectParent + result = result & wrappedTypeElement.validateWithFluentElementValidator().is(AptkCoreMatchers.IS_INTERFACE) + //.applyValidator(AptkCoreMatchers.IS_ASSIGNABLE_TO).hasOneOf(PageObjectParent.class) + .validateAndIssueMessages(); + + // now validate Elements + for (ExtractDataValueWrapper dataStorageElementWrapper : annotation.value()) { + + result = result & FluentElementValidator.createFluentElementValidator(dataStorageElementWrapper._annotatedElement()) + .applyValidator(AptkCoreMatchers.BY_MODIFIER).hasNoneOf(Modifier.STATIC, Modifier.DEFAULT) + .is(AptkCoreMatchers.IS_METHOD) + .applyValidator(AptkCoreMatchers.HAS_NO_PARAMETERS) + .applyValidator(AptkCoreMatchers.BY_RETURN_TYPE).hasOneOf(String.class) + .validateAndIssueMessages(); + + } + + return result; + + } + + /** + * Generates a class. + * + * Example how to use the templating engine. + * + * TODO: remove this + * + * @param wrappedTypeElement The TypeElement representing the annotated class + * @param annotation The PageObject annotation + */ + @DeclareCompilerMessage(code = "ERROR_001", enumValueName = "ERROR_COULD_NOT_CREATE_CLASS", message = "Could not create class ${0} : ${1}") + private void createClass(TypeElementWrapper wrappedTypeElement, DataObjectWrapper annotation) { + + + // Now create class + String packageName = wrappedTypeElement.getPackageName(); + ToImplementHelper toImplementHelper = new ToImplementHelper(wrappedTypeElement); + + + Set methodsToImplement = InterfaceUtils.getMethodsToImplement(wrappedTypeElement) + .stream() + // filter out all default methods + .filter(e -> !e.isDefault()) + // Filter out all static methods + .filter(e -> !e.hasModifiers(Modifier.STATIC)) + // filter out all PageObjectParent methods + .filter(e -> !(ElementWrapper.toTypeElement( + e.getEnclosingElement().get() + ).getQualifiedName().equals(DataObjectParentImpl.class.getCanonicalName())) + ) + .map(MethodsToImplementHelper::new) + .collect(Collectors.toSet()); + + Set imports = new HashSet<>(); + imports.addAll(toImplementHelper.getImports()); + imports.addAll(methodsToImplement.stream().map(MethodsToImplementHelper::getImports) + .flatMap(set -> set.stream()).collect(Collectors.toSet())); + + // Fill Model + Map model = new HashMap(); + model.put("imports", imports); + model.put("toImplementHelper", toImplementHelper); + model.put("packageName", packageName); + model.put("dataToExtract", annotation); + model.put("methodsToImplement",methodsToImplement); + + // create the class + String filePath = packageName + "." + toImplementHelper.getImplementationClassName(); + try { + SimpleJavaWriter javaWriter = FilerUtils.createSourceFile(filePath, wrappedTypeElement.unwrap()); + javaWriter.writeTemplate("/DataToExtract.tpl", model); + javaWriter.close(); + } catch (IOException e) { + wrappedTypeElement.compilerMessage().asError().write(DataObjectProcessorCompilerMessages.ERROR_COULD_NOT_CREATE_CLASS, filePath, e.getMessage()); + } + } + +} diff --git a/pogen4selenium-processor/src/main/java/io/toolisticon/pogen4selenium/processor/datatoextract/DataObjectWrapperExtension.java b/pogen4selenium-processor/src/main/java/io/toolisticon/pogen4selenium/processor/datatoextract/DataObjectWrapperExtension.java new file mode 100644 index 0000000..853a8d7 --- /dev/null +++ b/pogen4selenium-processor/src/main/java/io/toolisticon/pogen4selenium/processor/datatoextract/DataObjectWrapperExtension.java @@ -0,0 +1,22 @@ +package io.toolisticon.pogen4selenium.processor.datatoextract; + +import java.util.List; +import java.util.stream.Collectors; + +import io.toolisticon.aptk.annotationwrapper.api.CustomCodeMethod; +import io.toolisticon.aptk.tools.corematcher.AptkCoreMatchers; +import io.toolisticon.aptk.tools.fluentfilter.FluentElementFilter; +import io.toolisticon.pogen4selenium.api.DataObject; +import io.toolisticon.pogen4selenium.api.ExtractDataValue; + +public class DataObjectWrapperExtension { + + @CustomCodeMethod(DataObject.class) + public static List value(DataObjectWrapper dataObjectWrapper) { + + return FluentElementFilter.createFluentElementFilter(dataObjectWrapper._annotatedElement().getEnclosedElements()) + .applyFilter(AptkCoreMatchers.BY_ANNOTATION).filterByAllOf(ExtractDataValue.class).getResult().stream().map(e -> ExtractDataValueWrapper.wrap(e)).collect(Collectors.toList()); + + } + +} diff --git a/pogen4selenium-processor/src/main/java/io/toolisticon/pogen4selenium/processor/datatoextract/ExtractDataValueWrapperExtension.java b/pogen4selenium-processor/src/main/java/io/toolisticon/pogen4selenium/processor/datatoextract/ExtractDataValueWrapperExtension.java new file mode 100644 index 0000000..57d6105 --- /dev/null +++ b/pogen4selenium-processor/src/main/java/io/toolisticon/pogen4selenium/processor/datatoextract/ExtractDataValueWrapperExtension.java @@ -0,0 +1,27 @@ +package io.toolisticon.pogen4selenium.processor.datatoextract; + +import javax.lang.model.element.VariableElement; + +import io.toolisticon.aptk.annotationwrapper.api.CustomCodeMethod; +import io.toolisticon.aptk.tools.wrapper.ExecutableElementWrapper; +import io.toolisticon.aptk.tools.wrapper.VariableElementWrapper; +import io.toolisticon.pogen4selenium.api.ExtractDataValue; + +public class ExtractDataValueWrapperExtension { + + @CustomCodeMethod(ExtractDataValue.class) + public static String locatorConstantName(ExtractDataValueWrapper extractDataValueWrapper) { + + return VariableElementWrapper.wrap((VariableElement)extractDataValueWrapper._annotatedElement()).getSimpleName().replaceAll("_ID", "_LOCATION_" + extractDataValueWrapper.by().name()); + + } + + @CustomCodeMethod(ExtractDataValue.class) + public static String methodName(ExtractDataValueWrapper extractDataValueWrapper) { + + return ExecutableElementWrapper.wrap(extractDataValueWrapper._annotatedElement()).getSimpleName(); + + } + + +} diff --git a/pogen4selenium-processor/src/main/java/io/toolisticon/pogen4selenium/processor/datatoextract/MethodsToImplementHelper.java b/pogen4selenium-processor/src/main/java/io/toolisticon/pogen4selenium/processor/datatoextract/MethodsToImplementHelper.java new file mode 100644 index 0000000..c2d15c3 --- /dev/null +++ b/pogen4selenium-processor/src/main/java/io/toolisticon/pogen4selenium/processor/datatoextract/MethodsToImplementHelper.java @@ -0,0 +1,42 @@ +package io.toolisticon.pogen4selenium.processor.datatoextract; + +import java.time.Duration; +import java.util.HashSet; +import java.util.Set; + +import org.openqa.selenium.interactions.Actions; + +import io.toolisticon.aptk.tools.wrapper.ExecutableElementWrapper; + +public class MethodsToImplementHelper { + + private final ExecutableElementWrapper executableElementWrapper; + + public MethodsToImplementHelper(ExecutableElementWrapper executableElementWrapper) { + super(); + this.executableElementWrapper = executableElementWrapper; + } + + public String getMethodName() { + return this.executableElementWrapper.getSimpleName(); + } + + public String getByLocator() { + ExtractDataValueWrapper annotation = ExtractDataValueWrapper.wrap(this.executableElementWrapper.unwrap()); + return "By." + annotation.by().name().toLowerCase() + "(\"" + annotation.value() + "\")"; + } + + public Set getImports() { + Set imports = new HashSet<>(); + imports.addAll(this.executableElementWrapper.getImports()); + imports.add(Actions.class.getCanonicalName()); + imports.add(Duration.class.getCanonicalName()); + + return imports; + } + + + + + +} diff --git a/pogen4selenium-processor/src/main/java/io/toolisticon/pogen4selenium/processor/datatoextract/ToImplementHelper.java b/pogen4selenium-processor/src/main/java/io/toolisticon/pogen4selenium/processor/datatoextract/ToImplementHelper.java new file mode 100644 index 0000000..191dd3c --- /dev/null +++ b/pogen4selenium-processor/src/main/java/io/toolisticon/pogen4selenium/processor/datatoextract/ToImplementHelper.java @@ -0,0 +1,73 @@ +package io.toolisticon.pogen4selenium.processor.datatoextract; + +import java.util.HashSet; +import java.util.Set; + +import io.toolisticon.aptk.tools.TypeMirrorWrapper; +import io.toolisticon.aptk.tools.TypeUtils; +import io.toolisticon.aptk.tools.wrapper.TypeElementWrapper; +import io.toolisticon.pogen4selenium.api.PageObjectParent; +import io.toolisticon.pogen4selenium.runtime.PageObjectParentImpl; + +public class ToImplementHelper { + + private final TypeElementWrapper element; + + public ToImplementHelper(TypeElementWrapper element) { + super(); + this.element = element; + } + + public String getExtendsString() { + + return !hasTypeParameters() ? + element.getInterfaces().get(0).getSimpleName() + "Impl <" + this.element.getQualifiedName() + ">" + : element.getInterfaces().get(0).getSimpleName() + "Impl "; + } + + public String getInterfaceTypeVarString() { + return hasTypeParameters() ? "": ""; + } + + public String getTypeVarString() { + return hasTypeParameters() ? ">": ""; + } + + public boolean hasTypeParameters() { + return element.hasTypeParameters(); + } + + public String getInterfaceName() { + return this.element.getQualifiedName(); + } + + public String getImplementationClassName() { + return element.getSimpleName() + "Impl"; + } + + public Set getImports() { + Set result = new HashSet(); + result.add(PageObjectParent.class.getCanonicalName()); + result.add(PageObjectParentImpl.class.getCanonicalName()); + result.add(this.element.getQualifiedName()); + return result; + } + + + + boolean validate() { + + return this.element.getInterfaces().size() == 1 + && ( + TypeUtils.TypeComparison.isErasedTypeEqual(this.element.getInterfaces().get(0).unwrap(),TypeMirrorWrapper.wrap(PageObjectParent.class).unwrap()) + || this.element.getInterfaces().get(0).erasure().isAssignableTo(TypeMirrorWrapper.wrap(PageObjectParent.class)) + ); + } + + + + + + + +} diff --git a/pogen4selenium-processor/src/main/java/io/toolisticon/pogen4selenium/processor/datatoextract/package-info.java b/pogen4selenium-processor/src/main/java/io/toolisticon/pogen4selenium/processor/datatoextract/package-info.java new file mode 100644 index 0000000..d1bc343 --- /dev/null +++ b/pogen4selenium-processor/src/main/java/io/toolisticon/pogen4selenium/processor/datatoextract/package-info.java @@ -0,0 +1,12 @@ +/** + * This package contains the seleniumap annotation processor. + */ +@AnnotationWrapper(value={ DataObject.class, ExtractDataValue.class} +,bindCustomCode = {DataObjectWrapperExtension.class,ExtractDataValueWrapperExtension.class} +,usePublicVisibility = true) +package io.toolisticon.pogen4selenium.processor.datatoextract; + + +import io.toolisticon.aptk.annotationwrapper.api.AnnotationWrapper; +import io.toolisticon.pogen4selenium.api.DataObject; +import io.toolisticon.pogen4selenium.api.ExtractDataValue; \ No newline at end of file diff --git a/pogen4selenium-processor/src/main/java/io/toolisticon/pogen4selenium/processor/pageobject/ExtractDataValueWrapperExtension.java b/pogen4selenium-processor/src/main/java/io/toolisticon/pogen4selenium/processor/pageobject/ExtractDataValueWrapperExtension.java new file mode 100644 index 0000000..b1f9866 --- /dev/null +++ b/pogen4selenium-processor/src/main/java/io/toolisticon/pogen4selenium/processor/pageobject/ExtractDataValueWrapperExtension.java @@ -0,0 +1,5 @@ +package io.toolisticon.pogen4selenium.processor.pageobject; + +public class ExtractDataValueWrapperExtension { + +} diff --git a/pogen4selenium-processor/src/main/java/io/toolisticon/pogen4selenium/processor/pageobject/ExtractDataWrapperExtension.java b/pogen4selenium-processor/src/main/java/io/toolisticon/pogen4selenium/processor/pageobject/ExtractDataWrapperExtension.java new file mode 100644 index 0000000..4b14a9a --- /dev/null +++ b/pogen4selenium-processor/src/main/java/io/toolisticon/pogen4selenium/processor/pageobject/ExtractDataWrapperExtension.java @@ -0,0 +1,65 @@ +package io.toolisticon.pogen4selenium.processor.pageobject; + +import java.util.List; +import java.util.Optional; + +import javax.lang.model.element.ExecutableElement; + +import io.toolisticon.aptk.annotationwrapper.api.CustomCodeMethod; +import io.toolisticon.aptk.compilermessage.api.DeclareCompilerMessage; +import io.toolisticon.aptk.tools.TypeMirrorWrapper; +import io.toolisticon.aptk.tools.wrapper.ExecutableElementWrapper; +import io.toolisticon.aptk.tools.wrapper.TypeElementWrapper; +import io.toolisticon.pogen4selenium.api.DataObject; +import io.toolisticon.pogen4selenium.api.ExtractData; + +public class ExtractDataWrapperExtension { + + @CustomCodeMethod(ExtractData.class) + public static boolean isList(ExtractDataWrapper dataToExtractWrapper) { + TypeMirrorWrapper returnTypeMirror = ExecutableElementWrapper.wrap((ExecutableElement)dataToExtractWrapper._annotatedElement()).getReturnType(); + return returnTypeMirror.erasure().isAssignableTo(List.class); + } + + @CustomCodeMethod(ExtractData.class) + public static boolean isString(ExtractDataWrapper dataToExtractWrapper) { + TypeMirrorWrapper returnTypeMirror = ExecutableElementWrapper.wrap((ExecutableElement)dataToExtractWrapper._annotatedElement()).getReturnType(); + return returnTypeMirror.erasure().isAssignableTo(String.class); + } + + @CustomCodeMethod(ExtractData.class) + public static String getExtractedDataImplName(ExtractDataWrapper dataToExtractWrapper) { + + return getReturnExtractDataTypeMirror(dataToExtractWrapper).getTypeDeclaration() + "Impl"; + + } + + static TypeMirrorWrapper getReturnExtractDataTypeMirror(ExtractDataWrapper dataToExtractWrapper) { + + TypeMirrorWrapper returnTypeMirror = ExecutableElementWrapper.wrap((ExecutableElement)dataToExtractWrapper._annotatedElement()).getReturnType(); + + if (isList(dataToExtractWrapper)) { + return returnTypeMirror.getWrappedComponentType(); + } else { + return returnTypeMirror; + } + + } + + @CustomCodeMethod(ExtractData.class) + @DeclareCompilerMessage(code = "ERROR_002", enumValueName = "ERROR_RETURN_TYPE_MUST_BE_ANNOTATED_WITH_DATATOEXTRACT", message = "Return type of method annotated with {0} must be annotated with {1} or a List with component type annotated with {1}", processorClass = PageObjectProcessor.class) + public static boolean validate(ExtractDataWrapper dataToExtractWrapper) { + + Optional typeElementWrapper = getReturnExtractDataTypeMirror(dataToExtractWrapper).getTypeElement(); + + if (!typeElementWrapper.isPresent() || typeElementWrapper.get().hasAnnotation(DataObject.class) ) { + dataToExtractWrapper.compilerMessage().asError().write(PageObjectProcessorCompilerMessages.ERROR_RETURN_TYPE_MUST_BE_ANNOTATED_WITH_DATATOEXTRACT, ExtractData.class.getSimpleName(), DataObject.class.getSimpleName()); + return false; + } + + + return true; + } + + +} diff --git a/pogen4selenium-processor/src/main/java/io/toolisticon/pogen4selenium/processor/MethodsToImplementHelper.java b/pogen4selenium-processor/src/main/java/io/toolisticon/pogen4selenium/processor/pageobject/MethodsToImplementHelper.java similarity index 77% rename from pogen4selenium-processor/src/main/java/io/toolisticon/pogen4selenium/processor/MethodsToImplementHelper.java rename to pogen4selenium-processor/src/main/java/io/toolisticon/pogen4selenium/processor/pageobject/MethodsToImplementHelper.java index 68d3340..814fe02 100644 --- a/pogen4selenium-processor/src/main/java/io/toolisticon/pogen4selenium/processor/MethodsToImplementHelper.java +++ b/pogen4selenium-processor/src/main/java/io/toolisticon/pogen4selenium/processor/pageobject/MethodsToImplementHelper.java @@ -1,4 +1,4 @@ -package io.toolisticon.pogen4selenium.processor; +package io.toolisticon.pogen4selenium.processor.pageobject; import java.lang.annotation.Annotation; import java.time.Duration; @@ -10,11 +10,11 @@ import org.openqa.selenium.interactions.Actions; +import io.toolisticon.aptk.tools.wrapper.ExecutableElementWrapper; import io.toolisticon.pogen4selenium.api.ActionClick; import io.toolisticon.pogen4selenium.api.ActionMoveToAndClick; import io.toolisticon.pogen4selenium.api.ActionWrite; - -import io.toolisticon.aptk.tools.wrapper.ExecutableElementWrapper; +import io.toolisticon.pogen4selenium.api.ExtractData; public class MethodsToImplementHelper { @@ -59,6 +59,12 @@ public Optional getElementToMoveToAndClick() { : Optional.empty(); } + public Optional getExtractData() { + return Optional.ofNullable(ExtractDataWrapper.wrap(this.executableElementWrapper.unwrap())); + } + + + public String getNextImplClassName() { return this.executableElementWrapper.getReturnType().getSimpleName() + "Impl"; } @@ -73,7 +79,25 @@ public long getAfterPause() { public record ElementsToWrite( String elementVarName,String toWriteParameterName) {}; + + public boolean validate() { + + // default implementations must be ignored at validation + if (executableElementWrapper.isDefault()) { + return true; + } + + boolean validationResult = true; + + // validate methods related with data extraction + if(ExtractDataWrapper.isAnnotated(executableElementWrapper.unwrap())) { + validationResult = validationResult & ExtractDataWrapper.wrap(executableElementWrapper.unwrap()).validate(); + } + return validationResult; + + } + } diff --git a/pogen4selenium-processor/src/main/java/io/toolisticon/pogen4selenium/processor/PageObjectElementWrapperExtension.java b/pogen4selenium-processor/src/main/java/io/toolisticon/pogen4selenium/processor/pageobject/PageObjectElementWrapperExtension.java similarity index 91% rename from pogen4selenium-processor/src/main/java/io/toolisticon/pogen4selenium/processor/PageObjectElementWrapperExtension.java rename to pogen4selenium-processor/src/main/java/io/toolisticon/pogen4selenium/processor/pageobject/PageObjectElementWrapperExtension.java index 0fe4eb7..d807d69 100644 --- a/pogen4selenium-processor/src/main/java/io/toolisticon/pogen4selenium/processor/PageObjectElementWrapperExtension.java +++ b/pogen4selenium-processor/src/main/java/io/toolisticon/pogen4selenium/processor/pageobject/PageObjectElementWrapperExtension.java @@ -1,4 +1,4 @@ -package io.toolisticon.pogen4selenium.processor; +package io.toolisticon.pogen4selenium.processor.pageobject; import javax.lang.model.element.VariableElement; diff --git a/pogen4selenium-processor/src/main/java/io/toolisticon/pogen4selenium/processor/PageObjectProcessor.java b/pogen4selenium-processor/src/main/java/io/toolisticon/pogen4selenium/processor/pageobject/PageObjectProcessor.java similarity index 96% rename from pogen4selenium-processor/src/main/java/io/toolisticon/pogen4selenium/processor/PageObjectProcessor.java rename to pogen4selenium-processor/src/main/java/io/toolisticon/pogen4selenium/processor/pageobject/PageObjectProcessor.java index 8aa6ce1..6755a04 100644 --- a/pogen4selenium-processor/src/main/java/io/toolisticon/pogen4selenium/processor/PageObjectProcessor.java +++ b/pogen4selenium-processor/src/main/java/io/toolisticon/pogen4selenium/processor/pageobject/PageObjectProcessor.java @@ -1,4 +1,4 @@ -package io.toolisticon.pogen4selenium.processor; +package io.toolisticon.pogen4selenium.processor.pageobject; import java.io.IOException; import java.util.HashMap; @@ -71,10 +71,6 @@ public boolean processAnnotations(Set extends TypeElement> annotations, RoundE void processAnnotation(TypeElementWrapper wrappedTypeElement, PageObjectWrapper annotation) { - // ---------------------------------------------------------- - // TODO: replace the following code by your business logic - // ---------------------------------------------------------- - createClass(wrappedTypeElement, annotation); } @@ -100,6 +96,8 @@ boolean validateUsage(TypeElementWrapper wrappedTypeElement, PageObjectWrapper a } + + return result; } diff --git a/pogen4selenium-processor/src/main/java/io/toolisticon/pogen4selenium/processor/PageObjectWrapperExtension.java b/pogen4selenium-processor/src/main/java/io/toolisticon/pogen4selenium/processor/pageobject/PageObjectWrapperExtension.java similarity index 93% rename from pogen4selenium-processor/src/main/java/io/toolisticon/pogen4selenium/processor/PageObjectWrapperExtension.java rename to pogen4selenium-processor/src/main/java/io/toolisticon/pogen4selenium/processor/pageobject/PageObjectWrapperExtension.java index 42674ae..1c8e665 100644 --- a/pogen4selenium-processor/src/main/java/io/toolisticon/pogen4selenium/processor/PageObjectWrapperExtension.java +++ b/pogen4selenium-processor/src/main/java/io/toolisticon/pogen4selenium/processor/pageobject/PageObjectWrapperExtension.java @@ -1,4 +1,4 @@ -package io.toolisticon.pogen4selenium.processor; +package io.toolisticon.pogen4selenium.processor.pageobject; import java.util.List; import java.util.stream.Collectors; diff --git a/pogen4selenium-processor/src/main/java/io/toolisticon/pogen4selenium/processor/ToImplementHelper.java b/pogen4selenium-processor/src/main/java/io/toolisticon/pogen4selenium/processor/pageobject/ToImplementHelper.java similarity index 97% rename from pogen4selenium-processor/src/main/java/io/toolisticon/pogen4selenium/processor/ToImplementHelper.java rename to pogen4selenium-processor/src/main/java/io/toolisticon/pogen4selenium/processor/pageobject/ToImplementHelper.java index 05be005..389fb43 100644 --- a/pogen4selenium-processor/src/main/java/io/toolisticon/pogen4selenium/processor/ToImplementHelper.java +++ b/pogen4selenium-processor/src/main/java/io/toolisticon/pogen4selenium/processor/pageobject/ToImplementHelper.java @@ -1,4 +1,4 @@ -package io.toolisticon.pogen4selenium.processor; +package io.toolisticon.pogen4selenium.processor.pageobject; import java.util.HashSet; import java.util.Set; diff --git a/pogen4selenium-processor/src/main/java/io/toolisticon/pogen4selenium/processor/package-info.java b/pogen4selenium-processor/src/main/java/io/toolisticon/pogen4selenium/processor/pageobject/package-info.java similarity index 72% rename from pogen4selenium-processor/src/main/java/io/toolisticon/pogen4selenium/processor/package-info.java rename to pogen4selenium-processor/src/main/java/io/toolisticon/pogen4selenium/processor/pageobject/package-info.java index e8a6b1f..b5eaa52 100644 --- a/pogen4selenium-processor/src/main/java/io/toolisticon/pogen4selenium/processor/package-info.java +++ b/pogen4selenium-processor/src/main/java/io/toolisticon/pogen4selenium/processor/pageobject/package-info.java @@ -1,15 +1,16 @@ /** * This package contains the seleniumap annotation processor. */ -@AnnotationWrapper(value={PageObject.class, PageObjectElement.class, ActionClick.class, ActionMoveToAndClick.class, ActionWrite.class,Pause.class} -,bindCustomCode = {PageObjectWrapperExtension.class} +@AnnotationWrapper(value={PageObject.class, PageObjectElement.class, ActionClick.class, ActionMoveToAndClick.class, ActionWrite.class, ExtractData.class,Pause.class} +,bindCustomCode = {PageObjectWrapperExtension.class, ExtractDataWrapperExtension.class} ,usePublicVisibility = true) -package io.toolisticon.pogen4selenium.processor; +package io.toolisticon.pogen4selenium.processor.pageobject; import io.toolisticon.pogen4selenium.api.ActionClick; import io.toolisticon.pogen4selenium.api.ActionMoveToAndClick; import io.toolisticon.pogen4selenium.api.ActionWrite; +import io.toolisticon.pogen4selenium.api.ExtractData; import io.toolisticon.pogen4selenium.api.PageObject; import io.toolisticon.pogen4selenium.api.PageObjectElement; import io.toolisticon.pogen4selenium.api.Pause; diff --git a/pogen4selenium-processor/src/main/resources/DataToExtract.tpl b/pogen4selenium-processor/src/main/resources/DataToExtract.tpl new file mode 100644 index 0000000..d16755c --- /dev/null +++ b/pogen4selenium-processor/src/main/resources/DataToExtract.tpl @@ -0,0 +1,35 @@ +package ${ packageName }; + +!{for import : imports} +import ${import}; +!{/for} +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.FindBy; +import org.openqa.selenium.support.How; + +import io.toolisticon.pogen4selenium.api.By; +import io.toolisticon.pogen4selenium.api.ExtractDataValue; +import io.toolisticon.pogen4selenium.runtime.DataObjectParentImpl; + +/** + * An empty class. + */ +public class ${ toImplementHelper.implementationClassName } extends DataObjectParentImpl implements ${toImplementHelper.interfaceName}{ + + /** + * Constructor + */ + public ${ toImplementHelper.implementationClassName } (WebElement relativeContentRoot) { + super(relativeContentRoot); + } + + // implement methods +!{for dataToExtractValue : dataToExtract.value} + @Override + public String ${dataToExtractValue.methodName}(){ + return getValue(By.${dataToExtractValue.by}, "${dataToExtractValue.value}", ExtractDataValue.Kind.${dataToExtractValue.kind}, "${dataToExtractValue.name}"); + } +!{/for} + +} diff --git a/pogen4selenium-processor/src/main/resources/PageObject.tpl b/pogen4selenium-processor/src/main/resources/PageObject.tpl index 4399104..611997d 100644 --- a/pogen4selenium-processor/src/main/resources/PageObject.tpl +++ b/pogen4selenium-processor/src/main/resources/PageObject.tpl @@ -3,6 +3,9 @@ package ${ packageName }; !{for import : imports} import ${import}; !{/for} +import java.util.stream.Collectors; + +import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.support.FindBy; @@ -61,8 +64,17 @@ public class ${ toImplementHelper.implementationClassName } ${toImplementHelper. // Move to Element and click new Actions(getDriver()).moveToElement(${method.getElementToMoveToAndClick.get}Element).pause(300).click().build().perform(); !{/if} - +!{if method.getExtractData.isPresent} +!{if method.getExtractData.get.isList} + return getDriver().findElements(By.${method.getExtractData.get.by.correspondingByMethodName}("${method.getExtractData.get.value}")).stream().map( ${method.getExtractData.get.extractedDataImplName}::new).collect(Collectors.toList()); +!{elseif method.getExtractData.get.isString} + return +!{else} + return new ${method.getExtractData.get.extractedDataImplName}(getDriver().findElement(By.${method.getExtractData.get.by.correspondingByMethodName}("${method.getExtractData.get.value}"))); +!{/if} +!{else} return new ${method.getNextImplClassName}(getDriver()).pause(Duration.ofMillis(${method.afterPause}L)); +!{/if} } !{/for} diff --git a/pogen4selenium-processor/src/test/java/io/toolisticon/pogen4selenium/processor/datatoextract/DataToExtractProcessorTest.java b/pogen4selenium-processor/src/test/java/io/toolisticon/pogen4selenium/processor/datatoextract/DataToExtractProcessorTest.java new file mode 100644 index 0000000..c87de03 --- /dev/null +++ b/pogen4selenium-processor/src/test/java/io/toolisticon/pogen4selenium/processor/datatoextract/DataToExtractProcessorTest.java @@ -0,0 +1,45 @@ +package io.toolisticon.pogen4selenium.processor.datatoextract; + +import org.junit.Before; +import org.junit.Test; + +import io.toolisticon.aptk.tools.MessagerUtils; +import io.toolisticon.cute.Cute; +import io.toolisticon.cute.CuteApi; +import io.toolisticon.pogen4selenium.processor.datatoextract.DataObjectProcessor; + + +/** + * Tests of {@link io.toolisticon.pogen4selenium.api.DataObject}. + * + */ + +public class DataToExtractProcessorTest { + + + CuteApi.BlackBoxTestSourceFilesInterface compileTestBuilder; + + @Before + public void init() { + MessagerUtils.setPrintMessageCodes(true); + + compileTestBuilder = Cute + .blackBoxTest() + .given() + .processors(DataObjectProcessor.class); + } + + + @Test + public void test_valid_usage() { + + compileTestBuilder + .andSourceFiles("testcases/datatoextract/TestcaseValidUsage.java") + .whenCompiled() + .thenExpectThat() + .compilationSucceeds() + //.compilationFails() + .executeTest(); + } + +} \ No newline at end of file diff --git a/pogen4selenium-processor/src/test/java/io/toolisticon/pogen4selenium/processor/PageObjectProcessorMessagesTest.java b/pogen4selenium-processor/src/test/java/io/toolisticon/pogen4selenium/processor/pageobject/PageObjectProcessorMessagesTest.java similarity index 91% rename from pogen4selenium-processor/src/test/java/io/toolisticon/pogen4selenium/processor/PageObjectProcessorMessagesTest.java rename to pogen4selenium-processor/src/test/java/io/toolisticon/pogen4selenium/processor/pageobject/PageObjectProcessorMessagesTest.java index f19f7e2..835a7e6 100644 --- a/pogen4selenium-processor/src/test/java/io/toolisticon/pogen4selenium/processor/PageObjectProcessorMessagesTest.java +++ b/pogen4selenium-processor/src/test/java/io/toolisticon/pogen4selenium/processor/pageobject/PageObjectProcessorMessagesTest.java @@ -1,4 +1,4 @@ -package io.toolisticon.pogen4selenium.processor; +package io.toolisticon.pogen4selenium.processor.pageobject; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; diff --git a/pogen4selenium-processor/src/test/java/io/toolisticon/pogen4selenium/processor/PageObjectProcessorTest.java b/pogen4selenium-processor/src/test/java/io/toolisticon/pogen4selenium/processor/pageobject/PageObjectProcessorTest.java similarity index 83% rename from pogen4selenium-processor/src/test/java/io/toolisticon/pogen4selenium/processor/PageObjectProcessorTest.java rename to pogen4selenium-processor/src/test/java/io/toolisticon/pogen4selenium/processor/pageobject/PageObjectProcessorTest.java index a4e0bf1..16f9226 100644 --- a/pogen4selenium-processor/src/test/java/io/toolisticon/pogen4selenium/processor/PageObjectProcessorTest.java +++ b/pogen4selenium-processor/src/test/java/io/toolisticon/pogen4selenium/processor/pageobject/PageObjectProcessorTest.java @@ -1,4 +1,4 @@ -package io.toolisticon.pogen4selenium.processor; +package io.toolisticon.pogen4selenium.processor.pageobject; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; @@ -9,6 +9,7 @@ import io.toolisticon.aptk.tools.MessagerUtils; import io.toolisticon.cute.Cute; import io.toolisticon.cute.CuteApi; +import io.toolisticon.pogen4selenium.processor.pageobject.PageObjectProcessor; /** @@ -37,7 +38,7 @@ public void init() { public void test_valid_usage() { compileTestBuilder - .andSourceFiles("testcases/TestcaseValidUsage.java") + .andSourceFiles("testcases/pageobject/TestcaseValidUsage.java") .whenCompiled() .thenExpectThat() .compilationSucceeds() @@ -50,7 +51,7 @@ public void test_valid_usage() { public void test_readAnnotatedValue() { Cute.unitTest() .when() - .passInElement().fromSourceFile("testcases/TestcaseValidUsage.java") + .passInElement().fromSourceFile("testcases/pageobject/TestcaseValidUsage.java") .intoUnitTest((procEnv,element) ->{ try { diff --git a/pogen4selenium-processor/src/test/resources/testcases/datatoextract/TestcaseValidUsage.java b/pogen4selenium-processor/src/test/resources/testcases/datatoextract/TestcaseValidUsage.java new file mode 100644 index 0000000..cdad06c --- /dev/null +++ b/pogen4selenium-processor/src/test/resources/testcases/datatoextract/TestcaseValidUsage.java @@ -0,0 +1,16 @@ +package io.toolisticon.pogen4selenium.processor.tests; + +import io.toolisticon.cute.PassIn; +import io.toolisticon.pogen4selenium.api.DataObject; +import io.toolisticon.pogen4selenium.api.ExtractDataValue; + +@DataObject +interface TestcaseValidUsage { + + @ExtractDataValue("//input[@name='firstName']") + String firstName(); + + @ExtractDataValue("//input[@name='lastName']") + String lastName(); + +} \ No newline at end of file diff --git a/pogen4selenium-processor/src/test/resources/testcases/TestcaseValidUsage.java b/pogen4selenium-processor/src/test/resources/testcases/pageobject/TestcaseValidUsage.java similarity index 100% rename from pogen4selenium-processor/src/test/resources/testcases/TestcaseValidUsage.java rename to pogen4selenium-processor/src/test/resources/testcases/pageobject/TestcaseValidUsage.java diff --git a/pom.xml b/pom.xml index 3e7737a..f655b98 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 io.toolisticon.pogen4selenium pogen4selenium - 0.0.1 + 0.1.0 pom pogen4selenium Please refer to https://github.com/toolisticon/pogen4selenium @@ -84,6 +84,9 @@ 3.12.1 3.2.5 2.16.2 + + 1.20.0 + 1.6.13 clean install @@ -139,11 +142,7 @@ ${toolisticon.deployment.skip} - - com.spotify - dockerfile-maven - ${maven-docker-plugin.version} - + maven-enforcer-plugin @@ -254,6 +253,73 @@ versions-maven-plugin ${maven-versions-plugin.version} + + + + com.amashchenko.maven.plugin + gitflow-maven-plugin + ${gitflow-maven-plugin.version} + + + master + develop + feature/ + release/ + hotfix/ + support/ + origin + + true + true + false + true + + + + + + org.apache.maven.plugins + maven-gpg-plugin + ${maven-gpg-plugin.version} + + ${gpg.keyname} + + --batch + --yes + --pinentry-mode + loopback + + + + + sign-artifacts + verify + + sign + + + + + + + org.sonatype.plugins + nexus-staging-maven-plugin + ${maven-ne${nexus-staging-maven-plugin.version} + + true + sonatype-nexus-staging + https://oss.sonatype.org/ + + + + default-deploy + deploy + + deploy + + + +