Looping through links via WebDriver - javascript

I am currently testing a web page via WebDriver I/O. I would like to be able to select a couple of links and "click" them. Currently, I have the following:
it('Should click links', function(done) {
client
.elements('a').then(function(links) {
console.log(links);
console.log('---------');
for (var i=0; i<links.value.length; i++) {
client.getAttribute(links.value[i].ELEMENT)
}
expect(true).toBe(true);
done();
})
;
});
When I execute this test, I see the following in the console window:
{ state: 'success',
sessionId: '85d25e09-13d8-475a-81b6-87431d2d8f3c',
hCode: 1234567890,
value:
[ { ELEMENT: '0' },
{ ELEMENT: '1' },
{ ELEMENT: '2' } ],
class: 'org.openqa.selenium.remote.Response',
status: 0 }
---------
{ ELEMENT: '0' }
{ ELEMENT: '1' }
{ ELEMENT: '2' }
My question is, how do I "click" the link? When I printed out the link, I was expecting to see an ID, an href, an xpath, or some way to reference the link. But I don't see anything. When I look at the docs, they mention that the elements are returned as WebElement JSON objects. However, I can't seem to find any docs on WebElement.
What am I missing?

You tell it to click on the element, just like the docs say: http://webdriver.io/api/action/click.html
In your case, the code would be something like:
it('Should click links', function(done) {
client
.elements('a').then(function(links) {
console.log(links);
console.log('---------');
for (var i=0; i<links.value.length; i++) {
client.click(links[i])
}
expect(true).toBe(true);
done();
})
;
});
Your code will probably be different, but this should get you started. Now, a couple of quick points:
1) You should avoid having mulitple assertions inside a test. It violates OAPT and can make your tests harder to find when one is failing.
2) You might check out Protractor (http://www.protractortest.org) for E2E testing. It doesn't require Angular and it can make your testing life a lot easier.

Related

Enzyme simulate with 1 node

I have the following test case in react-native.
it('changes text', () => {
wrapper.find(InputBox).simulate('change', { target: { value: 'text Given' } });
});
There are 2 InputBoxes so this gives me an error saying 'simulate should run on a single node, but found 2'.
How can I fix this issue?
You could make use of selectors such as first or at. For instance, say you want to select the first InputBox, you will write:
wrapper.find(InputBox).first().simulate('change', { target: { value: 'text Given' } });
In the same way you can use last to get the last of matched nodes or at(index) to select matches by index.

it statement inside loop iteration for each expect statement, mocha

I have this array of objects.
let links = [
{
url: 'some url 1',
status: 200
},
{
url: 'some url 2',
status: 200
}
]
Which is the result of calling LinkFunction asyncronously inside before:
before(async () => {
try {
links = await LinkFunction();
} catch (err) {
assert.fail(err);
}
});
I would like to check if the url and status properties exist and if their types are correspondingly string and number.
Note: The specified object is just a sample of a big response. So the loop is required for iteration in any case.
I've done this iteration:
it('Array to contain some props', () => {
links.map(property => {
expect(property).to.have.property('url').to.be.a('string');
expect(property).to.have.property('status').to.be.a('number');
});
});
But I would like to have something like this:
it('Array to contain some props', () => {//or describe would be better here
links.map(property => {
it('link array to contain url string', () => {
expect(property).to.have.property('url').to.be.a('string');
});
it('link array to contain status number', () => {
expect(property).to.have.property('status').to.be.a('number');
});
});
});
Unfortunately it statements are ignored inside map. Maybe it's because of the several nested it statements. So How can I implement a similar logic?
Update:
My full code:
You might want to use forEach instead of map.
Also, "Passing arrow functions (aka "lambdas") to Mocha is discouraged" so you will probably want to change those to normal functions.
Having said that, it works fine if links is defined as mocha initially runs the test file and collects the individual it tests:
const expect = require('chai').expect;
describe('links', function() {
let links = [
{
url: 'some url 1',
status: 200
},
{
url: 'some url 2',
status: 200
}
]
links.forEach(function(property) {
it('link array to contain url string', function() {
expect(property).to.have.property('url').to.be.a('string');
});
it('link array to contain status number', function() {
expect(property).to.have.property('status').to.be.a('number');
});
});
});
..results in:
> mocha
links
√ link array to contain url string
√ link array to contain status number
√ link array to contain url string
√ link array to contain status number
4 passing (14ms)
Update
As you have found, it only works at the top level or with a describe:
before(function() {
it('will NOT work here', function() { });
});
it('will work here', function() {
it('will NOT work here', function() { });
});
Also, links must be available while the test is first running and the it tests are being collected by mocha so this doesn't work either:
describe('links', function() {
let links = [];
before(function() {
links = [
{
url: 'some url 1',
status: 200
},
{
url: 'some url 2',
status: 200
}
];
});
// This won't work...
links.forEach(function(property) {
// .. since links is still an empty array when this runs
it('should...', function() { /* ... */ });
});
});
From your question update it looks like your code retrieves links from an async function call in before. Because of this, there is no way to have links populated at the time the test is first running and the it tests are collected.
So it looks like you won't be able to map across the items in links to create your it tests, and will instead need to take the approach you described by mapping across the items in links within a single test.

Trying to understand browser.elementIdText command

I couldn't find much information or useful examples to understand how this method works on a Javascript UI testing framework. I have the following element which is returned in an array:
console.log(elementarray[0]);
{ ELEMENT: '25',
'element-6066-11e4-a52e-4f735466cecf': '25',
selector: '[data="abc"]',
value: { ELEMENT: '25' },
index: 0 }
however when I run:
browser.elementIdText(elementarray[0].ELEMENT)
I see this:
{ state: 'success',
sessionId: 'af7ef2fb-7d1d-456e-ad14-c5c1fd9d83c2',
hCode: 1013623656,
value: '17:55',
class: 'org.openqa.selenium.remote.Response',
_status: 0 }
How exactly is browser.elementIdText working here, can anyone provide a simple explanation with an example pls. I see information here that I am not seeing when I log the first item in the array and surely the value of elementarray[0].ELEMENT is just 25 right? as it's shown in the first property of the object?
Thanks for any useful replies.
elementIdText expect ID as argument. In order to get ID, you need to use allElem.value[0].ELEMENT for example. See below code.
describe('allx', () => {
it('allx', () => {
browser.url("https://the-internet.herokuapp.com/");
allElem=browser.elements('//div[2]/div/ul/li/a');
console.log(allElem.value[0].ELEMENT)
text=browser.elementIdText(allElem.value[0].ELEMENT).value;
console.log(text);
});
});

Sequelize: .createAssociation() or .setAssociation doesn't update the original object with created data

I've been stuck on this for a while. Take the following code as an example:
models.Summoner.findOne({
include: [{ model: models.RankedStats, as: 'SummonerRankedStats', required: true }],
where: { summonerId: summonerId, server: server }
}).then(function(summoner) {
models.RankedStats.create({
totalWins: 0,
totalLosses: 0
}).then(function(rankedStats) {
summoner.setSummonerRankedStats(rankedStats).then(function() {
console.log(summoner.SummonerRankedStats)
//This outputs undefined
summoner.getSummonerRankedStats().then(function(srs) {
console.log(srs)
//This outputs the RankedStats that were just created
})
models.Summoner.findOne({
include: [{ model: models.RankedStats, as: 'SummonerRankedStats', required: true }],
where: { summonerId: summonerId, server: server }
}).then(function(summoner) {
console.log(summoner.SummonerRankedStats)
//This outputs the SummonerRankedStats object
})
})
})
})
So, to put it simply... If I have a Summoner (var summoner) and perform a .setAssociation() or .createAssociation() on it, and then log summoner, the data created isn't there. If I fetch it again from the database (with .getAssociation() or by searching for that Summoner again) I can access it, but I was hoping to avoid that extra DB call.
Is there a way to add this information to the original object when using .create() or .set()? It can be achieved by doing something like:
summoner.dataValues.SummonerRankedStats = rankedStats
But that seems somewhat hacky :)
Is there a correct way to do it, or does it even make any sense?
Thanks in advance!

How to iterate through querySelectorAll results in Nightwatch

I can't seem to find the correct syntax for iterating through the innerHTML of a nodelist in Nightwatch. I'm trying to return the urls of every 'a' tag contained within the body content of a page, but I can't find a way to access the results of my querySelectorAll command in Nightwatch.
browser.execute(function() {
return document.querySelectorAll("div.field-item.even a");
},
function(tags){
console.log(tags.value);
console.log(tags.value[9]);
})
There are 10 links on the page I am testing. The query selector seems to be retrieving them all, since console.log(tags.value) prints the following:
[ { ELEMENT: '1' },
{ ELEMENT: '2' },
{ ELEMENT: '3' },
{ ELEMENT: '4' },
{ ELEMENT: '5' },
{ ELEMENT: '6' },
{ ELEMENT: '7' },
{ ELEMENT: '8' },
{ ELEMENT: '9' },
{ ELEMENT: '10' } ]
and console.log(tags.value[9]) prints:
{ ELEMENT: '10' }
I cannot find a way to retrieve the link from these results, however. Appending .innerHTML to any of these variables returns 'undefined'. Is there any way for me to iterate through the querySelectorAll results and retrieve the urls inside of it?
EDIT: It seems like I can get the same result if I use the following code:
browser.elements("css selector", "div.field-item.even a", function(tags) {
console.log(tags.value);
console.log(tags.value[9]);
})
I originally thought that I was working with a nodelist, but I think I'm actually being returned a WebElement JSON object as per the documentation for the .elements command.
I'm still unable to access the inner text, however. Any ideas?
I resolved the issue using a different method. I used the browser.elements command instead of querySelectorAll, and then used browser.elementIdAttribute to get the contents of each 'href' attribute in each 'a' tag.
//Get an WebElement JSON object array of all urls
browser.elements("css selector", "div.field-item.even a", function(link_array) {
for (var x = 0; x < link_array.value.length; x++){
//Fetch the url from each 'a' tag in the content
browser.elementIdAttribute(link_array.value[x].ELEMENT, "href", function(links) {
console.log(links.value);
});
}
})
This prints out every link within the content of the page.
selectAllRoles: function (client) {
client.elements('css selector', '.editorDetail ul li input[type=checkbox]', function(res) {
res.value.forEach(elementObject => {
client.elementIdClick(elementObject.ELEMENT);
});
});
return this;
},
Fill in your id's after the 'css selector'

Categories

Resources