Подтвердить что ты не робот

Пользовательские действия браузера в Protractor

Проблема:

В одном из наших тестов у нас есть "длинный клик" / "нажмите и удерживайте" функциональность, которую мы решаем, используя:

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

Что мы хотели бы идеально решить в одной строке, имея sleep() часть цепочки действий:

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

Понятно, что это не сработало, так как нет действия "sleep" .

Другим практическим примером может быть "человекоподобная типизация". Например:

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() и ввести пользовательские действия?


4b9b3361

Ответ 1

Да, вы можете расширить рамки действий. Но, строго говоря, получилось что-то вроде:

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

означает возиться с селенскими кишками. Итак, YMMV.

Обратите внимание, что Документация по транспортировке относится к webdriver.WebDriver.prototype.actions при объяснении действий, которые я подразумеваю, что она не изменяет и не добавляет к тому, что Селен обеспечивает.

Класс объекта, возвращаемый 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.

Вот как все выглядит, как только собрано. Сначала я привел пример, используя запас 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.

Ответ 2

Вот что я сделал (на основе идеального ответа @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();

Ответ 3

Я думаю, что можно расширить функцию 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();

Затем в вашем тесте вы можете ссылаться на файл Помощника следующим образом:

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

В моем тестовом наборе у меня есть несколько файлов-помощников, и на них ссылаются все мои тесты.

Ответ 4

У меня очень мало знаний о селене или транспортире, но я дам ему шанс.

Это предполагает, что

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

является допустимым синтаксисом для вашей проблемы, если это так, то это, скорее всего, сделает трюк

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