I am implementing a testcase in cypress where I want to match a list of dateTime values with a RegEx pattern.
All of this gets done in a forEach loop. It works for the first Item and fails on the 2nd item, even though they are the same.
Here is the code for reproduction:
const array = [
"2022-05-23 14:39:43.145",
"2022-05-23 14:39:43.145",
"2022-05-23 14:39:43.120",
"2022-05-23 14:39:43.120",
"2022-05-23 14:39:43.096",
"2022-05-23 14:39:43.096",
"2022-05-23 14:39:43.074",
"2022-05-23 14:39:43.074",
];
const dateTime = new RegExp(/\d\d\d\d-\d\d-\d\d\s\d\d:\d\d:\d\d\.\d\d\d/gm);
describe('tesst',() => {
it('should work', function() {
array.forEach((object) => {
expect(object).to.match(dateTime);
})
});
})
Edit It seems like the bug was the global flag (/g) of the RegEx pattern. However I do not get why this is an issue here. I'd be thankful for an explanation :)
You can make the example simpler to help eliminate factors,
it('tests with regex', function() {
expect("2022-05-23 14:39:43.145").to.match(dateTime) // passes
expect("2022-05-23 14:39:43.120").to.match(dateTime) // fails
})
If you look at the chaijs
library, this is how to.match()
is implemented
function assertMatch(re, msg) {
if (msg) flag(this, 'message', msg);
var obj = flag(this, 'object');
this.assert(
re.exec(obj)
, 'expected #{this} to match ' + re
, 'expected #{this} not to match ' + re
);
}
so the active ingredient is re.exec(obj)
, equivalent to dateTime.exec("2022-05-23 14:39:43.145")
and if you console.log that expression, the first call succeeds and the second returns null - which chai
interprets as a failure.
it('tests with regex', function() {
console.log(dateTime.exec("2022-05-23 14:39:43.145")) // ['2022-05-23 14:39:43.145', index: 0...
console.log(dateTime.exec("2022-05-23 14:39:43.120")) // null
})
The reason can be found at MDN RegExp.prototype.exec() Finding successive matches
If your regular expression uses the "g" flag, you can use the exec() method multiple times to find successive matches in the same string.
When you do so, the search starts at the substring of str specified by the regular expression's lastIndex property (test() will also advance the lastIndex property).
Note that the lastIndex property will not be reset when searching a different string, it will start its search at its existing lastIndex .
If we check the lastIndex
property after each step and repeat a few times, every 2nd date fails.
But after a failure lastIndex
is reset and the next test succeeds.
it('tests with regex', function() {
console.log(dateTime.exec("2022-05-23 14:39:43.145")) // ['2022-05-23 14:39:43.145', index: 0...
console.log(dateTime.lastIndex) // 23
console.log(dateTime.exec("2022-05-23 14:39:43.120")) // null
console.log(dateTime.lastIndex) // 0
console.log(dateTime.exec("2022-05-23 14:39:43.096")) // ['2022-05-23 14:39:43.096', index: 0...
console.log(dateTime.lastIndex) // 23
console.log(dateTime.exec("2022-05-23 14:39:43.074")) // null
console.log(dateTime.lastIndex) // 0
})
So you can make your loop work by manually resetting the lastIndex
it('should work', function() {
array.forEach(object => {
expect(object).to.match(dateTime); // passes every date
dateTime.lastIndex = 0;
})
})
(or removing the /g
flag)