Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package aquality.selenium.core.application;

import aquality.selenium.core.localization.*;
import aquality.selenium.core.logging.Logger;
import aquality.selenium.core.utilities.ISettingsFile;
import aquality.selenium.core.utilities.JsonSettingsFile;
Expand All @@ -11,7 +12,8 @@
/**
* Describes all dependencies which is registered for the project.
*/
public class AqualityModule<T extends IApplication> extends AbstractModule {
public class AqualityModule<T extends IApplication> extends AbstractModule
implements ILocalizationModule {

private final Provider<T> applicationProvider;

Expand All @@ -31,6 +33,8 @@ protected void configure() {
bind(ITimeoutConfiguration.class).to(TimeoutConfiguration.class).in(Singleton.class);
bind(IRetryConfiguration.class).to(RetryConfiguration.class).in(Singleton.class);
bind(IElementCacheConfiguration.class).to(ElementCacheConfiguration.class).in(Singleton.class);
bind(ILocalizationManager.class).to(getLocalizationManagerImplementation()).in(Singleton.class);
bind(ILocalizedLogger.class).to(getLocalizedLoggerImplementation()).in(Singleton.class);
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package aquality.selenium.core.localization;

/**
* This interface is used for translation messages to different languages.
*/
public interface ILocalizationManager {
/**
* Gets localized message from resources by its key.
* @param messageKey Key in resource file.
* @param args Arguments, which will be provided to template of localized message.
* @return Localized message.
*/
String getLocalizedMessage(String messageKey, Object... args);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package aquality.selenium.core.localization;

/**
* Describes implementations of localization services to be registered in DI container.
*/
public interface ILocalizationModule {
/**
* @return class which implements ILocalizationManager
*/
default Class<? extends ILocalizationManager> getLocalizationManagerImplementation() {
return LocalizationManager.class;
}

/**
* @return class which implements ILocalizedLogger
*/
default Class<? extends ILocalizedLogger> getLocalizedLoggerImplementation() {
return LocalizedLogger.class;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package aquality.selenium.core.localization;

/**
* Log messages in current language.
*/
public interface ILocalizedLogger {
/**
* 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.
* @param elementName Name of the element.
* @param messageKey Key in resource file.
* @param args Arguments, which will be provided to template of localized message.
*/
void infoElementAction(String elementType, String elementName, String messageKey, Object... args);

/**
* Logs localized message with INFO level.
* @param messageKey Key in resource file.
* @param args Arguments, which will be provided to template of localized message.
*/
void info(String messageKey, Object... args);

/**
* Logs localized message with DEBUG level.
* @param messageKey Key in resource file.
* @param args Arguments, which will be provided to template of localized message.
*/
void debug(String messageKey, Object... args);

/**
* Logs localized message with DEBUG level.
* @param messageKey Key in resource file.
* @param throwable Throwable to log.
* @param args Arguments, which will be provided to template of localized message.
*/
void debug(String messageKey, Throwable throwable, Object... args);

/**
* Logs localized message with WARN level.
* @param messageKey Key in resource file.
* @param args Arguments, which will be provided to template of localized message.
*/
void warn(String messageKey, Object... args);

/**
* Logs localized message with ERROR level.
* @param messageKey Key in resource file.
* @param args Arguments, which will be provided to template of localized message.
*/
void error(String messageKey, Object... args);

/**
* Logs localized message with FATAL level.
* @param messageKey Key in resource file.
* @param throwable Throwable to log.
* @param args Arguments, which will be provided to template of localized message.
*/
void fatal(String messageKey, Throwable throwable, Object... args);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package aquality.selenium.core.localization;

import aquality.selenium.core.configurations.ILoggerConfiguration;
import aquality.selenium.core.logging.Logger;
import aquality.selenium.core.utilities.ISettingsFile;
import aquality.selenium.core.utilities.JsonSettingsFile;
import com.google.inject.Inject;

public class LocalizationManager implements ILocalizationManager {
private static final String LANG_RESOURCE_TEMPLATE = "localization/%1$s.json";
private final ISettingsFile localizationFile;
private final Logger logger;
private final String locResourceName;

@Inject
public LocalizationManager(ILoggerConfiguration loggerConfiguration, Logger logger) {
this.logger = logger;
String language = loggerConfiguration.getLanguage();
locResourceName = String.format(LANG_RESOURCE_TEMPLATE, language.toLowerCase());
localizationFile = new JsonSettingsFile(locResourceName);
}

@Override
public String getLocalizedMessage(String messageKey, Object... args) {
String jsonKeyPath = "/".concat(messageKey);
if (localizationFile.isValuePresent(jsonKeyPath)) {
return String.format(localizationFile.getValue(jsonKeyPath).toString(), args);
}

logger.warn(String.format("Cannot find localized message by key '%1$s' in resource file %2$s",
jsonKeyPath, locResourceName));
return messageKey;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package aquality.selenium.core.localization;

import aquality.selenium.core.logging.Logger;
import com.google.inject.Inject;

public class LocalizedLogger implements ILocalizedLogger {

private final ILocalizationManager localizationManager;
private final Logger logger;

@Inject
public LocalizedLogger(ILocalizationManager localizationManager, Logger logger) {
this.localizationManager = localizationManager;
this.logger = logger;
}

private String localizeMessage(String messageKey, Object... args) {
return localizationManager.getLocalizedMessage(messageKey, args);
}

@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));
logger.info(message);
}

@Override
public void info(String messageKey, Object... args) {
logger.info(localizeMessage(messageKey, args));
}

@Override
public void debug(String messageKey, Object... args) {
logger.debug(localizeMessage(messageKey, args));
}

@Override
public void debug(String messageKey, Throwable throwable, Object... args) {
logger.debug(localizeMessage(messageKey, args), throwable);
}

@Override
public void warn(String messageKey, Object... args) {
logger.warn(localizeMessage(messageKey, args));
}

@Override
public void error(String messageKey, Object... args) {
logger.error(localizeMessage(messageKey, args));
}

@Override
public void fatal(String messageKey, Throwable throwable, Object... args) {
logger.fatal(localizeMessage(messageKey, args), throwable);
}
}
11 changes: 11 additions & 0 deletions src/main/resources/localization/be.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"loc.clicking": "Націскаем",
"loc.el.getattr": "Атрымліваем атрыбут '%1$s'",
"loc.get.text": "Атрымліваем тэкст элемента",
"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' прайшоў няўдала"
}
11 changes: 11 additions & 0 deletions src/main/resources/localization/en.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"loc.clicking": "Clicking",
"loc.el.getattr": "Getting attribute '%1$s'",
"loc.get.text": "Getting text from element",
"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"
}
11 changes: 11 additions & 0 deletions src/main/resources/localization/ru.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"loc.clicking": "Клик",
"loc.el.getattr": "Получение аттрибута '%1$s'",
"loc.get.text": "Получение текста элемента",
"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' прошел неудачно"
}
6 changes: 4 additions & 2 deletions src/main/resources/log4j.properties
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
log4j.rootLogger=INFO, stdout, file
log4j.rootLogger=DEBUG, stdout, file

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.encoding=UTF-8
log4j.appender.stdout.target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p - %m%n
log4j.appender.stdout.Threshold=INFO

log4j.appender.file = org.apache.log4j.RollingFileAppender
log4j.appender.file.File = target/log/log.log
log4j.appender.file.MaxFileSize=10MB
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p - %m%n
log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p - %m%n
log4j.appender.file.Threshold=DEBUG
114 changes: 114 additions & 0 deletions src/test/java/tests/localization/LocalizationManagerTests.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package tests.localization;

import aquality.selenium.core.configurations.ILoggerConfiguration;
import aquality.selenium.core.localization.ILocalizationManager;
import aquality.selenium.core.localization.LocalizationManager;
import aquality.selenium.core.logging.Logger;
import org.testng.Assert;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import tests.application.CustomAqualityServices;
import tests.application.browser.AqualityServices;

import java.util.MissingFormatArgumentException;

import static org.testng.Assert.assertEquals;

public class LocalizationManagerTests {
private static final String[] SUPPORTED_LANGUAGES = new String[]{"be", "en", "ru"};
private static final String CLICKING_MESSAGE_KEY = "loc.clicking";

@DataProvider
private Object[] keysWithParams() {
return new String[]{
"loc.el.getattr",
"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"};
}

@DataProvider
private Object[] keysWithoutParams() {
return new String[]{
CLICKING_MESSAGE_KEY,
"loc.get.text"};
}


private LocalizationManager getLocalizationManager() {
return new LocalizationManager(
AqualityServices.getServiceProvider().getInstance(ILoggerConfiguration.class),
Logger.getInstance());
}

private LocalizationManager getLocalizationManager(String language) {
return new LocalizationManager(() -> language, Logger.getInstance());
}

@Test
public void testShouldReturnUnknownKey() {
String unknownKey = "loc.unknown.fake.key";
Assert.assertEquals(unknownKey, getLocalizationManager().getLocalizedMessage(unknownKey));
}

@Test
public void testShouldBeRegisteredAsSingleton() {
assertEquals(CustomAqualityServices.getServiceProvider().getInstance(ILocalizationManager.class),
CustomAqualityServices.getServiceProvider().getInstance(ILocalizationManager.class));
}

@Test
public void testShouldBePossibleToUseForClicking() {
assertEquals(getLocalizationManager().getLocalizedMessage(CLICKING_MESSAGE_KEY), "Clicking",
"Logger should be configured in English by default and return valid value");
}

@Test
public void testShouldBePossibleToUseForClickingWithCustomLanguage() {
assertEquals(getLocalizationManager("be").getLocalizedMessage(CLICKING_MESSAGE_KEY), "Націскаем",
"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) {
Assert.assertThrows(MissingFormatArgumentException.class,
() -> getLocalizationManager(language).getLocalizedMessage(keyWithParams));
}
}

@Test(dataProvider = "keysWithoutParams")
public void testShouldReturnNonKeyAndNonEmptyValuesForKeysWithoutParams(String keyWithoutParams) {
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));
Assert.assertNotEquals(value, keyWithoutParams,
String.format("value of key %1$s in language %2$s should be defined", keyWithoutParams, language));
}
}

@Test(dataProvider = "keysWithParams")
public void testShouldReturnNonKeyAndNonEmptyValuesForKeysWithParams(String keyWithParams) {
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));
Assert.assertNotEquals(value, keyWithParams,
String.format("value of key %1$s in language %2$s should be defined", keyWithParams, language));
Assert.assertTrue(value.contains(params[0].toString()),
String.format("value of key %1$s in language %2$s should contain at least first parameter",
keyWithParams, language));
}
}

@Test
public void testShouldThrowWhenInvalidLanguageSupplied() {
Assert.assertThrows(IllegalArgumentException.class, () ->
getLocalizationManager("invalid").getLocalizedMessage(CLICKING_MESSAGE_KEY));
}
}
1 change: 1 addition & 0 deletions src/test/resources/TestSuite.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
<class name="tests.configurations.TimeoutConfigurationTests"/>
<class name="tests.configurations.RetryConfigurationTests"/>
<class name="tests.configurations.ElementCacheConfigurationTests"/>
<class name="tests.localization.LocalizationManagerTests"/>
</classes>
</test>
<test name="Use cases tests" parallel="methods" thread-count="10">
Expand Down