Capybara with js: true not finding checkbox - javascript

I need to test if Javascript alert is being displayed in validation proccess after checking one checkbox.
I'm using RSpec, Capybara with Webkit and Database Cleaner.
My test without JS: true :
it "alerts to choose two players" do
set_and_visit
first("input[type='checkbox']").set(true)
expect(page).to have_content('Remember that you must choose two players to start a game.')
end
Finds a checkbox but cannot find the alert message.
When I add JS - "alerts to choose two players", js: true do it returns an error
NoMethodError:
undefined method `set' for nil:NilClass
I've tried using check 'John Doe' and first("input[type='checkbox']").check but it still didn't work.

There are two possible issues that seem likely:
Asynchronous Issues
The default driver (rack-test) is synchronous, so the page will always be fully loaded by the time you run first. When using asynchronous drivers like capybara-webkit, you need to watch out for race conditions. The page may still be loading (or updating via Ajax) when first is called, so the input element may not have appeared yet.
Solution: use find("input[type='checkbox']").set(true) instead of first. This will cause Capybara to wait up to two seconds for the input to appear before giving up and raising an error, instead of immediately returning nil.
I wrote more about writing asynchronous tests in a blog post.
Hidden Elements
Because the default driver (rack-test) doesn't parse CSS or run JavaScript, it doesn't understand which elements are hidden or visible. This means that you can interact with hidden elements that real users can't click on or fill in.
On the other hand, drivers like capybara-webkit know which elements are actually visible to the user. By default, Capybara ignores all elements that it knows are hidden.
Solution: use find("input[type='checkbox']", visible: false).set(true)
Note that this won't work if the element is actually visible, and that it's probably better to figure out why the element is hidden. After all, if capybara-webkit can't find it, your users probably can't, either.
You can read more about how Capybara handles visibility on the elabs blog.

So, silly as it sounds the mistake I made was in a url - in my set_and_visit method I was visiting 'matches/new' instead of '/matches/new'. When tested, puts page.body was returning an empty string because it couldn't find that url.
Still I'm not sure why test including check 'John Doe' wasn't returning any error like the one with js: true.

Related

Cypress conditional testing code suggestions

I cant figure out how to automate if & else using Cypress. I am new to Cypress & JS and trying to work this out.
I have questions page, sometimes its 3 and sometimes its 4. Not consistent. I simply want to handle case where cypress click on No, if its finds that 4 "NO" button. Also CTA button in the end changes, if the 4th question is present the text of CTA changes.
The problem with "Should" in Cypress is that it asserts and then fails. I want something like IsDisplayed in selenium.
I know conditional testing is not recommended, but I dont have an option now. Please help me with this.
this is my code:
//cy.get(`button[name="cosmeticDamage"][value="${cosmetic}"]`).then($a => {
cy.get('div span.MultiScreenForm__content').then($a => {
if ($a.attr('name', `[name="cosmeticDamage"][value="${cosmetic}"]`).length > 0) {
cy.log('Cosmetic Damage Button does exist: ==>');
cy.get(`button[name="cosmeticDamage"][value="${cosmetic}"]`).click();
} else {
cy.log('Cosmetic Damage Button does not exist, do Nothing');
}
});
}
I know conditional testing is not recommended, but I dont have an option now.
Yes, you do have an option. I apologize up front, this is not a quick, easy answer. Conditional testing in cypress can be tricky, however, when you do understand it, your tests will be better for it. You do have a better option and I'm going to try my best to explain it, so here goes.
For situations like this, you have to adjust the way that you approach the problem. At the moment, you're basing your condition on the UI element which, as Cypress documentation states, will lead to exploding kittens. No one wants exploding kittens. What you want to do instead is change the source of truth your condition is based on from using the UI element to basing your condition on something more stable, like server response.
For me, personally, this was incredibly difficult to wrap my brain around how to actually do this in practice, so I'll try my best to explain.
So, currently, you're doing something like:
Request is made (#of questions) and page loads and view is set based on response.
If UI element is on page (4th question), then test button
What you want instead is:
Request is made (how many questions?)
Capture response from request with cy.route("someAlias") and cy.wait('#someAlias')
Your response from the server is the information that your UI elements are building themselves from. Base your condition on this instead. (see Routes & Aliases)
So your condition would be something like:
if questions returned in response > 3 then test button.
The theory here is, your server is a solid source of truth which has all the info you need, right up front. The DOM (UI elements) is not because it does not have all of the info you need up front and there is no guarantee at the time of your condition that it will have resolved the stuff you need in order to proceed.
If your server responds with more than 3 questions, there should be a button there. Switching the logic here makes your test more stable and you're actually testing the thing that you want to test. When you've got more than 3 questions, you should have a button. Not if there are more than three UI elements, then you should have a button. There is no guarantee that your button will have resolved by the time your condition is met for the UI elements. Your server response should be your source of truth, not the UI elements.
I don't know the logic that you have that makes the request so my answer isn't exact, however, let's assume that on page load, there is a request to /questions that responds with the questions that you're talking about (3 or 4). Your code would then look something like this:
// setup the route to wait for
cy.server();
cy.route("/questions").as("questions");
// do whatever you do that sends that request
cy.visit("/pageOfQuestions");
// wait for request and grab response using route alias
cy.wait("#questions").then(function(xhr) {
// find your path (I'm guessing here) to the info you need and test condition
// the condition and the path to the info will vary based on what your response
// actually contains
if(xhr.response.questions.length > 3) {
// test your button-y stuff here
cy.get(`button[name="cosmeticDamage"][value="${cosmetic}"]`).click();
} else {
cy.log("nothing to test");
}
The difference is when you base your condition (source of truth) on the UI elements, the DOM does not always resolve itself in the manner you expect. When you reach the condition (in this case the question UI elements), other things still haven't resolved themselves (button UI elements). You have to wait for two elements to align in the DOM -> Your source of truth (condition) and the UI element you want to test (button). Often, one hasn't loaded when the other has and you cannot rely on them loading consistently which is why cypress recommends never basing conditions on UI elements, unless you like exploding kittens.
Explained differently, let's say the DOM has 10 resources to load. You're saying to cypress:
if resource #7 looks like this then play with resource #1
Cypress goes and waits for resource 7. When it's ready it checks that the condition is met and then tries to play with resource 1... which the DOM possibly hasn't resolved yet. And actually, sometimes it could be loaded but you can never guarantee that it will be which will lead to flaky tests.
When you base your source of truth on the server response, you're only waiting for and testing the one UI element and the cypress built-in time outs can successfully wait for that one element to load without depending on another. On page load, did the server give us more than 4 things? Then play with UI element
If there's anything I can clarify, just ask.
Reference:
Conditional testing
Routes and Aliases
What I understand from your description and the snipped you provided is that [name="cosmeticDamage"] is always there for several elements, so I'm not really sure how does .length valorizes [name="cosmeticDamage"][value="${cosmetic}"], which could mean your condition is always true.
I would recommend to go for find, as it filters matching descendent DOM elements:
cy.get('div span.MultiScreenForm__content').then($form => {
if ($form.find(`[name="cosmeticDamage"][value="${cosmetic}"]`).is(':visible')) {
cy.get(`button[name="cosmeticDamage"][value="${cosmetic}"]`).click();
} else {
cy.log('Cosmetic Damage Button does not exist, do Nothing');
}
});

Diffing the DOM - how to write a unit test that checks that a script doesn't make any user-visible changes to elements on the page?

A JavaScript plugin that I've been writing recently has various failsafes built in that tell either the whole plugin or parts of it to hide itself and die under circumstances where it can't function. For example, let's say that one piece of functionality we offer is automatically generating a popover that shows competitors' prices for an item when the user hovers over it in an online store. Then we'd also have some checks that say that if we don't know any of the competitor's prices, or we can't identify the item, then don't show a popover.
I want to test that functionality-disabling using tests that follow roughly this structure:
Load our plugin onto a page where certain functionality ought to be disabled
Spoof some user action that would otherwise trigger that functionality
Assert that no visible changes have been made to the DOM. (i.e. no styling changes to visible elements, no addition or removal of elements unless they have display:none on)
Step #3 is the interesting one, of course. Is there an easy way to write that 'DOM unchanged' test in JavaScript? (Or alternatively in Selenium for Python, which is what I'm using to write my tests - but writing the check in JavaScript is probably more broadly useful since it can then be used in any JavaScript-testing environment.)
P.S. A couple of notes to head off the "You're doing it wrong!" crowd:
Yes, I know that I could just replace step #3 in the test above with a check that the specific changes that the plugin would otherwise make have not been made, and I may even decide to do this. But where those specific changes are poorly-specced and liable to change, this catch-all approach could be useful.
Yes, I also realise that just checking there are no immediate visual changes to the DOM when a event that's meant to be effect-free is triggered isn't strictly sufficient to prove that the nothing has broken. It's what'd be best for my current purposes, though. Plus it's interesting and fun even if it turns out not to be useful.
Use Mutation observers to detect that no mutations have occurred. You might want to checkout Mutation Summary, a very nice high-level wrapper for mutation observers. Checking that no mutations have occurred could be easy as checking that the returned array has length 0. See https://code.google.com/p/mutation-summary/.

Why focus an input on page load instead of inline?

Almost all web pages that I see designed to set the focus to an input box add the code into a body onload event. This causes the code to execute once the entire html document has loaded. In theory, this seems like good practice.
However, in my experience, what this usually causes is double work on the user, as they have already entered data into two or three fields and are typing into another when their cursor is jumped back without their knowledge. I've seen a staggering number of users type the last 2/3 of their password into the beginning of a username field. As such, I've always placed the JS focus code immediately after the input to insure there is no delay.
My question is: Is there any technical reason not to place this focus code inline? Is there an advantage to calling it at the end of the page, or within an onload event? I'm curious why it has become common practice considering the obvious practical drawbacks.
A couple thoughts:
I would use a framework like jQuery and have this type of code run on $(document).ready(.... window.onload doesn't run until everything on the page is fully loaded, which explains the delay you have experienced. $(document).ready(... runs when jQuery determines the DOM has been loaded. You could probably write the same sort of logic without jQuery, but it varies by browser.
I prefer to keep my Javascript separate from my HTML because it allows for a cleaner separation of concerns. Then your behavior is then kept separate from your document structure which is separate from your presentation in your CSS. This also allows you to more easily re-use logic and maintain that code — possibly across projects.
Google and Yahoo both suggest placing scripts at the bottom of the html page for performance reasons.
The Yahoo article: http://developer.yahoo.com/performance/rules.html#js_bottom
You should definitely place the script in the appropriate place if it means the correct user experience -- in fact I would load that part of the script (Used for tabbing inputs) before the inputs to ensure it always works no matter how slow the connection.
The "document.ready" function allows you to ensure the elements you want to reference are in the dom and fires right when your whole document dom is loaded (This does not mean images are fully loaded).
If you want you could have the inputs start out as disabled and then reenable them on document ready. This would handle the rare case the script is not ready yet when the inputs are displayed.
Well if you call it before whole page has loaded you really don't know if the element already has been loaded when you make your call. And if you make your call in pre-hand you should check if the element really exists even if you know it always should.
Then to make the call inline, which might seem ideal. But on the other hand it's really bad if a page takes that long to load that you can make several inputs during the loading phase.
Also you could check if input has been made etc.
Also it is possible to check if any input on page contains focus if($("input::focus, textarea::focus").length)... and otherwise set focus on desired input.
Use the autofocus HTML attribute to specify which element should initially receive focus. This decouples JavaScript and gracefully degrades in older browsers.

Handle Javascript focus when using sendKeys in WebDriver

It's quite common for a web application to use Javascript to focus to a text field onload. For example, in a login page, we may use Javascript to focus to the username field.
When using WebDriver with FirefoxDriver, I use sendKeys to populate these fields. However, most of the time, the Javascript focus function will get executed while WebDriver is sending actual characters, so my input for another field (e.g. the password field) is partial and the remaining goes to the focused field.
How should I handle this case? To my knowledge, the blocking API returns when the page content is loaded, not when Javascript execution is finished, so this situation is understandable. However, I don't think using "Wait" is a decent solution, since it means too much intrusion to the test development.
I came up with three possible solutions, none of them is actually guaranteed to work:
Does javascript's document.readyState return "complete"? If not, you could wait for that after every click().
You could wait until driver.switchTo().activeElement(); (which returns the active element) returns your text field, and begin your typing afterwards. I guess you should call driver.switchTo().defaultContent(); afterwards, too, but am not sure about it. A similar approach would be getting document.activeElement.
Um, that's somewhat hacky, too. Write your own sendKeys method which finds the element, remembers it, and then sends one key from the wanted string at a time. Because WebDriver makes sure to have focus on the beginning of the typing, it should work.

Why do textfields become disabled in IE6 for no reason?

I have made few changes on this huge JSF page, which is full of Javascript as well. I dont know which change make the problem happen.
The problem is: after some Javascript is executed, all the text fields in the page become readonly. The problem is not occurring in IE7 nor in Firefox. I have debugged all the javascript, there is no errors thrown! And there is nothing telling the fields to become readonly, since its working correctly in IE7.
Not sure what the problem is, could be CSS related? or Javascript? And why is it happening on IE6 only?
Note: "Don't support IE6 is not an option"
While IE may be buggy make trouble in some situations, I'm quite sure this is not an IE bug.
How do you tell the fields are read only? Do you get any optical confirmation or is it just that you can't click them any more? In that case, I'll bet you a beer that is's some invisible DIV or other layout element that, due to some CSS setting, squeezes itself above the input fields.
Try the IE developer toolbar to find out whether it's a layout problem.
If they are really disabled as in <input disabled> you need to show some JavaScript or (better) a link.
Still not sure what happened with that build, but what i was sure about is all the Ajax modifications i did was responsible for the problem.
The scenario was like:
Fill textfield1 (hit getValues1 , then hit a validate Ajax)
Fill textfield2 (hit getValues2 , then hit validate on both values together)
Fill textfield3 (hit getValues3 , then hit validate on all three values)
And a forth time again the same scenario. The page was built by a new to JSF guy, and it was very huge. I took long time refactoring it. But now its much better, each text field still have a getValues Ajax, but instead of validating them after getting all the values, i filter the allowed values on the server by sending the chosen criteria
The scenario now:
Fill textfield1 (hit getValues1 Ajax)
Fill textfield2 (hit getValues2 Ajax with value of 1, and get only allowed values)
... etc
The problem seems to be an Ajax racing conditions and at some moment IE6 was hanging or stuck in a deadlock, im not sure.
Lesson learned, "Refactoring once may take a week, but without every single issue will take longer"
um... don't support IE6??? ;)
Suggest disabling your CSS and seeing if the problem goes away. I'm not aware of any CSS tags that can disable a field, though.
Other than that, debugging is your only option. Remove all your .js and add it back in line-by-line until something breaks.
It will probably be hard for us to help you without seeing some code.
See if the HTML for the page has the 'disabled' attribute set on those INPUT elements. If so, then javascript is being used to enable the elements after the page has loaded. This is a not-uncommon technique to keep users from prematurely trying to interact with a page before all scripts have loaded.
If that's what is happening, then what you've probably done is break the way the script recognizes which elements need to be enabled. Since this is only happening on IE6, it sounds like the script might be doing some esoteric DOM navigation, which broke as a result of changes to the markup or CSS.
Unfortunately this is something you'll have to debug by reverting back to previous versions until you identify the change you made that broke the page.
Based on the other answers here, and some of your comments to them, it seems there is a JavaScript function in your page that sets elements to be enabled or disabled.
In order to help, we would have to see your code. Here is something you can do yourself, though, that would solve your problem:
Find that function (or ANY function) that sets elements in your page to disabled or enabled.
Depending on your development environment, there are different ways to do this, but somehow add a breakpoint there at the function.
Load the page.
Whenever that function is called, code execution will stop at that function. Whenever it stops, make sure that it was supposed to be called (and watch the call stack).
Eventually, you'll hit that breakpoint at a point where you weren't supposed to. Look at the call stack to see what caused it (which function resulted in a call to this function).

Categories

Resources