2015-09-25 18 views
13

Il problema:azioni del browser personalizzati in goniometro

In uno dei nostri test abbiamo un "long click"/"click and hold" functionality che risolviamo utilizzando:

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

che vorremmo risolvere idealmente in una linea da avente sleep() una parte della catena azione:

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

Chiaramente, questo non funzionerebbe poiché c'è no "sleep" action.

Un altro esempio pratico potrebbe essere la "digitazione di tipo umano". Per esempio:

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

Si noti che questi sono solo esempi, la questione è destinata ad essere generico.

la domanda:

E 'possibile estendere browser.actions() sequenze d'azione e di introdurre azioni personalizzate?


+1

Scavando in fonti di selenio, per quanto ho capito che non sarebbe stato possibile estendere in realtà le azioni, vorrei prendere in considerazione la creazione di una sorta di involucro come @ John Stennett ha suggerito. Se sei interessato, potrei dare una spiegazione del perché non potrebbe essere fatto (dal mio punto di vista, forse è possibile). –

+0

@MichaelRadionov si, ho avuto paura di avvolgerlo è l'unica opzione (non c'è niente di sbagliato comunque). Apprezzerei la tua intuizione, le tue risposte sono sempre dettagliate e utili. Inoltre, spero che l'argomento possa aiutare non solo me a risolvere il mio problema attuale, ma anche altri con richieste simili. Grazie! – alecxe

risposta

9

Sì, è possibile estendere il framework azioni. Ma, a rigor di termini, ottenendo qualcosa di simile:

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

significa scherzi con le budella di selenio. Quindi, YMMV.

Si noti che lo Protractor documentation si riferisce a webdriver.WebDriver.prototype.actions quando si spiegano le azioni, che io intendo per significare che non modifica o aggiunge a ciò che il Selenium fornisce.

La classe dell'oggetto restituita da webdriver.WebDriver.prototype.actions è webdriver.ActionSequence. Il metodo che effettivamente causa la sequenza per fare qualsiasi cosa è webdriver.ActionSequence.prototype.perform. Nell'implementazione predefinita, questa funzione accetta i comandi che sono stati registrati quando è stato chiamato .sendKeys() o .mouseDown() e il driver a cui è associato lo ActionSequence li pianifica in ordine. Così l'aggiunta di un metodo .sleepnon può essere fatto in questo modo:

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

In caso contrario, il sonno sarebbe successo fuori uso. Quello che devi fare è record l'effetto desiderato in modo che venga eseguito in seguito.

Ora, l'altra cosa da considerare è che il valore predefinito .perform() prevede solo l'esecuzione di webdriver.Command, che sono comandi da inviare al browser. Dormire non è uno di questi comandi. Quindi .perform() deve essere modificato per gestire quello che registreremo con .sleep(). Nel codice seguente ho deciso di fare in modo che .sleep() registri una funzione e modificato .perform() per gestire le funzioni oltre a webdriver.Command.

Ecco come appare l'insieme, una volta messo insieme. Per prima cosa ho dato un esempio usando Selenium di serie e poi aggiunto le patch e un esempio usando il codice modificato.

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

Nella mia implementazione di .perform() Ho sostituito le goog... funzioni che il codice di selenio usa con magazzino JavaScript.

+1

Bella scoperta! Ho approfondito il mio tentativo di aggiungere un comando personalizzato, ma non è così semplice, perché l'esecutore utilizza solo comandi registrati e API per l'aggiunta di un nuovo comando nascosto da "selenium-webdriver". Immagino che sapere come funziona il flusso di controllo è essenziale in questo genere di cose. –

2

penso che sia possibile estendere la funzione browser.actions() ma che è attualmente al di sopra il mio livello di abilità quindi mi lay out il percorso che avrei preso per risolvere questo problema. Vorrei raccomandare di impostare un "HelperFunctions.js" Oggetto pagina che conterrà tutte queste funzioni globali di supporto. In quel file puoi elencare le tue funzioni browser e farne riferimento in più test con tutto il codice in un'unica posizione.

Questo è il codice per il file "HelperFunctions.js" che mi sento di raccomandare la creazione di:

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

module.exports = new HelperFunctions(); 

Poi, nel tuo test si può fare riferimento al file Helper in questo modo:

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

Nella mia Test Suite ho alcuni setup di file Helper e vengono referenziati attraverso tutti i miei Test.

2

Ho poca conoscenza del selenio o del goniometro, ma ci provo.

Questo presuppone che

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

è sintassi valida per il vostro problema, se è così, allora questo sarebbe probabilmente fare il trucco

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

Ecco cosa ho fatto (basato sulla perfetta risposta di @ Louis).

Inserire il seguente nella onPrepare() nella configurazione goniometro:

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

Ora dovrete sleep() e clickAndHold() del browser azioni disponibili. Esempio di utilizzo:

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