web-gelistirme-sc.com

Selenyum Web Sürücüsü WebElement'i genişletilsin mi?

Selenyum tarafından önerilen Sayfa Nesnesi modelini takip ediyorum, ancak bir sayfa için nasıl daha özel bir WebElement oluşturabilirim. Özellikle, sayfalarımızda tablolar var ve bir tablonun belirli satırlarını almak, tablonun içeriğini döndürmek vb. İçin bazı yardımcı işlevler yazdım.

Şu anda, burada bir tabloya sahip oluşturduğum bir sayfa nesnesinin pasajı:

public class PermissionsPage  {

    @FindBy(id = "studyPermissionsTable")
    private WebElement permissionTable;

    @FindBy(id = "studyPermissionAddPermission")
    private WebElement addPermissionButton;

    ... 

}

Yani, yapmak istediğim şey şu izinlere sahip olmak: Daha önce bahsettiğim yöntemlerden bazılarına sahip daha özelleştirilmiş bir WebElement olabilme.

Örneğin:

public class TableWebElement extends WebElement {
    WebElement table;
    // a WebDriver needs to come into play here too I think

    public List<Map<String, String>> getTableData() {
        // code to do this
    }

    public int getTableSize() {
        // code to do this
    }

    public WebElement getElementFromTable(String id) {
        // code to do this
    }
}

Umarım bu açıklamaya çalıştığım şeyin anlamlıdır. Sanırım aradığım şey, bu özel WebElement öğesinin tabloya özgü bazı ek işlemler yapmasının bir yoludur. Bu özel öğeyi bir Sayfaya ekleyin ve Selenium'un ekleri temel alarak web bağlantılarını sayfaya kablolama şeklinden yararlanın.

Mümkün mü? Ve eğer öyleyse, bunun nasıl yapılabileceğini bilen var mı?

13
pipplupp

Tüm WebDriver arabirimlerini birleştiren bir arabirim oluşturdum:

public interface Element extends WebElement, WrapsElement, Locatable {}

Bir öğeyi kaydırırken, WebElements öğelerinin yapabileceği her şeyi toparlamak için var.

Sonra bir uygulama:

public class ElementImpl implements Element {

    private final WebElement element;

    public ElementImpl(final WebElement element) {
        this.element = element;
    }

    @Override
    public void click() {
        element.click();
    }

    @Override
    public void sendKeys(CharSequence... keysToSend) {
        element.sendKeys(keysToSend);
    }

    // And so on, delegates all the way down...

}

Ardından, örneğin bir onay kutusu:

public class CheckBox extends ElementImpl {

    public CheckBox(WebElement element) {
        super(element);
    }

    public void toggle() {
        getWrappedElement().click();
    }

    public void check() {
        if (!isChecked()) {
            toggle();
        }
    }

    public void uncheck() {
        if (isChecked()) {
            toggle();
        }
    }

    public boolean isChecked() {
        return getWrappedElement().isSelected();
    }
}

Komut dosyasımda kullanırken:

CheckBox cb = new CheckBox(element);
cb.uncheck();

Ayrıca Element sınıflarını sarmalamanın bir yolunu da buldum. Dahili PageFactory yerine bir kaç fabrika oluşturmanız gerekir, ancak yapılabilir ve çok fazla esneklik kazandırır.

Bu işlemi sitemde belgeledim: 

Ayrıca bu ve diğer sorulardan ilham alan selophane adlı bir projem var: selophane

26
Elijah Sarver

WebElement arabirimini uygulayan bir WebComponent sınıfı sağlayan WebDriver Extensions framework'ü kullanarak özel WebElements oluşturabilirsiniz

Özel Web Öğenizi oluşturun

public class Table extends WebComponent {
    @FindBy(tagName = "tr")
    List<Row> rows;

    public Row getRow(int row) {
        return rows.get(row - 1);
    }

    public int getTableSize() {
        return rows.size();
    }

    public static class Row extends WebComponent {
        @FindBy(tagName = "td")
        List<WebElement> columns;

        public WebElement getCell(int column) {
            return columns.get(column - 1);
        }
    }
}

... ve sonra bunu bir @FindBy ek açıklamasıyla PageObject'inize ekleyin ve PageFactory.initElements yöntemini çağırırken WebDriverExtensionFieldDecorator öğesini kullanın

public class PermissionPage {
    public PermissionPage(WebDriver driver) {
        PageFactory.initElements(new WebDriverExtensionFieldDecorator(driver), this);
    }

    @FindBy(id = "studyPermissionsTable")
    public Table permissionTable;

    @FindBy(id = "studyPermissionAddPermission")
    public WebElement addPermissionButton;
}

... ve testinizde kullanın

public class PermissionPageTest {
    @Test
    public void exampleTest() {
        WebDriver driver = new FirefoxDriver();
        PermissionPage permissionPage = new PermissionPage(driver);

        driver.get("http://www.url-to-permission-page.com");
        assertEquals(25, permissionPage.permissionTable.getTableSize());
        assertEquals("READ", permissionPage.permissionTable.getRow(2).getCell(1).getText());
        assertEquals("WRITE", permissionPage.permissionTable.getRow(2).getCell(2).getText());
        assertEquals("EXECUTE", permissionPage.permissionTable.getRow(2).getCell(3).getText());
    }
}




Veya WebDriver Extensions PageObject uygulamasını kullanın.

public class PermissionPage extends WebPage {
    @FindBy(id = "studyPermissionsTable")
    public Table permissionTable;

    @FindBy(id = "studyPermissionAddPermission")
    public WebElement addPermissionButton;

    @Override
    public void open(Object... arguments) {
        open("http://www.url-to-permission-page.com");
        assertIsOpen();
    }

    @Override
    public void assertIsOpen(Object... arguments) throws AssertionError {
        assertIsDisabled(permissionTable);
        assertIsDisabled(addPermissionButton);
    }
}

ve JUnitRunner WebElements için statik varsayım yöntemleriyle

import static com.github.webdriverextensions.Bot.*;

@RunWith(WebDriverRunner.class)
public class PermissionPageTest {

    PermissionPage permissionPage;

    @Test
    @Firefox
    public void exampleTest() {
        open(permissionPage);
        assertSizeEquals(25, permissionPage.permissionTable.rows);
        assertTextEquals("READ", permissionPage.permissionTable.getRow(2).getCell(1));
        assertTextEquals("WRITE", permissionPage.permissionTable.getRow(2).getCell(2));
        assertTextEquals("EXECUTE", permissionPage.permissionTable.getRow(2).getCell(3));
    }
}
3
AndiDev

Ayrıca DefaultFieldDecorator'ı genişleterek özel WebElement uygulaması yarattım ve PageFactory deseni ile harika çalışıyor. Ancak PageFactory'nin kendisini kullanma konusunda endişelerim var. Yalnızca 'statik' uygulamalarla (kullanıcı etkileşiminin uygulamanın bileşen düzenini değiştirmeyeceği) harika çalışıyor gibi görünüyor. 

Ancak, biraz daha karmaşık bir şeyle çalışmak istediğinizde, örneğin tablo içeren bir sayfanız varsa, her kullanıcının yanında bulunan Kullanıcıların listesini ve sil düğmesini içeren, PageFactory kullanımı sorunlu hale gelir.

İşte neden bahsettiğimi açıklamak için bir örnek:

public class UserList
 {
private UserListPage page;

UserList(WebDriver driver)
{
    page = new UserListPage(driver);
}

public void deleteFirstTwoUsers()
{
    if (page.userList.size() <2) throw new RuntimeException("Terrible bug!");  

    page.deleteUserButtons.get(0).click();
    page.deleteUserButtons.get(0).click();
}

class UserListPage {

@FindBy(xpath = "//span[@class='All_Users']")
List<BaseElement> userList;

@FindBy(xpath = "//span[@class='All_Users_Delete_Buttons']")
List<BaseElement> deleteUserButtons;

UserListPage(WebDriver driver)
 {
    PageFactory.initElements(new ExtendedFieldDecorator(driver), this);
 }
}

Bu nedenle, yukarıdaki senaryoda, bir deleteFirstTwoUsers () yöntemini çağırdığında, ikinci Kullanıcıyı "StaleElementReferenceException: stale element reference: element sayfa dokümanına eklenmedi" ile silmeye çalışırken başarısız olur. Bunun nedeni, Yapıcı'da 'sayfa' sadece bir kez başlatılmış ve PageFactory'de 'bilme şansı yok' :), Kullanıcıların miktarının azaldığı için oluyor.

Şimdiye kadar bulduğum geçici çözümler eter: 

1) Page Factory'yi bu gibi yöntemler için tamamen kullanmayın ve WebDriver'ı doğrudan bir tür döngü sırasında kullanın: driver.findElementsBy()...

2) veya bunun gibi bir şey yapın:

public void deleteFirstTwoUsers()
{
    if (page.userList.size() <2) throw new RuntimeException("Terrible bug!");  

    new UserListPage(driver).deleteUserButtons.get(0).click();
    new UserListPage(driver).deleteUserButtons.get(1).click();
}

3) ya da bu:

public class UserList {
  private WebDriver driver;

UserList(WebDriver driver)
{
    this.driver = driver;
}

UserListPage getPage { return new UserListPage(driver);})
public void deleteFirstTwoUsers()
{
    if (getPage.userList.size() <2) throw new RuntimeException("Terrible bug!");  

    getPage.deleteUserButtons.get(0).click();
    getPage.deleteUserButtons.get(0).click();
}

Ancak ilk örneklerde, sarılmış BaseElement öğenizi kullanmaktan yararlanamazsınız. Ve ikinci ve üçüncü birinde - sayfada yaptığınız her işlem için yeni Sayfa Fabrikası örnekleri oluşturursunuz. Yani sanırım her şey iki soruya indirildi: 

1) PageFactory'yi herhangi bir şey için gerçekten kullanmanın gerçekten uygun olduğunu düşünüyor musunuz? Her bir öğeyi böyle oluşturmak çok daha ucuz olurdu:

class UserListPage
{
private WebDriver driver;

UserListPage(WebDriver driver)
{
    this.driver = driver;
}

List<BaseElement> getUserList() {
    return driver.findElements(By.xpath("//span[@class='All_Users']"));
}

2) @Override driver.findElements yöntemini kullanmak mümkün müdür, böylece sarılmış BaseElement nesnemin bir örneğini döndürür, WebElement değil mi? Olmazsa, alternatif var mı?

1
Vlad

Bunu takiben yaptığım şey budur (başkası da aynı sorunu yaşarsa). Aşağıda, bir WebElement öğesine sarıcı olarak oluşturduğum sınıfın bir parçası verilmiştir:

public class CustomTable {

private WebDriver driver;
private WebElement tableWebElement;

public CustomTable(WebElement table, WebDriver driver) {
    this.driver = driver;
    tableWebElement = table;
}

public WebElement getTableWebElement() {
    return tableWebElement;
}

public List<WebElement> getTableRows() {
    String id = tableWebElement.getAttribute("id");
    return driver.findElements(By.xpath("//*[@id='" + id + "']/tbody/tr"));
}

public List<WebElement> getTableHeader() {
    String id = tableWebElement.getAttribute("id");
    return tableWebElement.findElements(By.xpath("//*[@id='" + id + "']/thead/tr/th"));
}   
.... more utility functions here
}

Sonra bunu şöyle referans vererek yaratan tüm Sayfalarda kullanıyorum:

public class TestPage {

@FindBy(id = "testTable")
private WebElement myTestTable;

/**
 * @return the myTestTable
 */
public CustomTable getBrowserTable() {
    return new CustomTable(myTestTable, getDriver());
}

Sevmediğim tek şey, sayfa tablo almak istediğinde, "yeni" bir tablo oluşturmasıdır. Bunu, tablodaki veriler güncellendiğinde gerçekleşen StaleReferenceExeptions öğesini engellemek için yaptım (yani hücre güncellendi, satır eklendi, satır kaldırıldı). Herhangi biri, istediği her seferinde yeni bir örnek oluşturmaktan nasıl kaçınabileceğim konusunda herhangi bir önerim varsa, bunun yerine güncellenmiş WebElement'i döndürür!

1
pipplupp

Yan not: WebElement sınıf değil, interface, yani sınıfınız daha çok şöyle görünür:

public class TableWebElement implements WebElement {

Ancak bu durumda mustall yöntemini WebDriver'da olan yöntemleri uygularsınız. Ve biraz overkill.

İşte bunu nasıl yaptığım - Selenyum tarafından önerilen ve önerdiğim PageObjects'ten tamamen kurtuldum ve kendi derslerimi alarak "tekerleği yeniden icat ettim". Tüm başvuru için bir sınıfım var:

public class WebUI{
  private WebDriver driver;    
  private WebElement permissionTable;   

   public WebUI(){
      driver = new firefoxDriver();
   }

  public WebDriver getDriver(){
     return driver;
  }

  public WebElement getPermissionTable(){
     return permissionTable;
  }

  public TableWebElement getTable(){
     permissionTable = driver.findElement(By.id("studyPermissionsTable"));
     return new TableWebElement(this);
  }
}

Ve sonra yardımcı derslerim var

public class TableWebElement{
  private WebUI webUI;

 public TableWebElement(WebUI wUI){
    this.webUI = wUI;
 }

 public int getTableSize() {
    // because I dont know exactly what are you trying to achieve just few hints
    // this is how you get to the WebDriver:
    WebElement element = webUI.getDriver().findElement(By.id("foo"));

    //this is how you get to already found table:
    WebElement myTable = webUI.getPermissionTable();

 }

}

Test örneği:

 @Test
 public void testTableSize(){
    WebUI web = new WebUI();
    TableWebElement myTable = web.getTable();
    Assert.assertEquals(myTable.getSize(), 25);
 }
1
Pavel Janicek

htmlelements framework'e bir bakın, tam olarak ihtiyacınız olana benziyor. Onay kutusu, radyo kutusu, tablo, form vb. Önceden uygulanmış ortak öğelere sahiptir, kendi ağacınızı kolayca oluşturabilir ve açık bir ağaç yapısı oluşturarak diğerlerine bir öğe ekleyebilirsiniz.

0
artkoshelev

Neden bir web öğesi sargısı yapmıyorsunuz? bunun gibi?

class WEBElment
{

public IWebElement Element;

public WEBElement(/*send whatever you decide, a webelement, a by element, a locator whatever*/)

{

Element = /*make the element from what you sent your self*/

}


public bool IsDisplayed()

{

return Element.Displayed;

}

} // end of class here

ama sınıfını bundan daha karmaşık hale getirebilirsin. sadece bir konsept

0
will

PageObject'de bir PageObject öğesini temsil eden bir "SubPageObject" dediğim şeyi oluştururdum ...

Bunun avantajı, özel yapım yöntemler oluşturabilmeniz ve bu SubPageObject içinde gerçekten ihtiyacınız olan WebElement öğelerini başlatabilmenizdir.

0
Franz Ebner

Adım 1) Element arayüzünü genişleten IWrapsElement adlı bir temel sınıf oluşturun

 public class Element : IWrapsElement
{
    public IWebElement WrappedElement { get; set; }
    public Element(IWebElement element)
    {
        this.WrappedElement = element;
    }
}

Adım 2) Her özel öğe için bir sınıf oluşturun ve Element sınıfından miras alın

 public class Checkbox : Element
{
    public Checkbox(IWebElement element) : base(element) { }

    public void Check(bool expected)
    {
        if (this.IsChecked()!= expected)
        {
            this.WrappedElement.Click();
        }
    }

    public bool IsChecked()
    {
        return this.WrappedElement.GetAttribute("checked") == "true";
    }
}

Kullanımı:

a)

Checkbox x = new Checkbox(driver.FindElement(By.Id("xyz")));
x.Check(true)

b) 

 private IWebElement _chkMale{ get; set; }
 [FindsBy(How = How.Name, Using = "chkMale")]

 private Checkbox ChkMale
    {
        get { return new Checkbox (_chkMale); }
    }
ChkMale.Check(true);
0
Happy Bird

WebElement'i genişletmek neden rahatsız edici? Selenyum paketini gerçekten geliştirmeye mi çalışıyorsunuz? Öyleyse, eklemeleriniz kullandığınız her bir web öğesine uygulanabilir, ancak yalnızca tablonuza özgü olanları değil, bu yöntemi genişletmek mantıklı olmaz.

Neden sadece böyle bir şey yapmıyorsun:

public class PermissionsPage extends TableWebElement{

...permissions stuff...

}

import WebElement

public class TableWebElement{

...custom web elements pertaining to my page...

}

Bunun sorunuzu yanıtlayıp yanıtlamadığını bilmiyorum, ama bana öyle görünüyordu ki, her şeyden çok bir sınıf mimarisi sorusuydu.

0
Greg