2012-07-25 30 views
10

Estoy siguiendo el patrón de objeto de página sugerida por selenio, pero ¿cómo iba a crear un WebElement más especializado para una página. Específicamente, tenemos tablas en nuestras páginas y he escrito algunas funciones auxiliares para obtener filas específicas de una tabla, devolver el contenido de una tabla, etc.Amplíe Selenium WebDriver WebElement?

Actualmente, aquí hay un fragmento de un objeto de página que creé que tiene una tabla:

public class PermissionsPage { 

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

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

    ... 

} 

Por lo tanto, lo que me gustaría hacer es tener que permissionsTable a ser un WebElement más personalizada que cuenta con algunos de los métodos que he mencionado antes.

Por ejemplo:

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 
    } 
} 

Espero que esto tiene sentido lo que estoy tratando de explicar. Creo que lo que estoy buscando es una forma de tener este WebElement personalizado para hacer algunas cosas adicionales que son específicas de la tabla. Agregue este elemento personalizado a una página y aproveche la forma en que Selenium conecta los elementos a la página según las anotaciones.

¿Es posible? Y si es así, ¿alguien sabe cómo se puede hacer esto?

Respuesta

20

creé una interfaz que combina todas las interfaces WebDriver:

public interface Element extends WebElement, WrapsElement, Locatable {} 

Es sólo existe para concluir todas las cosas WebElements pueden hacer al envolver un elemento.

A continuación, una aplicación:

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... 

} 

Entonces, por ejemplo, una casilla de verificación:

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(); 
    } 
} 

cuando se utiliza en mi guión:

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

También he llegado hasta con una forma de ajustar las clases Element. Debe crear algunas fábricas para reemplazar el PageFactory incorporado, pero es factible y brinda mucha flexibilidad.

he documentado sobre este proceso en mi sitio:

También tengo un proyecto llamado selophane que se inspiró en esta y otras cuestiones: selophane

+0

Así es como yo lo hice también, pero un poco diferente ... Creé una clase abstracta que implementa una interfaz personalizada que extiende la interfaz WebElement. cargó todos los métodos en el resumen y agregó métodos en la clase final. Y en mi InvocationHandler, intercepté el método y lo redirigí a los métodos del objeto webelement si están declarados en la interfaz de webelement; de lo contrario, envolví el elemento con el envoltorio webelement personalizado; e invocó el método. PD. +1 para el blog :) ¡gracias! – rrw

0

¿Por qué molestarse extendiéndose WebElement? ¿Estás tratando de desarrollar realmente el paquete de Selenio? Entonces, si lo es, no tendría sentido extender ese método a menos que sus adiciones sean aplicables a cada elemento web que use, no solo los que son específicos de su tabla

¿Por qué no hacer algo como esto?

public class PermissionsPage extends TableWebElement{ 

...permissions stuff... 

} 

import WebElement 

public class TableWebElement{ 

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

} 

no sé si eso responde a su pregunta, pero me pareció que era más una cuestión arquitectura de alta calidad más que cualquier otra cosa.

0

me gustaría crear lo que llamo un "SubPageObject", que representa un PageObject en un PageObject ...

La ventaja es que se pueden crear métodos hechos a medida para él y sólo puede inicializar WebElements que realmente necesita dentro de este SubPageObject.

1

Nota al margen: WebElement no es la clase, su interfaz que significa que su clase se vería más como esto:

public class TableWebElement implements WebElement { 

Pero en ese caso debe aplicar todos los métodos que se encuentran en WebDriver. Y es un poco exagerado.

Aquí es la manera cómo hacer esto - Me deshice por completo de PageObjects propuestas según lo propuesto por el selenio y "reinventar la rueda" por tener mis propias clases. Tengo una clase para toda la aplicación:

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); 
    } 
} 

Y luego tengo mis clases de ayuda

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(); 

} 

} 

la muestra de la prueba:

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

me gusta esta idea, "ll probarlo y le permiten saber cómo va. – pipplupp

+0

por cierto, no sé quién notó que esta respuesta no es útil. Tengo curiosidad de por qué alguien votó de esa manera. – pipplupp

+0

Al menos te gusta la idea :) –

0

por qué no hacer un envoltorio elemento web? ¿Me gusta esto?

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 

pero puede hacer que su clase sea más complicada que esta. solo un concepto

1

Como seguimiento de esto, esto es lo que terminé haciendo (en caso de que alguien más tenga este mismo problema). A continuación se muestra un fragmento de la clase que he creado como un contenedor a un WebElement:

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 
} 

Y entonces yo uso esto en cualquiera de las páginas que crean refiriéndose a ella como tal:

public class TestPage { 

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

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

La única cosa que No me gusta es que cuando la página quiere obtener la tabla, crea una "nueva" tabla. Hice esto para evitar StaleReferenceExeptions, que ocurre cuando los datos en la tabla se actualizan (es decir, celda actualizada, fila agregada, fila eliminada). Si alguien tiene alguna sugerencia sobre cómo puedo evitar crear una nueva instancia cada vez que se solicita, ¡sino que devuelve el WebElement actualizado que sería genial!

2

Puede crear elementos web personalizados utilizando el marco WebDriver Extensions que proporciona un bComponent clase que implementa la interfaz WebElement

Crear su WebElement encargo

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); 
     } 
    } 
} 

... y luego añadirlo a su PageObject con una anotación @FindBy y utilizar el WebDriverExtensionFieldDecorator al llamar al método PageFactory.initElements

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; 
} 

...y luego utilizarlo en su prueba

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()); 
    } 
} 




O incluso un mejor uso de la aplicación WebDriver Extensions PageObject

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); 
    } 
} 

y la JUnitRunner con la estática afirma métodos para WebElements

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)); 
    } 
} 
+0

Publiqué esta pregunta hace más de 2 años y me encuentro re-visitando este mismo escenario. Olvidé que incluso hice esta pregunta. ¡Estaba mirando las diversas respuestas y sus extensiones de disco web se ven increíbles! Rellenar los agujeros que dejan las hojas de selenio. Voy a leer su documentación y probarla. – pipplupp

0

Take a lo lo ok en htmlelements framework, parece exactamente lo que necesita. Tiene elementos comunes preinstalados, como checkbox, radiobox, table, form, etc., puedes crear fácilmente los tuyos e insertar uno en otros creando una estructura de árbol clara.

0

Paso 1) Crear una clase base llamada Element que se extiende la interfaz de IWrapsElement

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

Paso 2) Crear una clase para cada elemento a medida y heredar de la clase Element

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"; 
    } 
} 

Uso:

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); 
Cuestiones relacionadas