Why does the first assert work, but not the second? - watir-webdriver

I'm using Watir WebDriver with Firefox.
Here are two asserts for the same element. The first works, but not the second:
assert_match(/Please add user to GMT/, #browser.small(:class, "error").text)
assert_match(/Please add user to GMT/, #browser.div(:class, "eight mobile-three columns").small(:class, "error").text)
I need the second assert to work, because there are 8 error messages on the page, which are presented if the user does not populate 8 mandatory fields. All 8 errors have the identical HTML. So, I need to be able to step down from the parent.
Here is the HTML:
<div class="eight mobile-three columns">
<a id="btnAddUserToGMT" class="success radius button expand error" onclick="AddUserToGMT();" data-reveal-id="addToGMT" href="#"> … </a>
<small class="error" style="margin-top:0px;">
Please add user to GMT
</small>
</div>
Here is the error message:
Watir::Exception::UnknownObjectException: unable to locate element, using {:class=>"error", :tag_name=>"small"}
C:/Ruby193/lib/ruby/gems/1.9.1/gems/watir-webdriver-0.6.4/lib/watir-webdriver/elements/element.rb:490:in `assert_exists'
C:/Ruby193/lib/ruby/gems/1.9.1/gems/watir-webdriver-0.6.4/lib/watir-webdriver/elements/element.rb:85:in `text'
C:/Documents and Settings/Asserts_01.rb:22:in `testBasic'
The complaint for the second assert is:
unable to locate element, using {:class=>"error", :tag_name=>"small"}
And yet that same using was OK for the first assert.

Problem solved.
After discussion with the developer, it appears that by some unusual manipulation of automatically generated HTML, the text of the error message appears at its correct location on the page. But the assertion must be based on a different tag, which is specified at some completely different position on the page.
For example, I was trying to assert on this code at the correct position:
<small class="error" style="margin-top:0px;">
Gender is required
</small
Even unique xPath generated by FirePath failed to find this.
What I should have asserted on was a HIDDEN tag on a completely different part of the page.
<input id="errorMsgGenderID" name="errorMsgGenderID" type="hidden" value="Gender is required" />
There were several such tags for each mandatory field that was not populated, all bunched together on the same line. They were all "input" tags, which puzzled me.

Related

Locators combination

I am working on protractor to test the AngularJs application. Here I came across one scenario where I want to click on image for different users. But the id for image is same for all (say 10) users. So I found one more element that is one unique number allocated to each user. The code for 2 different users are:
USER1:
img id="searchPatientImgAdmittedM" class="img-circle picwidth" ng-click="getPatientVitalLabPharmacy(patient.patientId._id)" onclick="ShowHide(this)" src="icons/male.png" alt="" role="button" tabindex="0"
span class="clearfloat ng-binding">12339/span
USER2:
img id="searchPatientImgAdmittedM" class="img-circle picwidth" ng-click="getPatientVitalLabPharmacy(patient.patientId._id)" onclick="ShowHide(this)" src="icons/male.png" alt="" role="button" tabindex="0"
span class="clearfloat ng-binding">8841/span
EDIT:
The full HTML code
<div class="col-md-10 col-sm-9 col-xs-9 skin-font-color paddingTop7">
<span class="skin-font-color">
<span class="name clearfloat ng-binding">KRISHA</span>
<span class="clearfloat ng-binding">12348</span>
<img id="searchPatientImgAdmittedF" class="img-circle picwidth" ng-click="getPatientVitalLabPharmacy(patient.patientId._id)" onclick="ShowHide(this)" src="icons/femaleImages.jpg" alt="" role="button" tabindex="0">
</div>
I tried to do :
element(by.id('searchPatientImgAdmittedF')).all(by.tagName('‌​12348')).click();
// or
element(by.id('searchPatientImgAdmittedF')).element(by.tagNa‌​me('12348')).click()‌​;
How can I make combination of locators to click on this users. Only image part is clickable.
Thanks four your additions.
Now you're trying to click on a sister-element. There are several approaches to do so.
The one I'm usually using is:
element(by.cssContainingText('span.clearfloat','12348')).element(by.xpath('..')).$('#searchPatientImgAdmittedF').click();
//equal to
element(by.cssContainingText('span.clearfloat','12348')).element(by.xpath('..')).element(by.id('searchPatientImgAdmittedF')).click();
This evaluates first the identifiable tag with the unique number, then climbs up to its parent element, then from there gets the img-element with the ID.
The $() selector
The cssContainingText() selector
Another option would be to use isElementPresent(), which evaluates the existence of a child-element. However, the code is (from my point of view) more complex and I don't see, how cssContainingText() could be used there, so I don't try to do it here.
Thanks for your quick help in solving my issue. I want to add here that I found the answer to my problem and now I am able to click on the particular user I want from the list of many users. The code I am using is :
element(by.cssContainingText('span.clearfloat','12339'))
.element(by.xpath('/html/body/div[3]/div[1]/div[17]/div/div/table[4]/tbody/tr[3]/td[1]/div[1]/img'))
.click();
This is finding the child element first and then the parent element.The id was all same for all the users so it was not taking that and so I used only xpath along with unique number.
Thanks again for the help.

Angular2: ControlGroup inside ControlArray

I’m having a hard time figuring out how to iterate over a ControlArray that contains Controlgroups in a template. In TypeScript, so far I have created the ControlArray, and by iterating over data received from a remote service, I added a few ControlGroups to the array. Everything fine up to this point, and I can see the expected data structure in the console.
In the template, I have this:
<div *ngFor="#c of categories.controls">
<div ngControlGroup="c">
</div>
</div>
... where categories is the ControlArray (which holds an array of ControlGroups in its controls property). When I leave out the inner <div>, I don’t get an error, which suggests that Angular agrees with me that categories.controls is indeed an array. But as soon as I re-add the inner <div> (where I expect the local variable c to be one of the objects in the array), I get an exception with message “Cannot find control 'c' in [c in ]”. Also, I tried various other syntactical approaches, but none of them worked. In addition to a “Cannot find control …” method I also got “Cannot find a differ supporting object …”, but that didn’t take me any further.
Any hints regarding what I’m doing wrong?
ngControlGroup is defining a new control group. If I understand your question correctly, you want to actually be editing items within a control group inside a control array. Check out this plnkr: https://plnkr.co/edit/3gM2TuMGBW13HNATUcCO
<div *ngFor="#c of categories.controls; #i = index">
Control group {{i}}:
<div>
<input type="text" class="form-control m-b" [ngFormControl]="c.controls.title"/>
<input type="text" class="form-control m-b" [ngFormControl]="c.controls.id"/>
</div>
</div>
One error is
ngControlGroup="c"
which doesn't do any binding. It passes the literal c to ngControlGroup. It should be:
[ngControlGroup]="c"
The errors that are still produced after this fix seem because there are no controls.
Error is resolved by changing
ngControlGroup="c"
into
attr.ngControlGroup="c"
Because by assigning c to ngControlGroup you are just assigning the value instead of any binding. but strange why [ngControlGroup] still produces some error.apart from these here is working example
https://plnkr.co/edit/Yw21a1aSivNg4G6gYkhF?p=preview

The right way to fill and test a web form in ScalaTest and FluentLenium

I'm trying to fill, submit, and test a web form in Play Framework, using ScalaTest and FluentLenium. It seems like it should be very straightforward, but I'm having all kinds of problems.
First, part of the web form in question:
<form class="signin" id="loginform" method="POST" action="/login">
<div class="form-group">
<label for="name">Email Address:</label>
<input type="email" class="form-control" placeholder="Enter Email Address" id="email" name="email" required />
...
This works fine from a real web browser. Now the problem comes when I try to fill in and submit the form:
#RunWith(classOf[JUnitRunner])
#SharedDriver(deleteCookies = false)
#SharedDriver(`type` = SharedDriver.SharedType.PER_CLASS)
class TestWebsiteAuthentication extends Specification {
"Application" should {
"login as an administrative user on the web site" in new WithBrowser with GPAuthenticationTestUtility {
browser.goTo(loginURL)
browser.fill("#email").`with`(prerequisiteAccounts.head.userIdentity) must equalTo(OK)
...
At that last line, I get an exception:
[info] x login as an administrative user on the web site
[error] 'org.fluentlenium.core.action.FillConstructor#1c25c183' is not equal to '200' (TestWebsiteAuthentication.scala:93)
[error] Expected: 200
[error] Actual: org.fluentlenium.core.action.FillConstructor#1c25c183
Any ideas what I'm doing wrong here?
I've tried taking out the "must equalTo(OK)" but this just causes the form to fail on submit -- unfortunately, I haven't been able to find ANY documentation on how to do this, so I'm basically piecing it together bit by bit. Pointers to relevant documentation would be appreciated -- there doesn't seem to be anything complete at Tyrpesafe... just "teasers" that get you started, but no depth. :-(
When you write browser.fill("#email").``with``("x#y.com"), all you're really doing is telling Fluentlenium to edit the template to add a value attribute inside the input tag.
On the other hand, OK is an HTTP status code, so comparing them will naturally yield false.
When you say you tried to submit the form and it failed, i am assuming you did something such as:
browser.fill("#email").`with`("x#y.com")
browser.fill("#password").`with`("myPass")
browser.click("#button") // this should submit the form and load the page after login
and then tried to make an assertion such as:
browser.title() must equalTo("next page") // fails because "next page" != "login page"
one suggestion is to try something like this, Before browser.click:
browser.pageSource() must contain("xyz") // this will fail
When the above assertion fails, it will print the content of browser.pageSource() to your terminal, and you'll be able to see the modifications the Fill function did to the HTML.
In my case, I observed that my pageSource() now contained the following:
<input type="text" id="email" name="email" value="x#y.com"/>
<input type="password" id="password" name="password"/>
Notice how the first input has a value="x#y.com", but the second input is still empty. It turns out the second one is empty because it is an input of type password, however I eventually made the form login work.
Here is a list of things you can look into:
have a database enabled in that Spec
have it populated with Users (in case your form validation connects to a DB, that is)
from what I have experienced, using browser.goTo more than once in a test will not work well with form submission (anyone can confirm?)
Hope this helps

AngularJS retrieve from object based on entry in ng-repeat input

This application is for running a writing contest.
Coodinators are assigning entries to judges for them to judge. I have three sets of data I retrieve from the server, a judge list, an entries list and an assignment list that ties the two together. There can be a variable number of input fields...if a judge has agreed to judge 4 entries, there will be 4 inputs...if 7, then 7.
I have all of that working OK, but only insofar as the entry number can be input and the data updated.
Now I would like confirm that the entryID IS a valid ID by checking the list and also to show a field or two on the screen so the coordinator knows that they typed in the right entry.
The relevant section of the HTML
<div ng-app>
<div id="assignment" ng-controller="AssignData" ng-init="JudgeID=107;CategorySelect='MS';PublishSelect='P'">
<div ng-show="loaded">
<form class="entryform ng-cloak" name="assignform" ng-submit="sendForm()">
<p>Entry numbers assigned to this judge</p>
<p ng-repeat="assign in (formassigns =(assigns | filter:AssignedJudge))">
<input type="text" ng-model="assign.entryid" required/>
{{entries.authorname}} {{entries.entrytitle}}
</p>
<button type="submit">Save Assignments</button>
<p>This will keep the assignments attached to this judge.
You will be able to send all of your assignments to all
of your judges when you are finished.</p>
</form>
</div>
</div>
</div>
The part that I haven't been able to figure out is how to make entries.authorname and entries.entrytitle show up when the user types in an entryid that is in entries.entryid.
assigns and entries are both arrays of records using JSON
assigns is JSON made up of assigns.id, assigns.judgeid, assigns.entryid.
entries is JSON made up of entries.entryid, entries.entrytitle, entries.authorname
When assigns arrives, entryid is empty. The form is used to fill in the entryid and when it is filled in, I'd like to be able to show next to it the title and authorname for that entry.
NOTE: I've added some important information at the end of this answer. So please read to the end before you decide what you're going to do.
You're going to have to do something that does the look up.
Also a few other changes I'd add, mostly so you can actually validate the items in your repeat.
(There's a summary of what I did after the psuedo code below).
<div ng-app>
<div id="assignment" ng-controller="AssignData"
ng-init="JudgeID=107;CategorySelect='MS';PublishSelect='P'">
<div ng-show="loaded">
<form class="entryform ng-cloak" name="assignform" ng-submit="sendForm()">
<p>Entry numbers assigned to this judge</p>
<p ng-repeat="assign in (formassigns =(assigns | filter:AssignedJudge))"
ng-form="assignForm">
<input type="text" ng-model="assign.entryid"
ng-change="checkEntryId(assign, assignForm)"
name="entryid" required/>
<span ng-show="assignForm.entryid.$error.required">required</span>
<span ng-show="assignForm.$error.validEntry">
{{assignForm.$error.validEntry[0]}}</span>
{{assign.entry.authorname}} {{assign.entry.entrytitle}}
</p>
<button type="submit">Save Assignments</button>
<p>This will keep the assignments attached to this judge.
You will be able to send all of your assignments to all
of your judges when you are finished.</p>
</form>
</div>
</div>
</div>
Then in your controller, you'd add a function like so (be sure to inject $http or a service you wrote to pull the values from the server):
$scope.checkEntryId = function(assign, form) {
$http.get('/CheckEntry?id=' + assign.entryid,
function(entry) {
if(entry) {
assign.entry = entry;
form.$setValidity('validEntry', true);
} else {
form.$setValidity('validEntry', false, 'No entry found with that id');
}
}, function() {
form.$setValidity('validEntry', true, 'An error occurred during the request');
console.log('an error occurred');
});
};
The basic idea above:
Use ng-form on your repeating elements to allow for validation of those dynamic parts.
Create a function that you can pass your item and your nested form to.
In that function, make your AJAX call to see if the entry is valid.
Check the validity based on the response, and call $setValidity on your nested form you passed to the function.
Use ng-show on a span (or something) in your nested form to show your validation messages.
Also, assign your checked entry to your repeated object for display purposes. (you could use a seperate array if you want, I suppose, but that would probably get unnecessarily complicated).
I hope that helps.
EDIT: Other thoughts
You might want to wrap your call in a $timeout or some sort of throttling function to prevent the entry id check from spamming yoru server. This is an implementation detail that's totally up to you.
If this is a check you do all over the place, you'll probably want to create a directive to do it. The idea would be very similar, but you'll do the check inside of a $parser on the ngModelController.
The method I showed above will still actually update the model's entryid, even if it's invalid. This is usually not a big deal. If it is, you'll want to go with what I suggested in "other thought #2", which is a custom validation directive.
If you need more information about validation via custom directives I did a blog entry on that a while back

Why do forms timeout in ColdFusion?

We're on CF 8 and we have a CF application with a 1 hour session timeout specified.
<cfapplication sessionmanagement="yes" sessiontimeout="#CreateTimeSpan(0, 0, 60, 0)#" name="myAppName" clientmanagement="yes">
Within the CF administrator, we have a default session timeout of 1 hour, and a max session timeout of 1 day.
We're having some odd (intermittent) form timeout issues on submission of a regular form (not a flash form)...
Let's say the user hits the form page at 10:30am. (I know this because I'm appending the time to the "Action" property of the form tag). At 11:10am, the user submits the form, but none of the form struct is available to the action page, so the user gets an error.
I know that it's coming from the correct page since the CGI.referrer is defined properly.
In my custom error handler, I iterate over any form, session, or client structs -- and there is NO form struct defined at this point. All of the other Session and Client variables ARE still available.
Excerpts from Form Page:
<cfform name="chairsComment" id="chairsComment" action="library/save_chairsComment.cfm?Start=0224_153027" method="post" >
<input name="chairsCommentNumber" id="chairsCommentNumber" type="hidden" value="13" />
<textarea name="comment_13" rows="50" wrap="virtual" cols="100" id="comment_13" ></textarea>
<input name="save_answer" id="save_answer" type="submit" value="Save Response" />
</cfform>
And for the Action page, it throws an error on line 1:
<cfset whichCommentNumber = form.chairsCommentNumber>
It works during testing, but when some of our users use it, it throws the error that "Element CHAIRSCOMMENTNUMBER is undefined in FORM." We've just started looking at the specific browser that this individual is using (Safari 4.0.3) and asked him to upgrade to 5.x, but with SUCH a vanilla form, it seems an unlikely culprit.
Any Ideas?
In the midst of a discussion on Ray Camden's blog about file uploading, someone mentions a problem with Safari 4.0.5 throwing an error because the form field did not contain a file ... it's not the same problem, necessarily, but it could be related. Unfortunately, the commenter never returned with more information.
There's a post on another blog here where a commenter mentions an issue with Safari and a cfform inside a cfdiv tag. If your cfform is similarly nested, you might need to reverse the order (nest the cfdiv inside the form) to make this work.