【问题标题】:Custom browser actions in Protractor量角器中的自定义浏览器操作
【发布时间】:2015-12-23 17:10:54
【问题描述】:

问题:

在我们的一项测试中,我们有一个"long click"/"click and hold" functionality,我们使用以下方法解决:

browser.actions().mouseDown(element).perform();
browser.sleep(5000);
browser.actions().mouseUp(element).perform();

我们希望通过将sleep() 作为操作链的一部分在一行中理想地解决:

browser.actions().mouseDown(element).sleep(5000).mouseUp(element).perform();

显然,这是行不通的,因为有no "sleep" action

另一个实际示例可能是“类人打字”。例如:

browser.actions().mouseMove(element).click()
   .sendKeys("t").sleep(50)  // we should randomize the delays, strictly speaking
   .sendKeys("e").sleep(10)
   .sendKeys("s").sleep(20)
   .sendKeys("t")
   .perform();

请注意,这些只是示例,问题是通用的。

问题:

是否可以扩展browser.actions() 动作序列并引入自定义动作?


【问题讨论】:

  • 据我所知,深入研究 Selenium 资源是不可能实际扩展动作的,我会考虑创建某种包装器,如@John Stennett 所建议的。如果您有兴趣,我可以解释为什么无法完成(从我的角度来看,也许是可能的)。
  • @MichaelRadionov 是的,我担心包装它是唯一的选择(虽然它没有任何问题)。我会很感激你的洞察力,你的答案总是详细而有帮助的。此外,我希望该主题不仅可以帮助我解决当前的问题,还可以帮助其他有类似要求的人。谢谢!

标签: javascript selenium selenium-webdriver protractor


【解决方案1】:

我认为可以扩展browser.actions() 功能,但目前这超出了我的技能水平,因此我将列出解决此问题的路线。我建议设置一个“HelperFunctions.js”页面对象,其中包含所有这些全局帮助函数。在该文件中,您可以列出您的 browser 函数并在多个测试中引用它,并将所有代码放在一个位置。

这是我建议设置的“HelperFunctions.js”文件的代码:

var HelperFunctions = function() {
    this.longClick = function(targetElement) {
        browser.actions().mouseDown(targetElement).perform();
        browser.sleep(5000);
        browser.actions().mouseUp(targetElement).perform();
    };
};

module.exports = new HelperFunctions();

然后在您的测试中,您可以像这样引用 Helper 文件:

var HelperFunctions = require('../File_Path_To/HelperFunctions.js');

describe('Example Test', function() {
    beforeEach(function() {
        this.helperFunctions = HelperFunctions;

        browser.get('http://www.example.com/');
    });

    it('Should test something.', function() {
        var Element = element(by.className('targetedClassName'));
        this.helperFunctions.longClick(Element);
    });
});

在我的测试套件中,我设置了一些帮助文件,并且在我的所有测试中都引用了它们。

【讨论】:

    【解决方案2】:

    我对硒或量角器知之甚少,但我会试一试。

    这假设

    browser.actions().mouseDown(element).mouseUp(element).perform();
    

    对您的问题来说是有效的语法,如果是这样,那么这可能会解决问题

    browser.action().sleep = function(){
        browser.sleep.apply(this, arguments);
        return browser.action()
    }
    

    【讨论】:

      【解决方案3】:

      是的,你可以扩展动作框架。但是,严格来说,得到类似的东西:

      browser.actions().mouseDown(element).sleep(5000).mouseUp(element).perform();
      

      意味着搞乱 Selenium 的胆量。所以,YMMV。

      请注意,Protractor documentation 在解释操作时指的是webdriver.WebDriver.prototype.actions,我认为这意味着它不会修改或添加到 Selenium 提供的内容。

      webdriver.WebDriver.prototype.actions 返回的对象类是webdriver.ActionSequence。实际导致序列执行任何操作的方法是webdriver.ActionSequence.prototype.perform。在默认实现中,此函数采用调用.sendKeys().mouseDown() 时记录的命令,并让与ActionSequence 关联的驱动程序按顺序安排它们。所以添加一个.sleep 方法不能这样做

      webdriver.ActionSequence.prototype.sleep = function (delay) {
          var driver = this.driver_;
          driver.sleep(delay);
          return this;
      };
      

      否则,睡眠会无序。你要做的就是记录你想要的效果,以便稍后执行。

      现在,要考虑的另一件事是默认的.perform() 只期望执行webdriver.Command,这是要发送到浏览器的命令。睡觉不是这样的命令之一。所以必须修改.perform() 来处理我们要用.sleep() 记录的内容。在下面的代码中,我选择让.sleep() 记录一个函数并修改.perform() 以处理除webdriver.Command 之外的函数。

      这就是整个东西的样子,一旦放在一起。我首先给出了一个使用 Stock Selenium 的示例,然后添加了补丁和一个使用修改后代码的示例。

      var webdriver = require('selenium-webdriver');
      var By = webdriver.By;
      var until = webdriver.until;
      var chrome = require('selenium-webdriver/chrome');
      
      // Do it using what Selenium inherently provides.
      
      var browser = new chrome.Driver();
      
      browser.get("http://www.google.com");
      
      browser.findElement(By.name("q")).click();
      browser.actions().sendKeys("foo").perform();
      browser.sleep(2000);
      browser.actions().sendKeys("bar").perform();
      browser.sleep(2000);
      
      // Do it with an extended ActionSequence.
      
      webdriver.ActionSequence.prototype.sleep = function (delay) {
          var driver = this.driver_;
          // This just records the action in an array. this.schedule_ is part of
          // the "stock" code.
          this.schedule_("sleep", function () { driver.sleep(delay); });
          return this;
      };
      
      webdriver.ActionSequence.prototype.perform = function () {
          var actions = this.actions_.slice();
          var driver = this.driver_;
          return driver.controlFlow().execute(function() {
              actions.forEach(function(action) {
                  var command = action.command;
                  // This is a new test to distinguish functions, which 
                  // require handling one way and the usual commands which
                  // require a different handling.
                  if (typeof command === "function")
                      // This puts the command in its proper place within
                      // the control flow that was created above
                      // (driver.controlFlow()).
                      driver.flow_.execute(command);
                  else
                      driver.schedule(command, action.description);
              });
          }, 'ActionSequence.perform');
      };
      
      browser.get("http://www.google.com");
      
      browser.findElement(By.name("q")).click();
      browser.actions().sendKeys("foo")
          .sleep(2000)
          .sendKeys("bar")
          .sleep(2000)
          .perform();
      browser.quit();
      

      在我的 .perform() 实现中,我已将 Selenium 代码使用的 goog... 函数替换为普通 JavaScript。

      【讨论】:

      • 不错的发现!我已经更深入地尝试添加自定义命令,但这并不容易,因为执行程序仅使用注册的命令,并且用于添加新命令的 API 被“selenium-webdriver”隐藏。我想知道控制流是如何工作的对于这类事情是必不可少的。
      【解决方案4】:

      这就是我所做的(基于完美的@Louis 回答)。

      将以下内容放入量角器配置中的onPrepare()

      // extending action sequences
      protractor.ActionSequence.prototype.sleep = function (delay) {
          var driver = this.driver_;
          this.schedule_("sleep", function () { driver.sleep(delay); });
          return this;
      };
      
      protractor.ActionSequence.prototype.perform = function () {
          var actions = this.actions_.slice();
          var driver = this.driver_;
          return driver.controlFlow().execute(function() {
              actions.forEach(function(action) {
                  var command = action.command;
                  if (typeof command === "function")
                      driver.flow_.execute(command);
                  else
                      driver.schedule(command, action.description);
              });
          }, 'ActionSequence.perform');
      };
      
      protractor.ActionSequence.prototype.clickAndHold = function (elm) {
          return this.mouseDown(elm).sleep(3000).mouseUp(elm);
      };
      

      现在您将可以使用 sleep()clickAndHold() 浏览器操作。示例用法:

      browser.actions().clickAndHold(element).perform();
      

      【讨论】:

        猜你喜欢
        • 2012-07-21
        • 2022-10-17
        • 2017-04-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-09-10
        相关资源
        最近更新 更多