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)
Related
I have over 5000 .txt files stored locally on my app each file is at least 15 lines of words
So am trying to search with multiple words all over the 5000 list
Finally i was able to search in all of them but with only one problem
The app freezes until the whole process finished
Future<List<FatwaModel>> searchFatawy(String searchText) async {
if (searchText.isEmpty) return [];
emit(SearchFatawyLoadingState());
searchFatawyTxt.clear();
RegExp regExp = RegExp(
RemoveExtinctionsAtWord()
.normalise(searchText)
.trim()
.split(' ')
.where((element) => element.length > 1)
.join('|'),
caseSensitive: false,
);
Future.forEach(fullFatawy, (FatwaModel fatwa) {
bool check = regExp.hasMatch(RemoveExtinctionsAtWord().normalise(
RegExp(r'(?<=:)(.*)(?=)').firstMatch(fatwa.fatwaBody)?.group(0) ?? '',
));
if (check) searchFatawyTxt.add(fatwa);
}).then((value) {
emit(SearchFatawySuccessState());
});
// searchFatawyTxt = fullFatawy
// .where((fatwa) => regExp.hasMatch(RemoveExtinctionsAtWord().normalise(
// RegExp(r'(?<=:)(.*)(?=)').firstMatch(fatwa.fatwaBody)?.group(0) ??
// '',
// )))
// .toList();
//Sorting the list depending on how many keywords found in a single txt file
searchFatawyTxt.sort(
(FatwaModel a, FatwaModel b) {
int aMatchCount = regExp
.allMatches(
RemoveExtinctionsAtWord().normalise(
RegExp(r'(?<=:)(.*)(?=)').firstMatch(a.fatwaBody)?.group(0) ??
'',
),
)
.length;
int bMatchCount = regExp
.allMatches(
RemoveExtinctionsAtWord().normalise(
RegExp(r'(?<=:)(.*)(?=)').firstMatch(b.fatwaBody)?.group(0) ??
'',
),
)
.length;
return bMatchCount.compareTo(aMatchCount);
},
);
return searchFatawyTxt;
}
All am trying to do is showing a progress bar while the search is being process without freezing the app.
Instead of calling that method directly on your app ( on the main thread ), you will need to call it in another isolate that doesn't share a memory with the main thread.
ad the quickest and easiest way to do it is by calling a compute() method which spawns an isolate and runs the provided callback on that isolate, passes it the provided message, and (eventually) returns the value returned by callback.
Future<List<FatwaModel>> isolatedMethod = compute(searchFatawy, searchText);
Note that I am passing your method declaration, not calling it inside the compute().
and now you can use that isolatedMethod as the Future which you will use in your app.
As part of my nightwatch.js testing, I have the following code that will list all the values of an element (in this case, a list of UK towns);
"Page 2 Location SEO Crawl paths are displayed": function (browser) {
browser.elements('xpath', '//a[contains(#href,"location")]', function (results) {
results.value.map(function(element) {
browser.elementIdAttribute(element.ELEMENT, 'innerText', function(res) {
var resme = res.value;
console.log(resme)
});
});
});
},
This correctly list all the element values, as such;
What I'd now like to do is check that Nottingham is listed in the result, and Fail the test if it's not.
I installed the assert npm package to see if that would help, which changed my code to;
"Page 2 Location SEO Crawl paths are displayed": function (browser) {
browser.elements('xpath', '//a[contains(#href,"location")]', function (results) {
results.value.map(function(element) {
browser.elementIdAttribute(element.ELEMENT, 'innerText', function(res) {
var resme = res.value;
console.log(resme);
if (resme.includes("Nottingham")) {
assert.ok(true);
}
else {
assert.ok(false);
}
});
});
});
},
but this didn't work, as I kept getting the following error;
Is using the assert package the best way of testing this, or it there a more straightforward way of asserting that Nottingham is included in this list, and the tests fails if it's not.
I've tried using resme.includes("Nottingham"), but this doesn't fail the test.
Any help would be greatly appreciated.
Thanks.
Looks like the inner-most function (the one that has res as parameter) is called for every item, and resme is the item you are currently iterating, not an array, so the includes function is not working as expected.
I'm not familiar with this library but I guess you have to do something like this:
"Page 2 Location SEO Crawl paths are displayed": function (browser) {
var found = false;
browser.elements('xpath', '//a[contains(#href,"location")]', function (results) {
results.value.map(function(element) {
browser.elementIdAttribute(element.ELEMENT, 'innerText', function(res) {
var resme = res.value;
console.log(resme);
if (resme === "Nottingham") {
found = true;
// Maybe return something like null or 0 to stop iterating (would depend on your library).
}
});
});
});
assert.ok(found)
},
You init a variable "found" with a false value, and when you iterate over every value you set it to true if you find it. Optionally, you should break the iteration at that point. When the whole process is finished, assert that you found the value you wanted.
I have a meteor app for which I added the search-source package to search certain collections and it works partially. That is, when I search for the term foo bar it returns results for each of "foo" and "bar". This is fine, but I want to also be able to wrap the terms in quotes this way: "foo bar" and get results for an exact match only. at the moment when i do this i get an empty set. Here is my server code:
//Server.js
SearchSource.defineSource('FruitBasket', function(searchText, options) {
// options = options || {}; // to be sure that options is at least an empty object
if(searchText) {
var regExp = buildRegExp(searchText);
var selector = {$or: [
{'fruit.name': regExp},
{'fruit.season': regExp},
{'fruit.treeType': regExp}
]};
return Basket.find(selector, options).fetch();
} else {
return Basket.find({}, options).fetch();
}
});
function buildRegExp(searchText) {
// this is a dumb implementation
var parts = searchText.trim().split(/[ \-\:]+/);
return new RegExp("(" + parts.join('|') + ")", "ig");
}
and my client code:
//Client.js
Template.dispResults.helpers({
getPackages_fruit: function() {
return PackageSearch_fruit.getData({
transform: function(matchText, regExp) {
return matchText.replace(regExp, "<b>$&</b>")
},
sort: {isoScore: -1}
});
}
});
Thanks in advance!
I've modified the .split pattern so that it ignores everything between double quotes.
/[ \-\:]+(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)/
Thus, you can simply wrap an exact phrase search in double quotes and it won't get split.
There is one more thing; since we don't need the quotes, they are removed in the next line using a .map function with a regex that replaces double quotes at the start or the end of a string part: /^"|"$/
Sample code:
function buildRegExp(searchText) {
// exact phrase search in double quotes won't get split
var arr = searchText.trim().split(/[ \-\:]+(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)/);
var parts = arr.map(function(x){return x.replace(/^"|"$/g, '');});
return new RegExp("(" + parts.join('|') + ")", "ig");
}
console.log(buildRegExp("foo bar"));
console.log(buildRegExp("\"foo bar\""));
I am using this directive to keep user typing only number into input tag.
app.directive('validNumber', function () {
return {
require: '?ngModel',
link: function (scope, element, attrs, ngModelCtrl) {
if (!ngModelCtrl) {
return;
}
ngModelCtrl.$parsers.push(function (val) {
if (angular.isUndefined(val)) {
var val = '';
}
var clean = val.replace(/[^0-9\.]/g, '');
var decimalCheck = clean.split('.');
if (!angular.isUndefined(decimalCheck[0])) {
decimalCheck[0] = decimalCheck[0].slice(0, 10);
if (!angular.isUndefined(decimalCheck[1])) {
clean = decimalCheck[0] + '.' + decimalCheck[1];
}
else {
clean = decimalCheck[0];
}
//console.log(decimalCheck[0][0]);
}
if (!angular.isUndefined(decimalCheck[1])) {
decimalCheck[1] = decimalCheck[1].slice(0, 3);
clean = decimalCheck[0] + '.' + decimalCheck[1];
}
if (val !== clean) {
ngModelCtrl.$setViewValue(clean);
ngModelCtrl.$render();
}
return clean;
});
element.bind('keypress', function (event) {
if (event.keyCode === 32) {
event.preventDefault();
}
});
}
};
});
But now i want to custome this, that means user can type ONLY ONE of "+" or "-" in the first. I think i have to change this pattern of
var clean = val.replace(/[^0-9\.]/g, '');
i also try to change into val.replace(/[^0-9.+-]/g, ''). It works but incorrectly, with this pattern user can type more "+" and "-" in any position of input field. I just wanna keep user typing ONLY ONE of "+" or "-" in the first like "+1234" or "-1234"
This is more of a regex problem than an AngularJS one, so you might have more luck there: https://stackoverflow.com/questions/tagged/regex
I'll try help you though. I think the regex you want matches a single +-, then any number of digits, then optionally a decimal point, then any number of digits. A single regex to match that is:
^[+-]?[0-9]*\.?[0-9]*
Have a read about groups and the '?' operator. This regex allows:
+.
-.
which don't make sense as input. You could design clever regexes to omit those results, but I think it would be easier to check the entry programmatically.
Finally, there are also very likely regexes online to help you solve any regex problem you ever come across more comprehensivley than you could. Just google an english description next time, and check out this for what you want:
http://www.regular-expressions.info/floatingpoint.html
I am trying to mock my repository's Get() method to return an object in order to fake an update on that object, but my setup is not working:
Here is my Test:
[Test]
public void TestUploadDealSummaryReportUploadedExistingUpdatesSuccessfully()
{
var dealSummary = new DealSummary {FileName = "Test"};
_mockRepository.Setup(r => r.Get(x => x.FileName == dealSummary.FileName))
.Returns(new DealSummary {FileName = "Test"}); //not working for some reason...
var reportUploader = new ReportUploader(_mockUnitOfWork.Object, _mockRepository.Object);
reportUploader.UploadDealSummaryReport(dealSummary, "", "");
_mockRepository.Verify(r => r.Update(dealSummary));
_mockUnitOfWork.Verify(uow => uow.Save());
}
Here is the method that is being tested:
public void UploadDealSummaryReport(DealSummary dealSummary, string uploadedBy, string comments)
{
dealSummary.UploadedBy = uploadedBy;
dealSummary.Comments = comments;
// method should be mocked to return a deal summary but returns null
var existingDealSummary = _repository.Get(x => x.FileName == dealSummary.FileName);
if (existingDealSummary == null)
_repository.Insert(dealSummary);
else
_repository.Update(dealSummary);
_unitOfWork.Save();
}
And here is the error that I get when I run my unit test:
Moq.MockException :
Expected invocation on the mock at least once, but was never performed: r => r.Update(.dealSummary)
No setups configured.
Performed invocations:
IRepository1.Get(x => (x.FileName == value(FRSDashboard.Lib.Concrete.ReportUploader+<>c__DisplayClass0).dealSummary.FileName))
IRepository1.Insert(FRSDashboard.Data.Entities.DealSummary)
at Moq.Mock.ThrowVerifyException(MethodCall expected, IEnumerable1 setups, IEnumerable1 actualCalls, Expression expression, Times times, Int32 callCount)
at Moq.Mock.VerifyCalls(Interceptor targetInterceptor, MethodCall expected, Expression expression, Times times)
at Moq.Mock.Verify(Mock mock, Expression1 expression, Times times, String failMessage)
at Moq.Mock1.Verify(Expression`1 expression)
at FRSDashboard.Test.FRSDashboard.Lib.ReportUploaderTest.TestUploadDealSummaryReportUploadedExistingUpdatesSuccessfully
Through debugging I have found that the x => x.FileName is returning null, but even if i compare it to null I still get a null instead of the Deal Summary I want returned. Any ideas?
I'm guessing your setup isn't matching the call you make because they're two different anonymous lambdas. You may needs something like
_mockRepository.Setup(r => r.Get(It.IsAny<**whatever your get lambda is defined as**>()).Returns(new DealSummary {FileName = "Test"});
You could verify by setting a breakpoint in the Get() method of your repository and seeing if it is hit. It shouldn't be.