Создать PageElement на Selenide - правильный пример

Проблема (Вопрос) заключается …
Сделать кастомный input в фрейме на Selenide. Как правильно сделать? Пробовал инициализировать через Selenium, nullPointer при попытке взаимодействия с классом.
Мой imput должен содержать WebElement или SelenideElement внутри?
Должен ли я инициализировать аткой жлемент или селенид это должен сделать?

Я попытался сделать …
Все элементы сделаны на страницах как:
public SelenideElement nextButton = $x("//div[(text() =‘Skip’)]/following-sibling::div[@class]");

У меня получилось …
Все работает пока не начал делать PageElement. Вот:
@FindBy(xpath = “//h1[text()=‘Company name’]/following-sibling::input”)
private Input companyNameInput;

У меня не получилось …
Сделать PageElement класс Input.

Код

import lombok.Getter;
import org.openqa.selenium.Keys;
import org.openqa.selenium.WebElement;

@Getter
public class Input {

    private WebElement input;
    private String xpath;
    private String id;
    private String placeholder;

    public void setInput(WebElement input) {
        this.input = input;
    }

    public Input(WebElement input) {
        setInput(input);
        id = input.getAttribute("id");
        placeholder = input.getAttribute("placeholder");
    }

    public void sendKeys(CharSequence... keysToSend) {
        input.sendKeys(keysToSend);
    }

    public void clear() {
        input.click();
        input.sendKeys(Keys.CONTROL + "A");
        input.sendKeys(Keys.BACK_SPACE);
        input.sendKeys(Keys.ENTER);
    }

    public WebElement pressEnter() {
        input.sendKeys(Keys.ENTER);
        return input;
    }

    public void click() {
        input.click();
    }

Вот где nullpointer - greetAndDepot.getCompanyNameInput().sendKeys(“someCompany”);

Версии ОС и софта следующие … win 10, java 1.8

Вот так начинаю работать:
GreetingFleetAndDepot greetAndDepot = Selenide.page(GreetingFleetAndDepot.class);

Создаю pageObject страницы, там описаны SelenideElement и ElementsCollection. Вынес на страницы какие нужно методы, инкапсулировал тобишь. Ок. Теперь хочу сделать общий инпут на все эти страницы, потмоу что он одинаковый на всех трех. Как сделать то его?

Есть годные примеры? Поиск дает только одни pageobject у всех на уме.

Также такая ошибка при старте.
java.lang.IllegalArgumentException: Can not set com.here.selenide.elements.Input field com.selenide.webpages.GreetingFleetAndDepot.companyNameInput to null value

Должен ли мой page element имплементить SelenideElement или WebElement?

Класс Input должен наследоваться от ElementsContainer:

public class Input extends ElementsContainer {...}

Примеры, как всегда, можно посмотреть в тестах самого селенида:

P.S. В данном случае не вижу никакой необходимости создавать класс Input. В нём же нет ничего полезного, кроме обычного метода sendKeys, который и так есть у всех элементов. Не нужно плодить сущности без необходимости!

Я делаю чтобы очищать поле и многократно вводить. Селенидовский clear() не очищает его. Чтобы сокрыть костыли оригинального селениум, ctrl+a и прочее.

@Getter
public class Input extends ElementsContainer {

private SelenideElement input;
private String id;
private String placeholder;

public Input(SelenideElement input) {
    setInput(input);
    id = input.getAttribute("id");
    placeholder = input.getAttribute("placeholder");
}

public void setInput(SelenideElement input) {
    this.input = input;
}

public void sendKeys(CharSequence... keysToSend) {
    input.sendKeys(keysToSend);
}

public void clear() {
    input.click();
    input.sendKeys(Keys.CONTROL + "A");
    input.sendKeys(Keys.BACK_SPACE);
    input.sendKeys(Keys.ENTER);
}

public SelenideElement pressEnter() {
    input.sendKeys(Keys.ENTER);
    return input;
}

public void click() {
    input.click();
}

Вот инициализирую:

GreetingFleetAndDepot greetAndDepot = Selenide.page(GreetingFleetAndDepot.class);

Вот оно в pageObject:

@FindBy(xpath = "//h1[text()='Company name']/following-sibling::input")
private Input companyNameInput;

Получаю когда делаю например:
greetAndDepot.getCompanyNameInput().sendKeys(“someCompany”):

com.codeborne.selenide.ex.PageObjectException: Failed to create elements container for field companyNameInput

at com.codeborne.selenide.impl.SelenidePageFactory.createElementsContainer(SelenidePageFactory.java:128)
at com.codeborne.selenide.impl.SelenidePageFactory.decorate(SelenidePageFactory.java:193)
at com.codeborne.selenide.impl.SelenidePageFactory.initFields(SelenidePageFactory.java:85)
at com.codeborne.selenide.impl.SelenidePageFactory.initElements(SelenidePageFactory.java:74)
at com.codeborne.selenide.impl.SelenidePageFactory.page(SelenidePageFactory.java:61)
at com.codeborne.selenide.impl.SelenidePageFactory.page(SelenidePageFactory.java:49)
at com.codeborne.selenide.SelenideDriver.page(SelenideDriver.java:140)
at com.codeborne.selenide.Selenide.page(Selenide.java:838)
at com.here.selenide.tests.ThreeHappyPathManual.test(ThreeHappyPathManual.java:29)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:133)
at org.testng.internal.TestInvoker.invokeMethod(TestInvoker.java:598)
at org.testng.internal.TestInvoker.invokeTestMethod(TestInvoker.java:173)
at org.testng.internal.MethodRunner.runInSequence(MethodRunner.java:46)
at org.testng.internal.TestInvoker$MethodInvocationAgent.invoke(TestInvoker.java:824)
at org.testng.internal.TestInvoker.invokeTestMethods(TestInvoker.java:146)
at org.testng.internal.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:146)
at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:128)
at java.util.ArrayList.forEach(ArrayList.java:1259)
at org.testng.TestRunner.privateRun(TestRunner.java:794)
at org.testng.TestRunner.run(TestRunner.java:596)
at org.testng.SuiteRunner.runTest(SuiteRunner.java:377)
at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:371)
at org.testng.SuiteRunner.privateRun(SuiteRunner.java:332)
at org.testng.SuiteRunner.run(SuiteRunner.java:276)
at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:53)
at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:96)
at org.testng.TestNG.runSuitesSequentially(TestNG.java:1212)
at org.testng.TestNG.runSuitesLocally(TestNG.java:1134)
at org.testng.TestNG.runSuites(TestNG.java:1063)
at org.testng.TestNG.run(TestNG.java:1031)
at com.intellij.rt.testng.IDEARemoteTestNG.run(IDEARemoteTestNG.java:66)
at com.intellij.rt.testng.RemoteTestNGStarter.main(RemoteTestNGStarter.java:109)

Caused by: java.lang.NoSuchMethodException: com.here.selenide.elements.Input.()
at java.lang.Class.getConstructor0(Class.java:3082)
at java.lang.Class.getDeclaredConstructor(Class.java:2178)
at com.codeborne.selenide.impl.SelenidePageFactory.initElementsContainer(SelenidePageFactory.java:154)
at com.codeborne.selenide.impl.SelenidePageFactory.initElementsContainer(SelenidePageFactory.java:137)
at com.codeborne.selenide.impl.SelenidePageFactory.createElementsContainer(SelenidePageFactory.java:125)
… 35 more

Ясно.
Маленькое уточнение: не “Селенидовский clear()”, а “вебдрайверовский clear()” :slight_smile:

Чтобы подменить стандартную реализацию clear своим костылём, вовсе не нужно создавать свои компоненты, ElementsContainer и т.д. Достаточно переопределить метод “clear”. Что-то типа Commands.getInstance().add("clear", new MyClearImplementation());.

Но есть способ ещё проще:
Configuration.fastSetValue = true;

И можно забыть обо всех проблемах с clear.

P.S. Конкретно ваша ошибка вызвана тем, что в классе Input нет дефалтового конструктора. А как же вам селенид его создаст тогда без конструктора?

1 лайк