Not all automation tests run the same in different browsers - angularjs

I have automation tests that will need to be ran in chrome desktop edition and chrome mobile edition. One of my tests require the desktop edition to click on an element that only appears when hovering, however, the mobile edition does not require hover the elements are always visible. I am having troubles finding a way to tell the test running on ipad to ignore the hover step and just click the button. I could create a method just for ipad and separate spec file but I don't want to waste my time if there is an easy fix.

however, the mobile edition does not require hover the elements are always visible.
We may use that. Basically, if the element is visible, click on it, if not - hover and then click:
elm.isDisplayed().then(function (isDisplayed) {
if (!isDisplayed) {
// hover what you need to hover
}
elm.click();
});
There is also that getCapabilities() function that gives you access to the current capability object. You may have a helper function that would determine whether a hover is needed depending on the current browser the tests are executed in. Let's first write our isMobile sample function and define it on the browser object:
browser.isMobile = function(ver) {
var platformName, version;
return browser.getCapabilities().then(function(s) {
platformName = s.caps_.platformName;
version = s.caps_.version;
return /Android|iOS/.test(platformName);
});
};
Sample helper function:
function hoverClick(elm) {
return browser.isMobile().then(function (isMobile) {
if (!isMobile) {
browser.actions().mouseMove(elm).perform();
}
return elm.click();
});
}
See also: Protractor: accessing capabilities.
You may also extend the browser.actions() and add a custom hoverClick action, see:
Custom browser actions in Protractor

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/

Navigating back from other app breaks click handler

For our company we have a lot of devices rolled out with Mobicontrol Soti. This allows us to lock the device in something called a kiosk mode which disables the use of the homescreen and provides a custom screen that only have a set off apps we can decide.
One of the provided apps is a Ionic app that opens links in a browser (Soti Surf) but this gives 2 problems.
code
HTML:
<div (click)="$ctrl.doTheThing()"> something </div>
JS:
private doTheThing() {
this.inAppBrowser.create('surfs://' + url.replace(/^(https?:|)\/\//, ''), '_system');
}
First issue
First of all when I use the android back button the click doesn't seem to work anymore (I've put an alert in the first line of the doTheThing function, but nothings shows up).
Other buttons in the app seem to work just fine, when using the switch app button it also works
I tried:
preventDefault()
stopPropagation()
using the tappable attribute
(tap) instead of (click)
but none seem to work. Does anybody have an idea for fixing this?
Second issue
Note: this is less important
When opening a link it remembers the last page(in soti surf) so by using the back arrow it first navigates to the last link and when it has no more back locations it goes back to the app
I tried:
using the return value of the inAppBrowser.create() and calling close() when returning to my app
Version info
#ionic-native/core version: 4.16.0
cordova-android: 7.1.4
cordova -v: 8.1.2 (cordova-lib#8.1.1)
npm -v: 6.4.1
ionic: 4.12.0
nodeJS: 11.1.0
Here in ionic you can prevent device back button by using Navbar Class and using Lifecycle hook method
Here is a sample
ionViewDidLoad() {
this.navBar.backButtonClick = (e: UIEvent) => {
// todo something
if (condition )
{
this.getSaveDialog();
} else{
this.navCtrl.pop();
}
})
}
For opening In Appbrowser in your app, will lose control of your events, you can't track and manage the
state of pages.
It can be maintain by browser. you need to place hacks or alternative in your application which is opening in APP

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)
);

How can I tame multiple event listeners in a Fennec extension?

I'm trying to write a restartless add-on for Firefox Mobile that will insert content onto specific web pages. It all seems to work OK until I try disabling then re-enabling the add-on, at which point I get multiple responses to the page load event, and I can't figure out a way to sort them out.
Since Fennec uses the Electrolysis multi-process platform, I know that I need to split my code into chrome and content scripts. My bootstrap.js looks something like this (trimmed for clarity):
function startup(data, reason) {
mm = Cc["#mozilla.org/globalmessagemanager;1"].getService(Ci.nsIChromeFrameMessageManager);
mm.loadFrameScript(getResourceURISpec('content.js'), true);
}
function shutdown(data, reason) {
let mm = Cc["#mozilla.org/globalmessagemanager;1"].getService(Ci.nsIChromeFrameMessageManager);
mm.sendAsyncMessage("GeoMapEnh:Disable", {reason: reason});
}
function install(data, reason) {}
function uninstall(data, reason) {}
Basically, the bootstrap.js just launches a content script, and sends a message to tell it to clean up on shutdown. The content.js sets up an eventListener to watch for page loads, that looks a bit like this:
addMessageListener("GeoMapEnh:Disable", disableScript);
addEventListener("DOMContentLoaded", loadHandler, false );
function loadHandler(e) {
LOG("Page loaded");
// Do something cool with the web page.
}
function disableScript(aMessage) {
if (aMessage.name != "GeoMapEnh:Disable") {
return;
}
LOG("Disabling content script: " + aMessage.json.reason);
try {
removeEventListener("DOMContentLoaded", loadHandler, false );
removeMessageListener("GeoMapEnh:Disable", disableScript);
} catch(e) {
LOG("Remove failed: " + e);
}
}
function LOG(msg) {
dump(msg + "\n");
var consoleService = Cc["#mozilla.org/consoleservice;1"].getService(Ci.nsIConsoleService);
consoleService.logStringMessage(msg);
}
When I first run the extension, everything works fine. An instance of content.js is executed for each browser tab (and any new tabs I open) and my eventListener detects the page loads it is supposed to via the DOMContentLoaded event. When I disable the extension, everything still seems OK: page loads stop being detected.
When I re-enable the extension, it all goes wrong. I still get an instance of content.js executing for each open tab, but now, if I open new tabs, DOMContentLoaded triggers mutiple eventListeners and I can't distinguish which one should handle the event. Worse yet, some of the eventListeners are active, but do not give debug info via my LOG function, and do not all of them get removed if I disable the extension a second time.
I do want to watch all browser tabs, including any new ones, but I only want my extension to insert content on the page that triggers it, and only once per page load. I've tried the following without success:
Calling e.stopPropagation() to stop the event being passed to other listeners. No effect.
Calling e.preventDefault() and testing e.defaultPrevented to see if the event has already been handled. It never has.
Testing if (this === content.document) to see if the eventListener has been triggered by its own page content. Doesn't work, as I get multiple "true" responses.
Making the eventListener capturing. No effect.
Using the load event rather than DOMContentLoaded.
I can't set a shared variable to say the event has been handled as under Electrolysis, the different eventListeners will be executing in different contexts. Also, I wouldn't be able to distinguish between multiple tabs loading the same page and one page load being detected multiple times. I could do this via IPC message passing back to the chrome bootstrap script, but I then I wouldn't know how to address the response back to the correct browser tab.
Any ideas? Is this a Firefox bug, or am I doing something silly in my code? I am using Fennec Desktop v4 for development, and will target Fennec Android v6 for production.

How to stop Silverlight browser history recording NavigationState/bookmark changes?

We have a Silverlight Prism project with complex state information stored in the browser bookmark. This allows us to share bookmarks/links which will restore the application to the exact same visual state.
We do not however want trivial bookmark changes (i.e. non-navigation changes) to result in a browser history entry. Otherwise the browser back/forward buttons would also make simple changes (things like simple list selections, tab selections etc).
Q: Is there a way to still change the browser bookmark URL, but exclude it from the browser history, or (failing that) is it possible to remove entries from the browser history?
Our visual states are prioritised, so we know which ones actually should affect navigation and which are just for decoration. This can be determined either before the URL is changed or after, so your answer can use either situation. We can add a specific marker to the bookmark, indicating it should not be archived, if that would also help your solution.
Thanks
I created one answer myself, but would prefer not to rely on JavaScript features. I also need to check that window.location.replace is supported by all browsers.
I now call this method with a flag, to say if I want the bookmarking ignored in the browser history:
public void NavigateToBookmark(string bookMarkUrl, bool replaceUrl)
{
if (replaceUrl)
{
HtmlPage.Window.Invoke("NavReplace", bookMarkUrl);
}
else
{
HtmlPage.Window.NavigateToBookmark(bookMarkUrl);
}
}
then add this JavaScript in the Silverlight hosting page:
function NavReplace(url) {
var newurl = parent.location.href;
var index = newurl.indexOf("#");
if (index > 0) {
newurl = newurl.substr(0, newurl.indexOf("#"));
}
window.location.replace(newurl + "#" + url);
}
Any takers for a better way to do this?

Resources