Есть отличная удаленная работа для php+codeception+jenkins+allure+docker спецов. 100% remote! Присоединиться к проекту

Page Factory - Нужна помощь с нестандартным решением

page-factory
page-object
selenium
webdriver
Теги: #<Tag:0x00007f7b6526fb28> #<Tag:0x00007f7b6526f9e8> #<Tag:0x00007f7b6526f880> #<Tag:0x00007f7b6526f718>

(Srx) #1

Привет

Для ввода текста в текстовое поле нужно 2 степа:

  1. Click() на поле с одним ID
  2. SendKeys() в поле с другим ID

Пример:

public AccountPage typePhone(String s) {
	driver.findElement(By.id("telephone")).click();
	driver.findElement(By.id("telephone_i")).sendKeys(s);
	return this;
	
}

Как это выглядило бы с Page Factory:

@FindBy(how = How.ID, using = "telephone")
private WebElement txtTelephone;

@FindBy(how = How.ID, using = "telephone_i")
private WebElement txtTelephone2;

public AccountPage typePhone(String s) {
	txtTelephone.click();
	txtTelephone2.sendKeys(s);
	return this;
	
}

Проблема: как уменшить код и избавится от второго FindBy если я знаю что в конце второго ID будет всегда +"_i"

как я пытался: сделать чтото типо фреймворка/обертки которая будет доставать из елемента ID и добавлять " _i" (id +" _i"). Проблема в том что мне никак не вытащить ид из элемента.

@FindBy(how = How.ID, using = "telephone")
private WebElement txtTelephone;


public AccountPage typePhone(String s) {
	EnterText(txtTelephone, s);
	return this;
	
}


//В новом общем классе:
public void EnterText (WebElemenet element, String s) {	
	element.Click();

	//Вот тут проблема, не знаю как достать id
	String tmpstr = element.toString();

	//Добавляем к id элемента +"_i"
	driver.findElement(By.id(tmpstr+"_i")).sendKeys(s);

}

Есть идеи?


#2

Достать id можно из webElement с помощью http://selenium.googlecode.com/git/docs/api/java/org/openqa/selenium/WebElement.html#getAttribute-java.lang.String- .


(Dimencion Rider) #3

Case1:
можна написати css selector, типу input[id*=‘telephone’]
він знаходитиме всі id-шки що contains ‘telephone’

Case2:
Можна скористатись методом getAttribute і витягнти в String,
а вже потім працювати з цим текстом


(Piotr_Iazadji) #4

если на странице нет больше инпутов содержащих telephone:

@FindBy(how = How.Xpath, using = “//input[contains(@id, ‘telephone’)]”)
private WebElement txtTelephone;

если есть, то просто Xpath надо усложнить.


(dirzhov) #5

тут пэйдж фэктори не поможет… либо расширять пэйджфэктори, либо написать метод типа:
public WebElement getPhone(int number) {
if (number==0)
return driver.findElement(By.id(“telephone”));
else
return driver.findElement(By.id(String.format(“telephone_%s”, number)));
}

ну и вызов:
page.getPhone(0).sendKeys(“tralala”)

но удобней конечно же работать с прокси объектами, а не с объектами, возвращаемыми findElement… но это отдельная тема)


(Goshko Nazar) #6

а если завтра верстка поменяется? что вы будете делать? заново переписывать Factory и сами тесты?

Как по мне, лучше, метод - функциональность, поле/кнопка - аттрибут.


(dirzhov) #7

Если завтра верстка поменяется? Вы имеете в виду айдишники? Если так, то уже ничего не поможет) один путь - где-то что-то менять:) а по поводу пэйдж фэктори… тут нужно подумать… в данном случае может имеет смысл использовать еще так:

@FindBy(how = How.Xpath, using = "//input[contains(@id, 'telephone')]")
private List<WebElement> txtTelephone;

public AccountPage typePhone(String s) {
	EnterText(txtTelephone, s);
	return this;
}
public void EnterText (List<WebElement> element, String s) {
	element.get(0).click();
	element.get(1).sendKeys(s);
}

я изначально подумал, что элементов может быть N-ое число) а так с 2 элементами и проблем нет:smile: хотя этот вариант подойдет и для N элементов


(Srx) #8

dirzhov, последний вариант интерестный!. попробую отпишусь


(Srx) #9

Спасибо! Этот подход работает! Тестил на C#

    //находит 2 элемента с которыми я потом буду работать
    [FindsBy(How = How.XPath, Using = ".//*[@id='telephone' or @id='telephone_i']")] 
    public IList<IWebElement> txtPhone { get; set; }

    internal AccountPage  TypePhone(string value)
    {
        EnterText(txtPhone, value);
        return this;
    }

    private void EnterText(IList<IWebElement> element, string value)
    {
        element[0].Click();
        element[1].SendKeys(value);
    }

Дополнительный вопрос: На сколько правильно будет использовать такой подход с точки зрения написания тестов (скорость выполнения кода/нахождения элементов или др аспекты) по сравнению с этим:

    [FindsBy(How = How.Id, Using = "id=telephone")]
    public IWebElement txtPhone { get; set; }

    [FindsBy(How = How.Id, Using = "id=telephone_i")]
    public IWebElement txtPhone2 { get; set; }

    internal AccountPage TypePhone(string value)
    {
        EnterText(txtPhone, txtPhone2, value);
        return this;

    }

    internal void EnterText(IWebElement element, IWebElement element2, string value)
    {
        element.Click();
        element2.SendKeys(value);
    }

(Taras) #10

правильний только тогда если при каждом обращении по такому локатору будет только один елемент находить, а так можно просто написать локатор через contains как советовали више…вообше что ето за прикол со сменой id ? Пните UI разработчика - пусть class меняет при таком поведении , а не id


(Srx) #11

Using = “.//*[@id=‘telephone’ or @id=‘telephone_i’]” какраз находит 2 элемента с которыми я потом буду работать для ввода текста. один на который кликнуть, тогда второй становится “visible” и можно ввести текст.

Пните UI разработчика - пусть class меняет при таком поведении , а не id

Я бы с радостью:) К сожалению это Out-Of box продукт Microsoft Dynamics CRM (который тяжело кастомизируется под нужды клиента)


(Pasha Balakhonov) #12

Эмммм… А про функцию contains в xpath кто-то слышал вообще??? =)

@FindBy(xpath = “//input[contains(@id, “telephone”)]”)
private WebElement telephoneField;

public void setTelephone(String number) {
telephoneField.click();
telephoneField.sendKeys(number);
}


(Pasha Balakhonov) #13

И мне совсем непонятно, нафига для одного поля два локатора писать. =)


(Srx) #14

Да, но где гарантия что драйвер кликнет именно на нужный элемент (из найденных двух) а не на другой.

Надо чтобы клик был на telephone, а на telephone_i - sendkeys


(Pasha Balakhonov) #15

Тогда вообще не вижу проблемы. Просто пишем два элемента и все.


(Pasha Balakhonov) #16

Изначально я думал, что при клике на элементе к его id добавляется _id, поэтому и предложил один элемент с функцией contains/ =)


(Piotr_Iazadji) #17

А вы уверены, что элемент с id=‘telephone_i’ существует в дереве элементов одновременно с элементом c id=‘telephone’ ? Я понял из примера, что или первый или второй отображается в дереве элементов.