How to check if an element is clickable using Protractor test - javascript

I have an element that does not allow it to be clickable using CSS property pointer-events: none; How can I check whether that element is clickable or not as doing a .click() on the element throws an exception that I cannot catch UnknownError: unknown error: Element is not clickable at point The element is a link so I just want to check if the redirect happened but because of this error it ends the test right away and try catch cannot catch the exception.

I don't know about protractor, but using plain JS you can do:
window.getComputedStyle(element).getPropertyValue('pointer-events') == 'none';
however support for getComputedStyle may not be available in all browsers you wish to support, see MDN compatibility matrix, which indicates no support in IE 8, however it may not support the pointer-events CSS property anyway.

if you really want to use protractor you can use the following:
expect( protractor.ExpectedConditions.elementToBeClickable(element)).not.toBe(true);

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

I have wrote small check utility method, keep in mind it will click on element immediately when it become clickable:
import { ElementFinder, promise } from 'protractor';
export let testHelpers = {
isClickable(el: ElementFinder): promise.Promise<boolean> {
return new promise.Promise(resolve => {
let interval = setInterval(() => {
el.click().then(() => {
clearInterval(interval);
setTimeout(() => {
resolve(true);
}, 500);
}, () => { });
}, 100);
});
}
}
In your test code:
mport { testHelpers } from '../src/core/e2e/helpers';
describe('App', () => {
it('should do something', () {
let btn = $('.cls');
browser.wait(testHelpers.isClickable(btn), 3000);
});
});

Related

How to optimize native JavaScript code when refactoring it from Jquery

I need to rewrite some Jquery into native JavaScript code but I am facing a problem that I am not so sure how to solve.
This is Jquery code I need to rewrite in native JS:
$('.class1').click(function () {
setTimeout(() => {
$('.class2').css('top', '252px');
$('.class3').css('bottom', '0px');
}, 200);
$('.class2').css('z-index', '-1');
$('.class1').css('z-index', '-1');
});
And this is what I have written in native JavaScrip:
if (document.querySelector('.class1')){
document.querySelector('.class1').addEventListener('click', function () {
setTimeout(() => {
if (document.querySelector('.class2')) {
document.querySelector('.class2').style.top = '252px';
}
if ( document.querySelector('.class3')) {
document.querySelector('.class3').style.bottom = '0px';
}
}, 200);
if (document.querySelector('.class2')) {
document.querySelector('.class2').style.zIndex = '-1';
}
if ( document.querySelector('.class1')) {
document.querySelector('.class1').style.zIndex = '-1';
}
})
}
I was hoping that people could explain to me how to solve two things:
Is there a more elegant way to check for an element on the current page if the code runs on the whole site?
Is there something else that I can replace those if statements inside the function?
In Jquery those statements are executed one by one but in my case I need to check for an element first and if it is there do something with it.
You can make the code more succinct by storing the result of querySelector within a variable. Also note that a class selector in jQuery can return multiple elements, so the native equivalent of it is querySelectorAll().
As such you will need to loop through all the elements in that collection and add the event handlers, or update their style, as necessary. Due to this loop you don't need to explicitly check for the existence of the elements, as the forEach() will simply not execute if the collection is empty.
With that said, try this:
let class1 = document.querySelectorAll('.class1');
let class2 = document.querySelectorAll('.class2');
let class3 = document.querySelectorAll('.class3');
class1.forEach(el => {
el.addEventListener('click', e => {
setTimeout(() => {
class2.forEach(el => el.style.top = '252px');
class3.forEach(el => el.style.top = '0px');
}, 200);
class1.forEach(el => el.style.zIndex = -1);
class2.forEach(el => el.style.zIndex = -1);
});
});

How to test a ternary operator with Jasmine in javascript

I am trying to test a piece of my code using Jasmine, but I am stuck on which method to use and how to go about testing this particular function. Here is my code:
const foodInfoToggle1 = () => {
const foodInfo = document.querySelector('#first');
foodInfo.style.display === "none" ? foodInfo.style.display = "block" : foodInfo.style.display = "none";
}
This function encompasses toggle functionality and is assigned to a button. Once the button is clicked, the function runs to see if the paragraph is set to 'none'. If it is, then it switches to 'block' and vice versa. As you can see, my function is not accepting any parameters so I am finding it difficult to use test cases. How exactly would I go about testing this type of code using Jasmine.
describe('foodInfoToggle1', () => {
let element;
beforeEach(() => {
element = document.createElement('span');
spyOn(document, 'querySelector').and.callFake(selector => {
if (selector === '#first') return element;
});
});
it('Should change the display from none to block', () => {
element.style.display = 'none';
foodInfoToggle1();
expect(element.style.display).toEqual('block');
});
it('Should change the display from block to none', () => {
element.style.display = 'block';
foodInfoToggle1();
expect(element.style.display).toEqual('none');
});
});
I didn't find a quick way to include jasmine in the post, so hopefully all my syntax is correct.
The idea here being for each test, we create a dummy element and mockout the querySelector method. If it is called with the expected selector, return the dummy element.
Then for each of our tests, we put the style display in our expected initial value. We then call the method, and verify that the style display changed.

How can I convert scrollIntoView with smooth animation to a Promise?

I have to scrollIntoView a particular element smoothly and then do something.
Example
element.scrollIntoView({behavior: 'smooth'}).then(() => {
// Do something here
})
I know that it can't be done this way as native scrollIntoView doesn't return a Promise. But, how do I achieve something like this?
I'm using Angular 7 BTW. So if there are any directives that could help me achieve this, it would be great.
You can work with prototypes, I think this could be a solution to your problem without download any npm packages
/* Extends Element Objects with a function named scrollIntoViewPromise
* options: the normal scrollIntoView options without any changes
*/
Element.prototype.scrollIntoViewPromise = function(options){
// "this" refers to the current element (el.scrollIntoViewPromise(options): this = el)
this.scrollIntoView(options);
// I create a variable that can be read inside the returned object ({ then: f() }) to expose the current element
let parent = this;
// I return an object with just a property inside called then
// then contains a function which accept a function as parameter that will be execute when the scroll ends
return {
then: function(x){
// Check out https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API for more informations
const intersectionObserver = new IntersectionObserver((entries) => {
let [entry] = entries;
// When the scroll ends (when our element is inside the screen)
if (entry.isIntersecting) {
// Execute the function into then parameter and stop observing the html element
setTimeout(() => {x(); intersectionObserver.unobserve(parent)}, 100)
}
});
// I start to observe the element where I scrolled
intersectionObserver.observe(parent);
}
};
}
element.scrollIntoViewPromise({behavior: "smooth"}).then(()=>console.log("EHI!"));
I've created an example. I know it's not an angular application, but it's a good starting point. You just need to implement it (If you're using typescript you have to create an interface which extends Element with the new function).
One way you can solve this is by using smooth-scroll-into-view-if-nedded it actually return a promise so you can bind to it and apply your logic.
There is an idea how you may catch animation ending.
You may do it in vanilla JS with a 'scroll' event listener.
Check this example https://codepen.io/Floky87/pen/BEOYvN
var hiddenElement = document.getElementById("box");
var btn = document.querySelector(".btn");
var isScrolling;
function handleScroll(event) {
// Clear our timeout throughout the scroll
window.clearTimeout(isScrolling);
// Set a timeout to run after scrolling ends
isScrolling = setTimeout(function() {
alert(1);
document.removeEventListener("scroll", handleScroll);
}, 66);
}
function handleButtonClick() {
document.addEventListener("scroll", handleScroll, false);
hiddenElement.scrollIntoView({ block: "center", behavior: "smooth" });
}
btn.addEventListener("click", handleButtonClick);
I made it like this
const scrollIntoViewPromise = async (node: HTMLElement, options?: ScrollIntoViewOptions) => {
node.scrollIntoView(options);
return new Promise((resolve) => {
const intersectionObserver = new IntersectionObserver((entries) => {
const [entry] = entries;
if (entry.isIntersecting) {
setTimeout(() => {
resolve(true);
intersectionObserver.unobserve(node);
}, 100);
}
});
intersectionObserver.observe(node);
});
};

In cypress.io, how can I count a number of elements without failing the test if there are none?

Using cypress.io I can get a list of HTML elements matching a given CSS selector like so:
cypress.get(".some-class")
If no elements having the class 'some-class' are found on the page, the test fails. This is by design.
I would like to try to get a list of HTML elements as above, but not to fail the test if the number of elements is 0.
How can I achieve this using cypress.io?
You can use the length assertion
cypress.get('.some-class').should('not.have.length', 0);
You can do it following way.This will keep your test alive.
describe('test check element', function () {
it('testSelector reload', function () {
cy.visit('https://docs.cypress.io/api/utilities/$.html#Usage')
let found = false
let count=0
while (!found) {
const nonExistent = Cypress.$('.fake-selector')
if (!nonExistent.length) {
cy.reload()
found = false
count=count+1
cy.wait(1000)
if(count==5)
{
found = true
cy.log('Element not found after 5 seconds..Exit from loop!!!')
}
} else {
found = true
}
}
})
})

Reuse of functions

I think I know the theory behind the solution but I am having trouble implementing it. Consider following piece of code:
this.selectFirstPassiveService = function () {
getFirstPassiveService().element(by.tagName('input')).click();
}
this.clickAddTask = function () {
getFirstActiveService().element(by.tagName('a')).click();
}
this.selectTask = function () {
getFirstActiveService()
.element(by.tagName('option'))
.$('[value="0"]')
.click();
}
this.saveTask = function () {
getFirstActiveService().element(by.name('taskForm')).submit();
}
getFirstActiveService = function () {
return services.filter(function (elem) {
return elem.getAttribute('class').then(function (attribute) {
return attribute === 'service active ';
});
}).first();
}
getFirstPassiveService = function () {
return services.filter(function (elem) {
return elem.getAttribute('class').then(function (attribute) {
return attribute === 'service passive ';
});
}).first();
}
};
To minimalize code duplication, I created two functions:
* getFirstActiveService()
* getFirstPassiveService()
My spec goes as follows:
it('Select service', function () {
servicePage.selectFirstPassiveService();
servicePage.clickAddTask();
servicenPage.selectTask()();
});
Both clickAddTask() and selectTask() use the function called getFirstActiveService(). Everything runs fine in clickAddTask() but when I use the function in selectTask(), some elements (which are present) cannot be found by protractor.
Here goes my theory, every command in getFirstActiveService() is queued in the control flow when the function is called in clickAddTask() and is then executed. When reusing the function in selectTask() the commands aren't queued, the instance created in clickAddTask() is used and therefore, some elements cannot be found since the DOM has changed since then.
Now first question: Is my theory correct?
Second question: How can I fix this?
Thanks in advance!
Cheers
I refactored it a bit and it works now.
The problem was in the test itself, not in the reuse of functions.

Categories

Resources