diff --git a/README.md b/README.md index 40a8167..02e2245 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ We use interfaces where is possible, so you can implement your own version of ta ### Quick start -To start the project using Aquality.Selenium framework, you can [download our template BDD project by this link.](https://github.com/aquality-automation/aquality-appium-mobile-java-template) +To start the project using aquality.appium.mobile framework, you can [download our template BDD project by this link.](https://github.com/aquality-automation/aquality-appium-mobile-java-template) Alternatively, you can follow the steps below: diff --git a/pom.xml b/pom.xml index 15e1d18..efe712a 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.github.aquality-automation aquality-appium-mobile - 3.1.2 + 4.0.0 jar Aquality Appium Mobile @@ -172,7 +172,7 @@ com.github.aquality-automation aquality-selenium-core - 2.0.5 + 3.0.0 diff --git a/src/main/java/aquality/appium/mobile/configuration/Configuration.java b/src/main/java/aquality/appium/mobile/configuration/Configuration.java index 4a78722..cac74af 100644 --- a/src/main/java/aquality/appium/mobile/configuration/Configuration.java +++ b/src/main/java/aquality/appium/mobile/configuration/Configuration.java @@ -1,9 +1,6 @@ package aquality.appium.mobile.configuration; -import aquality.selenium.core.configurations.IElementCacheConfiguration; -import aquality.selenium.core.configurations.ILoggerConfiguration; -import aquality.selenium.core.configurations.IRetryConfiguration; -import aquality.selenium.core.configurations.ITimeoutConfiguration; +import aquality.selenium.core.configurations.*; import com.google.inject.Inject; public class Configuration implements IConfiguration { @@ -14,18 +11,20 @@ public class Configuration implements IConfiguration { private final ILoggerConfiguration loggerConfiguration; private final IElementCacheConfiguration elementCacheConfiguration; private final ITouchActionsConfiguration touchActionsConfiguration; + private final IVisualizationConfiguration visualizationConfiguration; @Inject public Configuration(ITimeoutConfiguration timeoutConfiguration, IRetryConfiguration retryConfiguration, IApplicationProfile applicationProfile, ILoggerConfiguration loggerConfiguration, IElementCacheConfiguration elementCacheConfiguration, - ITouchActionsConfiguration touchActionsConfiguration) { + ITouchActionsConfiguration touchActionsConfiguration, IVisualizationConfiguration visualizationConfiguration) { this.timeoutConfiguration = timeoutConfiguration; this.retryConfiguration = retryConfiguration; this.applicationProfile = applicationProfile; this.loggerConfiguration = loggerConfiguration; this.elementCacheConfiguration = elementCacheConfiguration; this.touchActionsConfiguration = touchActionsConfiguration; + this.visualizationConfiguration = visualizationConfiguration; } @Override @@ -57,4 +56,9 @@ public IElementCacheConfiguration getElementCacheConfiguration() { public ITouchActionsConfiguration getTouchActionsConfiguration() { return touchActionsConfiguration; } + + @Override + public IVisualizationConfiguration getVisualizationConfiguration() { + return visualizationConfiguration; + } } \ No newline at end of file diff --git a/src/main/java/aquality/appium/mobile/configuration/IConfiguration.java b/src/main/java/aquality/appium/mobile/configuration/IConfiguration.java index 10369e1..768cf96 100644 --- a/src/main/java/aquality/appium/mobile/configuration/IConfiguration.java +++ b/src/main/java/aquality/appium/mobile/configuration/IConfiguration.java @@ -1,10 +1,10 @@ package aquality.appium.mobile.configuration; -import aquality.selenium.core.configurations.IElementCacheConfiguration; -import aquality.selenium.core.configurations.ILoggerConfiguration; -import aquality.selenium.core.configurations.IRetryConfiguration; -import aquality.selenium.core.configurations.ITimeoutConfiguration; +import aquality.selenium.core.configurations.*; +/** + * Describes tools configuration. + */ public interface IConfiguration { /** @@ -48,4 +48,11 @@ public interface IConfiguration { * @return Configuration of touch actions. */ ITouchActionsConfiguration getTouchActionsConfiguration(); + + /** + * Gets configuration of VisualStateProvider and Dump manager. + * + * @return Visualization configuration. + */ + IVisualizationConfiguration getVisualizationConfiguration(); } \ No newline at end of file diff --git a/src/main/java/aquality/appium/mobile/elements/Element.java b/src/main/java/aquality/appium/mobile/elements/Element.java index f112017..ccebf02 100644 --- a/src/main/java/aquality/appium/mobile/elements/Element.java +++ b/src/main/java/aquality/appium/mobile/elements/Element.java @@ -12,6 +12,7 @@ import aquality.selenium.core.localization.ILocalizationManager; import aquality.selenium.core.localization.ILocalizedLogger; import aquality.selenium.core.utilities.IElementActionRetrier; +import aquality.selenium.core.visualization.IImageComparator; import aquality.selenium.core.waitings.IConditionalWait; import org.openqa.selenium.*; @@ -45,6 +46,11 @@ protected IElementFinder getElementFinder() { return AqualityServices.get(IElementFinder.class); } + @Override + protected IImageComparator getImageComparator() { + return AqualityServices.get(IImageComparator.class); + } + @Override protected IElementCacheConfiguration getElementCacheConfiguration() { return AqualityServices.get(IElementCacheConfiguration.class); diff --git a/src/main/java/aquality/appium/mobile/screens/IScreen.java b/src/main/java/aquality/appium/mobile/screens/IScreen.java index 8531c96..08da550 100644 --- a/src/main/java/aquality/appium/mobile/screens/IScreen.java +++ b/src/main/java/aquality/appium/mobile/screens/IScreen.java @@ -1,10 +1,16 @@ package aquality.appium.mobile.screens; import aquality.selenium.core.elements.interfaces.IElementStateProvider; +import aquality.selenium.core.forms.IForm; +import aquality.selenium.core.visualization.IDumpManager; import org.openqa.selenium.By; -import org.openqa.selenium.Dimension; -public interface IScreen { +import java.awt.*; + +/** + * Defines interface for any UI form. + */ +public interface IScreen extends IForm { /** * Locator for specified screen */ @@ -26,4 +32,13 @@ public interface IScreen { * @return provider to define element's state */ IElementStateProvider state(); + + + /** + * Gets dump manager for the current form that could be used for visualization purposes, + * such as saving and comparing dumps. + * + * @return form's dump manager. + */ + IDumpManager dump(); } diff --git a/src/main/java/aquality/appium/mobile/screens/Screen.java b/src/main/java/aquality/appium/mobile/screens/Screen.java index 0a86a2c..66466e1 100644 --- a/src/main/java/aquality/appium/mobile/screens/Screen.java +++ b/src/main/java/aquality/appium/mobile/screens/Screen.java @@ -1,44 +1,84 @@ package aquality.appium.mobile.screens; import aquality.appium.mobile.application.AqualityServices; +import aquality.appium.mobile.elements.interfaces.IElement; import aquality.appium.mobile.elements.interfaces.IElementFactory; -import aquality.appium.mobile.elements.interfaces.ILabel; +import aquality.selenium.core.configurations.IVisualizationConfiguration; import aquality.selenium.core.elements.interfaces.IElementStateProvider; +import aquality.selenium.core.forms.Form; +import aquality.selenium.core.localization.ILocalizedLogger; import org.openqa.selenium.By; -import org.openqa.selenium.Dimension; -public abstract class Screen implements IScreen { +import java.awt.*; +/** + * Defines base class for any UI form. + */ +public abstract class Screen extends Form implements IScreen { + /** + * Locator for specified form + */ private final By locator; + /** + * Name of specified form + */ private final String name; - private final ILabel screenLabel; + /** + * Screen element defined by its locator and name. + */ + private final IElement screenElement; /** * Constructor with parameters */ protected Screen(By locator, String name) { + super(IElement.class); this.locator = locator; this.name = name; - this.screenLabel = getElementFactory().getLabel(locator, name); + this.screenElement = getElementFactory().getLabel(locator, name); } + @Override public By getLocator() { return locator; } + @Override public String getName() { return name; } + @Override public Dimension getSize() { - return screenLabel.getElement().getSize(); + return screenElement.visual().getSize(); } + @Override public IElementStateProvider state() { - return screenLabel.state(); + return screenElement.state(); + } + + /** + * Gets form element defined by its locator and name. + * Could be used to find child elements relative to form element. + * + * @return form element. + */ + protected IElement getScreenElement() { + return screenElement; } protected IElementFactory getElementFactory(){ return AqualityServices.getElementFactory(); } + + @Override + protected IVisualizationConfiguration getVisualizationConfiguration() { + return AqualityServices.getConfiguration().getVisualizationConfiguration(); + } + + @Override + protected ILocalizedLogger getLocalizedLogger() { + return AqualityServices.getLocalizedLogger(); + } } diff --git a/src/main/resources/settings.json b/src/main/resources/settings.json index f427ceb..71111e0 100644 --- a/src/main/resources/settings.json +++ b/src/main/resources/settings.json @@ -51,5 +51,13 @@ "verticalOffset": 0.2, "horizontalOffset": 0.5 } + }, + "visualization": { + "imageExtension": "png", + "maxFullFileNameLength": 255, + "defaultThreshold": 0.012, + "comparisonWidth": 16, + "comparisonHeight": 16, + "pathToDumps": "./src/test/resources/visualDumps/" } } \ No newline at end of file diff --git a/src/test/java/samples/android/nativeapp/AndroidBasicInteractionsTest.java b/src/test/java/samples/android/nativeapp/AndroidBasicInteractionsTest.java index 15e9d43..8cea72f 100644 --- a/src/test/java/samples/android/nativeapp/AndroidBasicInteractionsTest.java +++ b/src/test/java/samples/android/nativeapp/AndroidBasicInteractionsTest.java @@ -63,6 +63,19 @@ public void testSendKeys() { Assert.assertEquals(searchScreen.getSearchResult(), query, "Search result don't match to entered query"); } + @Test + public void testSaveAndCompareScreenDump() + { + InvokeSearchScreen searchScreen = ApplicationActivity.SEARCH.open(); + Assert.assertTrue(searchScreen.state().isDisplayed(), String.format("%s should be opened", searchScreen.getName())); + final String customDumpName = String.format("my dump of %s", searchScreen.getName()); + searchScreen.dump().save(customDumpName); + Assert.assertEquals(searchScreen.dump().compare(customDumpName), 0, "Current screen should have no visual difference comparing to just saved dump"); + final String query = "Hello world!"; + searchScreen.typeQuery(query); + Assert.assertTrue(searchScreen.dump().compare() > 0, "Current screen after the search should have visual difference comparing to dump saved"); + } + @Test public void testRadioButton() { ITestRadioButton.super.testRadioButton(); diff --git a/src/test/java/samples/android/nativeapp/apidemos/screens/AndroidScreen.java b/src/test/java/samples/android/nativeapp/apidemos/screens/AndroidScreen.java index d4ee26c..67222ba 100644 --- a/src/test/java/samples/android/nativeapp/apidemos/screens/AndroidScreen.java +++ b/src/test/java/samples/android/nativeapp/apidemos/screens/AndroidScreen.java @@ -12,7 +12,6 @@ protected AndroidScreen(By locator, String name) { super(locator, name); } - @SuppressWarnings("unchecked") protected void startActivity(Activity activity) { AqualityServices.getLocalizedLogger().info("loc.application.android.activity.start", activity.getAppActivity(), diff --git a/src/test/java/samples/android/nativeapp/apidemos/screens/InvokeSearchScreen.java b/src/test/java/samples/android/nativeapp/apidemos/screens/InvokeSearchScreen.java index 4342e1b..b805733 100644 --- a/src/test/java/samples/android/nativeapp/apidemos/screens/InvokeSearchScreen.java +++ b/src/test/java/samples/android/nativeapp/apidemos/screens/InvokeSearchScreen.java @@ -16,10 +16,14 @@ public InvokeSearchScreen() { } public void submitSearch(String query) { - txbSearch.clearAndType(query); + typeQuery(query); btnStartSearch.click(); } + public void typeQuery(String query) { + txbSearch.clearAndType(query); + } + public String getSearchResult() { return lblSearchResult.getText(); } diff --git a/src/test/resources/settings.json b/src/test/resources/settings.json index f04bb75..bc76c3b 100644 --- a/src/test/resources/settings.json +++ b/src/test/resources/settings.json @@ -53,5 +53,13 @@ "verticalOffset": 0.2, "horizontalOffset": 0.5 } + }, + "visualization": { + "imageExtension": "png", + "maxFullFileNameLength": 255, + "defaultThreshold": 0.012, + "comparisonWidth": 16, + "comparisonHeight": 16, + "pathToDumps": "./src/test/resources/visualDumps/" } } \ No newline at end of file diff --git a/src/test/resources/visualDumps/Invoke Search/btnStartSearch.png b/src/test/resources/visualDumps/Invoke Search/btnStartSearch.png new file mode 100644 index 0000000..7453c46 Binary files /dev/null and b/src/test/resources/visualDumps/Invoke Search/btnStartSearch.png differ diff --git a/src/test/resources/visualDumps/Invoke Search/screenElement.png b/src/test/resources/visualDumps/Invoke Search/screenElement.png new file mode 100644 index 0000000..c03b3d8 Binary files /dev/null and b/src/test/resources/visualDumps/Invoke Search/screenElement.png differ diff --git a/src/test/resources/visualDumps/Invoke Search/txbSearch.png b/src/test/resources/visualDumps/Invoke Search/txbSearch.png new file mode 100644 index 0000000..e2cdb99 Binary files /dev/null and b/src/test/resources/visualDumps/Invoke Search/txbSearch.png differ