From db30415ebe82ede8da4564f1d34299f541725f93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alaksiej=20Miale=C5=A1ka?= Date: Tue, 23 Jun 2020 18:33:46 +0200 Subject: [PATCH] Feature/38 enhance element property getters logging (#54) * Added logPageSource parameter to settings * Added logging of values for getAttribute() and getText() of Element * Enhanced LocalizationManager to get value from core if the key is missed in passed assembly * Added logging to ElementStateProviders' waiting functions --- pom.xml | 2 +- .../configurations/ILoggerConfiguration.java | 6 ++ .../configurations/LoggerConfiguration.java | 14 ++++- .../elements/CachedElementStateProvider.java | 26 +++++---- .../elements/DefaultElementStateProvider.java | 55 +++++++++++++++--- .../selenium/core/elements/Element.java | 29 ++++++++-- .../core/elements/ElementStateProvider.java | 18 +++++- .../elements/interfaces/ILogElementState.java | 13 +++++ .../core/localization/ILocalizedLogger.java | 8 +++ .../localization/LocalizationManager.java | 16 +++++- .../core/localization/LocalizedLogger.java | 10 +++- .../localization/{be.json => core.be.json} | 14 ++++- .../localization/{en.json => core.en.json} | 12 +++- .../localization/{ru.json => core.ru.json} | 12 +++- src/main/resources/settings.json | 3 +- .../tests/applications/ICachedElement.java | 11 +++- .../browser/AqualityServices.java | 12 ++++ .../applications/browser/CachedLabel.java | 6 ++ .../applications/windowsApp/CachedButton.java | 6 ++ .../CachedElementStateProviderTests.java | 6 +- .../DefaultElementStateProviderTests.java | 8 ++- .../tests/elements/factory/CustomElement.java | 6 ++ .../elements/factory/CustomWebElement.java | 6 ++ .../LocalizationManagerTests.java | 56 ++++++++++++++++--- src/test/java/tests/logger/LoggerTests.java | 46 +++++++++++++++ src/test/resources/localization/be.json | 3 + 26 files changed, 352 insertions(+), 52 deletions(-) create mode 100644 src/main/java/aquality/selenium/core/elements/interfaces/ILogElementState.java rename src/main/resources/localization/{be.json => core.be.json} (56%) rename src/main/resources/localization/{en.json => core.en.json} (58%) rename src/main/resources/localization/{ru.json => core.ru.json} (60%) create mode 100644 src/test/resources/localization/be.json diff --git a/pom.xml b/pom.xml index dda6773..8ac2836 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.github.aquality-automation aquality-selenium-core - 1.1.0 + 1.2.0 jar Aquality Selenium Core diff --git a/src/main/java/aquality/selenium/core/configurations/ILoggerConfiguration.java b/src/main/java/aquality/selenium/core/configurations/ILoggerConfiguration.java index 99af3c8..8d652c3 100644 --- a/src/main/java/aquality/selenium/core/configurations/ILoggerConfiguration.java +++ b/src/main/java/aquality/selenium/core/configurations/ILoggerConfiguration.java @@ -10,4 +10,10 @@ public interface ILoggerConfiguration { * @return language used for logging. */ String getLanguage(); + + /** + * Perform page source logging in case of catastrophic failures or not. + * @return true if enabled, false otherwise. + */ + boolean logPageSource(); } diff --git a/src/main/java/aquality/selenium/core/configurations/LoggerConfiguration.java b/src/main/java/aquality/selenium/core/configurations/LoggerConfiguration.java index cb09a51..a8d65ee 100644 --- a/src/main/java/aquality/selenium/core/configurations/LoggerConfiguration.java +++ b/src/main/java/aquality/selenium/core/configurations/LoggerConfiguration.java @@ -6,15 +6,23 @@ public class LoggerConfiguration implements ILoggerConfiguration { private static final String DEFAULT_LANGUAGE = "en"; - private final ISettingsFile settingsFile; + private final String language; + private final boolean doLogPageSource; @Inject public LoggerConfiguration(ISettingsFile settingsFile){ - this.settingsFile = settingsFile; + language = settingsFile.getValueOrDefault("/logger/language", DEFAULT_LANGUAGE).toString(); + doLogPageSource = Boolean.parseBoolean( + settingsFile.getValueOrDefault("/logger/logPageSource", true).toString()); } @Override public String getLanguage() { - return settingsFile.getValueOrDefault("/logger/language", DEFAULT_LANGUAGE).toString(); + return language; + } + + @Override + public boolean logPageSource() { + return doLogPageSource; } } diff --git a/src/main/java/aquality/selenium/core/elements/CachedElementStateProvider.java b/src/main/java/aquality/selenium/core/elements/CachedElementStateProvider.java index 1359e55..e29d034 100644 --- a/src/main/java/aquality/selenium/core/elements/CachedElementStateProvider.java +++ b/src/main/java/aquality/selenium/core/elements/CachedElementStateProvider.java @@ -1,7 +1,7 @@ package aquality.selenium.core.elements; import aquality.selenium.core.elements.interfaces.IElementCacheHandler; -import aquality.selenium.core.localization.ILocalizedLogger; +import aquality.selenium.core.elements.interfaces.ILogElementState; import aquality.selenium.core.waitings.IConditionalWait; import org.openqa.selenium.By; import org.openqa.selenium.NoSuchElementException; @@ -24,13 +24,13 @@ public class CachedElementStateProvider extends ElementStateProvider { private final By locator; private final IConditionalWait conditionalWait; private final IElementCacheHandler elementCacheHandler; - private final ILocalizedLogger localizedLogger; - public CachedElementStateProvider(By locator, IConditionalWait conditionalWait, IElementCacheHandler elementCacheHandler, ILocalizedLogger localizedLogger) { + public CachedElementStateProvider(By locator, IConditionalWait conditionalWait, + IElementCacheHandler elementCacheHandler, ILogElementState logger) { + super(logger); this.locator = locator; this.conditionalWait = conditionalWait; this.elementCacheHandler = elementCacheHandler; - this.localizedLogger = localizedLogger; } protected List> getHandledExceptions() { @@ -52,11 +52,11 @@ protected boolean tryInvokeFunction(Predicate predicate, List tryInvokeFunction(WebElement::isDisplayed), ElementState.DISPLAYED.toString(), timeout); + return waitForCondition(() -> tryInvokeFunction(WebElement::isDisplayed), "displayed", timeout); } @Override public boolean waitForNotDisplayed(Duration timeout) { - return waitForCondition(() -> !isDisplayed(), "invisible or absent", timeout); + return waitForCondition(() -> !isDisplayed(), "not.displayed", timeout); } @Override @@ -99,12 +101,12 @@ public boolean isExist() { @Override public boolean waitForExist(Duration timeout) { - return waitForCondition(() -> tryInvokeFunction(element -> true), ElementState.EXISTS_IN_ANY_STATE.toString(), timeout); + return waitForCondition(() -> tryInvokeFunction(element -> true), "exist", timeout); } @Override public boolean waitForNotExist(Duration timeout) { - return waitForCondition(() -> !isExist(), "absent", timeout); + return waitForCondition(() -> !isExist(), "not.exist", timeout); } @Override diff --git a/src/main/java/aquality/selenium/core/elements/DefaultElementStateProvider.java b/src/main/java/aquality/selenium/core/elements/DefaultElementStateProvider.java index 8c89fea..0da4522 100644 --- a/src/main/java/aquality/selenium/core/elements/DefaultElementStateProvider.java +++ b/src/main/java/aquality/selenium/core/elements/DefaultElementStateProvider.java @@ -1,10 +1,12 @@ package aquality.selenium.core.elements; import aquality.selenium.core.elements.interfaces.IElementFinder; +import aquality.selenium.core.elements.interfaces.ILogElementState; import aquality.selenium.core.waitings.IConditionalWait; import org.openqa.selenium.By; import java.time.Duration; +import java.util.function.BooleanSupplier; public class DefaultElementStateProvider extends ElementStateProvider { @@ -12,7 +14,9 @@ public class DefaultElementStateProvider extends ElementStateProvider { private final IConditionalWait conditionalWait; private final IElementFinder elementFinder; - public DefaultElementStateProvider(By locator, IConditionalWait conditionalWait, IElementFinder elementFinder) { + public DefaultElementStateProvider(By locator, IConditionalWait conditionalWait, IElementFinder elementFinder, + ILogElementState logger) { + super(logger); this.locator = locator; this.conditionalWait = conditionalWait; this.elementFinder = elementFinder; @@ -25,7 +29,12 @@ public boolean isClickable() { @Override public void waitForClickable(Duration timeout) { - waitForIsClickable(timeout, false); + try { + waitForIsClickable(timeout, false); + } catch (Exception e) { + logElementState("loc.wait.for.state.failed", elementClickable().getStateName()); + throw e; + } } private boolean waitForIsClickable(Duration timeout, boolean catchTimeoutException) { @@ -35,7 +44,10 @@ private boolean waitForIsClickable(Duration timeout, boolean catchTimeoutExcepti } private boolean isElementInDesiredCondition(DesiredState elementStateCondition, Duration timeout) { - return !elementFinder.findElements(locator, elementStateCondition, timeout).isEmpty(); + return doAndLogWaitForState( + () -> !elementFinder.findElements(locator, elementStateCondition, timeout).isEmpty(), + elementStateCondition.getStateName(), + timeout); } @Override @@ -45,7 +57,10 @@ public boolean isDisplayed() { @Override public boolean waitForDisplayed(Duration timeout) { - return isAnyElementFound(timeout, ElementState.DISPLAYED); + return doAndLogWaitForState( + () -> isAnyElementFound(timeout, ElementState.DISPLAYED), + "displayed", + timeout); } private boolean isAnyElementFound(Duration timeout, ElementState state) { @@ -54,7 +69,10 @@ private boolean isAnyElementFound(Duration timeout, ElementState state) { @Override public boolean waitForNotDisplayed(Duration timeout) { - return conditionalWait.waitFor(() -> !isDisplayed(), timeout); + return doAndLogWaitForState( + () -> conditionalWait.waitFor(() -> !isDisplayed(), timeout), + "not.displayed", + timeout); } @Override @@ -64,12 +82,18 @@ public boolean isExist() { @Override public boolean waitForExist(Duration timeout) { - return isAnyElementFound(timeout, ElementState.EXISTS_IN_ANY_STATE); + return doAndLogWaitForState( + () -> isAnyElementFound(timeout, ElementState.EXISTS_IN_ANY_STATE), + "exist", + timeout); } @Override public boolean waitForNotExist(Duration timeout) { - return conditionalWait.waitFor(() -> !isExist(), timeout); + return doAndLogWaitForState( + () -> conditionalWait.waitFor(() -> !isExist(), timeout), + "not.exist", + timeout); } @Override @@ -86,4 +110,21 @@ public boolean waitForEnabled(Duration timeout) { public boolean waitForNotEnabled(Duration timeout) { return isElementInDesiredCondition(elementNotEnabled(), timeout); } + + private boolean doAndLogWaitForState(BooleanSupplier waitingAction, String conditionKeyPart, Duration timeout) + { + if (Duration.ZERO.equals(timeout)) + { + return waitingAction.getAsBoolean(); + } + + logElementState("loc.wait.for.state", conditionKeyPart); + boolean result = waitingAction.getAsBoolean(); + if (!result) + { + logElementState("loc.wait.for.state.failed", conditionKeyPart); + } + + return result; + } } diff --git a/src/main/java/aquality/selenium/core/elements/Element.java b/src/main/java/aquality/selenium/core/elements/Element.java index c520a8e..390bc93 100644 --- a/src/main/java/aquality/selenium/core/elements/Element.java +++ b/src/main/java/aquality/selenium/core/elements/Element.java @@ -2,7 +2,9 @@ import aquality.selenium.core.applications.IApplication; import aquality.selenium.core.configurations.IElementCacheConfiguration; +import aquality.selenium.core.configurations.ILoggerConfiguration; import aquality.selenium.core.elements.interfaces.*; +import aquality.selenium.core.localization.ILocalizationManager; import aquality.selenium.core.localization.ILocalizedLogger; import aquality.selenium.core.logging.Logger; import aquality.selenium.core.utilities.IElementActionRetrier; @@ -41,6 +43,8 @@ protected Element(final By loc, final String name, final ElementState state) { protected abstract ILocalizedLogger getLocalizedLogger(); + protected abstract ILocalizationManager getLocalizationManager(); + protected abstract IConditionalWait getConditionalWait(); protected abstract String getElementType(); @@ -53,10 +57,19 @@ protected IElementCacheHandler getCache() { return elementCacheHandler; } + protected ILoggerConfiguration getLoggerConfiguration() { + return getLocalizedLogger().getConfiguration(); + } + protected Logger getLogger() { return Logger.getInstance(); } + protected ILogElementState logElementState() { + return ((messageKey, stateKey) -> getLocalizedLogger().infoElementAction(getElementType(), getName(), messageKey, + getLocalizationManager().getLocalizedMessage(stateKey))); + } + @Override public By getLocator() { return locator; @@ -70,8 +83,8 @@ public String getName() { @Override public IElementStateProvider state() { return getElementCacheConfiguration().isEnabled() - ? new CachedElementStateProvider(locator, getConditionalWait(), getCache(), getLocalizedLogger()) - : new DefaultElementStateProvider(locator, getConditionalWait(), getElementFinder()); + ? new CachedElementStateProvider(locator, getConditionalWait(), getCache(), logElementState()) + : new DefaultElementStateProvider(locator, getConditionalWait(), getElementFinder(), logElementState()); } @Override @@ -81,7 +94,9 @@ public RemoteWebElement getElement(Duration timeout) { ? getCache().getElement(timeout) : (RemoteWebElement) getElementFinder().findElement(locator, elementState, timeout); } catch (NoSuchElementException e) { - logPageSource(e); + if (getLoggerConfiguration().logPageSource()) { + logPageSource(e); + } throw e; } } @@ -98,13 +113,17 @@ protected void logPageSource(WebDriverException exception) { @Override public String getText() { logElementAction("loc.get.text"); - return doWithRetry(() -> getElement().getText()); + String value = doWithRetry(() -> getElement().getText()); + logElementAction("loc.text.value", value); + return value; } @Override public String getAttribute(String attr) { logElementAction("loc.el.getattr", attr); - return doWithRetry(() -> getElement().getAttribute(attr)); + String value = doWithRetry(() -> getElement().getAttribute(attr)); + logElementAction("loc.el.attr.value", attr, value); + return value; } @Override diff --git a/src/main/java/aquality/selenium/core/elements/ElementStateProvider.java b/src/main/java/aquality/selenium/core/elements/ElementStateProvider.java index bc96bf0..255db0a 100644 --- a/src/main/java/aquality/selenium/core/elements/ElementStateProvider.java +++ b/src/main/java/aquality/selenium/core/elements/ElementStateProvider.java @@ -1,27 +1,39 @@ package aquality.selenium.core.elements; import aquality.selenium.core.elements.interfaces.IElementStateProvider; +import aquality.selenium.core.elements.interfaces.ILogElementState; import org.openqa.selenium.WebElement; public abstract class ElementStateProvider implements IElementStateProvider { + private final ILogElementState logger; + + protected ElementStateProvider(ILogElementState logger) { + this.logger = logger; + } + + protected void logElementState(String messageKey, String conditionKeyPart) { + String conditionKey = "loc.el.state.".concat(conditionKeyPart); + logger.logElementState(messageKey, conditionKey); + } + protected boolean isElementEnabled(WebElement element) { return element.isEnabled(); } protected DesiredState elementEnabled() { - return new DesiredState(this::isElementEnabled, "ENABLED") + return new DesiredState(this::isElementEnabled, "enabled") .withCatchingTimeoutException() .withThrowingNoSuchElementException(); } protected DesiredState elementNotEnabled() { - return new DesiredState(element -> !isElementEnabled(element), "NOT ENABLED") + return new DesiredState(element -> !isElementEnabled(element), "not.enabled") .withCatchingTimeoutException() .withThrowingNoSuchElementException(); } protected DesiredState elementClickable() { - return new DesiredState(webElement -> webElement.isDisplayed() && webElement.isEnabled(), "CLICKABLE"); + return new DesiredState(webElement -> webElement.isDisplayed() && webElement.isEnabled(), "clickable"); } } diff --git a/src/main/java/aquality/selenium/core/elements/interfaces/ILogElementState.java b/src/main/java/aquality/selenium/core/elements/interfaces/ILogElementState.java new file mode 100644 index 0000000..bb388e4 --- /dev/null +++ b/src/main/java/aquality/selenium/core/elements/interfaces/ILogElementState.java @@ -0,0 +1,13 @@ +package aquality.selenium.core.elements.interfaces; + +/** + * Describes interface that can log element state. + */ +public interface ILogElementState { + /** + * Logs element state. + * @param messageKey key of localized message to log. + * @param stateKey key of localized state to log. + */ + void logElementState(String messageKey, String stateKey); +} diff --git a/src/main/java/aquality/selenium/core/localization/ILocalizedLogger.java b/src/main/java/aquality/selenium/core/localization/ILocalizedLogger.java index d4cc70e..3826566 100644 --- a/src/main/java/aquality/selenium/core/localization/ILocalizedLogger.java +++ b/src/main/java/aquality/selenium/core/localization/ILocalizedLogger.java @@ -1,9 +1,17 @@ package aquality.selenium.core.localization; +import aquality.selenium.core.configurations.ILoggerConfiguration; + /** * Log messages in current language. */ public interface ILocalizedLogger { + /** + * Gets logger configuration. + * @return logger configuration. + */ + ILoggerConfiguration getConfiguration(); + /** * Logs localized message for action with INFO level which is applied for element, for example, click, send keys etc. * @param elementType Type of the element. diff --git a/src/main/java/aquality/selenium/core/localization/LocalizationManager.java b/src/main/java/aquality/selenium/core/localization/LocalizationManager.java index 1395b50..b7445dc 100644 --- a/src/main/java/aquality/selenium/core/localization/LocalizationManager.java +++ b/src/main/java/aquality/selenium/core/localization/LocalizationManager.java @@ -9,6 +9,7 @@ public class LocalizationManager implements ILocalizationManager { private static final String LANG_RESOURCE_TEMPLATE = "localization/%1$s.json"; private final ISettingsFile localizationFile; + private final ISettingsFile coreLocalizationFile; private final Logger logger; private final String locResourceName; @@ -17,14 +18,23 @@ public LocalizationManager(ILoggerConfiguration loggerConfiguration, Logger logg this.logger = logger; String language = loggerConfiguration.getLanguage(); locResourceName = String.format(LANG_RESOURCE_TEMPLATE, language.toLowerCase()); - localizationFile = new JsonSettingsFile(locResourceName); + ISettingsFile locFile = getLocalizationFileIfExist(locResourceName); + coreLocalizationFile = new JsonSettingsFile(String.format(LANG_RESOURCE_TEMPLATE, "core." + language.toLowerCase())); + localizationFile = locFile == null ? coreLocalizationFile : locFile; + } + + private static ISettingsFile getLocalizationFileIfExist(String fileName) { + return LocalizationManager.class.getClassLoader().getResource(fileName) == null + ? null + : new JsonSettingsFile(fileName); } @Override public String getLocalizedMessage(String messageKey, Object... args) { String jsonKeyPath = "/".concat(messageKey); - if (localizationFile.isValuePresent(jsonKeyPath)) { - return String.format(localizationFile.getValue(jsonKeyPath).toString(), args); + ISettingsFile locFile = localizationFile.isValuePresent(jsonKeyPath) ? localizationFile : coreLocalizationFile; + if (locFile.isValuePresent(jsonKeyPath)) { + return String.format(locFile.getValue(jsonKeyPath).toString(), args); } logger.warn(String.format("Cannot find localized message by key '%1$s' in resource file %2$s", diff --git a/src/main/java/aquality/selenium/core/localization/LocalizedLogger.java b/src/main/java/aquality/selenium/core/localization/LocalizedLogger.java index d9e5bc9..4d1d6e4 100644 --- a/src/main/java/aquality/selenium/core/localization/LocalizedLogger.java +++ b/src/main/java/aquality/selenium/core/localization/LocalizedLogger.java @@ -1,5 +1,6 @@ package aquality.selenium.core.localization; +import aquality.selenium.core.configurations.ILoggerConfiguration; import aquality.selenium.core.logging.Logger; import com.google.inject.Inject; @@ -7,17 +8,24 @@ public class LocalizedLogger implements ILocalizedLogger { private final ILocalizationManager localizationManager; private final Logger logger; + private final ILoggerConfiguration loggerConfiguration; @Inject - public LocalizedLogger(ILocalizationManager localizationManager, Logger logger) { + public LocalizedLogger(ILocalizationManager localizationManager, Logger logger, ILoggerConfiguration loggerConfiguration) { this.localizationManager = localizationManager; this.logger = logger; + this.loggerConfiguration = loggerConfiguration; } private String localizeMessage(String messageKey, Object... args) { return localizationManager.getLocalizedMessage(messageKey, args); } + @Override + public ILoggerConfiguration getConfiguration() { + return loggerConfiguration; + } + @Override public void infoElementAction(String elementType, String elementName, String messageKey, Object... args) { String message = String.format("%1$s '%2$s' :: %3$s", elementType, elementName, localizeMessage(messageKey, args)); diff --git a/src/main/resources/localization/be.json b/src/main/resources/localization/core.be.json similarity index 56% rename from src/main/resources/localization/be.json rename to src/main/resources/localization/core.be.json index 28ba29f..32ce8da 100644 --- a/src/main/resources/localization/be.json +++ b/src/main/resources/localization/core.be.json @@ -1,13 +1,23 @@ { "loc.clicking": "Націскаем", "loc.el.getattr": "Атрымліваем атрыбут '%1$s'", + "loc.el.attr.value": "Значэнне атрыбута '%1$s': [%2$s]", "loc.get.text": "Атрымліваем тэкст элемента", + "loc.text.value": "Тэкст элемента: [%1$s]", "loc.text.sending.keys": "Націскаем клавішы '%1$s'", "loc.no.elements.found.in.state": "Не знайшлі элементаў па лакатару '%1$s' у %2$s стане", "loc.no.elements.found.by.locator": "Не знайшлі элементаў па лакатару '%1$s'", "loc.elements.were.found.but.not.in.state": "Знайшлі элементы па лакатару '%1$s', але яны не ў жаданым стане %2$s", "loc.elements.found.but.should.not": "Не павінна быць знойдзена элементаў па лакатару '%1$s' у %2$s стане", "loc.search.of.elements.failed": "Пошук элемента па лакатару '%1$s' прайшоў няўдала", - "loc.element.not.in.state": "Элемент %1$s не стаў %2$s пасля таймаўта %3$s", - "loc.get.page.source.failed": "Адбылася памылка ў час атрымання разметкі старонкі" + "loc.wait.for.state": "Чакаем, пакуль элемент будзе %1$s", + "loc.wait.for.state.failed": "Элемент не стаў %1$s па заканчэнні часу чакання", + "loc.el.state.displayed": "бачны", + "loc.el.state.not.displayed": "нябачны або адсутны", + "loc.el.state.exist": "прысутны", + "loc.el.state.not.exist": "адсутны", + "loc.el.state.enabled": "даступны", + "loc.el.state.not.enabled": "недаступны", + "loc.el.state.clickable": "даступны для націску", + "loc.get.page.source.failed": "Адбылася памылка падчас атрымання разметкі старонкі" } \ No newline at end of file diff --git a/src/main/resources/localization/en.json b/src/main/resources/localization/core.en.json similarity index 58% rename from src/main/resources/localization/en.json rename to src/main/resources/localization/core.en.json index c327953..7d99add 100644 --- a/src/main/resources/localization/en.json +++ b/src/main/resources/localization/core.en.json @@ -1,13 +1,23 @@ { "loc.clicking": "Clicking", "loc.el.getattr": "Getting attribute '%1$s'", + "loc.el.attr.value": "Value of attribute '%1$s': [%2$s]", "loc.get.text": "Getting text from element", + "loc.text.value": "Element's text: [%1$s]", "loc.text.sending.keys": "Sending keys '%1$s'", "loc.no.elements.found.in.state": "No elements with locator '%1$s' were found in %2$s state", "loc.no.elements.found.by.locator": "No elements were found by locator '%1$s'", "loc.elements.were.found.but.not.in.state": "Elements were found by locator '%1$s' but not in desired state %2$s", "loc.elements.found.but.should.not": "No elements should be found by locator '%1$s' in %2$s state", "loc.search.of.elements.failed": "Search of element by locator '%1$s' failed", - "loc.element.not.in.state": "Element %1$s has not become %2$s after timeout %3$s", + "loc.wait.for.state": "Waiting for element to be %1$s", + "loc.wait.for.state.failed": "Element has not become %1$s after timeout", + "loc.el.state.displayed": "displayed", + "loc.el.state.not.displayed": "invisible or absent", + "loc.el.state.exist": "exist", + "loc.el.state.not.exist": "absent", + "loc.el.state.enabled": "enabled", + "loc.el.state.not.enabled": "disabled", + "loc.el.state.clickable": "clickable", "loc.get.page.source.failed": "An exception occurred while tried to save the page source" } \ No newline at end of file diff --git a/src/main/resources/localization/ru.json b/src/main/resources/localization/core.ru.json similarity index 60% rename from src/main/resources/localization/ru.json rename to src/main/resources/localization/core.ru.json index 812a206..a2257c0 100644 --- a/src/main/resources/localization/ru.json +++ b/src/main/resources/localization/core.ru.json @@ -1,13 +1,23 @@ { "loc.clicking": "Клик", "loc.el.getattr": "Получение аттрибута '%1$s'", + "loc.el.attr.value": "Значение аттрибута '%1$s'': [%2$s]", "loc.get.text": "Получение текста элемента", + "loc.text.value": "Текст элемента: [%1$s]", "loc.text.sending.keys": "Нажатие клавиши '%1$s'", "loc.no.elements.found.in.state": "Не удалось найти элементов по локатору '%1$s' в %2$s состоянии", "loc.no.elements.found.by.locator": "Не удалось найти элементов по локатору '%1$s'", "loc.elements.were.found.but.not.in.state": "Удалось найти элементы по локатору '%1$s', но они не в желаемом состоянии %2$s", "loc.elements.found.but.should.not": "Не должно быть найдено элементов по локатору '%1$s' в %2$s состоянии", "loc.search.of.elements.failed": "Поиск элемента по локатору '%1$s' прошел неудачно", - "loc.element.not.in.state": "Элемент %1$s не стал %2$s после таймаута %3$s", + "loc.wait.for.state": "Ожидание, пока элемент станет %1$s", + "loc.wait.for.state.failed": "Элемент не стал %1$s после таймаута", + "loc.el.state.displayed": "видимым", + "loc.el.state.not.displayed": "невидимым или остуствующим", + "loc.el.state.exist": "присутствующим", + "loc.el.state.not.exist": "отсутствующим", + "loc.el.state.enabled": "доступным", + "loc.el.state.not.enabled": "недоступным", + "loc.el.state.clickable": "кликабельным", "loc.get.page.source.failed": "Произошла ошибка во время получения разметки страницы" } \ No newline at end of file diff --git a/src/main/resources/settings.json b/src/main/resources/settings.json index cc2e51d..6c8b630 100644 --- a/src/main/resources/settings.json +++ b/src/main/resources/settings.json @@ -10,7 +10,8 @@ "pollingInterval": 300 }, "logger": { - "language": "en" + "language": "en", + "logPageSource": true }, "elementCache": { "isEnabled": false diff --git a/src/test/java/tests/applications/ICachedElement.java b/src/test/java/tests/applications/ICachedElement.java index bad5c0a..8c4a97b 100644 --- a/src/test/java/tests/applications/ICachedElement.java +++ b/src/test/java/tests/applications/ICachedElement.java @@ -6,6 +6,8 @@ import aquality.selenium.core.elements.interfaces.IElementCacheHandler; import aquality.selenium.core.elements.interfaces.IElementFinder; import aquality.selenium.core.elements.interfaces.IElementStateProvider; +import aquality.selenium.core.elements.interfaces.ILogElementState; +import aquality.selenium.core.localization.ILocalizationManager; import aquality.selenium.core.localization.ILocalizedLogger; import aquality.selenium.core.waitings.IConditionalWait; import org.openqa.selenium.By; @@ -20,6 +22,7 @@ public interface ICachedElement { IElementFinder getElementFinder(); IConditionalWait getConditionalWait(); ILocalizedLogger getLocalizedLogger(); + ILocalizationManager getLocalizationManager(); default IElementCacheHandler cache() { @@ -38,6 +41,12 @@ default void click() { } default IElementStateProvider state() { - return new CachedElementStateProvider(getLocator(), getConditionalWait(), cache(), getLocalizedLogger()); + return new CachedElementStateProvider(getLocator(), getConditionalWait(), cache(), logElementState()); + } + + default ILogElementState logElementState() { + return (messageKey, stateKey) -> getLocalizedLogger() + .infoElementAction(getClass().getName(), getLocator().toString(), messageKey, + getLocalizationManager().getLocalizedMessage(stateKey)); } } diff --git a/src/test/java/tests/applications/browser/AqualityServices.java b/src/test/java/tests/applications/browser/AqualityServices.java index 27324bc..8defe02 100644 --- a/src/test/java/tests/applications/browser/AqualityServices.java +++ b/src/test/java/tests/applications/browser/AqualityServices.java @@ -1,5 +1,6 @@ package tests.applications.browser; +import aquality.selenium.core.applications.AqualityModule; import com.google.inject.Injector; import io.github.bonigarcia.wdm.WebDriverManager; @@ -10,6 +11,10 @@ private AqualityServices() { super(AqualityServices::getApplication, null); } + private > AqualityServices(T module) { + super(AqualityServices::getApplication, () -> module); + } + private static AqualityServices getInstance() { return INSTANCE_CONTAINER.get(); } @@ -35,6 +40,13 @@ public static Injector getServiceProvider() { return getInstance().getInjector(); } + public static > void initInjector(T module) { + if (INSTANCE_CONTAINER.get() != null){ + INSTANCE_CONTAINER.remove(); + } + INSTANCE_CONTAINER.set(new AqualityServices(module)); + } + public static T get(Class type) { return getServiceProvider().getInstance(type); } diff --git a/src/test/java/tests/applications/browser/CachedLabel.java b/src/test/java/tests/applications/browser/CachedLabel.java index 9b92dcb..55531bd 100644 --- a/src/test/java/tests/applications/browser/CachedLabel.java +++ b/src/test/java/tests/applications/browser/CachedLabel.java @@ -3,6 +3,7 @@ import aquality.selenium.core.elements.ElementState; import aquality.selenium.core.elements.interfaces.IElementCacheHandler; import aquality.selenium.core.elements.interfaces.IElementFinder; +import aquality.selenium.core.localization.ILocalizationManager; import aquality.selenium.core.localization.ILocalizedLogger; import aquality.selenium.core.waitings.IConditionalWait; import org.openqa.selenium.By; @@ -52,4 +53,9 @@ public IConditionalWait getConditionalWait() { public ILocalizedLogger getLocalizedLogger() { return AqualityServices.get(ILocalizedLogger.class); } + + @Override + public ILocalizationManager getLocalizationManager() { + return AqualityServices.get(ILocalizationManager.class); + } } diff --git a/src/test/java/tests/applications/windowsApp/CachedButton.java b/src/test/java/tests/applications/windowsApp/CachedButton.java index 8ae0b88..0571b0c 100644 --- a/src/test/java/tests/applications/windowsApp/CachedButton.java +++ b/src/test/java/tests/applications/windowsApp/CachedButton.java @@ -3,6 +3,7 @@ import aquality.selenium.core.elements.ElementState; import aquality.selenium.core.elements.interfaces.IElementCacheHandler; import aquality.selenium.core.elements.interfaces.IElementFinder; +import aquality.selenium.core.localization.ILocalizationManager; import aquality.selenium.core.localization.ILocalizedLogger; import aquality.selenium.core.waitings.IConditionalWait; import org.openqa.selenium.By; @@ -53,4 +54,9 @@ public IConditionalWait getConditionalWait() { public ILocalizedLogger getLocalizedLogger() { return AqualityServices.get(ILocalizedLogger.class); } + + @Override + public ILocalizationManager getLocalizationManager() { + return AqualityServices.get(ILocalizationManager.class); + } } diff --git a/src/test/java/tests/elements/CachedElementStateProviderTests.java b/src/test/java/tests/elements/CachedElementStateProviderTests.java index d01277f..1e76630 100644 --- a/src/test/java/tests/elements/CachedElementStateProviderTests.java +++ b/src/test/java/tests/elements/CachedElementStateProviderTests.java @@ -5,6 +5,7 @@ import aquality.selenium.core.elements.ElementState; import aquality.selenium.core.elements.interfaces.IElementFinder; import aquality.selenium.core.elements.interfaces.IElementStateProvider; +import aquality.selenium.core.localization.ILocalizationManager; import aquality.selenium.core.localization.ILocalizedLogger; import aquality.selenium.core.waitings.IConditionalWait; import org.openqa.selenium.By; @@ -17,7 +18,10 @@ public IElementStateProvider state(By locator) { return new CachedElementStateProvider(locator, AqualityServices.get(IConditionalWait.class), new ElementCacheHandler(locator, ElementState.EXISTS_IN_ANY_STATE, AqualityServices.get(IElementFinder.class)), - AqualityServices.get(ILocalizedLogger.class)); + (messageKey, stateKey) -> + AqualityServices.get(ILocalizedLogger.class) + .infoElementAction("Element", locator.toString(), messageKey, + AqualityServices.get(ILocalizationManager.class).getLocalizedMessage(stateKey))); } } diff --git a/src/test/java/tests/elements/DefaultElementStateProviderTests.java b/src/test/java/tests/elements/DefaultElementStateProviderTests.java index c759811..35cca58 100644 --- a/src/test/java/tests/elements/DefaultElementStateProviderTests.java +++ b/src/test/java/tests/elements/DefaultElementStateProviderTests.java @@ -3,6 +3,8 @@ import aquality.selenium.core.elements.DefaultElementStateProvider; import aquality.selenium.core.elements.interfaces.IElementFinder; import aquality.selenium.core.elements.interfaces.IElementStateProvider; +import aquality.selenium.core.localization.ILocalizationManager; +import aquality.selenium.core.localization.ILocalizedLogger; import aquality.selenium.core.waitings.IConditionalWait; import org.openqa.selenium.By; import tests.applications.browser.AqualityServices; @@ -13,6 +15,10 @@ public class DefaultElementStateProviderTests implements IWebElementStateProvide public IElementStateProvider state(By locator) { return new DefaultElementStateProvider(locator, AqualityServices.get(IConditionalWait.class), - AqualityServices.get(IElementFinder.class)); + AqualityServices.get(IElementFinder.class), + (messageKey, stateKey) -> + AqualityServices.get(ILocalizedLogger.class) + .infoElementAction("Element", locator.toString(), messageKey, + AqualityServices.get(ILocalizationManager.class).getLocalizedMessage(stateKey))); } } diff --git a/src/test/java/tests/elements/factory/CustomElement.java b/src/test/java/tests/elements/factory/CustomElement.java index 4eb0731..9e7cc3f 100644 --- a/src/test/java/tests/elements/factory/CustomElement.java +++ b/src/test/java/tests/elements/factory/CustomElement.java @@ -6,6 +6,7 @@ import aquality.selenium.core.elements.ElementState; import aquality.selenium.core.elements.interfaces.IElementFactory; import aquality.selenium.core.elements.interfaces.IElementFinder; +import aquality.selenium.core.localization.ILocalizationManager; import aquality.selenium.core.localization.ILocalizedLogger; import aquality.selenium.core.utilities.IElementActionRetrier; import aquality.selenium.core.waitings.IConditionalWait; @@ -48,6 +49,11 @@ protected ILocalizedLogger getLocalizedLogger() { return AqualityServices.get(ILocalizedLogger.class); } + @Override + protected ILocalizationManager getLocalizationManager() { + return AqualityServices.get(ILocalizationManager.class); + } + @Override protected IConditionalWait getConditionalWait() { return AqualityServices.get(IConditionalWait.class); diff --git a/src/test/java/tests/elements/factory/CustomWebElement.java b/src/test/java/tests/elements/factory/CustomWebElement.java index 539a32d..258c692 100644 --- a/src/test/java/tests/elements/factory/CustomWebElement.java +++ b/src/test/java/tests/elements/factory/CustomWebElement.java @@ -6,6 +6,7 @@ import aquality.selenium.core.elements.ElementState; import aquality.selenium.core.elements.interfaces.IElementFactory; import aquality.selenium.core.elements.interfaces.IElementFinder; +import aquality.selenium.core.localization.ILocalizationManager; import aquality.selenium.core.localization.ILocalizedLogger; import aquality.selenium.core.utilities.IElementActionRetrier; import aquality.selenium.core.waitings.IConditionalWait; @@ -48,6 +49,11 @@ protected ILocalizedLogger getLocalizedLogger() { return AqualityServices.get(ILocalizedLogger.class); } + @Override + protected ILocalizationManager getLocalizationManager() { + return AqualityServices.get(ILocalizationManager.class); + } + @Override protected IConditionalWait getConditionalWait() { return AqualityServices.get(IConditionalWait.class); diff --git a/src/test/java/tests/localization/LocalizationManagerTests.java b/src/test/java/tests/localization/LocalizationManagerTests.java index 00cc44b..3bceb04 100644 --- a/src/test/java/tests/localization/LocalizationManagerTests.java +++ b/src/test/java/tests/localization/LocalizationManagerTests.java @@ -17,25 +17,37 @@ public class LocalizationManagerTests { private static final String[] SUPPORTED_LANGUAGES = new String[]{"be", "en", "ru"}; private static final String CLICKING_MESSAGE_KEY = "loc.clicking"; + private static final String CLICKING_VALUE_BE = "Націскаем"; + private static final String CLICKING_VALUE_EN = "Clicking"; @DataProvider private Object[] keysWithParams() { return new String[]{ "loc.el.getattr", + "loc.el.attr.value", + "loc.text.value", "loc.text.sending.keys", "loc.no.elements.found.in.state", "loc.no.elements.found.by.locator", "loc.elements.were.found.but.not.in.state", "loc.elements.found.but.should.not", "loc.search.of.elements.failed", - "loc.element.not.in.state"}; + "loc.wait.for.state", + "loc.wait.for.state.failed"}; } @DataProvider private Object[] keysWithoutParams() { return new String[]{ CLICKING_MESSAGE_KEY, - "loc.get.text"}; + "loc.get.text", + "loc.el.state.displayed", + "loc.el.state.not.displayed", + "loc.el.state.exist", + "loc.el.state.not.exist", + "loc.el.state.enabled", + "loc.el.state.not.enabled", + "loc.el.state.clickable"}; } @@ -46,7 +58,18 @@ private LocalizationManager getLocalizationManager() { } private LocalizationManager getLocalizationManager(String language) { - return new LocalizationManager(() -> language, Logger.getInstance()); + ILoggerConfiguration configuration = new ILoggerConfiguration() { + @Override + public String getLanguage() { + return language; + } + + @Override + public boolean logPageSource() { + return true; + } + }; + return new LocalizationManager(configuration, Logger.getInstance()); } @Test @@ -63,19 +86,19 @@ public void testShouldBeRegisteredAsSingleton() { @Test public void testShouldBePossibleToUseForClicking() { - assertEquals(getLocalizationManager().getLocalizedMessage(CLICKING_MESSAGE_KEY), "Clicking", + assertEquals(getLocalizationManager().getLocalizedMessage(CLICKING_MESSAGE_KEY), CLICKING_VALUE_EN, "Logger should be configured in English by default and return valid value"); } @Test public void testShouldBePossibleToUseForClickingWithCustomLanguage() { - assertEquals(getLocalizationManager("be").getLocalizedMessage(CLICKING_MESSAGE_KEY), "Націскаем", + assertEquals(getLocalizationManager("be").getLocalizedMessage(CLICKING_MESSAGE_KEY), CLICKING_VALUE_BE, "Logger should be configured in custom language when use custom profile, and return valid value"); } @Test(dataProvider = "keysWithParams") public void testShouldThrowFormatExceptionWhenKeysRequireParams(String keyWithParams) { - for (String language: SUPPORTED_LANGUAGES) { + for (String language : SUPPORTED_LANGUAGES) { Assert.assertThrows(MissingFormatArgumentException.class, () -> getLocalizationManager(language).getLocalizedMessage(keyWithParams)); } @@ -83,7 +106,7 @@ public void testShouldThrowFormatExceptionWhenKeysRequireParams(String keyWithPa @Test(dataProvider = "keysWithoutParams") public void testShouldReturnNonKeyAndNonEmptyValuesForKeysWithoutParams(String keyWithoutParams) { - for (String language: SUPPORTED_LANGUAGES) { + for (String language : SUPPORTED_LANGUAGES) { String value = getLocalizationManager(language).getLocalizedMessage(keyWithoutParams); Assert.assertFalse(value.isEmpty(), String.format("value of key %1$s in language %2$s should not be empty", keyWithoutParams, language)); @@ -94,8 +117,8 @@ public void testShouldReturnNonKeyAndNonEmptyValuesForKeysWithoutParams(String k @Test(dataProvider = "keysWithParams") public void testShouldReturnNonKeyAndNonEmptyValuesForKeysWithParams(String keyWithParams) { - for (String language: SUPPORTED_LANGUAGES) { - Object[] params = new String[] { "a", "b", "c" }; + for (String language : SUPPORTED_LANGUAGES) { + Object[] params = new String[]{"a", "b", "c"}; String value = getLocalizationManager(language).getLocalizedMessage(keyWithParams, params); Assert.assertFalse(value.isEmpty(), String.format("value of key %1$s in language %2$s should not be empty", keyWithParams, language)); @@ -107,6 +130,21 @@ public void testShouldReturnNonKeyAndNonEmptyValuesForKeysWithParams(String keyW } } + @Test + public void testShouldReturnNonKeyValueForKeysPresentInCoreIfLanguageMissedInSiblingAssembly() { + String localizedValue = getLocalizationManager("en").getLocalizedMessage(CLICKING_MESSAGE_KEY); + + Assert.assertEquals(localizedValue, CLICKING_VALUE_EN, "Value should match to expected"); + } + + @Test + public void testShouldReturnNonKeyValueForKeysPresentInCoreIfKeyMissedInSiblingAssembly() { + String localizedValue = getLocalizationManager("be").getLocalizedMessage(CLICKING_MESSAGE_KEY); + + Assert.assertEquals(localizedValue, CLICKING_VALUE_BE, "Value should match to expected"); + } + + @Test public void testShouldThrowWhenInvalidLanguageSupplied() { Assert.assertThrows(IllegalArgumentException.class, () -> diff --git a/src/test/java/tests/logger/LoggerTests.java b/src/test/java/tests/logger/LoggerTests.java index 929f146..5b590df 100644 --- a/src/test/java/tests/logger/LoggerTests.java +++ b/src/test/java/tests/logger/LoggerTests.java @@ -1,17 +1,25 @@ package tests.logger; +import aquality.selenium.core.applications.AqualityModule; +import aquality.selenium.core.elements.ElementState; import aquality.selenium.core.logging.Logger; import org.apache.log4j.*; +import org.openqa.selenium.By; +import org.openqa.selenium.NoSuchElementException; +import org.testng.Assert; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeGroups; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; +import tests.applications.browser.AqualityServices; +import tests.elements.factory.CustomWebElement; import java.io.File; import java.io.IOException; import java.lang.reflect.Field; import java.nio.charset.StandardCharsets; import java.nio.file.Files; +import java.time.Duration; import java.util.UUID; import static org.testng.Assert.*; @@ -21,6 +29,8 @@ public class LoggerTests { private static final String TEST_MESSAGE = "test message"; private static final String TEST_EXCEPTION_TEXT = "test exception"; private static final String LOG_4_J_FIELD_NAME = "log4J"; + private static final String LOG_PAGE_SOURCE_ENVIRONMENT_VARIABLE = "logger.logPageSource"; + private static final String PAGE_SOURCE_MESSAGE = "Page source:"; private Logger logger = Logger.getInstance(); private org.apache.log4j.Logger log4j; private Appender appender; @@ -40,6 +50,42 @@ private void initializeLog4jField() throws NoSuchFieldException, IllegalAccessEx log4j = ((ThreadLocal) log4jField.get(logger)).get(); } + @AfterMethod + private void cleanUpLogPageSourceAndBrowser() { + System.clearProperty(LOG_PAGE_SOURCE_ENVIRONMENT_VARIABLE); + if (AqualityServices.isApplicationStarted()){ + AqualityServices.getApplication().getDriver().quit(); + } + if (log4j != null){ + log4j.setLevel(Level.DEBUG); + } + } + + @BeforeMethod + public void cleanUpInjector() { + AqualityServices.initInjector(new AqualityModule<>(AqualityServices::getApplication)); + } + + @Test + public void testShouldBePossibleLogPageSourceWhenIsEnabledAndElementAbsent() throws IOException { + System.setProperty(LOG_PAGE_SOURCE_ENVIRONMENT_VARIABLE, "true"); + CustomWebElement label = new CustomWebElement(By.name("Absent element"), "Absent element", + ElementState.EXISTS_IN_ANY_STATE); + Assert.assertThrows(NoSuchElementException.class, () -> label.getElement(Duration.ZERO)); + assertTrue(isFileContainsText(appenderFile, PAGE_SOURCE_MESSAGE), + String.format("Log '%s' should contain message '%s'.", appenderFile.getPath(), PAGE_SOURCE_MESSAGE)); + } + + @Test + public void testShouldBePossibleNotLogPageSourceWhenIsDisabledAndElementAbsent() throws IOException { + System.setProperty(LOG_PAGE_SOURCE_ENVIRONMENT_VARIABLE, "false"); + CustomWebElement label = new CustomWebElement(By.name("Absent element"), "Absent element", + ElementState.EXISTS_IN_ANY_STATE); + Assert.assertThrows(NoSuchElementException.class, () -> label.getElement(Duration.ZERO)); + assertFalse(isFileContainsText(appenderFile, PAGE_SOURCE_MESSAGE), + String.format("Log '%s' should not contain message '%s'.", appenderFile.getPath(), PAGE_SOURCE_MESSAGE)); + } + @Test public void testAqualityServicesShouldReturnInstanceOfLogger() { assertEquals(logger, Logger.getInstance()); diff --git a/src/test/resources/localization/be.json b/src/test/resources/localization/be.json new file mode 100644 index 0000000..1e6de38 --- /dev/null +++ b/src/test/resources/localization/be.json @@ -0,0 +1,3 @@ +{ + "loc.somekey": "Нешта" +} \ No newline at end of file