如何从WebdriverIO中的WebElement继承

时间:2019-01-12 17:10:24

标签: javascript typescript selenium-webdriver webdriver webdriver-io

我寻求一种从webdriverio返回的WebElement对象继承的方法,而无需猴子修补和TS类型支持(必须自动完成)。有办法做这样的事吗?

from selenium.webdriver.support.ui import WebDriverWait

# wait max 10 seconds
newip = WebDriverWait(chrome_browser, 10).until(
    lambda chrome_browser: chrome_browser.find_element_by_xpath("/html/body/div[4]/div/div[1]/form/input")
)
newip.send_keys("D:\\p1.png")

# click the Submit using javascript
driver.execute_script('''
   document.querySelector('button.desktop-photo-submit.ng-scope').click()
''')

2 个答案:

答案 0 :(得分:3)

让我们举个例子,当我们在HTML中有一个新标记(my-app),并且必须使用webdriverIO来构建一个登录案例时,

假设这是HTML:

我们将使用组件对象模式,组件对象模式试图减少重复,并将组件的api移动到其自己的对象中。我们知道,为了与元素的影子DOM交互,我们首先需要宿主元素。为您的组件对象使用基类使这一过程变得非常简单。

这是一个基本的组件基类,它在其构造函数中使用host元素,并将该元素的查询展开到浏览器对象,因此可以在许多页面对象(或其他组件对象)中重用,而无需知道有关页面本身的任何信息。

    class Component {

      constructor(host) {
        const selectors = [];
        // Crawl back to the browser object, and cache all selectors
        while (host.elementId && host.parent) {
          selectors.push(host.selector);
          host = host.parent;
        }
        selectors.reverse();
        this.selectors_ = selectors;
      }

      get host() {
        // Beginning with the browser object, reselect each element
        return this.selectors_.reduce((element, selector) => element.$(selector), browser);
      }
    }

    module.exports = Component;

然后我们将要做的是,我们将为我们的应用程序登录组件编写一个子类:

const Component = require('./component');

class Login extends Component {

  get usernameInput() {
    return this.host.shadow$('input #username');
  }

  get passwordInput() {
    return this.host.shadow$('input[type=password]');
  }

  get submitButton() {
    return this.login.shadow$('button[type=submit]');
  }

  login(username, password) {
    this.usernameInput.setValue(username);
    this.passwordInput.setValue(password);
    this.submitButton.click();
  }
}

module.exports = Login;

最后,我们可以在登录页面对象内使用组件对象:

const Login = require('./components/login');

class LoginPage {

  open() {
    browser.url('/login');
  }

  get app() {
    return browser.$('my-app');
  }

  get loginComponent() {
    // return a new instance of our login component object
    return new Login(this.app.$('app-login'));
  }

}

现在,此组件对象现在可以在测试中用于使用应用程序登录Web组件的应用程序的任何页面或部分,而无需了解该组件的结构。如果以后决定更改Web组件的内部结构,则只需更新组件对象。

现在,我们通过使用Shadow Dom支持对复选框组件应用相同的方法:

public class CheckBox extends Component {
  public CheckBox(element) {
    this.element = element;
  }
  get checkBoxSelector() {
    return this.host.shadow$(element);
  }
  get void toggle() {
    checkBoxSelector().click();
  }
  get void check() {
    if (!isChecked()) {
      toggle();
    }
  }
  get void uncheck() {
    if (isChecked()) {
      toggle();
    }
  }
  get boolean isChecked() {
    return checkBoxSelector().isSelected();
  }
}

然后,我们可以编写一个Check Box Controller组件,该组件可以使用id获取复选框的实例并验证每一个必要条件。

const CheckBox= require('./components/CheckBox');
class CheckBoxController{
  open() {
    browser.url('/login');
  }
  get checkboxComponent() {

    // Using this we can verify whether the Specific Check Box has been Selected or Not
    let element = browser.$('[id="lpagecheckbox"]');
    return new CheckBox(element);
  }
}

注意:

请注意,这不是实际的代码,这只是模板的一部分,可以帮助我们着手解决问题。

来源争辩:

https://webdriver.io/docs/api/element/isSelected.html

https://webdriver.io/blog/2019/02/22/shadow-dom-support.html

https://webdriver.io/blog/2019/04/03/react-selectors.html

https://webdriver.io/docs/pageobjects.html

此外,如果我们使用的是Selenium Webdriver,这可以帮助我们实现它

这里有一个实际上结合了所有webdriver接口的接口,然后我们通过继承Element类来创建一个具体实现,最后让我们假设您需要继承和使用的任何组件它具有自己的实现,在这种情况下,假定应该从那时的Element Implementation Class继承复选框,最后通过实例化该对象使用它的一种古怪的方式。 CheckBox cb = new CheckBox(element);cb.uncheck();

步骤1:

创建一个包含所有WebDriver接口的接口:

public interface Element extends WebElement, WrapsElement, Locatable {}

第2步:

元素实现继承元素类:

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

}

步骤3: 考虑您使用的任何组件,在这种情况下,请假定复选框

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

使用方式:

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

如果您想更清晰地实现类似的方法,请参见第三个链接

public class Part2ExampleTest {
    private final WebDriver driver;

    @FindBy(id = "checkbox")
    CheckBox checkBox;

    protected Part2ExampleTest(WebDriver driver) {
        this.driver = driver;
    }

    protected static Part2ExampleTest initialize(WebDriver driver) {
        return ElementFactory.initElements(driver, Part2ExampleTest.class);
    }

    @Test
    public void simple() {
        WebDriver driver = new FirefoxDriver();
        Part2ExampleTest page = initialize(driver);

        PageLoader.get(driver, "forms.html");

        Assert.assertFalse(page.checkBox.isChecked());
        page.checkBox.check();
        Assert.assertTrue(page.checkBox.isChecked());

        driver.close();
    }
}

来源:

Extend Selenium WebDriver WebElement?

http://elisarver.com/2012/12/09/wrapping-webelement-1/

http://elisarver.com/2012/12/10/wrapping-webelement-2

答案 1 :(得分:-1)

IWebElement是一个接口,您可以仅在驱动程序类中实现。