testing all the links on a page using protractor - javascript

I've been able to work this out in java but so far I'm only able to open the webpage using jasmine js. In java, all the anchor tag links can be saved in List and then each link can be navigated using the browser driver object. But in jasmine js, I'm unable to store those links in an array. Here's what I've tried to do:
describe('demo', function()
{
it('mydemo', function()
{
browser.ignoreSynchronization = true;
browser.driver.get('https://www.google.co.in');
var array = [];
array.push(browser.findElement(by.xpath("//a[#href]")));
for(var i=0; i<array.length; i++)
{
expect(browser.navigate().to(array[i]));
}
}
}
A new browser window pops up, navigates to google and then closes. There seems to be a timeout issue. Using browser.ignoreSynchronization = true, the server ignores it as an angular application but still the timeout issues persists. Any suggestions?

To get all the links, call getAttribute on an ElementArrayFinder. It will return a Promise which once resolved will give you all the links.
Then call filter to exclude the dynamic links (href="javascript:...) and forEach to iterate each link:
browser.ignoreSynchronization = true;
$$("a[href]").getAttribute("href")
.then(links => links
.filter(link => !/^javascript/.test(link))
.forEach(link => {
console.log(link);
browser.driver.get(link);
})
);
Another and quicker way is to get all the links with execute script with a single call to the browser:
browser.ignoreSynchronization = true;
browser.driver.executeScript("return [].map.call(document.links, function(e){return e.href})")
.then(links => links
.filter(link => !/^javascript/.test(link))
.forEach(link => {
console.log(link);
browser.driver.get(link);
})
);

See following code.
$$('a').map(function(link) {
return link.getAttribute("href").then(function (href) {
return href.replace(/https\:\/\/app\.perflectie\.nl\//g, localhost);
});
}).then(function(links) {
links.forEach(function(link) {
browser.get(link);
expect(browser.getCurrentUrl()).not.toContain('/Error/');
});
});
For more innformation go to following links.
Link 1
Link 2
Hope this helps. :)

it('link elements', function () {
browser.ignoreSynchronization = true;
browser.get('https://www.google.co.in');
element.all(by.tagName('a')).each(function (elem) { // this is the important step, rest you can do whatever you want inside this
elem.getText().then(function (val) {
console.log('#### : ' + val)
})
})
});

Related

Clicking links with WebdriverIO

I have a web page that I am trying to test via Webdriver I/O. My question is, how do I click a couple of links via a test? Currently, I have the following:
var webdriverio = require('webdriverio');
var client = webdriverio.remote(settings).init()
.url('http://www.example.com')
.elements('a')
.then(function(links) {
for (var i=0; i<links.value.length; i++) {
console.log('Clicking link...');
var link = links.value[i].ELEMENT;
link.click().then(function(result) {
console.log('Link clicked!');
});
}
})
;
When the above gets executed, I get an error that says "click is not a function" on link. When I print link to the console, it looks like JSON, which would make sense since the documentation says that the elements function returns WebElement JSON objects. Still, I'm just trying to figure out how to click this link.
How does one do such?
Thanks!
You need elementIdClick
http://webdriver.io/api/protocol/elementIdClick.html
Here is an example
var settings = {
desiredCapabilities: {
browserName: 'firefox',
},
};
var webdriverio = require('webdriverio');
var client = webdriverio.remote(settings).init()
.url('http://www.example.com')
.elements('a')
.then(function(links) {
for (var i=0; i<links.value.length; i++) {
console.log('Clicking link...');
var link = links.value[i].ELEMENT;
client.elementIdClick(link).then(function(result) {
console.log('Link clicked!');
});
}
});
Result of the above code will be
Clicking link...
Link clicked!
Hello there you could do directly this though:
it clicks all elements a on the page
var client = webdriverio.remote(settings).init()
.url('http://www.example.com')
.click('a')
.end()
);
you could you a selector to target specific a elements
example:
.click("article .search-result .abstract .more")

Make a link from Electron open in browser

Is there any (simple/built-in way) to open a new browser (I mean default OS browser) window for a link from Electron instead of visiting that link inside your Electron app ?
You can simply use :
require("shell").openExternal("http://www.google.com")
EDIT: #Arjun Kava's answer is much better these days.
This answer is quite old and assumes you have jQuery.
const shell = require('electron').shell;
// assuming $ is jQuery
$(document).on('click', 'a[href^="http"]', function(event) {
event.preventDefault();
shell.openExternal(this.href);
});
mainWindow.webContents.on('new-window', function(e, url) {
e.preventDefault();
require('electron').shell.openExternal(url);
});
Requires that you use target="_blank" on your anchor tags.
My code snippet clue accordingly to the depreciations in Electron version ^12.0.0
const win = new BrowserWindow();
win.webContents.setWindowOpenHandler(({ url }) => {
// config.fileProtocol is my custom file protocol
if (url.startsWith(config.fileProtocol)) {
return { action: 'allow' };
}
// open url in a browser and prevent default
shell.openExternal(url);
return { action: 'deny' };
});
To make all Electron links to open externally in the default OS browser you will have to add an onclick property to them and change the href property so it doesn't load anything in the Electron app.
You could use something like this:
aTags = document.getElementsByTagName("a");
for (var i = 0; i < aTags.length; i++) {
aTags[i].setAttribute("onclick","require('shell').openExternal('" + aTags[i].href + "')");
aTags[i].href = "#";
}
But make sure the entire document has loaded before doing this otherwise it is not going to work.
A more robust implementation would look like this:
if (document.readyState != "complete") {
document.addEventListener('DOMContentLoaded', function() {
prepareTags()
}, false);
} else {
prepareTags();
}
function prepareTags(){
aTags = document.getElementsByTagName("a");
for (var i = 0; i < aTags.length; i++) {
aTags[i].setAttribute("onclick","require('shell').openExternal('" + aTags[i].href + "')");
aTags[i].href = "#";
}
return false;
}
Remember that if you load external files you will have to make them go through this process as well after they are fully loaded.
Some handy solutions can be found in this gist.
By listening on the body, the following solutions will work on <a> tags that may not yet exist when the JavaScript runs, but only appear in the DOM at a later time.
This one by luizcarraro requires jQuery:
$('body').on('click', 'a', (event) => {
event.preventDefault();
require("electron").shell.openExternal(event.target.href);
});
You can change the selector to target only certain links, e.g. '#messages-view a' or 'a.open-external'.
Here is an alternative without any library (derived from zrbecker's):
document.body.addEventListener('click', event => {
if (event.target.tagName.toLowerCase() === 'a') {
event.preventDefault();
require("electron").shell.openExternal(event.target.href);
}
});
Consult the gist for more examples.
I use this method with Electron v.13.
We intercept the user's navigation (window.location) and open the URL in the default browser.
See the doc : https://www.electronjs.org/docs/latest/api/web-contents#event-will-navigate
const { shell } = require('electron');
window.webContents.on('will-navigate', function (e, url) {
e.preventDefault();
shell.openExternal(url);
});
On tsx syntax (Electron):
import { shell } from "electron";
shell.openExternal("http://www.google.com")
In the view component use simple a link:
<a href="https://google.com" target="_blank" rel="noreferrer">
<button type="button">
button title
</button>
</a>
And in file public/electron.js add default behavior for all a link navigations:
function createWindow() {
...
// Open urls in the user's browser
win.webContents.setWindowOpenHandler((edata) => {
shell.openExternal(edata.url);
return { action: "deny" };
});
}
To open an external link in an Electron's Project you will need the module Shell (https://www.electronjs.org/docs/api/shell#shell) and the method openExternal.
But if you are looking for an abstract way to implement that logic is by creating a handler for a custom target to your target attribute.
const {shell} = require('electron');
if (document.readyState != "complete") {
document.addEventListener('DOMContentLoaded', function() {
init()
}, false);
} else {
init();
}
function init(){
handleExternalLinks();
//other inits
}
function handleExternalLinks(){
let links = document.getElementsByTagName('a')
let a,i = 0;
while (links[i]){
a = links[i]
//If <a target="_external">, so open using shell.
if(a.getAttribute('target') == '_external'){
a.addEventListener('click',(ev => {
ev.preventDefault();
let url = a.href;
shell.openExternal(url);
a.setAttribute('href', '#');
return false;
}))
}
console.log(a,a.getAttribute('external'))
i++;
}
}
To run an Electron project in your actual browser (Chrome, Mozilla, etc), add this to your script are external script:
aTags = document.getElementsByTagName("a");
for (var i = 0; i < aTags.length; i++) {
aTags[i].setAttribute("onclick","require('shell').openExternal('" + aTags[i].href + "')");
aTags[i].href = "#";
}

Reload the page using sammy.js

I am using sammy.js for single page application in asp.net mvc. Everything is fine, but I am facing one problem which is that I can not reload the page. For example When I am in the dashboard my URL is
http://localhost:1834/#/Home/Index?lblbreadcum=Dashboard
layout.cshtml
<script>
$(function () {
var routing = new Routing('#Url.Content("~/")', '#page', 'welcome');
routing.init();
});
</script>
routing.js
var Routing = function (appRoot, contentSelector, defaultRoute) {
function getUrlFromHash(hash) {
var url = hash.replace('#/', '');
if (url === appRoot)
url = defaultRoute;
return url;
}
return {
init: function () {
Sammy(contentSelector, function () {
this.get(/\#\/(.*)/, function (context) {
var url = getUrlFromHash(context.path);
context.load(url).swap();
});
}).run('#/');
}
};
}
I want to reload the page by clicking the dashboard menu/link. But click event not firing because link is not changing. But if I want to go another page then it is fine. Please help me out. Thanks.
I think you have to append the same partial again. You can't "update" the partial in that meaning.
As you say in your post, when you click another link and then back again it works.
That's what you'll have to do. Append the same page/partial again, by doing that you clear all variables and recreate them, by that simulating a refresh.
EDIT: Added example
Observe that I didn't copy your code straight off but I think you'll understand :)
And I don't use hash (#) in my example.
var app = Sammy(function () {
this.get('/', function (context) {
// context is equalient to data.app in the custom bind example
// currentComponent('home'); I use components in my code but you should be able to swith to your implementation
var url = getUrlFromHash(context.path);
context.load(url).swap();
});
this.bind('mycustom-trigger', function (e, data) {
this.redirect('/'); // force redirect
});
this.get('/about', function (evt) {
// currentComponent('about'); I use components in my code but you should be able to swith to your implementation
var url = getUrlFromHash(context.path);
context.load(url).swap();
});
}).run();
// I did an easy example trigger here but I think you will need a trigger on your link-element. Mayby with a conditional check wheter or not to trigger the manually binding or not
$('.navbar-collapse').click(function () {
app.trigger('mycustom-trigger', app);
});
Please read more about events and routing in sammy.js
Good luck :)
An easier and cleaner way to force the route to reload is to call the Sammy.Application refresh() method:
import { sammyApp } from '../mySammyApp';
const url = `${mySearchRoute}/${encodeURIComponent(this.state.searchText)}`;
if (window.location.hash === url) {
sammyApp.refresh();
else {
window.location.hash = url;
}

Click all anchor tags on page with given class, but cancel prior to navigation

Trying to automate some testing for some analytics tracking code, and I'm running into issues when I try passing links into the each() method.
I copied a lot of this from stackoverflow - how to follow all links in casperjs, but I don't need return the href of the link; I need to return the link itself (so I can click it). I keep getting this error: each() only works with arrays. Am I not returning an array?
UPDATE:
For each anchor tag that has .myClass, click it, then return requested parameters from casper.options.onResourceReceived e.g. event category, event action, etc. I may or may not have to cancel the navigation the happens after the click; I simply only need to review the request, and do not need the follow page to load.
Testing steps:
click link that has .myClass
look at request parameters
cancel the click to prevent it from going to the next page.
I'm new to javascript and casper.js, so I apologize if I'm misinterpreting.
ANOTHER UPDATE:
I've updated the code to instead return an array of classes. There are a few sketchy bits of code in this though (see comments inline).
However, I'm now having issues canceling the navigation after the click. .Clear() canceled all js. Anyway to prevent default action happening after click? Like e.preventDefault();?
var casper = require('casper').create({
verbose: true,
logLevel: 'debug'
});
casper.options.onResourceReceived = function(arg1, response) {
if (response.url.indexOf('t=event') > -1) {
var query = decodeURI(response.url);
var data = query.split('&');
var result = {};
for (var i = 0; i < data.length; i++) {
var item = data[i].split('=');
result[item[0]] = item[1];
}
console.log('EVENT CATEGORY = ' + result.ec + '\n' +
'EVENT ACTION = ' + result.ea + '\n' +
'EVENT LABEL = ' + decodeURIComponent(result.el) + '\n' +
'REQUEST STATUS = ' + response.status
);
}
};
var links;
//var myClass = '.myClass';
casper.start('http://www.leupold.com', function getLinks() {
links = this.evaluate(function() {
var links = document.querySelectorAll('.myClass');
// having issues when I attempted to pass in myClass var.
links = Array.prototype.map.call(links, function(link) {
// seems like a sketchy way to get a class. what happens if there are multiple classes?
return link.getAttribute('class');
});
return links;
});
});
casper.waitForSelector('.myClass', function() {
this.echo('selector is here');
//this.echo(this.getCurrentUrl());
//this.echo(JSON.stringify(links));
this.each(links, function(self, link) {
self.echo('this is a class : ' + link);
// again this is horrible
self.click('.' + link);
});
});
casper.run(function() {
this.exit();
});
There are two problems that you're dealing with.
1. Select elements based on class
Usually a class is used multiple times. So when you first select elements based on this class, you will get elements that have that class, but it is not guaranteed that this will be unique. See for example this selection of element that you may select by .myClass:
myClass
myClass myClass2
myClass myClass3
myClass
myClass myClass3
When you later iterate over those class names, you've got a problem, because 4 and 5 can never be clicked using casper.click("." + links[i].replace(" ", ".")) (you need to additionally replace spaces with dots). casper.click only clicks the first occurrence of the specific selector. That is why I used createXPathFromElement taken from stijn de ryck to find the unique XPath expression for every element inside the page context.
You can then click the correct element via the unique XPath like this
casper.click(x(xpathFromPageContext[i]));
2. Cancelling navigation
This may depend on what your page actually is.
Note: I use the casper.test property which is the Tester module. You get access to it by invoking casper like this: casperjs test script.js.
Note: There is also the casper.waitForResource function. Have a look at it.
2.1 Web 1.0
When a click means a new page will be loaded, you may add an event handler to the page.resource.requested event. You can then abort() the request without resetting the page back to the startURL.
var resourceAborted = false;
casper.on('page.resource.requested', function(requestData, request){
if (requestData.url.match(/someURLMatching/)) {
// you can also check requestData.headers which is an array of objects:
// [{name: "header name", value: "some value"}]
casper.test.pass("resource passed");
} else {
casper.test.fail("resource failed");
}
if (requestData.url != startURL) {
request.abort();
}
resourceAborted = true;
});
and in the test flow:
casper.each(links, function(self, link){
self.thenClick(x(link));
self.waitFor(function check(){
return resourceAborted;
});
self.then(function(){
resourceAborted = false; // reset state
});
});
2.2 Single page application
There may be so many event handlers attached, that it is quite hard to prevent them all. An easier way (at least for me) is to
get all the unique element paths,
iterate over the list and do every time the following:
Open the original page again (basically a reset for every link)
do the click on the current XPath
This is basically what I do in this answer.
Since single page apps don't load pages. The navigation.requested and page.resource.requested will not be triggered. You need the resource.requested event if you want to check some API call:
var clickPassed = -1;
casper.on('resource.requested', function(requestData, request){
if (requestData.url.match(/someURLMatching/)) {
// you can also check requestData.headers which is an array of objects:
// [{name: "header name", value: "some value"}]
clickPassed = true;
} else {
clickPassed = false;
}
});
and in the test flow:
casper.each(links, function(self, link){
self.thenOpen(startURL);
self.thenClick(x(link));
self.waitFor(function check(){
return clickPassed !== -1;
}, function then(){
casper.test.assert(clickPassed);
clickPassed = -1;
}, function onTimeout(){
casper.test.fail("Resource timeout");
});
});

Protractor E2E Testing Error : Object [object Object] has no method 'getWindowHandle'

I am trying to check the pop up for facebook login opening on click of button .
Error : Object [object Object] has no method 'getWindowHandle'.
Code Snippet generating error :
describe('Tests', function() {
var ptor;
var handlePromise;
var util = require('util');
beforeEach(function() {
ptor = protractor.getInstance();
handlePromise = ptor.getAllWindowHandles();
var handlesDone = false;
ptor.get('/SiteB_Upgrade_Device/app/index.html#/Recommendations#page');
ptor.findElement(by.id('fb')).click();
ptor.ignoreSynchronization = true;
});
describe('login', function() {
return it('should switch to popUp\'s handle', function() {
handlePromise.then(function(handles) {
var popUpHandle = handles[0];
var handle = browser.driver.switchTo().window(popUpHandle).getWindowHandle();
expect(handle).toEqual(popUpHandle);
});
},30000);
});
});
Here is what I currently use to navigate through popups/tabs :
// do stuff that will trigger the popup
// ...
browser.getAllWindowHandles().then(function (handles) {
// switch to the popup
browser.switchTo().window(handles[1]);
// make sure the popup is now focused
expect(browser.getCurrentUrl()).toEqual('popup/url');
// do stuff with the popup
// ...
// go back to the main window
browser.switchTo().window(handles[0]);
// make sure we are back to the main window
expect(browser.getCurrentUrl()).toEqual('original/url');
});
You just need to make sure that your popup is really a new window and not juste some kind of popover ( in which case you can just target it with css selectors ).
Another thing to keep in mind when you change tabs/popups it that the target page might not have angularjs loaded in it, which will render protractor useles. If you face this case you can simply use browser.driver as a replacement for browser to navigate a non angular page.
Hope this helps.

Categories