Cannot find button element, but can find sibling elements - javascript

Html is here (note - this part is nested in an iframe):
<div data-reactroot="">
<div class="src-scenes-authenticated-components-sign-in-form-styles__main--I0vWu">
<div class="src-scenes-authenticated-components-sign-in-form-styles__featureList--2RQdY"></div>
<button class="src-components-button-styles__defaultButtonTheme--2-m2n src-components-button-styles__button--2hZHd">
<div class="src-components-button-styles__content--28F4J">
<div class="src-scenes-authenticated-components-sign-in-form-styles__signInButton--3AQAr">Sign In</div>
</div>
</button>
<div class="src-scenes-authenticated-components-sign-in-form-styles__footer--1kMhz"></div>
</div>
</div>
Here is how I've tried targeting the <button> element:
.elementByXPath('/html/body/div[2]/div/div') // parent div of button
.elementByXPath('/html/body/div[2]/div/div/div[1]') // first child
.elementByXPath('/html/body/div[2]/div/div/div[2]') // sibling AFTER button
.elementByXPath('/html/body/div[2]/div/div/button') // error - NoSuchElement
I've tried finding that button using child::button, and following-sibling::button...still no luck. I've also tried finding the <div> elements nested within the button. Still can't find it. I have also tried finding the button element after finding the first child (since it is the second child), still get the same error (NoSuchElement).
Another thing I tried was .moveTo()...which does work, but when I click on it nothing happens.
What am I missing?
Full test code here:
import wd, { asserters } from 'wd';
import configureLogging from './helpers/logging';
import { setup, config } from './helpers/config.js';
const {
capabilities,
testTimeout } = config;
export default function owaChromeLogin() {
describe('Login flow via OWA for Chrome', function() {
this.timeout(testTimeout);
let driver;
setup();
before(function() {
driver = wd.promiseChainRemote();
configureLogging(driver);
let desired = {
...capabilities.owa.chrome
};
return driver.init(desired);
});
after(function() {
return driver;
.quit();
});
// this is all to log into OWA AND the plugin
it('should go to OWA and log in', function() {
return driver
.get('https://outlook.office365.com/owa/')
.elementById('i0116') // username
.type('****#****.onmicrosoft.com')
.elementById('idSIButton9') // 'next' button
.click()
.waitForElementById('i0118', asserters.isDisplayed, 10000, 500) // password field...flaky - StaleElementReference
.type('****')
.elementById('idSIButton9') // 'sign in' button
.click()
.elementById('idSIButton9') // 'yes' button to stay signed in
.click()
.waitForElementByXPath('//*[#id="primaryContainer"]/div[4]/div/div[1]/div/div[4]/div[1]/div/div[1]/div/div/div[2]/div/button', asserters.isDisplayed, 5000, 300)
.should.eventually.exist;
});
it('should click New Email', function() {
return driver
.elementByXPath('//*[#id="primaryContainer"]/div[4]/div/div[1]/div/div[4]/div[1]/div/div[1]/div/div/div[1]/div/button[1]')
.click()
.waitForElementByXPath('//*[#id="primaryContainer"]/div[4]/div/div[1]/div/div[4]/div[3]/div/div[5]/div[1]/div/div[3]/div[4]/div/div[1]/div[2]/div[3]/div[2]/div[1]/button[1]', asserters.isDisplayed, 5000, 300)
.should.eventually.exist;
});
it('should be able to open the Outreach plugin', function() {
return driver
// wait for AND click on Outreach logo button (bottom RH)
.waitForElementByXPath('//*[#id="primaryContainer"]/div[4]/div/div[1]/div/div[4]/div[3]/div/div[5]/div[1]/div/div[3]/div[4]/div/div[1]/div[2]/div[3]/div[2]/div[2]/div/div/div/div/div[1]/div/div/button', asserters.isDisplayed, 5000, 300)
.click()
.waitForElementByXPath('/html/body/div[12]/div/div/div/div/div/div/ul/li[5]', asserters.isDisplayed, 5000, 300) // "Open Outreach"
.click()
// need to target items within iframe
.waitForElementById('exttsp0', asserters.isDisplayed, 10000, 300) // iframe (parent)
.elementByXPath('/html/body/div[2]/div/div') // parent div of button
.elementByXPath('/html/body/div[2]/div/div/div[1]') // first child
.elementByXPath('/html/body/div[2]/div/div/button') // error - NoSuchElement
});
});
}

resolved! After watching my auto test run for the hundredth time, I realized the output of the .waitForElementByXPath('xpath of parent element') was providing several elements instead of just one. This was occurring before the iframe it exists in is loaded. So, I needed to add a time delay (sleep) before trying to target the iframe - .frame(0)...because its all in the first iframe for me), then I was able to target the button by id. I know using .sleep() isn't ideal, but I plan on circling back to that later.
Overall, here is what the test methods look like:
wd.addPromiseChainMethod('getSignInButton', function() {
return this
.sleep(3000)
.frame(0)
.waitForElementById('parentDiv', asserters.isDisplayed, 10000, 300)
.elementById('signInButon')
.click()
});

Related

Hide Load More button on InfiniteScroll.js when hitting last page

I'm looking to hide my "Load More" button when I'm on the last page, but it seems to be refusing to hide the button when scrolling to the last page.
I am using infinitescroll.js
I have the current script to infinite scroll X pages, then show a load more button. Though I think this is causing conflict with checking if it's the last page. I've tried numerous ways, but I can't seem to make it work with both.
I have the following script code.
<script>
let $container = $('.article-feed').infiniteScroll({
path: '.nav-next a',
append: '.card',
history: 'push',
button: '.view-more-button',
hideNav: '.pagination',
status: '.page-load-status'
});
let $viewMoreButton = $('.view-more-button');
// get Infinite Scroll instance
let infScroll = $container.data('infiniteScroll');
$container.on( 'load.infiniteScroll', onPageLoad );
$container.on( 'last.infiniteScroll', onPageLoad, function( event, body, path ) {
console.log(`Last page hit on ${path}`);
$viewMoreButton.attr("style", "display: none !important");
});
function onPageLoad() {
if ( infScroll.loadCount == 1 ) {
// after 2nd page loaded
// disable loading on scroll
$container.infiniteScroll( 'option', {
loadOnScroll: false,
});
// show button
$viewMoreButton.show();
// remove event listener
$container.off( 'load.infiniteScroll', onPageLoad );
}
}
</script>
Any help with this would be great. The console.log reports that it has hit the last page properly, but the button does not hide itself.

$ionicposition only setting correct values on a modal the first time a modal is opened

I have created a custom select directive and within this directive when you tap into the options it should use $ionicposition to locate the selected option based on the HTML element ID, and then scroll to it using $ionicscroll delegate.
This is the function which locates the option, and then scrolls to it:
private scrollToOption(selectedCode: activeh.objects.ICode): void {
this.$timeout(() => {
let item: HTMLElement = document.getElementById("code-" + selectedCode.CodeID);
if(item){
var itemPosition = this.$ionicPosition.offset(angular.element(item));
this.$ionicScrollDelegate.$getByHandle('modalContent').scrollTo(0, itemPosition.top + 40, false);
}
}, 200);
}
This is where the scrollTo function is called:
private createModal(): void {
this.$ionicModal.fromTemplateUrl('app/shared/directives/selectoption/selectoption.modal.html', {
scope: this.$scope,
hardwareBackButtonClose: false
}).then((modal) => {
this.selectModal = modal;
this.selectModal.show();
if (this.selectedVal !== undefined) {
this.scrollToOption(this.selectedVal);
}
});
}
So like mentioned in the title, this code works perfectly but only the first time that the modal is opened. After the modal has been closed and opened again the $ionicposition.offset is returning values of only 0.
I found a (probably partial) solution to this, wherein instead of using .hide() to hide the modal I use .remove() to completely remove it forcing a complete rebuild.
This mimics the behaviour where it worked the first time as now every time the modal is opened is the 'first' time.

Bootstrap-confirmation not respecting options

Just adding the bootstrap-confirmation extension for Bootstrap popover to some buttons on a project. I'm having issues with the options not being respected. I'm trying to get the popups to work as singletons and dismiss when the user clicks outside of them singleton and data-popout options, respectively - both set to true. I'm also not seeing any of my defined callback behavior happening.
I defined the options both in the HTML tags and in a function and neither works. Still getting multiple boxes and they don't dismiss as expected.
My JS is loaded after all other libraries and is in my custom.js file in my footer.
JS is as follows:
$(function() {
$('body').confirmation({
selector: '[data-toggle="confirmation"]',
singleton: true,
popout: true
});
$('.confirmation-callback').confirmation({
onConfirm: function() { alert('confirm') },
onCancel: function() { alert('cancel') }
});
});
An example of the box implemented on a button in my HTML is the following:
<a class="btn btn-danger" data-toggle="confirmation" data-singleton="true" data-popout="true"><em class="fa fa-trash"></em></a>
Any pointers would be appreciated. I even changed the default options in the bootstrap-confirmation.js file itself to what I want and still no luck.
Turns out I needed to rearrange a couple things to get this to work. I've left in the last_clicked_id etc stuff as I needed to add that to get the id value of what I'd just clicked.
// Product removal popup logic
var last_clicked_id = null;
var last_clicked_product = null;
$('.btn.btn-danger.btn-confirm').click(function () {
last_clicked_id = $(this).data("id");
last_clicked_product = $(this).data("product");
});
$('.btn.btn-danger.btn-confirm').confirmation({
singleton: true,
popout: true,
onConfirm: function () {
alert("DEBUG: Delete confirmed for id : " + last_clicked_product);
// TODO: Add AJAX to wipe entry and refresh page
},
onCancel: function () {
alert("DEBUG: Delete canceled for id : " + last_clicked_product);
}
});
I was a step ahead of myself with the callback logic which was not getting executed. Fixed by simply adding it to onConfirm: and onCancel: key values in the .confirmation() function. A bit of a RTFM moment there but this was unfortunately not very clear in the documentation.

Difficulty attaching event handler to dynamically generated modal window elements

This question is an ongoing learning / discovery of these three questions. This issue for me started here:
First Post
Second Post
Now this post is regarding #StephenMuecke post about attaching the event handler dynamically. This was new to me so I had to read up but now I see that it does make sense.
Well after reading documentation and numerous SO posts I still can't seem to get the click event handler to fire??
This time I decided to take a different approach. I created a jsfiddle demonstrating the problem. http://jsfiddle.net/ramjet/93nqs040/17/
However the jsfiddle I had to change somewhat from reality to get it to work within their framework. Below is the actual code.
Parent Window script that launches modal...the alert Bound does fire.
<script>
$(document).ready(function ()
{
$("#new").click(function (e)
{
e.preventDefault();
var ischanging = false;
var financierid = 0;
var detailsWindow = $("#window").data("kendoWindow");
if (!detailsWindow)
{
// create a new window, if there is none on the page
detailsWindow = $("#window")
// set its content to 'loading...' until the partial is loaded
.html("Loading...")
.kendoWindow(
{
modal: true,
width: "800px",
height: "400px",
title: "#T("...")",
actions: ["Close"],
content:
{
url: "#Html.Raw(Url.Action("ActionName", "Controller"))",
data: { financierId: financierid, isChanging: ischanging }
}
})
.data("kendoWindow").bind('refresh', function (e)
{
alert('Bound');
$('document').on("click", "#save", function () { alert("i work");});
}).center();
}
detailsWindow.open();
});
</script>
The modal full html I didn't think was needed but if it is I will update it. This is just the element I am trying to dynamically bind to.
<input type="button" id="save" style="margin-right:10px;" value="Save Record" />
document doesn't need quotes:
$(document).on("click", "#save", function () { alert("i work");});
"document" searches for an element of document, not the actual document
$("document").length; //0
$(document).length; //1

Redefining a jQuery dialog button

In our application we use a general function to create jQuery dialogs which contain module-specific content. The custom dialog consists of 3 buttons (Cancel, Save, Apply). Apply does the same as Save but also closes the dialog.
Many modules are still using a custom post instead of an ajax-post. For this reason I'm looking to overwrite/redefine the buttons which are on a specific dialog.
So far I've got the buttons, but I'm unable to do something with them. Is it possible to get the buttons from a dialog (yes, I know) but apply a different function to them?
My code so far:
function OverrideDialogButtonCallbacks(sDialogInstance) {
oButtons = $( '#dialog' ).dialog( 'option', 'buttons' );
console.log(oButtons); // logs the buttons correctly
if(sDialogInstance == 'TestInstance') {
oButtons.Save = function() {
alert('A new callback has been assigned.');
// code for ajax-post will come here.
}
}
}
$('#dialog').dialog({
'buttons' : {
'Save' : {
id:"btn-save", // provide the id, if you want to apply a callback based on id selector
click: function() {
//
},
},
}
});
Did you try this? to override button's callback based on the need.
No need to re-assign at all. Try this.
function OverrideDialogButtonCallbacks(dialogSelector) {
var button = $(dialogSelector + " ~ .ui-dialog-buttonpane")
.find("button:contains('Save')");
button.unbind("click").on("click", function() {
alert("save overriden!");
});
}
Call it like OverrideDialogButtonCallbacks("#dialog");
Working fiddle: http://jsfiddle.net/codovations/yzfVT/
You can get the buttons using $(..).dialog('option', 'buttons'). This returns an array of objects that you can then rewire by searching through them and adjusting the click event:
// Rewire the callback for the first button
var buttons = $('#dialog').dialog('option', 'buttons');
buttons[0].click = function() { alert('Click rewired!'); };
See this fiddle for an example: http://jsfiddle.net/z4TTH/2/
If necessary, you can check the text of the button using button[i].text.
UPDATE:
The buttons option can be one of two forms, one is an array as described above, the other is an object where each property is the name of the button. To rewire the click event in this instance it's necessary to update the buttons option in the dialog:
// Rewire the callback for the OK button
var buttons = $('#dialog').dialog('option', 'buttons');
buttons.Ok = function() { alert('Click rewired!'); };
$('#dialog').dialog('option', 'buttons', buttons);
See this fiddle: http://jsfiddle.net/z4TTH/3/
Can you try binding your new function code with Click event of Save?
if(sDialogInstance == 'TestInstance') {
$('#'+savebtn_id).click(function() {
alert('A new callback has been assigned.');
// code for ajax-post will come here.
});
}

Categories