量角器中的自定义浏览器操作

IT技术 javascript selenium selenium-webdriver protractor
2021-03-21 12:05:12

问题:

在我们的一项测试中,我们使用以下方法解决“长按”/“单击并按住”功能

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

我们希望通过拥有sleep()动作链的一部分在一行中理想地解决

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

显然,这行不通,因为没有“睡眠”操作

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

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()动作序列并引入自定义动作?


4个回答

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

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

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

注意,量角器文件指的是webdriver.WebDriver.prototype.actions解释的行为,这是我采取意味着它不会修改或补充selenium什么时提供。

返回的对象类webdriver.WebDriver.prototype.actionswebdriver.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()只期望 execute webdriver.Command,它们是要发送到浏览器的命令。睡觉不是这样的命令之一。所以.perform()必须修改以处理我们将要记录的内容.sleep()在下面的代码中,我选择.sleep()记录一个函数并修改.perform()为处理除webdriver.Command.

这是整个事情的样子,一旦放在一起。我首先给出了一个使用股票 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()我已将goog...Selenium 代码使用函数替换为常用 JavaScript。

不错的发现!我已经更深入地尝试添加自定义命令,但这并不容易,因为执行程序仅使用注册的命令,并且用于添加新命令的 API 被“selenium-webdriver”隐藏。我想知道控制流的工作原理在这种事情中是必不可少的。
2021-05-16 12:05:12

这是我所做的(基于完美的@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();

我认为可以扩展该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);
    });
});

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

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

这假设

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

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

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