Custom is Obsolete - fluentvalidation

I updated a project to the latest version of Fluent Validation and I get a warning:
'AbstractValidator<AccountSignInModel>.Custom(Func<AccountSignInModel, ValidationFailure>)'
is obsolete: 'Use model-level RuleFor(x => x) instead'
When I am using the following code:
When(x => !String.IsNullOrEmpty(x.Password) && !String.IsNullOrEmpty(x.Username), () => {
Custom(x => {
Boolean valid = service.ValidateCredentials(x.Username, x.Password));
if (!valid)
return new ValidationFailure("Credentials", "Authentication failed");
return null;
});
});
I don't know how to convert this into a RuleFor(x => x).
Or is there another alternative to custom?

We decided to use Fluent Validation recently on our application. So I am fairly new to this, figuring stuff as we go forward.
Stumbled upon your issue while searching for a different issue. There are not many resources on this. Thought I would share my thoughts, if it can help you.
Here is what I would do.
public NewCustomValidator(Interface int)
{
CascadeMode = CascadeMode.Continue; // you could use StopOnFirstFailure or Continue
RuleFor(x=> x.UserName).NotEmpty(); // Will return an error if null or empty
RuleFor(x=> x.Password).NotEmpty();
RuleSet("SomeNameHere", () =>
{
CascadeMode = CascadeMode.StopOnFirstFailure;
var isValid = false;
RuleFor(x=> new { x.UserName, x.Password })
.Must((u , p) =>
{
valid = ValidateCredentials(u, p);
return valid;
})
.WithMessage("You can show any message if you want");
RuleFor(x => x.UserName).Must((s) => isValid);
RuleFor(x => x.Password).Must((s) => isValid);
});
}
So, I am basically using your method to define in a Rule Set. You may have to add some code to validate the ruleSet.
var result = validator.Validate(NewCustom, ruleSet: "SomeNameHere");
Disclaimer: This code may not compile. But it will give you an idea on how to approach the problem. If you have better ideas or If you could make the code work, please post the answer. That will help me gain some more knowledge. Thanks.

Related

How to save just part of URL in Cypress (without domain name)

I'm writing CY test and was trying to solve it by myself for couple of hours but unsuccessfully. Could you please help me here a bit :)
Each time I run the test I'm getting new URL, e.g.
https://website.com/en/info/is/here/
And I need to save only
/en/info/is/here/ (so without domain name)
I need to compare it later with another href.
Could you please advise me the way how to do it or at least the direction? Thanks a lot!
The cy.location() command gives you named parts, so from the example pathname is the part you need
cy.visit('http://localhost:8000/app/index.html?q=dan#/users/123/edit')
cy.location().should((loc) => {
..
cy.wrap(loc.pathname).as('url1')
...
})
If you have search or hash as well
cy.visit('http://localhost:8000/app/index.html?q=dan#/users/123/edit')
cy.location().should((loc) => {
..
cy.wrap(loc.pathname + loc.search + loc.hash).as('url1')
...
})
You can use .split() on the URL string.
Where you save it depends on the location of it's use.
Inside one test:
let pathname
cy.url().then((url) => url.split('/').slice(3)).as('pathname1')
...
cy.get('#pathname1').then(pathname1 => {
expect(pathname1).to.eq(pathname2)
})
Between tests:
let pathname1
it('gets first pathname', () => {
cy.url().then((url) => pathname1 = url.split('/').slice(3))
})
it('uses first pathname', () => {
expect(pathname1).to.eq(pathname2)
})
Use the URL interface to parse your string (also used by cy.location)
const urlString = 'https://website.com/en/info/is/here/'
const url = new URL(urlString)
const pathname = url.pathname // yields "/en/info/is/here/"
You can use the following :
let firstUrl = null;
let secondUrl = null;
cy.url().then(url => {
firstUrl = url;
});
/* sometimes later */
cy.url().then(url => {
secondUrl = url;
});
/* sometimes later */
expect(firstUrl).to.equal(secondUrl)
If you want to just compare some part of these URL I recommend you using a regex expressions.
You can use the javascript split to do this:
let partUrl
cy.url().then((url) => {
partUrl = url.split('com')[1] //saves /en/info/is/here/
})
You could use .replace() and the Cypress baseUrl value, and then store that value in a Cypress environment variable.
cy.url().then((url) => {
Cypress.env('someUrl', url.replace(Cypress.config('baseUrl'), '');
}).then(() => {
cy.log(Cypress.env('someUrl'));
})

Mocking a return value for a Subject - Unit Testing with Jasmine

I'm unit testing and part of the testing has a Subject. I'm new to Subjects and get the general gist of how they work but am struggling to mock a return value on one. I've tried various ways in the hopes of stumbling on the correct way like using a spy and returnvalue to return the number 3.
In the component:
....
private searchEvent: Subject<string> = new Subject<string>();
....
this.searchEvent.pipe(debounceTime(500)).subscribe(value => {
if (value.length >= 3) {
this.retrieveAssets(value);
}
})
....
In my spec file I basically have:
component['searchStockEvent'].subscribe(x=> of(3));
fixture.whenStable().then(() => {
expect(component['retrieveAssets']).toHaveBeenCalled();
});
searchEvent being private will make it difficult to call next directly on the subject so you have to find a way of what makes searchEvent emit a value of greater than 3 and go that route.
For this demo, we will make it public:
....
public searchEvent: Subject<string> = new Subject<string>();
....
this.searchEvent.pipe(debounceTime(500)).subscribe(value => {
if (value.length >= 3) {
this.retrieveAssets(value);
}
})
....
import { fakeAsync, tick } from '#angular/core/testing';
it('should call retrieve assets', fakeAsync(() => {
component.searchEvent.next(3);
// we need to pass 501ms in a fake way
tick(501);
expect(component.retreiveAssets).toHaveBeenCalled();
}));

UAT > How to check the ordering of data in a table using Selenium?

I need to test that a resulting list is ordered date descending using selenium.
this.Then(/^the list items should be ordered by date descending$/, (arg1): CucumberStepOutput => {
actions
return 'pending';
});
I'm sure this is something that has been done many times by people in the selenium community - I am hoping someone will share best practice.
If anyone is interested in the answer or at least the solution I ended up with see the snippet below.
It's a bit ugly (comment with suggestions to clean up) but works!
this.Then(/^the list items should be in descending$/, (): CucumberStepOutput => {
return new Promise<void>((resolve, reject) => {
let expectedValues = ['value1',
'value2',
'value3'
];
client.elements('.element-class').then(elements => {
let resultDates: Array<string> = [];
elements.value.reduce<Promise<void>>((chain, nextElement) => {
return chain.then(() => {
return client.elementIdText(nextElement.ELEMENT).then(text => {
resultVa.push(text.value);
});
});
}, Promise.resolve()).then(()=>{
JSON.stringify(resultValues).should.equal(JSON.stringify(expectedValues));
resolve();
})
});
});
});

Making http requests inside a for loop ANGULAR JS 2

I am trying to work with the youtube API.
In order to get the icons for the first nth videos I have to make a request.
I was thinking to make a for loop and inside that loop there would be the request.
The problem with this approach is that I am getting the responses with the wrong order and completely random.
So my question :
is there a way to make a for loop wait for a response? I am also able to work with the RxJS operators but I don't know what I should search for
Thanks in advance
You could leverage the Observable.forJoin method. In this case, the "global" callback will be called when all requests have ended.
Here is a sample:
Observable.forkJoin([
this.http.get('/req1').map(res => res.json()),
this.http.get('/req2').map(res => res.json()),
(...)
]).subscribe(results => {
// Called when all requests have ended
var result1 = results[0];
var result2 = results[1];
(...)
});
In your particular use case, you can leverage in addition the flatMap operator:
this.http.get('/videos').map(res => res.json())
.flatMap(videos => {
return Observable.forkJoin(videos.map((video) => {
return this.http.get(`/video/${video.id}/icon`)
.map(res => res.json());
});
}).subscribe(results => {
// all icons received here
});
So I ended up using something like this.
searchVideo( videoIdArray ) {
let observableBatch = [];
let data;
let i;
let videosTempArray: Array<Video>=[];
for(i=0;i<videoIdArray.length;i++){
let videoTemp: Video= {};
videosTempArray.push(videoTemp);
}
videosTempArray.forEach(( videoTemp, key ) => {
observableBatch.push( this.http.get(BASE_URL_VIDEO + '?part=statistics%2Csnippet' + '&id=' + videoIdArray[key].videoId + '&key=' + API_TOKEN)
.map((res: Response) => {
res.json();
// console.log(key);
data = res.json();
videosTempArray[key].channelId=data.items[0].snippet.channelId;
videosTempArray[key].tags=data.items[0].snippet.tags;
videosTempArray[key].views=data.items[0].statistics.viewCount;
videosTempArray[key].likes=data.items[0].statistics.likeCount;
videosTempArray[key].dislikes=data.items[0].statistics.dislikeCount;
return videosTempArray[key];
}
)
);
});
return Observable.forkJoin(observableBatch);
}
thanks for the help!!!

Conditional sign-in validation using Fluent Validation

I am trying to use Fluent Validation and it seems easy to use at the beginning but now there is some problem. I need to validate a SignIn view model, shown below:
public SignInViewModelValidator(IMembershipService membershipService)
{
_membershipService = membershipService;
RuleFor(x => x.EMail).NotEmpty().EmailAddress();
RuleFor(x => x.Password).NotEmpty().Length(6, 20);
Custom(x =>
{
var user = _membershipService.ValidateUser(x.EMail, x.Password);
if (user == null)
return new ValidationFailure("EMail", "Your E-Mail Address or password was invalid.");
return null;
});
}
But I'm getting all the errors at once, like this:
'E Mail' should not be empty.
Your E-Mail Address or password was invalid.
'Password' should not be empty.
How can I change this behavior to not check the Custom validation rule when the other rules are invalid? In other words, it should only check the Custom validation rule when 'EMail' and 'Password' fields are valid.
I managed this in this way:
public SignInViewModelValidator(IMembershipService membershipService){
_membershipService = membershipService;
bool firstPhasePassed = true;
RuleFor(x => x.EMail)
.NotEmpty().WithMessage("")
.EmailAddress().WithMessage("")
.OnAnyFailure(x => { firstPhasePassed = false; });
RuleFor(x => x.Password)
.NotEmpty().WithMessage("")
.Length(6, 16).WithMessage("")
.OnAnyFailure(x => { firstPhasePassed = false; });
When(x => firstPhasePassed, () =>
{
Custom(x =>
{
if (_membershipService.ValidateUser(x.EMail, x.Password) == null)
return new ValidationFailure("EMail", "");
return null;
});
});
}
You can use the When method to check your custom rule only when your email/password rules are valid.
To make this easier, I suggest moving your Custom rule logic into a separate method (something like IsValidEmailAndPassword) and use the Must method to validate both email and password. Since you're passing in multiple parameters (email and password) into that method, read the documentation on the overload of Must that "accepts an instance of the parent object being validated" in order to implement this rule.
Hopefully those links point you to the right direction.