One of strings in array to match an expression

2020-06-08 16:41发布

The Problem:

I have an array of promises which is resolved to an array of strings. Now the test should pass if at least one of the strings matches a regular expression.

Currently, I solve it using simple string concatenation:

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

Obviously, this does not scale well and is not particularly readable.

The Question:

Is is possible to solve it using a custom jasmine matcher, or jasmine.any() or custom asymmetric equality tester?

5条回答
霸刀☆藐视天下
2楼-- · 2020-06-08 16:45

If this works,

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

I think you can write this as follows;

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

And it's scalable. :)

查看更多
做自己的国王
3楼-- · 2020-06-08 16:59

As said in the comments, although I initially use map, reduce would allow you to do what you need, and in this caste at least makes a lot more sense:

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

Or writing the same thing, but using ES6 arrow functions:

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

Both do the same thing, the reduce callback will default to false, until the v.match expression evaluates to true.
I'm assuming this is obvious to most people, but I thought I'd provide both syntaxes and some explanation for future reference


Perhaps this solution could be optimized a bit more, to stop matching the pattern once a single match has been found:

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

All I did was to use the current reduce value as default (once that has been set to true, there's no point in testing any other string value). To ensure v.match evaluates to a boolean instead of an array, I just used !!v.match(). That part is optional though. In ES6, the same thing looks like this:

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

This might perform better with big data sets (considering the match calls stop once the first match was found, as opposed to v.match being called every time).

查看更多
何必那么认真
4楼-- · 2020-06-08 17:02

If those [text1, text2, text3] is texts from ElementFinder .getText() then you can also to try with Expected Conditions (You know that i am huge fan of EC right? :) ).

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

For simple elements: http://www.protractortest.org/#/api?view=ExpectedConditions.prototype.textToBePresentInElement

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

Notice, that unfortunatelly .textToBePresentInElement works only with single ElementFinder, ArrayElementFinder is not supported. But in that case you can make something with .filter() and assert that returned list is empty.

查看更多
放我归山
5楼-- · 2020-06-08 17:04

You could simply use map to get a list of boolean and then assert the result with toContain(true):

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

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

You could also use a custom matcher:

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

And the custom matcher declared 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
          };
        }
      };
    }
  });
});
查看更多
来,给爷笑一个
6楼-- · 2020-06-08 17:06

Array.prototype.some() looks like what you actually looking for.

protractor.promise.all([text1, text2, text3]).then(function (values) {
    expect(values.some(v => v.match(/expression/)).toBe(true);
});

For more information https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/some

查看更多
登录 后发表回答