2016-06-20 60 views
17

Il problema:Una delle corde in serie in modo che corrisponda l'espressione

Ho una serie di promesse che si risolve a un array di stringhe . Ora il test dovrebbe passare se almeno una delle stringhe corrisponde a un'espressione regolare.

Attualmente, ho risolto utilizzando semplici concatenazione stringa:

protractor.promise.all([text1, text2, text3]).then(function (values) { 
    expect(values[0] + values[1] + values[2]).toMatch(/expression/); 
}); 

Ovviamente, questo non scala bene e non è particolarmente leggibile.

la domanda:

Is è possibile risolvere utilizzando un personalizzato gelsomino matcher, o jasmine.any() o custom asymmetric equality tester?

+0

perché non si basta usare una variabile ('x = false;') e 'values.map (function (v) {x = v.match (/ espressione /) || X; }); 'e quindi solo' expect (x) .toBe (true); '? –

+4

Cosa c'è di sbagliato in 'expect (values.some (function (i) {return /expression/.match(i);}). ToBe (true);'? – haim770

+0

Sembra che un matcher personalizzato potrebbe essere la strada da percorrere: http : //jasmine.github.io/2.0/custom_matcher.html –

risposta

6

Si potrebbe semplicemente utilizzare map per ottenere un elenco di boolean e poi affermare il risultato con toContain(true):

var all = protractor.promise.all; 
var map = protractor.promise.map; 

expect(map(all([text1, text2, text3]), RegExp.prototype.test, /expression/)).toContain(true); 

Si potrebbe anche usare un matcher personalizzato:

expect(all([text1, text2, text3])).toContainPattern(/expression/); 

E il matcher personalizzato dichiarata in beforeEach:

beforeEach(function() { 
    jasmine.addMatchers({ 
    toContainPattern: function() { 
     return { 
     compare: function(actual, regex) { 
      return { 
      pass: actual.some(RegExp.prototype.test, regex), 
      message: "Expected [" + actual + "] to have one or more match with " + regex 
      }; 
     } 
     }; 
    } 
    }); 
}); 
+0

Solo per curiosità: quali sono i vantaggi di 'tutto', che a me sembra che sia l'equivalente di' map' su 'values.reduce()'? So che non ha molta importanza, ma intuitivamente direi che 'reduce' ha maggiori probabilità di ottenere risultati migliori su insiemi di dati di grandi dimensioni, specialmente se si interrompe l'abbinamento dopo che è stata trovata la prima corrispondenza valida (cfr. La mia risposta aggiornata). Ad ogni modo, +1 perché la tua risposta è più in linea con l'OP che vuole un approccio più simile al gelsomino –

+0

@Elias Van Ootegem, 'all' non è lo stesso di' map'. 'all' risolve una serie di promesse, mentre' map' risolve una singola promessa. L'uso di 'map' su' reduce' non avrà alcun impatto sulle prestazioni poiché tutto il costo è nel recuperare il testo per ogni elemento, che è una chiamata al browser per ciascun elemento. E se fosse di qualche preoccupazione, allora 'Array.some()' con '/expression/.test (item)' sarebbe più appropriato e meno costoso di 'Array.reduce' con' item.match (/ expression /) '. –

+0

Sì, ma '[] .reduce' (come al secondo approccio nella mia risposta) consente di saltare la chiamata 'match' una volta trovata una corrispondenza positiva, quindi ci saranno tra 1 e n chiamate di corrispondenza, mentre' map' e 'all' sembra elaborare tutti i valori dell'array, o mi manca qualcosa? –

7

Come detto nei commenti, anche se inizialmente ho usato map, ridurre permetterebbe di fare ciò che è necessario, e in questa casta, almeno rende molto più senso:

protractor.promise.all([text1, text2, text3]).then(function (values) { 
    expect(
     values.reduce(function(p, v) { 
      return v.match(/expression/) || p; 
     }, false) 
    ).toBe(true); 
}); 

o scrivendo la stessa cosa , ma che utilizzano ES6 funzioni di direzione:

protractor.promise.all([text1, text2, text3]).then(function(values) { 
    exptect(
     values.reduce((p, v) => v.match(/expression/) || p, false) 
    ).toBe(true); 
}); 

Entrambi fanno la stessa cosa, la richiamata ridurre imposterà falso, fino a quando l'espressione v.match restituisce true.
Sto assumendo questo è evidente ai più, ma ho pensato di fornire entrambe le sintassi e qualche spiegazione per riferimento futuro


Forse questa soluzione potrebbe essere ottimizzato un po 'di più, smettere di corrispondenza il modello di una volta una singola partita è stata trovata:

protractor.promise.all([text1, text2, text3]).then(function (values) { 
    expect(
     values.reduce(function(p, v) { 
      return p || !!v.match(/expression/); 
     }, false) 
    ).toBe(true); 
}); 

Tutto quello che ho fatto è stato quello di utilizzare il valore di ridurre la corrente come predefinito (una volta che è stato impostato su true, non c'è nessun punto nel testare qualsiasi altro valore stringa). Per garantire che v.match valuti su un valore booleano anziché su un array, ho semplicemente utilizzato !!v.match(). Quella parte è opzionale però. Nel ES6, la stessa cosa è simile al seguente:

protractor.promise.all([text1, text2, text3]).then(function(values) { 
    exptect(
     values.reduce((p, v) => p || !!v.match(/expression/), false) 
    ).toBe(true); 
}); 

Questo potrebbe funzionare meglio con set di dati di grandi (considerando il match chiamate si fermano una volta che il primo match è stato trovato, al contrario di v.match essere chiamato ogni volta).

6

Se funziona,

protractor.promise.all([text1, text2, text3]).then(function (values) { 
    expect(values[0] + values[1] + values[2]).toMatch(/expression/); 
}); 

Penso che si possa scrivere questo come segue;

protractor.promise.all([text1, text2, text3]).then(function (values) { 
    expect(values.join('')).toMatch(/expression/); 
}); 

Ed è scalabile. :)

+0

Ci potrebbero essere alcuni problemi con questo approccio. Se 'values' assomiglia a questo, per esempio:' ['foo', 'bar', 123, 'car'] 'e l'espressione è'/foobar/', unendo tutti i valori in un'unica grande stringa, il risultante stringa corrisponderà/contenga '/ foobar /', ma nessuno dei valori individuali effettivi lo farebbe. Non sarebbe desiderabile IMO –

4

Se quelli [testo1, testo2, testo3] sono testi da ElementFinder .getText() allora puoi anche provare con le condizioni attese (sai che sono un grande fan di EC giusto? :)).

describe('test', function() { 

    it('test', function() { 
     var EC = protractor.ExpectedConditions; 
     browser.get('http://www.protractortest.org/testapp/ng1/#/form'); 

     var textToContain = 'Check'; //Notice, that this is not 'equals', this is 'contains' 
     var elementTextToCheck1 = EC.textToBePresentInElement($('#checkboxes h4'), textToContain); // Here it will be true : Checkboxes 
     var elementTextToCheck2 = EC.textToBePresentInElement($('#animals h4'), textToContain); //false 
     var elementTextToCheck3 = EC.textToBePresentInElement($('#transformedtext h4'), textToContain); //false 

     var oneElementShouldContainText = EC.or(elementTextToCheck1, elementTextToCheck2, elementTextToCheck3); 

     expect(oneElementShouldContainText()).toBeTruthy(`At least one element should contain "${textToContain}"`); 
    }) 
}); 

Per elementi semplici: http://www.protractortest.org/#/api?view=ExpectedConditions.prototype.textToBePresentInElement

Per textArea, ingressi: http://www.protractortest.org/#/api?view=ExpectedConditions.prototype.textToBePresentInElementValue

dati, che sfortunatamente .textToBePresentInElement funziona solo con singolo ElementFinder, ArrayElementFinder non è supportato. In tal caso, puoi creare qualcosa con .filter() e affermare che l'elenco restituito è vuoto.

Problemi correlati