From d77141ec19ec444c76618b0e6c2c8235453a977c Mon Sep 17 00:00:00 2001 From: Aleksey2 Meleshko Date: Tue, 14 Mar 2023 20:57:22 +0100 Subject: [PATCH] Implement visualization classes and tests Typo in enum ElementsCount MORE_THAN_ZERO was fixed in aquality.selenium.core package --- README.md | 2 +- pom.xml | 4 +- .../mobile/configuration/Configuration.java | 14 +++-- .../mobile/configuration/IConfiguration.java | 15 +++-- .../appium/mobile/elements/Element.java | 6 ++ .../appium/mobile/screens/IScreen.java | 19 +++++- .../appium/mobile/screens/Screen.java | 54 +++++++++++++++--- src/main/resources/settings.json | 8 +++ .../AndroidBasicInteractionsTest.java | 13 +++++ .../apidemos/screens/AndroidScreen.java | 1 - .../apidemos/screens/InvokeSearchScreen.java | 6 +- src/test/resources/settings.json | 8 +++ .../Invoke Search/btnStartSearch.png | Bin 0 -> 6409 bytes .../Invoke Search/screenElement.png | Bin 0 -> 6515 bytes .../visualDumps/Invoke Search/txbSearch.png | Bin 0 -> 465 bytes 15 files changed, 127 insertions(+), 23 deletions(-) create mode 100644 src/test/resources/visualDumps/Invoke Search/btnStartSearch.png create mode 100644 src/test/resources/visualDumps/Invoke Search/screenElement.png create mode 100644 src/test/resources/visualDumps/Invoke Search/txbSearch.png 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 0000000000000000000000000000000000000000..7453c4657470758f871a053cdd39521af0c83191 GIT binary patch literal 6409 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..c03b3d828b23b26eb3a1bfc4a1b0ab33bf1f193b GIT binary patch literal 6515 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 literal 0 HcmV?d00001