2015-09-25 2 views
13

Проблема:Пользовательские действия браузера в транспортир

В одном из наших тестов мы имеем "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() последовательности действий и ввести пользовательские действия?


+1

копания в источники селена, насколько я понимаю, это не было бы возможным фактически расширить действия, я бы рассмотреть вопрос о создании своего рода обертке, как @John Стеннет предложил. Если вы заинтересованы, я могу дать объяснение, почему это невозможно сделать (с моей точки зрения, возможно, это возможно). –

+0

@MichaelRadionov да, я боялся обертывания, это единственный вариант (в этом нет ничего плохого). Я был бы признателен за ваше понимание, ваши ответы всегда будут подробными и полезными. Кроме того, я надеюсь, что эта тема поможет не только мне решить мою текущую проблему, но и другим с похожими запросами. Благодаря! – alecxe

ответ

9

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

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

означает, баловаться с потрохами селена. Итак, 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.

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

+1

Приятная находка! Я пошел глубже, чтобы добавить пользовательскую команду, но это не так просто, потому что исполнитель использует только зарегистрированные команды, а API для добавления новой команды скрывается 'selenium-webdriver '. Я думаю, что знание того, как работает поток контроля, имеет важное значение в таких вещах. –

2

Я думаю, можно расширить функцию browser.actions() но в настоящее время выше моего уровня квалификации, поэтому я выложу маршрут, который я хотел бы предпринять, чтобы решить эту проблему. Я бы рекомендовал создать «Объект страницы», который будет содержать все эти глобальные вспомогательные функции. В этом файле вы можете перечислить свои функции 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() 
} 
4

Вот что я сделал (на основе идеального ответа Луи).

Поместите следующее в 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(); 
Смежные вопросы