Protractor Reloading Page Between It Statenents - jasmine

I completely understand if there isn't enough information, but here is what happens - the code below executes just fine. However, if I try to delete the 'cancelButton.click()' action and uncomment the second it statement, the page reloads and the cancelButton does not get clicked. This isn't the only place it's happening. It's almost like I have to put everything in one it statement, or the page reloads at random. I do NOT have any beforeEach statements.
it("Should click stuff ", function(){
actionsButton.click();
checkOutButton.click();
cancelButton.click();
});
//it('Shouldn't fail like it is', function(){
// cancelButton.click();
//});

Are you using any afterEach() or onPrepare()? There may be something in those blocks that is triggering your page reload issue.

Related

If element exists wait for it to disappear

So I'm trying to write some cypress code and the documentation imo isn't really clear.
I have two scenarios.
A page is loaded with no loading spinner.
A page is loaded with a loading spinner.
I would like to write code that would satisfy both scenarios and let the test just continue.
If the page does not have a loading spinner element: Continue with the tests as usual.
If the page does have a loading spinner element: Wait until the element disappears and then continue
Cypress has a lot of fancy functions but the documentation on how to use them just isn't clear enough.
I tried with the following piece of code:
try {
cy.getByTestId('loader-spinner')
.should('exist')
.then(el => {
el.should('not.exist');
});
} catch (error) {
cy.getByTestId('loader-spinner').should('not.exist');
}
Because of the timing aspect it can be tricky to get this test right.
Controlling Triggers
You really need to know what controls the spinner - usually it's a call to API. You can then delay that call (or rather it's response) to "force" the spinner to appear.
To do that, use an intercept
cy.intercept(url-for-api-call,
(req) => {
req.on('response', (res) => res.delay(100)) // enough delay so that spinner appears
}
)
// whatever action triggers the spinner, e.g click a button
cy.getByTestId('loader-spinner') // existence is implied in this command
// if the spinner does not appear
// the test will fail here
cy.getByTestId('loader-spinner').should('not.exist') // gone after delay finishes
Two scenarios
First off, I don't think your two scenario idea is going to help you write the test correctly.
You are trying to conditionally test using try..catch (nice idea, but does not work). The trouble is conditional testing is flaky because of the timing aspect, you get the test working in a fast environment then it starts to break in a slower one (e.g CI).
Better to control the conditions (like delay above) then test page behaviour under that condition.
To test that the spinner isn't appearing, return a stub in the intercept It should be fast enough to prevent the spinner showing.
cy.intercept(url-for-api-call, {stubbed-response-object})
// whatever action triggers the spinner, e.g click a button
cy.getByTestId('loader-spinner').should('not.exist') // never appears
Take a look at When Can The Test Blink?
You should be able to just use a should('not.exist') assertion, causing Cypress to wait for the element to not exist. Remember, Cypress automatically retries up until the timeout, so if you haven't changed the global timeout, then the following will try for up to 4 seconds.
cy.getByTestId('loader-spinner')
.should('not.exist');
If you find the test failing because the element still exists, you can bump the timeout. Below, I've defined a 10s (10000ms) timeout for the should() command.
cy.getByTestId('loader-spinner')
.should('not.exist', { timeout: 10000 });
Additionally, you may find that the element does still exist, but is not visible. In that case, change not.exist to not.be.visible

Should I use wait() to wait for dialog to close? Is there a better way?

I want to test that when I click a button that closes a modal dialog (ng material) that the modal does not exist.
This test actually closes the modal but it passes
it('should close the modal when the close button is clicked', () => {
cy.get('#close-new-category').click();
cy.get('#new-category').should('exist');
});
This test works also
it('should close the modal when the close button is clicked', () => {
cy.get('#close-new-category').click();
cy.get('#new-category').should('not.exist');
});
This test fails, if I add a wait
it('should close the modal when the close button is clicked', () => {
cy.get('#close-new-category').click();
cy.wait(500);
cy.get('#new-category').should('exist');
});
This test passes as we should expect, but is using wait() the best way?
it('should close the modal when the close button is clicked', () => {
cy.get('#close-new-category').click();
cy.wait(500);
cy.get('#new-category').should('not.exist');
});
I only ask because the documentation says this is an anti-pattern and there should be a better way.
You should not need to use wait() for the purpose of these tests. I think these examples are testing too much like unit tests and not enough like end to end tests.
The reason case #1 passes is that after clicking close the modal still does exist for some small amount of time. The should() checks immediately in this case and still sees your modal, so it passes and moves on.
Case #2 also passes because should() has built in retry logic, this is one of the key features of cypress. Immediately after clicking close, the should() checks and finds that the modal does exist. Because the modal is found, it will wait and try again. Eventually the modal does go away and this should() passes.
For the purposes of your testing scenario, you only need to care about case #2 because it tests that your modal does go away. After that should() passes, you are able to move forward to the next step of your test knowing that the modal is gone. The fact that it was still there initially doesn't matter to what you want to accomplish next.

slowness when using hashchange event

I have a website that uses ajax for paging, the page system works according to the hashchange event, whenever I want to move to another page, I call a function that change the hash to the page number, when the hash changed the hashchange event is fired and call a function to get the page data .
However, it works perfectly except one thing, if I change the page more than 3-4 times the page will not respond and will crash, I check the network tap in the Inspect element in google chrome and what I see is when I change the page the number of ajax requests will be doubled and the transferred data also will be doubled, which will cause a memory leak.
Eventually, I've tried to do the paging thing without hashchange to see if the problem will be solved, and it worked like charm.
Can you please till me what to do ? thanks in advance
hashchange event
$(window).bind('hashchange', function () {
search(0);
});
changehash function
function ChangeHash(p) {
window.location.hash = p;
}
page button
$('#Pages').append("<button type='button' class='btn btn-default"+active+"' Onclick=\"ChangeHash(" + a + ")\">" + a + "</button>");
The code you've given doesn't show it, but most likely what's happening is that after each xhr you are re-running that bind call.
Hence, you are double, triple, quadruple binding the event unintentionally - which is precisely what the network log shows: haschange is running 2,3,4,5 ... times until the browser crashes.
To avoid this, make sure
$(window).bind('hashchange', function () {
search(0);
});
Is only run once.

Loading forms with .load kills the submit button in Firefox

I am currently loading several forms into a webpage with:
$(document).ready(function () {
$('#content').load('php_script.php', function() {
$(this).find('#someForm').ajaxForm(function() {
alert('Success!');
});
$(this).find('.someOtherForm').ajaxForm(function() {
alert('Success!');
});
});
});
This works in Chrome, Chromium and IE who loads the forms and everything works as it should (Clicking submit sends a request to the php-script defined in the form's action, which adds stuff to a db, and shows the alert dialog). In Firefox (v10.0.2) this code loads the forms into the DOM and displays them, but when clicking submit on any of the forms nothing happens.
At first I suspected ajaxForm, but changing the above code to:
$(document).ready(function () {
$('#content').load('php_script.php');
});
yields almost the same result, the difference being that the user is sent to the script defined as the action (Except for Firefox, where nothing happens).
How do I make Firefox not kill the submit button?
I solved it, bad HTML from my side:
<table><form ...>
<tr>...</tr>
</form></table>
Instead it should look like:
<form ...><table>
<tr>...</tr>
</table></form>
The validator did not catch this since it was loaded via jQuery (and I forgot to validate the page serving the forms), and Firefox buggered out.
The code above looks ok to me...
Have you had a look in firebug if there are any errors? Maybe there is a conflicting Id or something.
Maybe the form isnt completely loaded into the dom yet, might be worth giving live binding a try
Found this in the docs:
...jQuery uses the browser's .innerHTML property to parse the retrieved document and insert it into the current document. During this process, browsers often filter elements from the document such as , , or elements. As a result, the elements retrieved by .load() may not be exactly the same as if the document were retrieved directly by the browser...
If you inspect the form is it the same as in other browsers?

very strange jquery issue

I am trying to run a function on page load but its not working. I've added it in every place I can think of and the ONLY thing that works is:
$("html").mousemove(function(event) {
$('#project_thumbs_container').masonry('reload');
});
I've tried delays but I have resorted to the hacky above method :(
Does anyone have any suggestions as to why my function won't run?
UPDATE:
I am using masonry for jquery. My problem is when I load a page that uses masonry with ajax, it shows them in a single column. $('#project_thumbs_container').masonry('reload'); resets it properly, but it only works using the above mousemove method.
It sounds like you have one of two problems:
1) Malformed HTML which is causing an error, which isn't allowing the code to parse correctly when using the document onReady syntax: $(function() { ... });
2) Masonry might be loading asynchronously, which means that the "onReady" callback might not be the one that you want to be using. Your Ajax call would look more like this:
$('body').load('index.html', function() {
$('#project_thumbs_container').masonry();
});
Unless someone has a better answer, I just put the code in my fadeIn(); snippet after ajax call is complete:
this.fadeIn('slow', function() {
$('#project_thumbs_container').masonry('reload');
});
Seems to work.
Try something like this.
$(document).ready(function(){
$('#project_thumbs_container').masonry('reload');
});
You can put this code anywhere on the page and it should work, as long as the dependencies have already been loaded.
Just put your function in this
$(document).ready(function() {
// Handler for .ready() called.
// your function
});
when your page load the function will execute

Resources