Cypress conditional testing code suggestions - javascript

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');
}
});

Related

Cypress and unstable DOM

I am using cypress for some e2e tests on a react application and I am having problems clicking on an element.
I have this simple script:
it('can bookmark/unbookmark the posting', () => {
cy.visit(pdfPosting.url)
.wait(1000)
.contains('Save')
.should('be.visible')
.click();
cy.visit('/jobs/jobbox')
.contains(pdfPosting.title)
.should('be.visible');
cy.visit(pdfPosting.url)
.wait(1000)
.contains('Saved')
.should('be.visible')
.click();
cy.visit('/jobs/jobbox')
.findByText(pdfPosting.title)
.should('not.exist');
}
});
My problem starts because the Save and Saved buttons are inside a react component that is refreshed several times, for unknown reasons, when page is loaded. Test fails because sometimes the component is refreshed between the contains and the click on the button.
Right now the only way to workaround this problem was adding the waits you see on the test, but I really hate this hack.
First thing I tried to do, was catching the error and retrying the contains.should.click block, but cypress does not allows this (https://docs.cypress.io/guides/core-concepts/conditional-testing.html#Error-Recovery) and ever worst the workarounds proposed on this article are not working to me (or I do not know how to do it)
I also tried to work with events (https://docs.cypress.io/api/events/catalog-of-events.html#Uncaught-Exceptions) but I could not found any that I could use to retry only a part of the code and continue with the execution of the test.
I tried to talk with developers, if they could fix the refresh issue, but the short story is that they won't. Final user does not realize about this refresh on the component and there is not budget to investigate the problem.
Also I was discussing with developers if we could add something, somewhere, to indicate that the loading of the component had finished, but, again, this seems to be not possible. They cant not grant that loading of the component has finished because they do not know where these refreshes come from.
Do you know how I can use cypress to handle this situation?
It's a bad practice use waits in cypress.
I'd suggest use the playground
https://docs.cypress.io/guides/core-concepts/test-runner.html#Selector-Playground
to find your items. Try to follow the Uniqueness principle and use the cy.get('[data-test-selector=you-selector']); instead of wait. It'll wait for you.
For example, visit and cy.get the title page.
Also, if you have animations, it might cause DOM issues with back and forth. If you remove them it'll be faster and more stable.

Capybara with js: true not finding checkbox

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.

Is there any DOM event that occurs when a page completes its change?

I'd like to do things like this;
elem1 = document.getElementById(id1);
elem1.click()
// The page has been changed
elem2 = document.getElementById(id2);
elem2.click()
My problem is, if the second 'getElementById(id2)' is called before the HTML of document.documentElement completes its change, it sometimes fails to get elem2 because there is no HTML element that has id2. (Every javascript sentence in the above code is sent from 3rd party like Selenium Server, and there is a javascript loop that gets the sentence and runs it one by one. You can think the loop as a thing like Selenium Core)
I'm trying to write a tool like Selenium or Watir. So I can't use those tools. I think Selenium or Watir has a way to send the second command after it waits until the page has been completely changed as the result of the first command. I tried events like window.onwebkittransitionend because I'm trying this on Webkit, but it did not work.
I don't think promise or async can solve this problem, because it needs more than enforcing sequences. It needs to verify if the second page is fully loaded, because the second command can be the others like fetching the information of rendered images rather than just click.
Is there any event or a way to check if page change is completed? Oh, and I can't use jQuery unfortunately. I need to do it with plain Javascript.
Thanks for reading my question.

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/.

How much content being replaced with an AJAX call is too much?

I run into a common problem when trying to do AJAX development. Where possible, I like to try and just update data in an existing layout, and not the layout itself. For example, take the div below:
<div id="content-5">Here is some content</div>
I would get the updated value for content-5 from the server and just replace the contents of content-5 with the value. This makes a lot of sense for simple data replacements where the value is always going to be displayed in its pure form.
Sometimes the content is more complicated, and I have to actually get more than just raw data... maybe there is some logic to determine how a value is displayed and perhaps the style needs to be different depending on the data inside. In that case, I generally produce the HTML on the server side and inject the HTML into the element instead of just raw data.
Example: A status field from the controller comes back as "complete", but from the design doc, "complete" is supposed to show the user the text "Available" and it needs to be styled in a way different from other statuses.
Doing this in Javascript would require some in-depth view knowledge that the template layer probably already handles. The end result would be the same (code snippet below), but the difference is that there could possibly be some code duplication and a far more complicated Javascript layer.
<div id="content-5"><span class="success">Available</span></div>
Without fail, the requirement comes up that the system will need to handle "new" contents as well. The easiest solution to implement is to just get all of the content's at the same time so that I do not need to handle the extra complexity of injecting a new element instead of just replacing existing content.
So, I create a new template, wrap the contents in another element with an ID, and bulk replace all of the content divs at the same time any time there is a change.
<div id="allContent">
<div id="content-1">Some content A</div>
<div id="content-2">Some content B</div>
<div id="content-3">Some content C</div>
<div id="content-4">Some content D</div>
<div id="content-5">Some content E</div>
</div>
At some point, I have to wonder: Where is the line? At some point it feels like I'll eventually just be replacing the whole page with an AJAX request. Would this really be a problem?
I realize this may be pretty subjective, but what are some good strategies for determining to which level you should be replacing content with AJAX? Replacing just the data seems to be my preferred method when possible as it makes the AJAX controllers very simple. Replacing larger chunks of HTML from a template seems to be the easiest for handling more complicating layout and design issues and also feels like it could be more easily maintained. Are there other options I have not considered?
I expect there will be some discussion about manipulating the DOM programatically, but I personally really dislike this. The code ends up looking pretty horrible and really starts to integrate too much layout and design into the JS layer for my liking. Since I generally work with template libraries of some sort (whether raw PHP, PHP templates like Smarty or JSP in Java) it seems to make more sense to leave as much visual design there as possible.
EDIT
Based on the first few answers, it seems like this is being read as trying to keep the user on the same page but navigating around around the site or otherwise changing the page in a radical way with each update. The question is more about how to determine where the layout work for AJAX calls should happen and whether or not it is an acceptable practice to change large chunks of code with an AJAX request, knowing that replacement code may look nearly identical to what had been there before.
I think the most important requirement is the refresh requirement. If after several AJAX updates I hit refresh, the page I was just looking at should be the page that arrives. If the page reverts to a previous state for any reason then the URL is wrong. If for any reason your AJAX data is going to make the URL in the browser invalid then you should not be using AJAX to fetch that data.
There are exceptions, of course for data the is even newer than the last AJAX request. But that's obviously not what I'm talking about. A live chat screen could receive an update between the last AJAX request and the refresh. No big deal. I'm talking about the logical content and the URL describing it should always be in sync.
Complete personal opinion ex nihil, my rule of thumb is to change no more than 1 "panel" unit or 33% of the page whichever is less.
The basis for this is that the user should be able to clearly recognise the previous page state is related to the new state - how would you feel if you were suddenly teleported into the building to your right? Be gentle with your poor user.
There are also serious technical questions about the benefits of moving and inserting basically a page worth of data, which I think is a bit of an AJAX anti-pattern. What benefit does AJAX provide if you're going to do that?
Your specific question seems dependant on the supposition that the response coming back from your AJAX request isn't "just" data. This feels wrong to me from a separation of concerns point of view: I would expect a page to have all the layout information it requires already, the AJAX response itself to provide nothing more than dumb data/markup, and the JS event handler which created the request to sew the two together, MVC style. In that respect I think, yes, you're doing too much.
(by panel, I mean one logical design element - a menu, a ribbon, an item metadata panel, etc..)
edit: now that I think about it, I think SO's user profile page breaks my rule of thumb with those tab clicks
Depending on whether you want people to be able to to link to / bookmark etc the current page, you might want to navigate the user's browser.
This isn't a concern for some apps like GMail etc, and they won't ever refresh the page.
For myself, I tend to think it's a good practice to navigate the browser when navigating to a logically different place. eg. a person's profile vs. a list of their messages.
Sorry if this is vague, it's rather subjective :-)
A good guideline for something like this is to ask yourself, "Is this dynamic application 'content', or is it content-content?" Your use case sounds like application content that will change with each user. This is probably the best place for Ajax, but with everything, it's always nice not to just have one hammer. You don't want to do too much on one page. For instance, if one part breaks, the entire thing might, thereby frustrating the user.
Anywhere you're looking at actual page content or anything where the information is static, I strongly suggest avoiding the use of JavaScript, as it runs the risk of being invisible to search engines. Make sure anything linking to information like this is crawlable. The first step towards this is dynamic generation on the server side rather than browser side.
If you're using Smarty templates to produce a page, just fragment a template into various meaningful sections - news.tpl, email.tpl, weather.tpl - and have a master.tpl producing the structure of the page and calling child templates.
Then, if you're for example using an AJAX call triggered by a timeout to refresh the news, you can just call the server, cram the necessary data into news.tpl, and return the results into the news div you set up with master.tpl. This way your news layout is always following the pattern of news.tpl. (If you used JavaScript to manipulate formatting bits or set up event handling on document load, you'll need to attach that post-processing to fire after the AJAX call.)
You haven't really gotten specific about the types of things you're trying to replace here, and my initial reaction is that, if a single event is triggering multiple sections of the page to update at once, that's a sign that maybe you should be coallating those sections into a single display.
How much formatting gets done on the server end versus how much gets done on the client end with JavaScript? I'd say server-side formatting if possible, that way you have code that reflects discussions you've made about display layout and logic. Client-side formatting can be used for more interface-based issues - sorting rows in a table and alternating row colors with :odd and :even selectors, showing and hiding divs to create a "tabbed display" without hitting the server since the data won't change just from selecting a new tab, that sort of thing.
Finally, AJAX is one-way. If your web page is a view on a database, this isn't as much of a problem, but using AJAX manipulation to take the place of normal navigation is a terrible idea.
If you were habitually replacing the entire contents of a page using AJAX calls, I would agree that you have a problem. However, it appears to me that you are attempting to carefully think through the implications of your design and attempting, where possible, to avoid what annakata has called this "AJAX anti-pattern."
My rule is a bit simpler: as long as a substantial amount of context (e.g. menu on the left, header, various controls, page title, etc.) remains on a page, I am Ok with replacing almost anything with an AJAX call. That being said, I've never struggled with a page that has as much AJAX-generated code as you are.
I do have one question though: isn't it possible to encode state so that you can just replace some of the Divs in your example rather than all of them? If not, have you thought about doing so?

Categories

Resources