diff --git a/README.md b/README.md index 04c3068..dd569aa 100644 --- a/README.md +++ b/README.md @@ -93,6 +93,7 @@ public interface TestPagePageObject extends PageObjectParent static final String COUNTER_INCREMENT_BUTTON_ID = "counterIncrementButton"; @ActionMoveToAndClick(COUNTER_INCREMENT_BUTTON_ID) + @Pause(value = 500L) TestPagePageObject clickCounterIncrementButton(); @@ -102,11 +103,14 @@ public interface TestPagePageObject extends PageObjectParent @ExtractData(by = io.toolisticon.pogen4selenium.api.By.XPATH, value = DATA_EXTRACTION_FROM_TABLE_XPATH) TestPageTableEntry getFirstTableEntry(); - default String getCounter() { + @ExtractDataValue(by = By.XPATH, value="//fieldset[@name='counter']/span[@id='counter']") + String getCounter(); + + // you can always provide your own methods and logic + default String providedGetCounter() { 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"); @@ -236,6 +240,28 @@ public class TestPageTest { ``` +#### Methods provided by fluent api + +There are some default methods provided by the fluent api: + +##### verify +By using the verify methods it's possible to do check state of elements, i.e. if elements are present or clickable. Expected state is configured in PageObjectElement annotation. If not set explicitely all elements are expected to be present byx default. + +##### doAssertions +It's possible to inline assertions done via your favourite testing tools. +By providing this method it's not necessary to hassle with local variables anymore. + +##### execute +The execute method allows you to do test steps dynamically, like reading data from the web page and doing things based on the extracted data. +It can also be used to switch to another page object type. This can be useful if input data is expected to be validated and should stay on the same page and show an error message. + + +## Best practices + +There are a few things you should consider as best practices + +- Naming convention: Please use specific prefixes for you page object methods. This can be 'do' for all actions and 'get' for reading data. +- Page objects should define just the happy path. Special cases like failing validations can be handled in the unit tests via the execute method(you can map to another page object type in it). ## Example diff --git a/pogen4selenium-api/pom.xml b/pogen4selenium-api/pom.xml index 4876167..9566103 100644 --- a/pogen4selenium-api/pom.xml +++ b/pogen4selenium-api/pom.xml @@ -9,7 +9,7 @@ io.toolisticon.pogen4selenium pogen4selenium - 0.1.0 + 0.2.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 35af24e..9cd4eec 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 @@ -2,7 +2,9 @@ public enum By { ID("id"), - XPATH("xpath"); + XPATH("xpath"), + /** ELEMENT MUST ONLY BE USED TO EXTRACT DATA IN PAGE OBJECTS */ + ELEMENT(""); private final String correspondingByMethodName; 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 index cd21154..1e36d3b 100644 --- a/pogen4selenium-api/src/main/java/io/toolisticon/pogen4selenium/api/ExtractDataValue.java +++ b/pogen4selenium-api/src/main/java/io/toolisticon/pogen4selenium/api/ExtractDataValue.java @@ -29,11 +29,21 @@ String name() default ""; enum Kind{ - TEXT, - ATTRIBUTE, - CSS_VALUE, - TAG_NAME, - ACCESSIBLE_NAME; + TEXT("getText"), + ATTRIBUTE("getAttribute"), + CSS_VALUE("getCssValue"), + TAG_NAME("getTagName"), + ACCESSIBLE_NAME("getAccessibleName"); + + private final String elementMethodName; + + private Kind(String elementMethodName) { + this.elementMethodName = elementMethodName; + } + + public String getElementMethodName() { + return this.elementMethodName; + } } } diff --git a/pogen4selenium-example/pom.xml b/pogen4selenium-example/pom.xml index afe9ebf..e2786cd 100644 --- a/pogen4selenium-example/pom.xml +++ b/pogen4selenium-example/pom.xml @@ -9,7 +9,7 @@ io.toolisticon.pogen4selenium pogen4selenium - 0.1.0 + 0.2.0 pogen4selenium-example @@ -33,7 +33,7 @@ io.toolisticon.pogen4selenium pogen4selenium-api - 0.1.0 + 0.2.0 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 index 39f0076..0b955b2 100644 --- a/pogen4selenium-example/src/main/java/io/toolisticon/pogen4selenium/example/TestPagePageObject.java +++ b/pogen4selenium-example/src/main/java/io/toolisticon/pogen4selenium/example/TestPagePageObject.java @@ -5,26 +5,36 @@ import org.openqa.selenium.WebDriver; import io.toolisticon.pogen4selenium.api.ActionMoveToAndClick; +import io.toolisticon.pogen4selenium.api.ActionWrite; import io.toolisticon.pogen4selenium.api.By; import io.toolisticon.pogen4selenium.api.ExtractData; +import io.toolisticon.pogen4selenium.api.ExtractDataValue; +import io.toolisticon.pogen4selenium.api.ExtractDataValue.Kind; import io.toolisticon.pogen4selenium.api.PageObject; import io.toolisticon.pogen4selenium.api.PageObjectElement; import io.toolisticon.pogen4selenium.api.PageObjectParent; +import io.toolisticon.pogen4selenium.api.Pause; @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.INPUT_FIELD_ID, by = By.ID, value="input_field" ) + static final String INPUT_FIELD_ID = "inputField"; @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(); + TestPagePageObject writeToInputField(@ActionWrite(INPUT_FIELD_ID) String value); + + @ExtractDataValue(by=By.ELEMENT, value = INPUT_FIELD_ID, kind=Kind.ATTRIBUTE, name="value") + String readInputFieldValue(); + + @ActionMoveToAndClick(COUNTER_INCREMENT_BUTTON_ID) + @Pause(value = 500L) + TestPagePageObject clickCounterIncrementButton(); @ExtractData(by = io.toolisticon.pogen4selenium.api.By.XPATH, value = DATA_EXTRACTION_FROM_TABLE_XPATH) List getTableEntries(); @@ -32,11 +42,14 @@ public interface TestPagePageObject extends PageObjectParent @ExtractData(by = io.toolisticon.pogen4selenium.api.By.XPATH, value = DATA_EXTRACTION_FROM_TABLE_XPATH) TestPageTableEntry getFirstTableEntry(); - default String getCounter() { + @ExtractDataValue(by = By.XPATH, value="//fieldset[@name='counter']/span[@id='counter']") + String getCounter(); + + // you can always provide your own methods and logic + default String providedGetCounter() { 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"); 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 index 0dfccfe..16cb6bf 100644 --- a/pogen4selenium-example/src/test/java/io/toolisticon/pogen4selenium/example/TestPageTest.java +++ b/pogen4selenium-example/src/test/java/io/toolisticon/pogen4selenium/example/TestPageTest.java @@ -96,5 +96,17 @@ public void incrementCounterTest() { ; } + @Test + public void writeToAndReadFromInputField() { + + TestPagePageObject.init(webDriver) + .writeToInputField("TEST!!!") + .pause(Duration.ofMillis(200L)) + .doAssertions(e -> { + MatcherAssert.assertThat(e.readInputFieldValue(), Matchers.is("TEST!!!")); + }); + + } + } diff --git a/pogen4selenium-integrationTest/pom.xml b/pogen4selenium-integrationTest/pom.xml index c5b6565..0a7e615 100644 --- a/pogen4selenium-integrationTest/pom.xml +++ b/pogen4selenium-integrationTest/pom.xml @@ -9,7 +9,7 @@ io.toolisticon.pogen4selenium pogen4selenium - 0.1.0 + 0.1.1-SNAPSHOT pogen4selenium-integrationTest diff --git a/pogen4selenium-processor/dependency-reduced-pom.xml b/pogen4selenium-processor/dependency-reduced-pom.xml index aa8359f..92db545 100644 --- a/pogen4selenium-processor/dependency-reduced-pom.xml +++ b/pogen4selenium-processor/dependency-reduced-pom.xml @@ -3,7 +3,7 @@ pogen4selenium io.toolisticon.pogen4selenium - 0.1.0 + 0.1.1-SNAPSHOT 4.0.0 pogen4selenium-processor diff --git a/pogen4selenium-processor/pom.xml b/pogen4selenium-processor/pom.xml index f956a77..62063d6 100644 --- a/pogen4selenium-processor/pom.xml +++ b/pogen4selenium-processor/pom.xml @@ -9,7 +9,7 @@ io.toolisticon.pogen4selenium pogen4selenium - 0.1.0 + 0.2.0 pogen4selenium-processor 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 index b1f9866..c7f6e4a 100644 --- 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 @@ -1,5 +1,34 @@ package io.toolisticon.pogen4selenium.processor.pageobject; +import io.toolisticon.aptk.annotationwrapper.api.CustomCodeMethod; +import io.toolisticon.pogen4selenium.api.By; +import io.toolisticon.pogen4selenium.api.ExtractDataValue; + public class ExtractDataValueWrapperExtension { + + @CustomCodeMethod(ExtractDataValue.class) + public static String getFinalMethodCall(ExtractDataValueWrapper extractDataValueWrapper) { + + String command = ( + extractDataValueWrapper.by() == By.ELEMENT ? + extractDataValueWrapper.value() + "Element" + : "getDriver().findElement(By." + extractDataValueWrapper.by().getCorrespondingByMethodName() + "(\"" + extractDataValueWrapper.value() + "\"))" + ) + + "."; + + switch (extractDataValueWrapper.kind()) { + case ATTRIBUTE: + case CSS_VALUE: { + + return command + extractDataValueWrapper.kind().getElementMethodName() + "(\"" + extractDataValueWrapper.name() + "\");"; + } + default: { + + return command + extractDataValueWrapper.kind().getElementMethodName() + "();"; + } + } + + + } } diff --git a/pogen4selenium-processor/src/main/java/io/toolisticon/pogen4selenium/processor/pageobject/MethodsToImplementHelper.java b/pogen4selenium-processor/src/main/java/io/toolisticon/pogen4selenium/processor/pageobject/MethodsToImplementHelper.java index 814fe02..aaec83c 100644 --- a/pogen4selenium-processor/src/main/java/io/toolisticon/pogen4selenium/processor/pageobject/MethodsToImplementHelper.java +++ b/pogen4selenium-processor/src/main/java/io/toolisticon/pogen4selenium/processor/pageobject/MethodsToImplementHelper.java @@ -63,6 +63,9 @@ public Optional getExtractData() { return Optional.ofNullable(ExtractDataWrapper.wrap(this.executableElementWrapper.unwrap())); } + public Optional getExtractDataValue() { + return Optional.ofNullable(ExtractDataValueWrapper.wrap(this.executableElementWrapper.unwrap())); + } public String getNextImplClassName() { diff --git a/pogen4selenium-processor/src/main/java/io/toolisticon/pogen4selenium/processor/pageobject/package-info.java b/pogen4selenium-processor/src/main/java/io/toolisticon/pogen4selenium/processor/pageobject/package-info.java index b5eaa52..52d9a00 100644 --- a/pogen4selenium-processor/src/main/java/io/toolisticon/pogen4selenium/processor/pageobject/package-info.java +++ b/pogen4selenium-processor/src/main/java/io/toolisticon/pogen4selenium/processor/pageobject/package-info.java @@ -1,8 +1,8 @@ /** * This package contains the seleniumap annotation processor. */ -@AnnotationWrapper(value={PageObject.class, PageObjectElement.class, ActionClick.class, ActionMoveToAndClick.class, ActionWrite.class, ExtractData.class,Pause.class} -,bindCustomCode = {PageObjectWrapperExtension.class, ExtractDataWrapperExtension.class} +@AnnotationWrapper(value={PageObject.class, PageObjectElement.class, ActionClick.class, ActionMoveToAndClick.class, ActionWrite.class, ExtractData.class, ExtractDataValue.class ,Pause.class} +,bindCustomCode = {PageObjectWrapperExtension.class, ExtractDataWrapperExtension.class, ExtractDataValueWrapperExtension.class} ,usePublicVisibility = true) package io.toolisticon.pogen4selenium.processor.pageobject; @@ -11,6 +11,7 @@ import io.toolisticon.pogen4selenium.api.ActionMoveToAndClick; import io.toolisticon.pogen4selenium.api.ActionWrite; import io.toolisticon.pogen4selenium.api.ExtractData; +import io.toolisticon.pogen4selenium.api.ExtractDataValue; 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/PageObject.tpl b/pogen4selenium-processor/src/main/resources/PageObject.tpl index 611997d..e782973 100644 --- a/pogen4selenium-processor/src/main/resources/PageObject.tpl +++ b/pogen4selenium-processor/src/main/resources/PageObject.tpl @@ -64,11 +64,11 @@ 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.getExtractDataValue.isPresent} + return ${method.getExtractDataValue.get.getFinalMethodCall} +!{elseif 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} diff --git a/pogen4selenium-processor/src/test/resources/testcases/pageobject/TestcaseValidUsage.java b/pogen4selenium-processor/src/test/resources/testcases/pageobject/TestcaseValidUsage.java index 0e244fa..50b3208 100644 --- a/pogen4selenium-processor/src/test/resources/testcases/pageobject/TestcaseValidUsage.java +++ b/pogen4selenium-processor/src/test/resources/testcases/pageobject/TestcaseValidUsage.java @@ -3,6 +3,8 @@ import io.toolisticon.cute.PassIn; import io.toolisticon.pogen4selenium.api.ActionClick; import io.toolisticon.pogen4selenium.api.ActionWrite; +import io.toolisticon.pogen4selenium.api.By; +import io.toolisticon.pogen4selenium.api.ExtractDataValue; import io.toolisticon.pogen4selenium.api.PageObject; import io.toolisticon.pogen4selenium.api.PageObjectElement; import io.toolisticon.pogen4selenium.api.PageObjectParent; @@ -26,6 +28,9 @@ public interface LoginPage extends PageObjectParent{ LoginPage writeUserName(@ActionWrite(USERNAME_ID) String username); LoginPage writePassword(@ActionWrite(PASSWORD_ID) String password); + @ExtractDataValue(by=By.XPATH, value="/div", kind=ExtractDataValue.Kind.ATTRIBUTE, name="href") + String getLinkHref(); + @ActionClick(SUBMIT_BUTTON_ID) LoginPage clickSubmitButton(); diff --git a/pom.xml b/pom.xml index a4f876a..824aad4 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 io.toolisticon.pogen4selenium pogen4selenium - 0.1.0 + 0.2.0 pom pogen4selenium Please refer to https://github.com/toolisticon/pogen4selenium