2015-10-24 16 views
11

La Storia:promesse risolto e respinti in un costume Jasmine Matcher

Abbiamo sviluppato una matcher gelsomino personalizzata che fa 2 cose principali:

  • mouse su un determinato elemento
  • controllo che v'è una descrizione comando indicata con un testo desiderato

Attuazione:

toHaveTooltip: function() { 
    return { 
     compare: function(elm, expectedTooltip) { 
      var tooltipPage = requirePO("tooltip"); 

      browser.actions().mouseMove(elm).perform(); 
      browser.wait(EC.visibilityOf(tooltipPage.tooltip), 5000, "Tooltip is still not visible."); 

      return { 
       pass: tooltipPage.tooltip.getText().then(function(actualTooltip) { 
        return jasmine.matchersUtil.equals(actualTooltip, expectedTooltip); 
       }), 
       message: "Element does not have the tooltip '" + expectedTooltip + "'." 
      }; 
     } 
    }; 
}, 

dove tooltipPage è una pagina oggetto definito separatamente:

var Tooltip = function() { 
    this.tooltip = element(by.css(".tooltip")); 
}; 

module.exports = new Tooltip(); 

L'utilizzo è molto conveniente per noi e aiuta veramente a seguire il principio DRY mantenere la nostra base di codice di prova pulita e leggibile:

expect(page.fromDateInput).toHaveTooltip("After"); 

il problema e la questione:

Ora, quello che sto cercando di fare è di avere i casi matcher maniglia 2 uso separatamente:

  • non c'è tooltip al passaggio del mouse mostrato a tutti (che è, in sostanza, la promessa browser.wait() respinto)
  • c'è un tooltip, ma non quella desiderata

Come posso migliorare la matcher per essere in grado di gestire questi due problemi separatamente e segnalare diversi errori?

Quello che ho provato:

toHaveTooltip: function() { 
    return { 
     compare: function(elm, expectedTooltip) { 
      var tooltipPage = requirePO("tooltip"); 

      browser.actions().mouseMove(elm).perform(); 

      return browser.wait(EC.visibilityOf(tooltipPage.tooltip), 5000, "Tooltip is still not visible.").then(function() { 
       return { 
        pass: tooltipPage.tooltip.getText().then(function(actualTooltip) { 
         return jasmine.matchersUtil.equals(actualTooltip, expectedTooltip); 
        }), 
        message: "Element does not have the tooltip '" + expectedTooltip + "'." 
       }; 
      }, function() { 
       return { 
        pass: false, 
        message: "No tooltip shown on mouse over the element" 
       } 
      }); 
     } 
    }; 
}, 

Qui ho cercato di risolvere browser.wait() esplicitamente e gestire il "successo" e casi "errore" a parte. Ciò si è tradotto in un timeout Jasmine Spec e un enorme testo "rossa" sulla console:

Expected ({ ptor_: ({ setFileDetector: Function, ... 
5 minutes scrolling here 
... InnerHtml: Function, getId: Function, getRawId: Function }) to have tooltip 'After'. 

ho paura che non posso restituire un promessa dalla funzione "confrontare".

+1

ciao, posso chiedere quale versione di gelsomino stai usando? 1.xo 2.x? – vrachlin

+1

@vrachlin sicuro, gelsomino 2. Grazie. – alecxe

risposta

4

Come per jasminewd2(un adattatore per Jasmine-to-WebDriverJS Utilizzato da goniometro.)code -

Un aspettativa risolve eventuali promesse fatte per i valori attuali e attesi, così come il pass proprietà dell'oggetto result.

Quindi, se non del tutto c'è una funzione asincrona o di una promessa che deve essere risolto in un matcher custom/aspettativa quindi ha bisogno di essere avvolto al valore result.pass, in modo che attende goniometro per la promessa da risolvere .

Nella domanda, si verifica un errore jasmine spec timeout perché il goniometro non è in grado di capire che esiste una promessa che deve essere risolta prima di eseguire quella particolare operazione. Per risolverlo, passare direttamente la funzione asincrona nell'istruzione expect o passarlo al valore pass dell'oggetto result. Ecco il codice per questo -

toHaveTooltip: function() { 
    return { 
     compare: function(elm, expectedTooltip) { 
      var tooltipPage = requirePO("tooltip"); 

      browser.actions().mouseMove(elm).perform(); 

       return { 
        pass: browser.wait(EC.visibilityOf(tooltipPage.tooltip), 5000, "Tooltip is still not visible.").then(function() { 
          tooltipPage.tooltip.getText().then(function(actualTooltip) { 
           return jasmine.matchersUtil.equals(actualTooltip, expectedTooltip); 
          }), 
         }, function() { 
          return false; 
         }), 
        message: "Error Occured" 
       } 
     } 
    }; 
}, 

Tuttavia, il problema con il codice precedente è che un messaggio di errore personalizzato non può essere creato. Per risolverlo, il metodo migliore che ho trovato è stato restituire esplicitamente l'oggetto result, in modo che possa essere assegnato un messaggio di errore come richiesto. Ecco un esempio -

var result = {}; 
result.pass = browser.wait(EC.visibilityOf(tooltipPage.tooltip), 5000, "Tooltip is still not visible.").then(function() { 
        tooltipPage.tooltip.getText().then(function(actualTooltip) { 
         result.message = "Element does not have the tooltip '" + expectedTooltip + "'."; 
         return jasmine.matchersUtil.equals(actualTooltip, expectedTooltip); 
        }), 
       }, function() { 
        result.message = "No tooltip shown on mouse over the element"; 
        return false; 
       }); 
return result; 

Nota: Se non c'è message proprietà nell'oggetto result, quindi goniometro cercherà di creare un messaggio di errore generico stesso e conterrà l'oggetto promessa (Un messaggio lungo di partenza con - { ptor_: ... }) come mostrato nella domanda.

Spero che aiuti.

+1

Grazie mille per la spiegazione: ho applicato alcune correzioni minori (pubblicate in una risposta separata) agli ultimi snippet di codice e ora funziona perfettamente! – alecxe

2

Beh, mi ricordo di aver letto da qualche parte che il gelsomino 2 non supporta il tipo di fiammifero che stai cercando di fare (con la funzione asincrona all'interno), e restituendo le promesse .. cercherò di trovare la fonte e aggiornare qui. Inoltre non dovresti fare le azioni del mouse all'interno del matcher, non è questo il punto dei matchers.

Quindi in sostanza ciò che sto dicendo e suggerendo è il seguente: Se si desidera un codice pulito, esportare quanto segue in una funzione e chiamarlo.

var checkToolTipVisibility (elm, expectedTooltip) { 
    browser.actions().mouseMove(elm).perform(); 
    browser.wait(EC.visibilityOf(tooltipPage.tooltip), 5000, "Tooltip is still not visible.");//optional then here if you want to fail with a timeout or something... 
    expect(tooltipPage.tooltip.getText()).toEqual(expectedTooltip); 
} 

checkToolTipVisibility(page.fromDateInput, "After");//usage 

Penso che sia una soluzione molto semplice e pulito, che non richiede alcun matchers personalizzati, ed è il modo in cui il gelsomino di fare le cose (non funzioni asincrone in matchers), questo è il modo che uso nel mio codice, tranne quelle funzioni siedono in un file utils.js che richiedo quando necessario.

Spero di aver aiutato, e continuerò a cercare la fonte della mia prima affermazione!

+3

"Beh, mi ricordo di aver letto da qualche parte che il gelsomino 2 non supporta il tipo di fiammifero che stai provando a fare (con funzione asincrona all'interno) e restituendo le promesse .." Vero ma il goniometro introduce nel pacchetto 'jasminewd2', che consente a Jasmine avere accoppiatori che funzionano con il tipo di promessa prodotta da Selenium, quindi non penso che tu abbia l'ultima parola lì. Oltre a questo, sono d'accordo che un matcher dovrebbe evitare di cambiare lo stato della pagina. – Louis

+1

La tua prima affermazione è apparentemente errata. Se così fosse, allora non c'era modo che qualcuno potesse passare la funzione 'getText()' a un'istruzione 'expect'. Il goniometro utilizza le librerie di jasmine attraverso jasminewd2 (un ponte tra webdriverjs e gelsomino) e la dimostrazione di questa affermazione si trova lì. [Ecco di più] (https://github.com/angular/jasminewd/blob/jasminewd2/index.js#L158) –

+1

@GirishSortur non volevo dire che non si può passare una promessa come primo argomento, ma il secondo, 'expect (promise) .to Something (notPromise)' ... – vrachlin

0

Sulla base del @Girish Sortur's perfect answer, ecco il codice completo del matcher che ora sta funzionando perfettamente gestire sia tooltip mancanti e un diverso caso di testo tooltip separatamente:

toHaveTooltip: function() { 
    return { 
     compare: function(elm, expectedTooltip) { 
      var tooltipPage = requirePO("tooltip"), 
       result = {}; 

      // mouse over the element 
      browser.actions().mouseMove(elm).perform(); 

      // wait for tooltip to appear and handle errors 
      result.pass = browser.wait(EC.visibilityOf(tooltipPage.tooltip), 5000).then(function() { 
       return tooltipPage.tooltip.getText().then(function(actualTooltip) { 
        result.message = "Expected tooltip: '" + expectedTooltip + "'. Actual tooltip: '" + actualTooltip + "'."; 
        return jasmine.matchersUtil.equals(actualTooltip, expectedTooltip); 
       }) 
      }, function() { 
       result.message = "No tooltip shown on mouse over the element"; 
       return false; 
      }); 
      return result; 
     } 
    }; 
},