I'm using WebdriverIO to test some basic functionality on an Ionic (+ Angular) application. The framework setup works all right, but I can't find a way to click on certain HTML elements. For instance, this is some HTML sample:
<div class="tabbar show-tabbar" role="tablist" style="top: 166px; display: flex;">
<a class="tab-button has-title has-title-only" href="#" role="tab" id="tab-t0-0" aria-controls="tabpanel-t0-0"
aria-selected="true">
<span class="tab-button-text">Blah</span>
<div class="button-effect"></div>
</a>
<a class="tab-button has-title has-title-only" href="#" role="tab" id="tab-t0-1" aria-controls="tabpanel-t0-1"
aria-selected="false">
<span class="tab-button-text">Foo</span>
<div class="button-effect"
style="transform: translate3d(83px, -103px, 0px) scale(1); height: 240px; width: 240px; opacity: 0; transition: transform 395ms ease 0s, opacity 277ms ease 118ms;"></div>
</a>
<a class="tab-button has-title has-title-only" href="#" role="tab" id="tab-t0-2" aria-controls="tabpanel-t0-2"
aria-selected="false">
<span class="tab-button-text">Bar</span>
<div class="button-effect"
style="transform: translate3d(3px, -99px, 0px) scale(1); height: 240px; width: 240px; opacity: 0; transition: transform 395ms ease 0s, opacity 277ms ease 118ms;"></div>
</a>
<div class="tab-highlight animate" style="transform: translate3d(570px, 0px, 0px) scaleX(285);"></div>
</div>
And this is a super-simple test case that I'm doing to test the functionality:
import { expect } from "chai";
describe("Some Test", () => {
const logingUrl: string = "url0";
const appUrl: string = "url1";
it("Some Test Again", () => {
browser.url(logingUrl);
browser.url(appUrl);
const tab = $("#tab-t0-2");
tab.click();
expect(tab.getAttribute("aria-selected")).to.equal("true");
});
});
..but every time I run it, I get some weird error message that the element is no clickable at some point. Any clues?
[0-0] element click intercepted in "Some Test Again"
element click intercepted: Element <a class="tab-button has-title has-title-only" href="#" role="tab" id="tab-t0-2" aria-controls="tabpanel-t0-2" aria-selected="false">...</a> is not clickable at point (666, 170). Other element would receive the click: <ion-backdrop disable-activated="" role="presentation" tappable="" class="backdrop-no-tappable" style="opacity: 0.5;"></ion-backdrop>
(Session info: headless chrome=75.0.3770.100)
This is one of those classic Angular/Ionic backdrop gotcha's.
Let's start with the error message: element #tab-t0-2 is not clickable at point (coordinates), another element would receive the click: ion-backdrop.
This tells us that your targeted element cannot be clicked as the ion-backdrop tag is rendered on top of it. The ion-backdrop component is a short animation (usually used for modals), in this case, the semi dimming of the background (opacity: 0.5).
✖ Solution 1: Easy way to counter it? Explicitly expect for it to disappear, then click the targeted element.
it("Some Test Again", () => {
browser.url(logingUrl);
browser.url(appUrl);
// Explicitly wait for the backdrop animation to disappear:
const backdrop = $('.backdrop-no-tappable');
backdrop.waitForExist(5000, true, 'Backdrop still present');
const tab = $("#tab-t0-2");
tab.click();
expect(tab.getAttribute("aria-selected")).to.equal("true");
});
✖ Solution 2: Another thing you can try is only click on the tab element, once it is fully visible and intractable in the DOM (this is kind of a best-practice):
const tab = $("#tab-t0-2");
tab.waitForDisplayed(5000);
// For 'wdio-v4' users:
// tab.waitForVisible(5000);
tab.click();
expect(tab.getAttribute("aria-selected")).to.equal("true");
Related
I'm using a very simple sample of the material ui Snackbar component, that renders the following HTML
<div class="MuiSnackbar-root-352 MuiSnackbar-anchorOriginBottomLeft-358">
<p class="MuiTypography-root-199 MuiTypography-body2-207 MuiPaper-root-121 MuiPaper-elevation6-129 MuiSnackbarContent-root-373" role="alertdialog" aria-describedby="message-id" direction="up" style="transform: translate(0px, 0px); transition: transform 225ms cubic-bezier(0, 0, 0.2, 1) 0ms;">
<div class="MuiSnackbarContent-message-374">
<span id="message-id">TunisiaNet created!</span>
</div>
</p>
</div>
for the following code
<Snackbar
anchorOrigin={{
vertical: 'bottom',
horizontal: 'left',
}}
open={this.state.entityGroupAdded}
autoHideDuration={4000}
onClose={this.handleClose}
ContentProps={{
'aria-describedby': 'message-id',
}}
message={<span id="message-id">{this.state.name} created!</span>}
/>
This naturally leads to an HTML specification incompatibility. The issue is that I don't know how to tell the Snackbar to create a div instead of a p element
I get the error:
0.chunk.js:101009 Warning: validateDOMNesting(...): <div> cannot appear as a descendant of <p>.
in div (created by SnackbarContent)
in p (created by Typography)
in Typography (created by WithStyles(Typography))
in WithStyles(Typography) (created by Paper)
in Paper (created by WithStyles(Paper))
in WithStyles(Paper) (created by SnackbarContent)
in SnackbarContent (created by WithStyles(SnackbarContent))
Might wanna check your Material UI version or any override settings you might have made which includes typography components. I just tested a snackbar with the exact same code and the p tag (which uses typography, this part is causing the error) does not exist as a p tag but a different classname
<div class="MuiSnackbar-root-185 MuiSnackbar-anchorOriginBottomLeft-191">
<div class="MuiTypography-root-313 MuiTypography-body1-322 MuiPaper-root-20 MuiPaper-elevation6-28 MuiSnackbarContent-root-310" role="alertdialog" aria-describedby="message-id" style="transform: translate(0px, 0px); transition: transform 225ms cubic-bezier(0, 0, 0.2, 1) 0ms;">
<div class="MuiSnackbarContent-message-311">
<span id="message-id">Note archived</span></div>
</div>
For other who is looking for an answer other than something wrong with the overrides, most likely if nothing wrong with your overrides, and even before you check for that. Look into your code if it contain a component that inherit the props of Typography component like DialogContentText and you also have another Typography component or a component that will transfer to <p> tag in HTML as a child of that component.
In the the error above message props will become
<p class="MuiTypography-root-199 MuiTypography-body2-207 MuiPaper-root-121 MuiPaper-elevation6-129 MuiSnackbarContent-root-373" role="alertdialog" aria-describedby="message-id" direction="up" style="transform: translate(0px, 0px); transition: transform 225ms cubic-bezier(0, 0, 0.2, 1) 0ms;">
<div class="MuiSnackbarContent-message-374">
<span id="message-id">TunisiaNet created!</span>
</div>
</p>
I think this is not showing anymore in the new versions of material. But if you want to fix something like that take a look;
This will give errors
const someFunction= (): JSX.Element => {
return (
<DialogTitle>
Some title
</DialogTitle>
<DialogContentText>
<Typography variant='h5'>
Sometext
<Typography />
</DialogContentText>
<TextField
autoComplete='off'
margin='dense'
variant="outlined"
id='name'
label='email'
type='email'
fullWidth
/>
);
};
this will fix it
const someFunction= (): JSX.Element => {
return (
<DialogTitle>
Some title
</DialogTitle>
<DialogContentText variant='h5'>
Sometext
</DialogContentText>
<TextField
autoComplete='off'
margin='dense'
variant="outlined"
id='name'
label='email'
type='email'
fullWidth
/>
);
};
In this case for example you can use the normal props of Typography with DialogContentText.
Also you can user component='div' as prop of Typography, it could fix it, I didn't test
I am running E2E on my angular application using Protractor. How to write script for toggle button.
<span class="bootstrap-switch-handle-on bootstrap-switch-primary" style="width: 41px;">ON</span>
<span class="bootstrap-switch-label" style="width: 41px;"> </span>
<span class="bootstrap-switch-handle-off bootstrap-switch-default" style="width: 41px;">OFF</span>
I can't guess the DOM of your page. Firstly, I'd rather not use xpath. IMHO in protractor by should be lowercase.
I assume that after clicking the element class of span changes.
maybe try sth like that:
const button = element(by.css('span.bootstrap-switch-primary'));
button.getAttribute('class').then((classes) => {
if (classes.indexOf('bootstrap-switch-handle-on') === -1) {
return button.click();
}
}
I am trying to click a button which is displayed/visible/present.
When doing it manually, user was able to click the button.
If the test was executed, you will notice that it is trying to click the button, but nothing is happening.
I also tried to put a very long wait and tried to click it manually during automation.
But, when clicking it, nothing also happens.
I cannot share the site since it is in a proxy.
This is the HTML of the button and it looks normal:
<a class="x-btn x-unselectable x-box-item x-toolbar-item x-btn-default-small" style="min-width: 75px; right: auto; left: 328px; top: 0px; margin: 0px;" hidefocus="on" unselectable="on" role="button" aria-hidden="false" aria-disabled="false" id="button-1011" tabindex="-1" data-componentid="button-1011">
<span id="button-1011-btnWrap" data-ref="btnWrap" role="presentation" unselectable="on" style="" class="x-btn-wrap x-btn-wrap-default-small ">
<span id="button-1011-btnEl" data-ref="btnEl" role="presentation" unselectable="on" style="" class="x-btn-button x-btn-button-default-small x-btn-text x-btn-button-center ">
<span id="button-1011-btnIconEl" data-ref="btnIconEl" role="presentation" unselectable="on" class="x-btn-icon-el x-btn-icon-el-default-small " style=""></span>
<span id="button-1011-btnInnerEl" data-ref="btnInnerEl" unselectable="on" class="x-btn-inner x-btn-inner-default-small">Save</span>
</span>
</span>
</a>
Code:
global.elmCBSave = element.all(by.cssContainingText('span', 'Save')).last();
it('should click the Save button.', function() {
global.elmCBSave.click();
});
I also tried using browser.executeSrcipt, this is working when executed thru console:
browser.executeScript('$(".x-btn-inner.x-btn-inner-default-small:eq(3)").click()')
There are a few things you can try.
Be sure to pass done into your callback and handle the returned promise from the click event
it('should click the Save button.', function(done) {
global.elmCBSave.click().then(function(){
done();
});
});
Also, I tend to put an expected condition to wait for element to be clickable: http://www.protractortest.org/#/api?view=ProtractorExpectedConditions.prototype.elementToBeClickable
var EC = protractor.ExpectedConditions;
// Waits for the element with id 'abc' to be clickable.
it('should click the Save button.', function(done) {
browser.wait(EC.elementToBeClickable($('#abc')), 5000).then(function(){
global.elmCBSave.click().then(function(){
done();
});
});
});
I have a problem with programming jQuery and I hope that you can help me even if a speak english like an alien %).
I'm doing a site with horizontal scrolling, you can see it here http://www.kinetics.ru/index_kin.html
I have a menu at the left and two buttons PREV and NEXT at the bottom.
Menu is working correctly, but I need to let the bottom buttons work.
By clicking for example on NEXT I need that code made a click on next menu btn after the one that have style 'on'.
The code is:
<div class="menu">
<ul class="nav">
<li id="main"><img src="img/menu/about.png" /></li>
<li><img src="img/menu/uslugi.png"/></li>
<li><img src="img/menu/portfolio.png"/></li>
<li><img src="img/menu/clients.png"/></li>
<li><img src="img/menu/contacts.png"/></li>
</ul>
</div>
...
<div class="bottomNav" style="position: absolute; z-index: 11">
<div style="height: 26px; width:98px; margin:-75px 0 0 500px; position: fixed" class="back"><img src="img/back.png"</div>
<div style="height: 26px; width:114px; margin:-25px 0 0 600px; position: fixed" id="next"><img src="img/forward.png"</div>
And my not working jQuery code for next btn:
$('bottomNav, #next img').click(function(){
if ($('ul.nav').find('img').hasClass('on')){
$('ul.nav').next().click();
}
});
Also I've tried to do like this:
$('bottomNav, #next img').click(function(){
$('ul.nav img').click(function(){
if ($(this).hasClass('on')){
$(this).next().click();}
});
});
P.S. Sorry for my noobish question. I just got a task at work to make a site and nobody cares that a designer is NOT the same as web-designer. I have no possibility to learn first, I have to learn and do at same time.
You have to review jQuery selectors and as well your HTML code, it's terribly wrong. Firebug says:
I modified your code in proper way:
<div class="bottomNav" style="position: absolute; z-index: 11">
<div id="prev" style="height: 26px; width:98px; margin:-75px 0 0 500px; position: fixed">
<img src="img/back.png" />
</div>
<div id="next" style="height: 26px; width:114px; margin:-25px 0 0 600px; position: fixed" >
<img src="img/forward.png" />
</div>
</div>
JavaScript (It's pretty simple and I didn't test it, but it should give you an idea).
$('#prev').click(function () {
var $activeLi = getActiveLi();
var $prevLi = $parentLi.prev();
if ($prevLi.length > 0) {
$prevLi.find('a').click();
}
});
$('#next').click(function () {
var $activeLi = getActiveLi();
var $nextLi = $parentLi.next();
if ($nextLi.length > 0) {
$nextLi.find('a').click();
}
});
function getActiveLi() {
var $activeImg = $('.menu .nav img.on');
var $activeLi = $activeImg.closest('li');
return $activeLi;
};
But beeing seriously: before continuing developing your web site you seriously should put some effort in learnin hot to use jQuery and how to structure HTML...
Solved!
Thank you again!
Your code was really helpfull.
I have modified it a little bit for my needs and also changed in line
var $activeLi = $activeImg.closest('li');
closest('li') to parents('li'), cause it wasn't working like that.
So now code looks like this:
$('#prev').click(function () {
var $prevLi = getActiveLi().prev();
if ($prevLi.length > 0){$prevLi.find('img').click();}
});
$('#next').click(function () {
var $nextLi = getActiveLi().next();
if ($nextLi.length > 0){$nextLi.find('img').click();}
});
function getActiveLi() {
var $activeImg = $('.menu .nav img.on');
var $activeLi = $activeImg.parents('li');
return $activeLi;
};
Also i guess, that i have understood why firebug was showing so big disaster in code... i have forgot to close the tag :)))
Anyway...MANY THANKS TO YOU!
P.S. third day reading a giant book about jQuery :)
I've been trying to resolve an issue for some time with connectToSortable and nested lists. I've created an example at jsfiddle that shows the basic code:
http://jsfiddle.net/ZYSYM/
What I would like to achieve is the ability for a user to drag a "New Item" from the right list into the sub items list. However, the issue is that it adds the item to both the sub and parent lists.
I've read that perhaps the Greedy option may resolve this, however I'm not sure if it's working correctly or I've misinterpreted how it's meant to work. I've googled and searched around but could not find anyone with the exact same issue.
I've also performed some debugging and the greedy option fires the line parentInstance._out.call(parentInstance, event); in jquery-ui, which I think is for this purpose, but it doesn't seem to have any affect.
I'm not an expert in jQuery so any help is greatly appreciated. Perhaps I've missed something simple or there's a better way of doing this?
Thanks and please let me know if you require any further information.
This is little trickier than simple cases:
The Html:
<div style="float: left;">
<ul id="list_1">
<li>Existing 1</li>
<li>Existing 2</li>
<li>
<ul id = "list_2">
<li><dl> <dt>Existing Sub 1</dt></dl></li>
<li>Existing Sub 2</li>
<li>Existing Sub 3</li>
<li>Existing Sub 4</li>
</ul>
</li>
<li>Existing 3</li>
<li>Existing 4</li>
</ul>
</div>
<div style="float: right; margin-top: 30px;">
<ul id="from_list">
<li class="new_item">New Item 1</li>
<li class="new_item">New Item 2</li>
<li class="new_item">New Item 3</li>
<li class="new_item">New Item 4</li>
</ul>
</div>
The CSS:
#list_1 .dropzone {
background-color: #FFFFFF;
border-bottom: 4px solid #FFFFFF;
height: 6px;
}
The Javascript:
var selector = '#list_1, #list_2';
$('#list_1 li').prepend('<div class="dropzone"></div>');
$('#list_1 li, #from_list li').draggable({
opacity: .8,
handle: ' > dl',
addClasses: false,
helper: 'clone',
zIndex: 100 }
);
$('#list_1 .dropzone').droppable({
accept: '#from_list li',
tolerance: 'pointer',
drop: function(e, ui) {
var li = $(this).parent();
var child = !$(this).hasClass('dropzone');
//If this is our first child, we'll need a ul to drop into.
if (child && li.children('ul').length == 0) {
li.append('<ul/>');
}
//ui.draggable is our reference to the item that's been dragged.
if (child) {
li.children('ul').append(ui.draggable);
}
else {
li.before(ui.draggable);
}
//reset our background colours.
li.find('dl,.dropzone').css({ backgroundColor: '', borderColor: '' });
},
over: function() {
$(this).filter('dl').css({ backgroundColor: '#ccc' });
$(this).filter('.dropzone').css({ borderColor: '#aaa' });
},
out: function() {
$(this).filter('dl').css({ backgroundColor: '' });
$(this).filter('.dropzone').css({ borderColor: '' });
}
});
You can see the result here:
http://jsfiddle.net/F3nck/
Credit due:
I found the solution and adapted to your example from:
http://boagworld.com/technology/creating-a-draggable-sitemap-with-jquery/