I am using Behat/Mink with Selenium for acceptance testing. I need to determine if my web page is making a badly formed call to the server via Ajax. The problem is, the server will attempt to "correct" badly-formed code and return valid data nonetheless.
Is there a way to "intercept" and validate ajax calls made from my website?
Right now my FeatureContext class looks like:
public function performAnAction()
{
$this->enterInField('test', 'field');
$this->hitOKButton();
$this->assertResponseContains('success');
}
I would like to do something like:
public function performAnAction()
{
$this->enterInField('test', 'field');
$this->hitOKButton();
$ajax = $this->getAllAjaxCalls();
foreach ($ajax as $call) {
// perform some validation
}
$this->assertResponseContains('success');
}
Here are two great resources for doing checks on ajax calls
The behat mink way
http://docs.behat.org/cookbook/behat_and_mink.html#defining-our-own-featurecontext
This is a neat solution using _before and _after overrides to getting deeper inside the base functionality and is very interesting but reading through it will help you get a better understanding of what the framework is really doing under the covers
http://blog.scur.pl/2012/06/ajax-callback-support-behat-mink/
I think you could simply use the built in wait function with the js callback to get what you want in your custom step def by putting your response into a jquery data[] object on any html element and verify your expected output that way.
$this->getSession()->wait(5000,
"$('.someCssClassSelectorToElementWithResponseStuff').length > 0"
);
If your element were a js object that would work
Or if it comes back as a jquery object you could use .size() instead of length
just make sure your injected js evaluates to true of false to get your pass/fail
Related
I have a Blazor Server app that is a multi-step "wizard" form. After each relevant step the state is adjusted, and new HTML is shown/hidden via conditional statements (simple example below).
if (IsStepSignature)
{
<div>Signature HTML here</div>
}
This all works just fine. My problem comes when I need to invoke some JS logic on the dynamically generated HTML from above (e.g. click handlers to hook up external JS libraries). When I handle the "Next" click, I can invoke the JS just fine...but it is not yet seeing the dynamic HTML from above. Is there a way to invoke some JS, and control it so that it doesn't execute until after the page is redrawn from the C# code execution?
5/18/2020 Update from Nik P
Can leverage some flags and use OnAfterRenderAsync to control this ordering. This does work, but it does require some extra hops to/from the server. Below is what I see when implementing this. This may just be the nature of Blazor Server, as one of the pros/cons is some known added chattiness. In total these requests were 2.5K, so extremely small.
CLIENT --> Browser Dispatch Event (NEXT CLICK)
Render <-- SERVER
CLIENT --> Render Complete
Invoke JS <-- SERVER
CLIENT --> Invoke Complete
The issue you are having has to do with the element not existing in the client side HTML at all until after the re-render takes place. So one way to do this is to set a boolean flag in your C# code that says there is code that needs to be run after the render, and populate support fields that you will need for your JS Interop. Whenever you need to run the JS interop, set your flag to true, set your support fields to the values you need for the JS interop call, and then do something that kicks a DOM diff calculation. (Even StateHasChanged should be enough, but adding your items conditionally as you mentioned will also do it) Then, override your OnAfterRenderAsync method as follows:
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if(firstRender)
{
// any first render code
}
if(yourFlag)
{
YourJSInteropMethod(supportField1, supportfield2);
yourflag = false;
}
}
The simplicity in this approach is that the DOM update will always happen ahead of the OnAfterRenderAsync call, so your HTML will be populated with what you are targeting with JS.
I am trying to perform a client side form validation using an episerver xform
the compiled html looks like this: http://codepen.io/anon/pen/ojGGJw
Any guidance on how to achieve that?
I am thinking about using .validate library but I will have an issue if we add a new control to the form through epi.
Also i tried to use an AJAX call with something like this:
$.ajax({
url: "/industry/XFormPost?XFormId=0643b992-56c6-40a5-91eb-c557443630e0&failedAction=Failed&successAction=Success&contentId=36",
data: $(this).serialize(),
type: "POST",
success: function () {
alert('Hello this is a valid form');
}
});
it fires the event but does not save my form into the DB. even though all the fields i passed are valid
Regrettably XForms in its current state is notoriously cumbersome to work with when it comes to customization. For custom rendering and/or validation we often implement our own rendering completely.
There are numerous articles about how to achieve it, but it can be time-consuming. :/
Client-side validation only can of course be achieved by attaching event handlers to the default generated HTML, but that's usually not enough. Although this can combined with server-side events, it is difficult to properly customize how XForms work for the end-user without custom rendering.
Here's what i ended up doing , to include a full client side validation for my form using .validate() js library.
I am sure there is other ways to achieve that, so here's my version:
EpiServer has a class field that you can use for all of your controls (shamefully there is no hidden field by the way, but that's a different story).
So i added a css class named 'requiredField' and added extra classes for different kind of validations , such as 'emailField' for email validation and 'halfWidthField' for CSS layout purposes for my form.
In order to .Validate to work , i need to add necessary attributes. to achieve that , I created a small script to add those attributes based on the class names I already assigned.
my JS code looks something like this:
$(".contact-form").find(".requiredfield").each(function () {
$(this).prop('required', true);
});
$(".contact-form").find(".emailfield").each(function () {
$(this).attr('type', 'email');
});
lastly: for the ajax call to happen , I changed the view and made an Ajax call instead of a regular post call.
#using (Ajax.BeginForm("", "",
new AjaxOptions
{
HttpMethod = "POST",
Url = Model.ActionUri
}
))
{
Html.RenderXForm(Model.Form);
}
It works as expected and I can customize the validation as needed.
The final form looks something like this:
I'm using openui5. There is a constructor Function for UI control Button,unable to see the prototype properties of the Button but the same thing when executed in browser console, shows up!
sap.m.Button.prototype.Move = function(){
console.log('Move');
}
var oButton = new sap.m.Button({text:"Hello"});
oButton.Move(); // throws undefined function!
The same code when executed browser in console, it works!
jsbin --> http://jsbin.com/tepum/1/edit
After running the code I find that creating the first instance of sap.m.Button causes script to change the prototype of sap.m.Button. It's valid in JavaScript but not very smart if you ask me.
A first creation causes a synchronous request (no no as well) to fetch library-parameters.json.
If you run the code the second time it will have prototype.move because creating an instance of Button will not change the Button.prototype.
The capital M in Move would suggest a constructor function so I would advice changing it to lower case.
Since fetching the parameters is synchronous you can create the first instance and then set the prototype:
console.log("First Button creation changes Button.prototype");
var oButton = new sap.m.Button({text:"Hello"});
sap.m.Button.prototype.move = function(){
console.log('Move');
}
oButton.placeAt('content');
oButton.move(); // logs Move
My guess is that this is done to lazy load controls, if a Button is never created then the json config files are never loaded for these unused controls. It has a couple of drawbacks though.
You have to create an instance first before you can set the prototype.
The config files are synchronously loaded so when creating first instance of many controls with a slow connection would cause the app to be unresponsive.
A better way would be for a factory function to return a promise so you create the control the same way every time and the config files can be fetched asynchronously.
[update]
Looking at the config it seems to be config for the whole gui library so I can't see any reason why this is loaded only after creating a first instance. A library that changes it's object definitions when creating instances is not very easy to extend because it's unpredictable. If it only changes prototype on first creation then it should be fine but it looks like the makers of the library didn't want people to extend it or they would not make the object definition unpredictable. If there is an api documentation available then maybe try to check that.
[update]
It seems the "correct" way to extend controls is to use extend.
#HMR is right the correct way to extend a control is by using the extend function provided by UI5 managed objects, see http://jsbin.com/linob/1/edit
in the example below when debugging as mentoned by others you will notice that the control is lazy loaded when required, any changes you make prior are lost when loaded
jQuery.sap.declare("my.Button");
jQuery.sap.require("sap.m.Button");
sap.m.Button.extend("my.Button", {
renderer: {}
});
my.Button.prototype.Move = function() {
console.log('Move');
};
var oButton = new my.Button({
text: "Hello"
});
oButton.placeAt('content');
oButton.Move();
It's not hiding the prototype per se. If a constructor function exits normally then you get that function's prototype. But, if a constructor function actually returns some other object then you get that other object's prototype, so it's not valid to assume that just because you added to the Button prototype that when you call new Button() that you will see your method on whatever you get back. I'm sure if you de-obfuscate that code you'll find that the constructor you are calling has a "return new SomeOtherInstanceOfButton()" or similar at the end of it.
Edit: Ok it's a bit difficult to see what's really going on in that sap code but, it looks like they have code that overwrites the prototypes of controls to add features to them, such as: sap.ui.core.EnabledPropagator, and those things aren't run until you actually instantiate a button. So if you change your code to instantiate the button on the page, then add to it's prototype, then construct and call the method, it works fine. Like so:
http://jsbin.com/benajuko/2/edit
So I guess my answer is, when you run it from console it's finished mucking around with that prototype, whereas in your test you were adding to the prototype, then constructing the button for the first time (which changes the prototype again) then trying to call your old one, which is no longer there.
The situation is that I have a page that uses some AJAX calls to retrieve content from the server, then puts those results into a chunk of html generated by another script. The problem is, I can't select with watin any of the elements of this new piece of html. It can be viewed in the browser, and comes up when I hit F12 and scan through the code, but still WatiN can't see it.
Is this because WatiN only scans through the html source of the page, and not the current version of the HTML? I think a similar situation would be:
html -
<script type="text/javascript">
$('#foo').html("gak");
</script>
...
<div id="foo">bar</div>
then when I try and assert -
Assert.IsTrue(browser.Div("foo")).ContainsText("gak"));
it will return false.
Any ideas on this? or is my best option to just write a bunch of jQuery, and browser.Eval() it?
I test AJAX pages quite a bit. The key is to wait until the asnyc postback has completed. If you have
Assert.IsFalse(browser.Div("foo")).ContainsText("gak");
browser_action_that_changes_bar_to_gak
>> Here you need to wait <<
Assert.IsTrue(browser.Div("foo")).ContainsText("gak");
In the "wait" section you can do a System.Threading.Thread.Sleep(numberOfMilliseconds) <- this is not the best way, but it is really simple. Once you determine that waiting is what you need to do, a better way to wait is to poll the status rather than way numberOfMilliseconds each time. I believe different AJAX libraries do things differently, but what works for me is really similar to this: http://pushpontech.blogspot.com/2008/04/ajax-issues-with-watin.html
I put the JavaScript into an Eval() in a helper function in the my base Page class rather than having to inject it into every page like the article did.
.
my Base Page class contains:
public bool IsInAsyncPostBack()
{
const string isAsyncPostBackScript = "Sys.WebForms.PageRequestManager.getInstance().get_isInAsyncPostBack()";
return bool.Parse(this.Document.Eval(isAsyncPostBackScript));
}
And then my WaitForAsyncPostback is basically the same as in the linked post, but I added a max wait time. Before going to Page classes (awesome; do it!) I made these static functions somewhere else and it worked too.
This is almost surely a timing issue. The jQuery has not updated when you test. Rather than introducing any artificial pause or wait it's best to wait for something to show that your AJAX has worked as expected.
In this case a WaitUntil should do the job nicely:
Assert.IsTrue(browser.Div("foo")).WaitUntil(c => c.Text.Contains("gak")));
This works for most updates and the like. Another common waiting pattern is on data loading say, where you'd have a spinning wheel displayed. Then you could wait until this wheel is gone with a something like:
WaitUntil(c => c.Style.Display == "none");
Please brief me about the Ajax testing with selenium RC.
As in Ajax element not reflect on the view-source, but using firebug we can see the changes in HTML source code.
There are two methods associated with it Ajax testing..
1-The method "waitForCondition (java.lang.String script, java.lang.String timeout), to provide script we have to create a java script by own or it should be the same Ajax script/java script present on the web page.
Please correct me if i am wrong on below point..
2-The method "waitForElemantPresent(Locator)", We check the Element in the firebug and check the same in this method is self waitForElemantPresent(Locator).
Let me know if anything else I am missing testing Ajax application.
I got help on this from one article and with help of #Hannibal
http://agilesoftwaretesting.com/?p=111
JQuery: “jQuery.active”
Prototype: “Ajax.activeRequestCount”
Dojo: “dojo.io.XMLHTTPTransport.inFlight.length”
So if there is Ajax call we can use second option.
selenium.waitForCondition(
"selenium.browserbot.getCurrentWindow().jQuery.active == 0",
timeout);
To answer your first point, yes waitForCondition(javascript,timeout) will run the javascript till it returns a true value OR when timeout happens. You should take a look at the api documentation for this as you need to use browserbot to run the script in your application window. Link to API documentation is here
In Selenium 1, one way by which you can handle the Ajax conditions are by creating custom functions which will wait till the condition is met or till a timeout happens. While a normal Selenium.isElementPresent will fail immediately if the element is not present, your custom function will wait for some more time (the time for Ajax to load) before it fails. As an example you can refer the following custom method for isElementPresent. This is written in JAVA, you should be able to use the same logic in the programming language that you use.
public boolean AjaxIsElementPresent(String elementToLookFor, int timeOutValueInSeconds){
int timer=1;
while(timer<=timeOutValue){
if(selenium.isElementPresent(locator))
return true;
else
Thread.sleep(1000);
}
return false;
}
This method will return a boolean false if the element is not present even after the specified timeoutValue. If it finds the element within the timeoutvalue then it returns a true.
I have seen some in built functions for AjaxCondition handling in Selenium 2. But I have not used it. You can refer to Selenium 2 code base