【问题标题】:How to inherit from WebElement in WebdriverIO如何从 WebdriverIO 中的 WebElement 继承
【发布时间】:2019-06-07 06:52:02
【问题描述】:

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

class Checkbox extends WebdriverIOWebElement {
    constructor() {
       super($('div'))
    }
    // overriding base method
    isDisplayed(): boolean {
        // blabla some new logic here
    }

    check() {
        if(!this.isChecked()) {
            this.click()
        }
    }

    uncheck() {
        if(this.isChecked()) {
            this.click()
        }
    }
}

【问题讨论】:

  • 我认为这是一个组合比继承更好的情况。您的类可以包含对 WebElement 的引用而不是从它继承,并且您可以提供一个在需要时公开 WebElement 的函数。
  • 我建议创建一个页面对象。无法按照您的建议创建类继承。
  • @ChristianB 我确实使用 PageObject。我想制作典型的 WebElement,如 Checkbox、Select、Input 或我自己的块,如 LoginForm,甚至是典型的元素集合,如 - Messages。我为 ProtractorJS 做了类似的事情 - github.com/Xotabu4/protractor-element-extend/blob/master/…
  • @Xotabu4 只需创建一个 PO,提供与之交互所需的所有必要功能。无需继承完整的元素原型。
  • WebdriverIOWebElement 类在哪里?在 GitHub 上的 webdriverio/webdriverio 中找不到它

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


【解决方案1】:

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

【讨论】:

    【解决方案2】:

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

    假设这是 HTML:

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

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

        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;
    

    那么我们要做的是,为我们的app-login组件编写一个子类:

    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 Support 对 Check Box 组件应用相同的方法:

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

    然后我们可以编写一个复选框控制器组件,该组件可以使用 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类创建一个特定的实现,最后让我们假设您需要的任何组件我们都应该继承并使用它自己的implementation ,在这种情况下,让我们假设应该从 Element Implementation Class 继承的 Check 框,最后是通过实例化对象来使用它的 Cranky 方式。 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

    【讨论】:

    • 感谢您的回答!不幸的是,这不是我所期望的,因为 Component 不会是 WebDriverIO 元素的实例。我认为第二个 Java 示例更相关 - 我希望我不需要实现接口...
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-03-10
    • 2011-01-07
    • 2013-10-21
    • 1970-01-01
    • 2016-06-04
    • 2018-04-12
    • 1970-01-01
    相关资源
    最近更新 更多