CasperJS can't access ReactJS shadow DOM - reactjs

I've been trying to wait and verify for a selector that was generated by reactjs (using shadow dom elements)
However, casperjs seems unable to locate the element I was trying to check. Is this an issue for CasperJS or maybe its engine PhantomJS?
I would like to know more about on how to handle this properly.
Here's some of my code
casper.then(function(){
this.waitForSelector('#someid', function(){
// found
}, function(){
// error - always go here
}, 5000);
});
Thanks in advance

Related

Can't find item in cypress even though it's in DOM

No matter what selectors I use, I keep getting the response "Expected to find element: ... but never found it. The curious thing is that in the DOM I am able to find this element and the selector for it (I used this to find the element
Code I have tried
cy.get("#main-content [title='Main']").click({force:true})
But after running the test, Cypress can't find the item no matter what selector it uses. It's also strange that in the preview of the test I'm not able to select the item but it selects a much larger area of the whole page editor.
Anyone know any other way to approach the problem? I will be very grateful for any help
I tried various types of selectors and tools, including in Selenium Webdriver but the same problem occurred
I highly recommend cypress-iframe package to access your elements within the iframe.
Follow the instructions to install it, then use it in your test like so:
cy.iframe('#mgl-pageeditor')
.find('#main-content [title=Main]')
.click()
It looks like you're trying to reach an element that is inside of an iframe element. With most automation frameworks, you need to handle switching into the iframe. Cypress is no different.
Cypress is able to access elements inside iframes, but you have to be explicit when you do.
I normally grab the iframe element, cy.wrap() it's contents, then use the .within() function to execute commands inside the context of it.
// Basic example
cy.get('iframe').then(iframe => {
cy.wrap(iframe.contents()).within(() => {
cy.get("#main-content [title='Main']").click({force:true});
})
})
You can make it easier by turning it into a custom command that handles the cy.wrap() and .within() for you.
// Typescript example of custom .switchToIframe()
Cypress.Commands.add('switchToIframe', { prevSubject: true }, (prevSubject: JQuery<HTMLIFrameElement>, callback: () => void): void => {
cy.wrap(prevSubject.contents(), { log: false }).within(callback);
});
// Test file
it('iframe test', () => {
cy.get('iframe').switchToIframe(() => {
cy.get("#main-content [title='Main']").click({force:true});
})
})
Working with iframes in Cypress: https://www.cypress.io/blog/2020/02/12/working-with-iframes-in-cypress/

Can't find element using CSS Selector or xPath in AngularJS

I'm trying to make an automation test using Protractor, but I can't click on any element I want to... I'm very new to Angular and I'm just learning Protractor. Here is the element I'm trying to click:
<div _ngcontent-bhx-c21="" class="mg-country" title="CAN"><div _ngcontent-bhx-c21="" class="country-image CAN"></div><span _ngcontent-bhx-c21="" class="country-text">CAN</span></div>
This is the error I'm getting, testing on Chrome with Mac OS:
Failed: script timeout
(Session info: chrome=93.0.4577.82)
(Driver info: chromedriver=93.0.4577.63 (ff5c0da2ec0adeaed5550e6c7e98417dac77d98a-refs/branch-heads/4577#{#1135}),platform=Mac OS X 10.14.6 x86_64)
This is one thing I've tried:
describe('login page', function () {
it('should select a location', function () {
browser.get('https://app.io');
element(by.css('[title="CAN"]')).click();
browser.sleep(2000);
});
});
I've also tried replacing the '[title="CAN"]' with the xPath, (which I've read should not be used in Angular but it didn't work anyway), and the CSS Selector path.
I have 3 other country option buttons, and the only difference between them all is the title, so I'm thinking that needs to be my identifier, but I just can't get it to work.
When the app initially loads, it attempts to go to the url specified, but it's soon redirected to a different page which is the page that contains the element I'm trying to locate. I've also tried initially loading the page it gets redirected to and that also didn't work.
try the following:
element(by.css('[title=CAN]')).click();
or
elements(by.css('.mg-country')).get(0).click();

Google maps not always fully rendering in Ionic

Having trouble with always rendering google maps in my Ionic app. When I first land on a view from a list of items on the previous view, the map always renders in its complete state. However, if I go back to the previous view and tap a different business, or even the same one, it appears as if the map is only rendering 25% of the complete map. I'm having this issue on both the emulator and on my iPhone.
Example
Code
getData.getBusinesses()
.then(function(data) {
// get businesses data from getData factory
})
.then(function(data) {
// get businesses photo from getData factory
})
.then(function(data) {
// get some other business stuff
})
.then(function() {
// get reviews for current business from separate async call in reviews factory
})
.then(function() {
// instantiate our map
var map = new GoogleMap($scope.business.name, $scope.business.addr1, $scope.business.city, $scope.business.state, $scope.business.zip, $scope.business.lat, $scope.business.long);
map.initialize();
})
.then(function() {
// okay, hide loading icon and show view now
},
function(err) {
// log an error if something goes wrong
});
What doesn't make sense to me is that I'm using this exact code for a website equivalent of the app, yet the maps fully load in the browser every time. The maps also fully load when I do an ionic serve and test the app in Chrome. I did also try returning the map and initializing it in a following promise, but to no avail.
I've also tried using angular google maps, but the same issue is occurring. I think I might want to refactor my gmaps.js (where I'm creating the Google Maps function) into a directive, but I don't know if that will actually fix anything (seeing as angular google maps had the same rendering issue).
I don't think the full code is necessary, but if you need to see more let me know.
EDIT
It seems that wrapping my map call in a setTimeout for 100ms always renders the map now. So I guess the new question is, what's the angular way of doing this?
I'm seeing similar issues with ng-map in Ionic. I have a map inside of a tab view and upon switching tabs away from the map view and back again, I would often see the poorly rendered and greyed out map as you describe above. Two things that I did that may help fix your issue:
Try using $state.go('yourStateHere', {}, {reload: true}); to get back to your view. The reload: true seemed to help re-render the map properly when the map was within the tab's template.
After wrapping the map in my own directive, I found the same thing happening again and wasn't able to fix it with the first suggestion. To fix it this time, I started with #Fernando's suggestion and added his suggested $ionicView.enter event to my directive's controller. When that didn't work, I instead added a simple ng-if="vm.displayMap" directive to the <ng-map> directive and added the following code to add it to the DOM on controller activation and remove it from the DOM right before leaving the view.
function controller($scope) {
vm.displayMap = true;
$scope.$on('$ionicView.beforeLeave', function(){
vm.displayMap = false;
});
}
Hope that helps.
don't use setTimeout on this!
You need to understand that the map is conflicting with the container size or something (example: map is loading while ionic animation is running, like swiping).
Once you understand this, you need to set map after view is completely rendered.
Try this on your controller:
$scope.$on('$ionicView.enter', function(){
var map = new GoogleMap($scope.business.name,
$scope.business.addr1, $scope.business.city,
$scope.business.state, $scope.business.zip,
$scope.business.lat, $scope.business.long);
map.initialize();
});

Protractor + chrome driver: Element is not clickable at point

Hi I am having some trouble getting a basic protractor test to work.
My setup:
I use requirejs so I init angular using angular.bootstrap(), not the ng-app attr. According to protractor docs this is not supported out of the box, but seems to work fine for tests that don' involve clicking.
Protractor conf.json:
"use strict";
exports.config = {
specs: '../E2ETests/**/*.js',
chromeOnly: true,
getPageTimeout: 30000,
allScriptsTimeout: 30000
}
I use some third party jquery plugs which I wrap in directives, I suspect these might be part of the issue.
The test:
"use strict";
describe('When clicking should add stuff', function () {
var ptor;
beforeEach(function () {
browser.get('https://localhost/myApp');
ptor = protractor.getInstance();
});
it('add stuff', function () {
// If I comment this, the test pass.
element(by.id('add-stuff-button')).click();
// This does not matter fails on the line above..
expect(browser.getTitle()).toBeDefined();
});
});
The error:
UnknownError: unknown error: Element is not clickable at point (720, 881). Other element would receive the click: <div class="col-md-5 col-md-offset-5">...</div>
(Session info: chrome=37.0.2062.124)
(Driver info: chromedriver=2.10.267521,platform=Windows NT 6.1 SP1 x86_64)
Thoughts
The chromedriver do find the button, because if I change the id it complains that no element is found. So I think the problem is that the button moves from its initial position. As the element(***) function should wait for angular to be done, I suspect that its the third party plugins that might interfere as they might not use angular api's fetching data etc. So angular think its done but then the third party plug populates and moves stuff around.
Any ideas what to do?
If the third party plugs is the problem, can I somehow tell angular that third party stuff is going on and then later tell it when its done?
Thx
Br
Twd
You should set window size in your config file
onPrepare: function() {
browser.manage().window().setSize(1600, 1000);
}
Following worked fine for me:
browser.actions().mouseMove(element).click();
Edit: If above does not work try chaining perform() method too(I got this as an edit suggestion, I have not tested it but somebody could verify it and comment)
browser.actions().mouseMove(element).click().perform();
This happens if the chrome window is too small, try to add inside the beforeEach
browser.driver.manage().window().setSize(1280, 1024);
Or simply use the Actions class:
browser.actions().mouseMove(elem).click().perform();
Had the same issue but was not related to the window size but had to wait for ngAnimation to end.
So I had to wait until the element was clickable with.
const msg = 'Waiting for animation timeout after 1s';
const EC = new protractor.ProtractorExpectedConditions();
await browser.wait(EC.elementToBeClickable(model.elements.button.checkCompliance), 1000, `${msg} panel`);
await model.elements.button.checkCompliance.click();
#note - I am using async/await node 8 feature, you could just as well convert this to regular Promises.
Also using ProtractorExpectedConditions instead of ExpectedConditions see documentation
Maybe It is not applicable in your case, but I've encountered the same problem and based on Milena's answer I've been looking for another element obscuring my button (in my case, a dropdown menu in the top right of my screen).
It appears to be the Connected to Browser Sync notification message sent by browsersync, launched by Gulp. The message vanished after a short time, but after my onClick() call.
To remove the notification, in my gulpfile, I've added the notify: false param when initializing browsersync:
browserSync.init(files, {
server: {
baseDir: "dist",
index: "index.html"
},
notify: false
});
I fix this problem by using browser time sleep.
browser.driver.sleep(3000)
before giving click button
You can define the desired screen resolution through your protractor configuration file (e.g. protractor.conf.js or config.js) for consistent test behavior.
For example with Chrome browser:
exports.config = {
specs: [
// ...
],
capabilities: {
browserName: 'chrome',
chromeOptions: {
args: [
'--window-size=1600,900',
'--headless'
]
}
}
// ...
}
Explanations
window-size argument will launch Chrome with a 1600 by 900 window.
headless will launch headless Chrome, allowing you to have your tests run with the specified window size (1600 by 900) even if your screen resolution is lower than that.
You may want to have two configurations, one for developers (without headless mode) who always have a high resolution screen and one for build servers (headless mode) where screen resolution is sometimes a mystery and could be lower than what your application / test is designed for. Protractor configuration file are javascript and can be extended to avoid code duplication.
I had the same error and purely adjusting the screen size did not fix it for me.
Upon further inspection it looked as though another element was obscuring the button, hence the Selenium test failed because the button was not (and could not be) clicked. Perhaps that's why adjusting the screen size fixes it for some?
What fixed mine was removing the other element (and later adjusting the positioning of it).
This works better than specifying the window size, in case you test need to run on multiple displays.
browser.manage().window().maximize();
Other way, you can try this:
this.setScrollPage = function (element) {
function execScroll() {
return browser.executeScript('arguments[0].scrollIntoView()',
element.getWebElement())
}
browser.wait(execScroll, 5000);
element.click();
};
You could also try turning off any debug tools you might be using. I was using Laravel and debugbar and had to set APP_DEBUG to false.
From Gal Malgarit's answer,
You should set window size in your config file
onPrepare: function() {
browser.manage().window().setSize(1600, 800);
}
If it still doesn't work you should scroll to the element's location
browser.executeScript('window.scrollTo(720, 881);');
element(by.id('add-stuff-button')).click();
Note that this was sometime caused by a top navigation bar or bottom navigation bar / cookie warning bar covering the element. With angular 2, when clicking it scrolls until the element is only just on page. That means that when scrolling down to click something, if there is a bottom navigation, then this will obstruct the click. Similarly, when scrolling up it can be covered by the top navigation.
For now, to get around the scrolling up, I am using the following:
browser.refresh();
browser.driver.sleep(3000);
I made sure that I removed the bottom bar by clicking to close it before the test started.
That means the element is not within the visible area. There are several ways to handle this:
Force click the element regardless visibility
await browser.executeScript('arguments[0].click();', $element.getWebElement());
Scroll to the element and then click
await browser.executeScript(`arguments[0].scrollIntoView({block: "center"});`, $element.getWebElement());
await $element.click()
Maximize the working area of browser's window before tests
beforeAll(async () => await browser.driver
.manage()
.window()
.setSize(1920, 1080)
);

"Failed to execute 'postMessage'" error from Froogaloop via AngularJS directive

I wrote an AngularJS directive for vimeo videos with built in play/pause functionality using their froogaloop library.
It's works great! The only issue is that I get the following error when the page first loads.
Failed to execute 'postMessage' on 'DOMWindow': The target origin provided ('http://player.vimeo.com') does not match the recipient
window's origin
Am I initializing the froogaloop object wrong in the directive?
Any suggestions would be most appreciated.
You can check it out the plunker here: http://plnkr.co/edit/GKWNk3LhX0MR3lhpfqyA
I recommend to execute the code in the onLoad event from <iframe>. Then you are ensured that the code will execute when iframe is ready for receiving messages.
There are plenty ways to do it:
You can use jQuery if you already have it in your project: $('iframe').load(callback) or
write an EventListener: iframe_element.addEventListener('load', callback) or
use plain onload callback: iframe_element.onload = callback.
Where callback is the method which uses Froogaloop.
But you have to know that some of those solutions might have some drawbacks on some old/MS browsers browsers.
For me it looks like angularjs triggers Player API before actually rendering the iframe on the page. At least if I postpone scope.$watch it works fine:
$timeout(function() {
scope.$watch('controlBoolean', function() {/* your code goes here */});
});

Resources