javascript - Acciones de navegador personalizadas en Protractor
selenium selenium-webdriver (4)
El problema:
En una de nuestras pruebas tenemos una funcionalidad de "clic largo" / "clic y mantener" que resolvemos mediante el uso de:
browser.actions().mouseDown(element).perform();
browser.sleep(5000);
browser.actions().mouseUp(element).perform();
Lo que nos gustaría resolver idealmente en una línea haciendo que
sleep()
forme parte de la cadena de acción:
browser.actions().mouseDown(element).sleep(5000).mouseUp(element).perform();
Claramente, esto no funcionaría ya que no existe una acción de "suspensión" .
Otro ejemplo práctico podría ser el "tipeo humano". Por ejemplo:
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();
Tenga en cuenta que estos son solo ejemplos, la pregunta debe ser genérica.
La pregunta:
¿Es posible extender las secuencias de acción de
browser.actions()
e introducir acciones personalizadas?
Creo que es posible extender la función
browser.actions()
, pero actualmente está por encima de mi nivel de habilidad, así que diseñaré la ruta que tomaría para resolver este problema.
Recomendaría configurar un objeto de página "
HelperFunctions.js
" que contendrá todas estas funciones de ayuda global.
En ese archivo puede enumerar
browser
funciones de su
browser
y hacer referencia a él en múltiples pruebas con todo el código en una ubicación.
Este es el código para el archivo "HelperFunctions.js" que recomendaría configurar:
var HelperFunctions = function() {
this.longClick = function(targetElement) {
browser.actions().mouseDown(targetElement).perform();
browser.sleep(5000);
browser.actions().mouseUp(targetElement).perform();
};
};
module.exports = new HelperFunctions();
Luego, en su prueba, puede hacer referencia al archivo auxiliar de esta manera:
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);
});
});
En mi Test Suite tengo algunos archivos de configuración de ayuda y se hace referencia a ellos en todas mis pruebas.
Esto es lo que hice (basado en la respuesta perfecta de @ Louis).
Ponga lo siguiente en
onPrepare()
en la configuración del transportador:
// 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);
};
Ahora tendrá las acciones del navegador
sleep()
y
clickAndHold()
disponibles.
Ejemplo de uso:
browser.actions().clickAndHold(element).perform();
Tengo muy poco conocimiento de selenio o transportador, pero lo intentaré.
Esto supone que
browser.actions().mouseDown(element).mouseUp(element).perform();
es una sintaxis válida para su problema, si es así, esto probablemente funcionaría
browser.action().sleep = function(){
browser.sleep.apply(this, arguments);
return browser.action()
}
Sí, puede ampliar el marco de acciones. Pero, estrictamente hablando, obtener algo como:
browser.actions().mouseDown(element).sleep(5000).mouseUp(element).perform();
significa jugar con las entrañas de Selenium. Entonces, YMMV.
Tenga en cuenta que la
documentación
del
transportador se
refiere a
webdriver.WebDriver.prototype.actions
al explicar las acciones, lo que considero que no modifica ni agrega a lo que proporciona Selenium.
La clase de objeto devuelta por
webdriver.WebDriver.prototype.actions
es
webdriver.ActionSequence
.
El método que realmente hace que la secuencia haga algo es
webdriver.ActionSequence.prototype.perform
.
En la implementación predeterminada, esta función toma los comandos que se grabaron cuando llamó a
.sendKeys()
o
.mouseDown()
y tiene el controlador al que está asociado
ActionSequence
programarlos en orden.
Por lo
.sleep
,
NO se puede
agregar un método
.sleep
esta manera
:
webdriver.ActionSequence.prototype.sleep = function (delay) {
var driver = this.driver_;
driver.sleep(delay);
return this;
};
De lo contrario, el sueño no funcionaría . Lo que debe hacer es registrar el efecto que desea para que se ejecute más tarde.
Ahora, la otra cosa a considerar es que el
.perform()
predeterminado solo espera ejecutar
webdriver.Command
, que son comandos que se enviarán al navegador.
Dormir no es uno de esos comandos.
Entonces
.perform()
tiene que ser modificado para manejar lo que vamos a grabar con
.sleep()
.
En el siguiente código, he optado por que
.sleep()
grabe una función y modifique
.perform()
para manejar funciones además de
webdriver.Command
.
Así es como se ve todo, una vez juntos. Primero he dado un ejemplo usando Stock Selenium y luego agregué los parches y un ejemplo usando el código modificado.
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();
En mi implementación de
.perform()
, he reemplazado las funciones
goog...
que el código de Selenium usa con JavaScript
goog...
.