Test passing locally but not in CI - cypress - cypress

I have this code:
<div class="input-group" data-cy="k-item">
<input type="text" placeholder="my_placeholder" value="my_value">
<div data-cy="delete-field" role="button" tabindex="-1" class="r_field">
<i class="close"></i>
</div>
</div>
I have my cypress code for clicking on "close" and check that the item when submit is deleted in the table:
cy.getBySel('k-item').within(() => {
cy.getBySel('delete-field').click().click()
})
cy.getBySel('register').click()
cy.intercept('GET', 'route_intercepted').as('getRoute')
cy.wait('#getRoute')
cy.get('table').should('not.contain', 'my_item')
})
The test is passing in my local and even in headless mode but when in pipeline it is failing saying:
AssertionError: Timed out retrying after 4000ms: Expected not to find content: 'my_item' within the selector: 'table' but continuously found it.
I think the item is not getting deleted after the submit. I am experiencing the same issue with force:true.
Have anyone experienced this issue please and also is there any other way to make the same tests more robust.

Add another assertion for an element on the page that is not delayed in rendering. This could be a header, spinner, etc. but must be something immediately rendered, not something async as this list of items appears to be.
cy.wait('#getRoute')
// add another assertion here
cy.get('table').should('not.contain', 'my_item')

Try moving the intercept to the top of the test. It's a "listener" so it should be set up before the delete event is triggered.
cy.intercept('GET', 'route_intercepted').as('getRoute')
cy.getBySel('k-item').within(() => {
cy.getBySel('delete-field').click()
cy.getBySel('register').click()
cy.wait('#getRoute')
cy.get('table').should('not.contain', 'my_item')
})
Also the alias wait needs an #, but I presume that was a typo.

How about you increase the timeout to 7 seconds and try. Mostly in CI systems since the resources are shared so the execution speed might vary, If the line below works locally then my best guess is increasing the timeout should do the job.
cy.get('table', {timeout: 7000}).should('not.contain', 'my_item')

You have to wait the response on the wait call and just then you can check your element:
cy.getBySel('k-item').within(() => {
cy.getBySel('delete-field').click().click();
})
cy.getBySel('register').click();
cy.intercept('GET', 'route_intercepted').as('getRoute');
cy.wait('#getRoute').then(()=>{ //wait the response of your GET, then check
cy.get('table').should('not.contain', 'my_item');
})
})

Related

Laravel Dusk: Wait for a load mask/overlay to finish

I am having difficulty getting some tests to pass because of a load mask I put over the entire screen during AJAX calls (transparent black with a loading gif in the middle).
I can get it to work if I pause(2000) each time, but the amount of AJAX calls in the app make that the slow solution. I am trying to use waitUntilMissing but it's not working.
I have a div with two classes: load-mask and loading.
load-mask is always there
loading class applied to div during all vue.js AJAX calls; this is the load mask
When AJAX response received, loading class removed
So in my test I am trying to do this:
$browser->loginAs($this->user)
->visit(new CustomInputPage)
->saveNarrative('Lorem ipsum dolor')
->visit(new CustomInputPage)
->waitUntilMissing('.load-mask')
->assertSee('Lorem ipsum dolor');
Load a page
Enter text & hit save
reload page
make sure text is still there
I get an error on the assertSee because once the page loads, the loading is applied as it makes it's AJAX call so it hasn't loaded the text yet.
I am assuming waitForMissing('.load-mask') is passing before the load mask even starts, so it tries to assertSee during the loading process, but it's an assumption.
Is there a working solution outside just using pause?
EDIT: I am currently trying to click around the page to reload the AJAX instead of a full page load. I am still experiencing the same issue.
Now I am trying this instead:
$browser->loginAs($this->user)
->visit(new CustomInputPage)
->saveNarrative('Lorem ipsum dolor')
->clickLink('Tab 2')
->waitUntilMissing('.load-mask')
->assertSee('Lorem ipsum dolor');
Clicking a tab will load the AJAX call I'm referring to. I notice in the screenshot it's always showing my .loading animation.
So...Tab 2 should be an empty box but the screenshot shows the text hasn't been cleared yet and load mask is still present (happens after response is received).
So I can't get the timing on a completed AJAX call down without using pause. Should I try another way?
EDIT 2
Here is where my load mask is being inserted. The vue.js axios library.
import store from '../store';
let numberOfAjaxCAllPending = 0;
axios.interceptors.request.use(function (config) {
// Do something before request is sent
numberOfAjaxCAllPending++;
store.commit('isLoading', true);
return config;
}, function (error) {
numberOfAjaxCAllPending = 0;
store.commit('isLoading', false);
// Do something with request error
return Promise.reject(error);
});
// Add a response interceptor
axios.interceptors.response.use(function (response) {
// Do something with response data
numberOfAjaxCAllPending--;
if (numberOfAjaxCAllPending === 0) {
store.commit('isLoading', false);
}
return response;
}, function (error) {
numberOfAjaxCAllPending = 0;
store.commit('isLoading', false);
// Do something with response error
return Promise.reject(error);
});
And on each Laravel blade template, I tried this first:
<div :class="{ loading: $store.state.isLoading }" class="load-mask"></div>
But now I am trying this way, with the loading class always present and just toggling display none/block
<div :style="{display: $store.getters.getLoadingDisplay }" class="loading"></div>
Same issue occurs this way as well. The load mask is present and I am also noticing the text box isn't being cleared when switching tabs in Laravel Dusk (the tabs clear fine in a regular browser).
EDIT 3
The first assertion passes, then it fails when trying to ->press('Save'). I get this error:
Facebook\WebDriver\Exception\UnknownServerException: unknown error: Element <button type="button" class="btn btn-sm mt-3 btn-primary" style="float: left;">...</button> is not clickable at point (440, 912). Other element would receive the click: <div class="loading" style="display: block;"></div>
This is the test I am trying to run. As you can see, I have a waitUntilMissing and an assertMissing right after each other. Then I press Save and it says there's still a load mask. If I wait 2 seconds before the keys statement, it works fine.
$browser->loginAs($this->user)
->visit(new CustomInputPage)
->waitUntilMissing('.loading')
->assertMissing('.loading')
->keys('.ql-editor', 'Test Text')
->press('Save')
->waitUntilMissing('.loading');

Cypress picks up the wrong element?

I am trying to find and click this element using Cypress:
<input class="form-control btn btn-primary" type="button" value="Log out">
I have tried several variations, but the closest is this:
cy.get("input[type='button']").filter('.btn-primary').should('have.value','Log out').click()
So when I run this, I get the following response:
expected [ <input.form-control.btn.btn-primary>, 1 more... ] to have value Log out, but the value was Edit.
Sure, there is a button there called Edit, but it is not the one I want. I have specified what I want with the should('have.value','Log out') clause.
So - why does it insist on trying to use the wrong element & failing?
Update: I finally got this working.
This is the solution I went with in the end:
cy.get("input[type='button']").filter('.btn-primary').eq(1)
.should('have.value','Log out').then(($btn) => {
$btn.click()
})
can you try
cy.get('input').find("[value='Log out']").click()
or
cy.get("[value='Log out']").click()
Obviously you need your developers to add ids, but I know your situation.
You could also try if there is only one btn-primary
cy.get('.btn-primary').click()
This was the solution that worked for me:
cy.get("input[type='button']").filter('.btn-primary').eq(1)
.should('have.value','Log out').then(($btn) => {
$btn.click()
})
How about to set an unique testID for that button and get it as simple as it can be.
Something like:
data-test-id= "log-out-button" //put that in your button code
and then in Cypress:
cy.get('[data-test-id="log-out-button"]')
.click()
even though you can set a function to get those testID's more effectively:
Command.Cypress.add('getTestID', (testID) => {
cy.get(`[data-test-id="${testId}"]`)
})
and now everything you do for getting that button (or every element with testID is: cy.getTestID('log-out-button')

Timed out waiting for asynchronous script result while executing protractor scripts with appium

I have a problem while running more than one test in protractor : Timed out waiting for asynchronous script result after 60010 s
The code of tutorial script which is executed just after the login script :
Here the code i'm using in my config file from A Code proposed in another question but it didn't solve my problem !
onPrepare: function() {
return browser.getProcessedConfig().then(function(config) {
var browserName = config.capabilities.browserName;
browser.manage().timeouts().setScriptTimeout(60000);
});
PS : Even if i put an incorrect location for the element i have the error of time out and not this element cannot be found ! as if that line of code "the click into tutorial button" is never executed
Is it because tutorial make an ajax call ?
Here my html code :
</div></md-card-content> </md-card><!-- end ngIf: !expandChart --> </div> </div> </div></md-content> </div></div> <!-- Google Analytics: change UA-XXXXX-X to be your site's ID --> <!--<script>--> <!--!function(A,n,g,u,l,a,r){A.GoogleAnalyticsObject=l,A[l]=A[l]||function(){--> <!--(A[l].q=A[l].q||[]).push(arguments)},A[l].l=+new Date,a=n.createElement(g),--> <!--r=n.getElementsByTagName(g)[0],a.src=u,r.parentNode.insertBefore(a,r)--> <!--}(window,document,'script','https://www.google-analytics.com/analytics.js','ga');--> <!--ga('create', 'UA-XXXXX-X');--> <!--ga('send', 'pageview');--> <!--</script>--> <script src="scripts/vendor.js"></script> <script src="cordova.js"></script> <script src="scripts/scripts.js"></script> <script src="https://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/sha1.js"></script> <script src="https://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/sha256.js"></script> <script src="https://crypto-js.googlecode.com/svn/tags/3.1.2/build/components/enc-base64-min.js"></script> <div class="introjs-overlay" style="top: 0;bottom: 0; left: 0;right: 0;position: fixed;opacity: 0.8;"></div><div class="introjs-helperLayer " style="width: 538px; height:366px; top:64px;left: 195px;"></div><div class="introjs-tooltipReferenceLayer" style="width: 538px; height:366px; top:64px;left: 195px;"><div class="introjs-tooltip" style="left: 546px;"><div class="introjs-tooltiptext">Watchlist view. Swipe the row in the grid to the left to show the delete action.</div><div class="introjs-bullets"><ul><li><a class="active" href="javascript:void(0);" data-stepnumber="1"> </a></li><li> </li><li> </li><li> </li><li> </li><li> </li><li> </li><li> </li></ul></div><div class="introjs-progress" style="display: none;"><div class="introjs-progressbar" style="width:12.5%;"></div></div><div class="introjs-arrow left" style="display: inherit;"></div><div class="introjs-tooltipbuttons"><a class="introjs-button introjs-skipbutton" href="javascript:void(0);">Don't show it again!</a>PreviousNext</div></div></div></body></html>​
1. Regarding with route check
In case after first spec, user got logged in and the route changed. Make sure all are navigated before any test executed.
expect(browser.getCurrentUrl()).toContain('#/the_route_of_logged_in');
// '#/' is just illustration. You can remove it to make it shorter
// => like this ...toContain('the_route_of_logged_in');
2. Regarding with click on tutorial
browser.wait(EC.elementToBeClickable(tutorial), 10000);
Do the browser.wait with EC for click-able button before attempt to click it (it seem like you got good approach here)
=> SUMMING UP you can give this a try:
'user strict';
var EC = protractor.ExpectedConditions;
describe('tutorials', function () {
it('should make click into tutorial button', function () {
expect(browser.getCurrentUrl()).toContain('the_route_of_logged_in');
var tutorial = $('.introjs-nextbutton');
browser.wait(EC.elementToBeClickable(tutorial), 8000, 'Timed out');
tutorial.click();
browser.sleep(8080); // regardless we are not reaching this point. But I will suggest to reduce this sleep time like 1000 (1s).
});
});
3. (optional) in case 2 points above does not help
In your all of your spec login-spec.js and tutorial-spec.js. Add process.nextTick(done); in a afterAll() block to ensure if there are no any Jasmine Reporters being stuck after a spec.
describe('foo', function(){
afterAll(function(done){
process.nextTick(done);
});
it('should bar...', function() {});
}
P.S. Beware that I am totally have no clue if my suggestions/approach could help. As debugging with e2e-test always painful... because we are always likely not knowing "where are the errors come from". So all I can do is giving you suggestions.
(sometimes it took me hours to just observe the behaviors of browser to identify an issue of e2e-test)
And DO NOT COPY PASTE my code into your code. I typed it with the images you provide, I can make some typos there.
Add parameter to conf.js under capabilities:
maxSessions: 1,
it should help.
Also your timeoutinterval might be too high 30000 should be enough.
Or on prepare change line to :
browser.manage().timeouts().implicitlyWait(5000);
#EDIT:
change to sth similar to this found something like this
baseUrl is 10.0.2.2 instead of localhost because it is used to access the localhost of the host machine in the android
emulator/device
baseUrl: 'http://10.0.2.2:' + (process.env.HTTP_PORT || '8000'),
Capabilities new command:
newCommandTimeout: 60
Also use of promises might be helpfull instead of timeouts
someethingToDo.click().then(function(){
return somethingToDo.click();
}).then(function(){
//morecode
});
I think you might have an issue with how your timeouts are set up. Remove all timeout references from your config file and try something like this (adjust accordingly to include other configurations as needed):
exports.config = {
allScriptsTimeout: 60000,
getPageTimeout: 30000,
jasmineNodeOpts: {
defaultTimeoutInterval: 62000,
}
}
Finally i tried to solve my problem by adding this call back
describe("long asynchronous specs", function() {
beforeEach(function(done) {
done();
}, 10000);
});
Here is a link from jasmine Asynchronous_Support that help me understand time out problems.
Hope that can help you,

Ajax POST issue with comment system

I downloaded a php/ajax comment script almost 2 years ago and been having this small issue since day one. I tried to contact the author, but he's no where to be found.
Sometimes when I click the "Send" button, the button stays disabled and nothing happens at all. It just continues to show the animated busy picture. I do not receive any error messages at all.
I'm wondering if any of you pros can help me with this?
Here is the relevant code :
<div class="comment_heading">Leave a Comment</div>
<div class="post_comment">
<textarea name="txtpostcomment" id="txtpostcomment-'.$postid.'" class="txtpostcomment"></textarea>
<button class="btnpostcomment" id="btnpostcomment-'.$postid.'" onclick="comment('.$postid.');" type="button">Send</button>
<input type="hidden" name="token" id="token" value="'.$_SESSION['token'].'">
<script>document.getElementById("txtpostcomment-'.$postid.'").focus();</script>
</div>
comment = function(postid1)
{
var txt = $('#txtpostcomment-'+postid1);
var btn = $('#btnpostcomment-'+postid1);
var comment1 = $(txt).val();
var token = $("#token").val();
$(btn).css('background-image', 'url(/comments/submit-busy.gif)');
$(btn).attr('disabled', true);
$.post("/comments/submit.php",{commenting:1, postid:postid1, comment: comment1, name: name, token: token},
function(msg)
{
if(msg.status)
{
$('.post_comment .error_msg').remove();
$('.comment-list-'+postid1).prepend(msg.html);
$(txt).val('');
$('.comChars').empty();
}
else
{
$('.post_comment .error_msg').remove();
$('.error_msg').clone().appendTo('.post_comment');
$('.error_msg:last').append(msg.error);
}
$(btn).css('background-image', 'none');
$(btn).attr('disabled', false);
$(txt).attr('disabled', false);
},'json');
}
It appears that your code is not checking for error conditions just in case the Ajax request fails (due to network disconnection, server error etc). Check your javascript console for any js errors. Since it is using $.post you might want to extend the code to add error handling if the jQuery version that you are using supports it. Or, alternatively, use $.ajax. For more info see http://api.jquery.com/jquery.post or http://api.jquery.com/jquery.ajax

How can I stop a form from processing/submitting that is using jquery AJAX submission?

I have a form with two buttons, a submit button and a cancel/close button. When the user clicks submit, the entered data is validated using http://www.position-absolute.com/articles/jquery-form-validator-because-form-validation-is-a-mess/. If everything validates, the form is submitted with jQuery/AJAX. That all works fine and dandy. I run into problems with the cancel button though. I want the cancel button to require confirmation. If the user chooses to proceed, they are taken to a page of my choosing. If they decide they don't want to cancel, then they are simply left on the page. It's the last part that isn't working.
My form code looks like this:
<form name="createPage" id="createPage" method="post" action="pager.php" class="ajax updateForm">
<input name="whatever" type="text" />
<button type="submit" id="submitQuickSave" class="submitSave"><span>save</span></button>
<button type="submit" id="submitCancel" class="submitClose" onclick='confirm_close()'><span>close</span></button>
</form>
My current cancel script looks like the following. If the user does indeed want to cancel, I unbind the form submit so that validation isn't executed. The form then proceeds to submit and includes cancel as a parameter in the action attribute. I handle the cancellation server side and direct the user to a new page.
function confirm_close()
{
var r=confirm("All changes since your last save operation will be discarded.");
if (r==true)
{
$(".ajax").unbind("submit");
}
else
{
}
}
I cannot figure out what to put in the 'else' argument. What happens is that if the users cancels the cancellation (i.e., return false), then the form still tries to submit. I cannot make it stop. I've tried several things from this site and others without success:
event.stopImmediatePropogation
.abort()
Any ideas? Basically, how can I get the cancel/close button work properly?
Consider separating your JavaScript from your HTML. With this in mind, you could write the handler for your the click event you're trying to intercept like this:
$("button#cancel").click(function($event) {
var r = confirm("All changes since your last save operation will be discarded.");
if (r) {
$(".ajax").unbind("submit");
}
else {
$event.preventDefault();
}
});
You would have to tweak your HTML and add an id attribute to the cancel button:
<button id="cancel" type="submit" value="cancel">Cancel</button>
Example here: http://jsfiddle.net/wvFDy/
Hope that helps!
I believe you just
return false;
Let me know if this works.

Resources