How to check if an element is not clickable with Protractor? - javascript

It's trivial to test if an element is clickable with Protractor, but I'm stuck scratching my head trying to figure out how to check if an element is not clickable.
I've attempted to wrap the click function in a try/catch so that when an error is thrown when trying to click it should catch it and let the test pass; however, this does not work.
Here is my code for the method that does the check:
return this.shouldSeeDisabledFunds()
.then(function() {
var clickable = true;
try {
fundsElem.first().click();
} catch (e) {
clickable = false;
console.log(clickable);
} finally {
console.log(clickable);
}
console.log(clickable);
// All the way through, clickable is still true, and the console log in the
// catch is not called. I believe this is because click is asynchronous.
})
;

I have found a solution that works for this. As click() returns a promise you can simply .then off of it and throw in the successful click handler and override the catch handler to do nothing which makes the test pass if the element is not clickable.
return this.shouldSeeDisabledFunds()
.then(function() {
fundsElem.first().click()
.then(
function() {
throw "Can click Funds element that should be disabled";
},
function() {}
)
;
})
;

Maybe not applicable in your case, but a better way to check if an element is clickable is checking if it is both visible and enabled: elem.isDisplayed() and elem.isEnabled(). This way you don't accidentally click on buttons when you're not supposed to.
Fyi, there will be a library coming to help with cases like this: https://github.com/angular/protractor/pull/1703

There are actually two methods to check it.
1) Using ExpectedConditions
var EC = protractor.ExpectedConditions;
// Waits for the element with id 'abc' to not be clickable.
browser.wait(EC.not(EC.elementToBeClickable($('#abc'))), 5000);
If found to be clickable, it will return error.
2) Using protractor's isEnabled, isDisplayed and isPresent
So as far as my understanding goes, you can create isNotClickable, which will return false only if element is present, displayed or enabled and true otherwise:
function isNotClickable(element) {
return element.isPresent().then((isPresent) => {
if (isPresent) {
return element.isDisplayed().then((isDisplayed) => {
if (isDisplayed) {
return !element.isEnabled();
}
return true;
});
}
return true;
});
}

To verify Clickable : element.isDisplayed().toBe(true)
Not Clickable : element.isDisplayed().toBe(false)
Worked for me.

Related

doc.createTreeWalker is not a function

When I click the button with text 'Create' the text is replaced to 'Confirm?'
This is the HTML:
and the pageObject:
create() {
return cy.get('im-page.hydrated', { includeShadowDom: true })
.find('im-button', { includeShadowDom: true })
.eq(1)
.find('button', { includeShadowDom: true })
.click({ force: true })
}
confirmBtn() {
return cy.get('im-page.hydrated').find('im-button')
.eq(1)
.find('button.success.outline')
.contains('Confirm?')
}
Then when Cypress click on Confirm I got this error:
There's an issue logged doc.createTreeWalker is not a function #20813 but not yet resolved.
From the source code the doc part refers to the previous subject, i.e the element before the .find() which above is cy.get('im-page.hydrated').find('im-button').eq(1).
My guess is because the button changes on the Create click, something in that previous subject becomes invalid when you try to access the Confirm button.
A couple of ideas to try (just guesses at this stage)
// using jquery to avoid the treeWalker (used by the .find() command)
cy.get('im-page.hydrated im-button:eq(1) button.success.outline:contains(Confirm)')
// using an alias and "withinSubject" option to explicitly define `doc`
cy.get('im-page.hydrated im-button:eq(1)`).as('parent')
cy.get('#parent').then($parent => {
cy.get('button.success.outline:contains(Confirm)', { withinSubject: $parent })
You should turn on shadowDOM globally to avoid missing any parts that need it.
// cypress.json
{
...
"includeShadowDom": true
}
#3 - just do a simple search for "Confirm", since likely only one thing at a time needs confirming.
cy.contains('button.success.outline', 'Confirm')

return from parent function after click

I looked all around, but couldn't find any help with this problem.
So I have this if that is calling a function. In that I am waiting for a button press to happen. The button, therefore, should return true or false, but it's only returning to its anon-function running inside the click event and I don't know how to return from its parent function.
if ( saveConfirm() ) {
saveFunction()
}
function saveConfirm () {
$('#confirm').click(function() {
$('modal').modal('hide')
return true
})
$('#abort').click(function() {
$('modal').modal('hide')
return false
})
}
Hope you guys understand what i mean and maybe someone can help me with how to return from the call to the if through the button that is pressed.
Click handlers execute asynchronously, you should move saveFunction call to confirm click handler. There is no way for saveConfirm return boolean value you are trying to return in click handler.
$('#confirm').click(function() {
$('modal').modal('hide');
saveFunction();
})
$('#abort').click(function() {
$('modal').modal('hide');
})
You bind events in saveConfirm(). Try code below
$('#confirm').click(function() {
$('modal').modal('hide');
saveFunction();
})
$('#abort').click(function() {
$('modal').modal('hide');
})

if ignores the condition JQuery

I'm trying to set up a div that can only be clicked once, but my if keeps ignoring the condition and I really dont know why.
$(document).ready(function() {
var defaultState = true;
if (defaultState) {
defaultState = false;
$("#element").click(function() {
//Do stuff to elements
}
});
I tried solving it a different way. I want this condition only to fill one div with context from another one, but only one single time. So I tried making the condition like this: if($("#element").html().length === 0) I even checked with the console for the value of my condition, and even if it was at 5000, clearly not 0 anymore, it kept ignoring my condition and went into the if.
Once you bind the click handler, it's bound. From that point, until you unbind it, that handler will always be triggered.
It sounds like one() would be what you're looking for:
$('#element').one('click', function() {
//...
});
That will only trigger once.
The event handler is already attached that first time through, right after the document.ready runs.
You can just use the jQuery .one() event handler.
jQuery .one() documentation
$("#element").one('click', function() {
//Do stuff to elements
});
$(document).ready(function() {
$("#element").one('click', function() {
//Do stuff to elements
});
});
OR
$("#element").on('click', function(){
//Do what you want
});
//Later in your code
$("#element").off('click');
If you're set on using a flag variable you can do it like this too:
$(document).ready(function() {
var defaultState = true;
$("#element").click(function(e) {
if (defaultState) {
//Do what you want
}
defaultState = false;
}
});
Once you have added a event listener $("#element").click(function() { it will be bounded to that element. But what you can do it to put your if inside the click function and give false to your flag variable once inside the if.
The advantage is you can give the variable true again, and you click will run "again".
$(document).ready(function () {
var defaultState = true;
$("#element").click(function () {
if (defaultState) {
defaultState = false;
//Do stuff to elements
} else {
return false;
}
});
});

jquery "return false" not working properly inside update panel

I used jquery to validate a text box placed within update Panel
$("[id*='btnCreate']").live('click', function ()
{
var regex = new RegExp("^[a-zA-Z0-9]+$");
if (!regex.test($("[id*='txtbxTemplateName']").val()))
{
alert('hit');
$("[id*='lblError']").text() = 'Template name is invalid!';
return false;
}
});
the click event function is being called, but after returning false it is still hitting the code behind.
Please help me on this.
I presume you mean by "still hitting the code behind" that the default action of the button is not being prevented. This is because the code never gets to return false because of an error:
$("[id*='lblError']").text() = 'Template name is invalid!';
return false;
should be
$("[id*='lblError']").text('Template name is invalid!');
return false;
You have just exposed, however, why return false is a bad way to prevent the default action of an event. It is bad because any errors in the error handler will mean that the default action is not prevented, because return false is the last statement in the handler.
If you use event.preventDefault(), all kinds of beautiful things will happen. Chief among them is that you can place the statement earlier in the handler:
$("[id*='btnCreate']").live('click', function (event) {
var regex = new RegExp("^[a-zA-Z0-9]+$");
if (!regex.test($("[id*='txtbxTemplateName']").val())) {
event.preventDefault();
alert('hit');
$("[id*='lblError']").text('Template name is invalid!');
}
});

Javascript Alertify with return from confirm

I'm trying to use alertify.js as a confirmation dialog for all my confirm scripts. But it just isn't working like regular JS confirm does. In the code below I never get a return true
function aConf ( mes ) {
alertify.confirm( mes, function (e) {
return e;
});
}
Delete
Of course if I replace aConf with JS' confirm it works. So why is alertify not sending me back it's outcome?
Because confirm is a blocking function (no javascript will run until it returns true/false), and alertify is non-blocking (JS keeps executing). Alertify does not immediately return a true/false, but instead, it probably returns undefined immediately, then it calls a callback function later, after the user clicks OK or Cancel. The return value from that callback function has no effect in your example, because the onclick code has already finished running (because it is non-blocking).
Assuming you are using this: https://github.com/fabien-d/alertify.js/
This is how it actually works with a callback function, not a return value:
alertify.confirm( message, function (e) {
if (e) {
//after clicking OK
} else {
//after clicking Cancel
}
});
For your code sample, you might try something like this:
function performDelete ( a_element ) {
// perform your delete here
// a_element is the <a> tag that was clicked
}
function confirmAction ( a_element, message, action ) {
alertify.confirm(message, function(e) {
if (e) {
// a_element is the <a> tag that was clicked
if (action) {
action(a_element);
}
}
});
}
Delete
EDIT: updated to be a generic confirm dialog that calls a callback function if the user clicks ok.

Categories

Resources