Вы не можете сделать это с простым ванильным ароматом PageObjects и PageFactory.
Вам потребуется выполнить некоторые настройки, после чего вы сможете это сделать.
Чтобы это сделать, выв основном потребуется следующее
- Настроенная реализация
org.openqa.selenium.support.pagefactory.ElementLocator
, чтобы вы могли иметь возможность обрабатывать вызовы findElement
и findElements
, используя предоставленный экземпляр WebDriver
.Реализация по умолчанию просто запрашивает аннотации. - Настраиваемая реализация
org.openqa.selenium.support.pagefactory.ElementLocatorFactory
, так что она может создавать ваши настроенные ElementLocator
экземпляры вместо стандартных. - Настраиваемый анализатор аннотаций, такчто вы можете анализировать пользовательские аннотации, добавленные к объектам
WebElement
, расширяя org.openqa.selenium.support.pagefactory.AbstractAnnotations
- И последнее, но не менее важное: пользовательскую аннотацию, которая захватывает некоторые метаданные, которые можно использовать для чтения локатора.из файла JSON для данного веб-элемента
Со всеми этими функциями вы можете сделать следующее
- Определить свою пользовательскую аннотацию таким образом, чтобы она позволяла идентифицировать локатор уникальным образом изfile.
- Определите пользовательский анализатор аннотаций, который будет считывать атрибуты вашей пользовательской аннотации и создаст
By
объект - Определите пользовательский
ElementLocator
так, чтобы он использовал пользовательский анализатор аннотаций для поискаelements. - Определите пользовательский
ElementLocatorFactory
так, чтобы он создавал экземпляры пользовательского ElementLocator
.
Я написал всю эту концепцию в качестве блога некоторое время назад.Пожалуйста, посмотрите на это здесь
Чтобы получить полное представление о том, как работает PageFactory, взгляните на этот блог, который я создал.
Кто-то воспринял мой блог как отправную точку и создал библиотеку, которая может сделать все это для вас.Вы можете использовать его здесь https://github.com/shchukax/search-with
Ради полноты я включаю все фрагменты кода из моего блога
json будет выглядеть так
[
{
"pageName": "HomePage",
"name": "abTesting",
"locateUsing": "xpath",
"locator": "//a[contains(@href,'abtest')]"
},
{
"pageName": "HomePage",
"name": "checkBox",
"locateUsing": "xpath",
"locator": "//a[contains(@href,'checkboxes')]"
},
{
"pageName": "CheckboxPage",
"name": "checkBox1",
"locateUsing": "xpath",
"locator": "//input[@type='checkbox'][1]"
},
{
"pageName": "CheckboxPage",
"name": "checkBox2",
"locateUsing": "xpath",
"locator": "//input[@type='checkbox'][2]"
}
]
Пользовательская аннотация будет выглядеть следующим образом:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention (RetentionPolicy.RUNTIME)
@Target (ElementType.FIELD)
public @interface SearchWith {
String inPage() default "";
String locatorsFile() default "";
String name() default "";
}
Пользовательская ElementLocator
реализация
import org.openqa.selenium.By;
import org.openqa.selenium.SearchContext;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.pagefactory.AbstractAnnotations;
import org.openqa.selenium.support.pagefactory.ElementLocator;
import java.util.List;
public class FileBasedElementLocator implements ElementLocator {
private final SearchContext searchContext;
private final boolean shouldCache;
private final By by;
private WebElement cachedElement;
private List<WebElement> cachedElementList;
public FileBasedElementLocator(SearchContext searchContext, AbstractAnnotations annotations) {
this.searchContext = searchContext;
this.shouldCache = annotations.isLookupCached();
this.by = annotations.buildBy();
}
@Override
public WebElement findElement() {
if (cachedElement != null && shouldCache) {
return cachedElement;
}
WebElement element = searchContext.findElement(by);
if (shouldCache) {
cachedElement = element;
}
return element;
}
@Override
public List<WebElement> findElements() {
if (cachedElementList != null && shouldCache) {
return cachedElementList;
}
List<WebElement> elements = searchContext.findElements(by);
if (shouldCache) {
cachedElementList = elements;
}
return elements;
}
}
Пользовательская фабрика локатора элементов будет выглядеть следующим образом
import org.openqa.selenium.SearchContext;
import org.openqa.selenium.support.pagefactory.ElementLocator;
import org.openqa.selenium.support.pagefactory.ElementLocatorFactory;
import java.lang.reflect.Field;
public class FileBasedElementLocatorFactory implements ElementLocatorFactory {
private final SearchContext searchContext;
public FileBasedElementLocatorFactory(SearchContext searchContext) {
this.searchContext = searchContext;
}
@Override
public ElementLocator createLocator(Field field) {
return new FileBasedElementLocator(searchContext, new CustomAnnotations(field));
}
}
Customреализация AbstractAnnotations
import com.google.common.base.Preconditions;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import org.openqa.selenium.By;
import org.openqa.selenium.support.CacheLookup;
import org.openqa.selenium.support.pagefactory.AbstractAnnotations;
import organized.chaos.annotations.SearchWith;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.lang.reflect.Field;
import java.util.Iterator;
class CustomAnnotations extends AbstractAnnotations {
private final Field field;
CustomAnnotations(Field field) {
this.field = field;
}
@Override
public By buildBy() {
SearchWith search = field.getAnnotation(SearchWith.class);
Preconditions.checkArgument(search != null, "Failed to locate the annotation @SearchWith");
String elementName = search.name();
String pageName = search.inPage();
String locatorsFile = search.locatorsFile();
Preconditions
.checkArgument(isNotNullAndEmpty(elementName), "Element name is not found.");
Preconditions.checkArgument(isNotNullAndEmpty(pageName), "Page name is missing.");
Preconditions.checkArgument(isNotNullAndEmpty(locatorsFile), "Locators File name not provided");
File file = new File(locatorsFile);
Preconditions.checkArgument(file.exists(), "Unable to locate " + locatorsFile);
try {
JsonArray array = new JsonParser().parse(new FileReader(file)).getAsJsonArray();
Iterator&lt;JsonElement&gt; iterator = array.iterator();
JsonObject foundObject = null;
while (iterator.hasNext()) {
JsonObject object = iterator.next().getAsJsonObject();
if (pageName.equalsIgnoreCase(object.get("pageName").getAsString()) &&
elementName.equalsIgnoreCase(object.get("name").getAsString())) {
foundObject = object;
break;
}
}
Preconditions.checkState(foundObject != null, "No entry found for the page [" + pageName + "] in the "
+ "locators file [" + locatorsFile + "]");
String locateUsing = foundObject.get("locateUsing").getAsString();
if (! ("xpath".equalsIgnoreCase(locateUsing))) {
throw new UnsupportedOperationException("Currently " + locateUsing + " is NOT supported. Only xPaths "
+ "are supported");
}
String locator = foundObject.get("locator").getAsString();
Preconditions.checkArgument(isNotNullAndEmpty(locator), "Locator cannot be null (or) empty.");
return new By.ByXPath(locator);
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
}
}
@Override
public boolean isLookupCached() {
return (field.getAnnotation(CacheLookup.class) != null);
}
private boolean isNotNullAndEmpty(String arg) {
return ((arg != null) && (! arg.trim().isEmpty()));
}
}
Вот как может выглядеть класс объекта страницы
import org.openqa.selenium.WebElement;
import organized.chaos.annotations.SearchWith;
public class HomePage {
public static final String PAGE = "HomePage";
@SearchWith (inPage = HomePage.PAGE, locatorsFile = "src/main/resources/locators.json", name = "abTesting")
private WebElement abTestingLink = null;
@SearchWith (inPage = HomePage.PAGE, locatorsFile = "src/main/resources/locators.json", name = "checkBox")
private WebElement checkBoxLink = null;
public HomePage() {
}
public CheckBoxPage navigateToCheckBoxPage() {
checkBoxLink.click();
return new CheckBoxPage();
}
}
Другой класс страницы
import org.openqa.selenium.WebElement;
import organized.chaos.annotations.SearchWith;
public class CheckBoxPage {
private static final String PAGE = "CheckBoxPage";
@SearchWith (inPage = CheckBoxPage.PAGE, locatorsFile = "src/main/resources/locators.json", name = "checkBox1")
private WebElement checkBoxOne;
@SearchWith (inPage = CheckBoxPage.PAGE, locatorsFile = "src/main/resources/locators.json", name = "checkBox2")
private WebElement checkBoxTwo;
public void unCheckCheckBoxTwo() {
if (checkBoxTwo.isSelected()) {
checkBoxTwo.click();
}
}
public boolean isCheckBoxTwoUnchecked() {
return (! checkBoxTwo.isSelected());
}
}
А вот тестовый класс, которыйпотребляет это
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.remote.RemoteWebDriver;
import org.openqa.selenium.support.PageFactory;
import org.openqa.selenium.support.pagefactory.ElementLocatorFactory;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import organized.chaos.pages.CheckBoxPage;
import organized.chaos.pages.HomePage;
import organized.chaos.support.FileBasedElementLocatorFactory;
public class AlteredPageFactoryDemo {
private RemoteWebDriver driver;
@BeforeClass
public void setup() {
driver = new ChromeDriver();
}
@AfterClass
public void tearDown() {
if (driver != null) {
driver.quit();
}
}
@Test
public void testMethod() {
driver.get("https://the-internet.herokuapp.com/");
HomePage homePage = new HomePage();
ElementLocatorFactory factory = new FileBasedElementLocatorFactory(driver);
PageFactory.initElements(factory, homePage);
CheckBoxPage checkboxPage = homePage.navigateToCheckBoxPage();
PageFactory.initElements(factory, checkboxPage);
checkboxPage.unCheckCheckBoxTwo();
Assert.assertTrue(checkboxPage.isCheckBoxTwoUnchecked());
}
}