Добрый день. Столкнулся с небольшой проблемой при разработке тестов c PageFactory и Selenide.
Проблема состоит в том, что не находятся элементы(NoSuchException), расположенные внутри iframe - элементы внутри “fooFrame” из примера, находятся только элементы из Header.
Пример:
public class MainPage {
@FindBy(id = "fooFrame") //фрейм
private FooFrame fooFrame;
@FindBy(id = "header")
private Header header;
public Header getHeader() {
return header;
}
public FooFrame getFooFrame() {
return fooFrame;
}
public class FooFrame extends ElementsContainer {
@FindBy(id = "otherFrame") //фрейм
private OtherForm otherForm;
@FindBy(id = "fooInput")
private SelenideElement fooInput;
@FindBy(id = "fooButton")
private SelenideElement fooButton;
public SelenideElement getFooInput() {
return fooInput;
}
public SelenideElement getFooButton() {
return fooButton;
}
public OtherForm getOtherForm() {
return otherForm;
}
}
Я понимаю, но как это сделать в концепции с page-factory (@FindBy)?
Раньше у меня был класс-расширение FooBy, где элементы объявлялись так:
private final static FooBy FOO_INPUT = FooBy.id("fooInput").inFrame(FOO_FRAME); - указывался фрейм, в котором находится элемент.
private final static FooBy FOO_FRAME = ExBy.id("fooFrame");
Внутри класса FooBy:
public FooBy inFrame(By frameId) {
this.frame = frameId;
return this;
}
переопределены методы findElement, findElements и в них вызывается метод
Selenide.switchTo().frame($(frame));
При работе с page-factory ориентировался также на статью:
Мабуть ще один мінус в сторону підходу з PageFactory. Якщо просто інкапсулювати в методах пейджобджектів якісь “бізнес” дії допущені на сторінці і не виносити локатори\елементи в філди то й проблем таких не виникатиме.
Не будут ли в этом случае классы с pageobject излишне перегруженными, когда в одном классе и field, и бизнес методы. Основная идея использования pageFactory у нас была в том, чтобы переиспользовать блоки ElementsContainer на других Page.
Да, я читал несколько комментариев в других темах, почему Selenide хоть и поддерживает PageFactory, но вы не рекомендуете его использовать, но пока альтернативного архитектурного решения у нас не наблюдается.
Справа в тому, що в більшості випадків філди-елементи і їхні гетери є зайвими. Краще просто “бізнес” дії, щоб в тесті було якось так:
FooFrame.SetFoo(“12345”).ClickFoo();
//next actions here or needed assertions
Привет!
Не понял, чего именно не наблюдается? Это же очень просто делается безо всяких “архитектурных решений” и фабрик. Например, так (схематично):
public class FooFrame {
private By fooFrame = By.id("fooFrame");
private By fooInput = By.id("fooInput");
private By fooButton = By.id("fooButton");
public doFooBusinessOperation(String businessParameter) {
switchTo(frame(fooFrame));
$(fooInput).val(businessParameter);
$(fooButton).click();
switchTo(defaultFrame());
}
}
ну я в свое время сделал такое, и у меня теперь своя аннотация @Chunk, которая решает проблему вставки фрейма як части елемента страници либо даже другого куска страници как поле класа другого Page Object-a, ну и @FindBy тоже можно использовать в паралель
Как раз таки в дополнение к тому, что Солнцев написал. И чтобы обращаться к таким объектам, надо написать просто new FooFrame(); И работай с полями своего объекта как хочешь) И не нужно писать аннотации, гетеры, особенно последнее не стоит писать
class MainPage{
void doBusinessOpInFooFrame(){
switchTo(frame(fooFrame));
$(fooInput).val(businessParameter);
$(fooButton).click();
switchTo(defaultFrame());
}
class ClassWithTests{
@Test
public void fooTest(){
open("/");
new MainPage().doBusinessOpInFooFrame();
}
}
Если хочется, то сделай метод MainPage openMainPage() в MainPage, который вернёт this;
openBrowser() - обычный метод инициализации браузера и открытия стартовой страницы. Да, с названием метода промахнулся.
На самом деле структура страницы немного посложнее и обычно состоит как минимум из 3 вложенных фреймов, т.е. по аналогии может быть: