Nightwatch JS page object returns undefined - selenium

I'm using Nightwatch with mocha.
I try to get an element's text from the page object. When trying to compare the received text to another text I receive an error "AssertionError: expected undefined to equal 'Text'".
This is the Page Object function:
const Commands = {
getInstanceLabel() {
this.getText('.DropdownSelect__label', (result) => {
return result.value;
});
}
}
And this is the Test code:
it('Should sort the collection in ascending order by default', (client) => {
const labelText = client.page.instanceCollectionPage().getInstanceLabel();
expect(labelText).to.equal('Text');
});
Why is this showing undefined?

The thing is that you are using arrow functions, and as mentioned in mdn:
An arrow function expression has a shorter syntax compared to function
expressions and does not bind its own this, arguments, super, or
new.target.
You can fix it in two different ways:
using function:
e.g. (you can use this)
it('Should launch', function (browser) {
const url = browser.launchUrl;
browser.url(url).waitForElementVisible('body', 1000);
browser.getText('#txtWelcome', function (result) {
this.verify.equal(result.value, 'Welcome');
});
});
using browser:
e.g. (you need to access the browser object direcly)
it('Should launch', (browser) => {
const url = browser.launchUrl;
browser.url(url).waitForElementVisible('body', 1000);
browser.getText('#txtWelcome', (result) => {
browser.verify.equal(result.value, 'Welcome');
});
});
Those are just examples on how to use this, I can not provide more details on your issue because you don't show what InstanceCollection does.

Related

cypress not executing "then" portion

I am trying to write a custom cypress command but the code in my then portion is not executing. any help will be appreciated thanks
my command looks similar to this:
Cypress.Commands.add("ex", () => {
const links=[]
cy.get("*[class='link post']").each((link)=>{
links.push(link.href)
}).then(() => {
var i=0;
while (links[i]) {
cy.visit(link)
i++
}
})
})
There are a few things going on here we should step through.
In your each() block, link.href will return an undefined value, so when you get to your then method, you have no links in your array to visit. Instead of links.push(link.href), try links.push(links.attr('href') to grab the value of your href attribute.
In your then method, your while loop isn't the most efficient way of looping through your array (and it will most likely error out for you with an undefined value). You should instead use a .forEach(), like so:
links.forEach((link)=>{
cy.visit(link)
)
If you do not need to persist the links array, then your entire command can be majorly simplified:
Cypress.Commands.add("ex", () => {
cy.get("*[class='link post']")
.then((links) => {
links.each((link)=>{
cy.visit(link.attr('href'))
})
})
});
To add to Kerry's answer,
The parameter given to a .then() callback is a jQuery object, containing one or more elements found by cy.get(...)
To iterate over the elements, you need to de-structure the jQuery object with the spread operator,
Cypress.Commands.add("visitLinks", () => {
cy.get("*[class='link post']")
.then($links => { // $links is a jQuery wrapper
[...$links].forEach(link => { // link is a raw element
const url = link.getAttribute('href') // apply DOM method
cy.visit(url)
})
})
});
or if you want to use the Cypress iterator .each() instead of .then(),
Cypress.Commands.add("visitLinks", () => {
cy.get("*[class='link post']")
.each($link => { // jQuery wrapped element
const href = $link.attr('href') // apply jQuery method
cy.visit(href)
})
});
However
It's going to break.
cy.visit() navigates the page, which changes the DOM in the page, so on the 2nd iteration of .each(), Cypress sees things have changed and crashes (probably a "detached element" error).
You should separate the query (grabbing the links) from the action (visiting them).
Cypress.Commands.add("getLinks", () => {
const found = [];
cy.get("*[class='link post']")
.each($link => { // jQuery wrapped element
const href = $link.attr('href') // apply jQuery method
found.push(href)
})
.then(() => found) // wait until iteration finishes
// then return the array of links
});
Use it like this
cy.getLinks()
.then(links => {
links.forEach(link => cy.visit(link))
})

WebDriverIO : getText(), click() methods are not working - it says they are not function

HomePage:
class homePage {
get pageHeader() { return $('//h1[contains(text(),"Best")]'); }
get pagesubHeader() { return $('div.banner-text-content>p.sub-text'); }
get supportLink() { return $('//li/span[contains(text(),"Support")]') }
HomeElement Page:
const homePage = require("../Pages/homePage")
describe("Home Page Elements handle", function () {
it("Verify HomePage Elements", async function () {
// await browser.url('https://www.freshworks.com/');
let text =homePage.pageHeader.getText();
console.log(text);
})
})
Error:
Home Page Elements handle Verify HomePage Elements
homePage.pageHeader.getText is not a function
}
module.exports = new homePage();
You use async, so this tells me you are working with WebdriverIO Async mode. This means, all the functions will mostly return a promise.
So proper way of doing it is that you need to await for the browser/element functions:
const pageHeader = await homePage.pageHeader;
const text = await pageHeader.getText();
console.log(text);
Remember, $, $$ and basically any functions of the browser or of the element are promises in async mode, so essentially, you attempted to "getText()" or "click()" on a promise object.

How to run method inside a response function using Vue.js

I'm trying to run a method when I get success response from API, but the method dont run. I made a quick example here to show.
The test() function should be executed after i get the response, since its calling another API endpoint. Here is the vue.js code.
var app = new Vue({
el: "#contents",
data: {
id: null,
details: [],
},
methods: {
fetchProductDetails: function(){
let vue = this;
axios.post("/api/get-details", {
id : id
})
.then(function (response) {
vue.details = response.data;
this.test();
})
.catch(function (error) {});
},
test: function () {
console.log(app.details);
}
},
mounted: function(){
this.fetchProductDetails();
},
});
You should run vue.test() instead of this.test(), just like you use vue.details = response.data instead of this.details = response.data.
When using an unnamed function in .then(), this no longer refers to your vue application, but to the unnamed function. You could use ES6 arrow function syntax in order to avoid having to set this to a specific variable, as arrow functions use their parent's scope for this instead of setting this to refer to themselves:
axios.post("/api/get-details", { id: this.id })
.then(response => {
this.details = response.data;
this.test();
})
.catch(error => { console.log(error)});
Arrow functions (and ES6 in general) are not supported by IE11 however. so you'd need to use Babel to compile it back to something ES5 JavaScript if you need to support older browsers.

Restify: Set default formatter

Also asked in official Restify repo: #1224
Hi,
Is it possible to have one default formatter that can handle any accept type that is not defined.
For Example:
restify.createServer({
formatters: {
'application/json': () => {},
// All other requests that come in are handled by this, instead of throwing error
'application/every-thing-else': () => {}
}
});
By all appearances, this is impossible. Since the formatters are stored in a dictionary, there is no way to create a key that matches every input (that would kind of defeat the point of a dictionary anyway...) The only way to accomplish this kind of thing outside of JSON would be with a regular expression, and regular expressions don't work with JSON.
Here is a program I wrote to test this.
var restify = require("restify");
var server = restify.createServer({
formatters: {
'application/json': () => { console.log("JSON") },
"[\w\W]*": () => { console.log("Everything else") } // Does not work
}
});
server.get("/", (req, res, next) => {
console.log("Root");
res.setHeader("Content-Type", "not/supported");
res.send(200, {"message": "this is a test"});
next()
});
server.listen(10000);
Also here is a link to the documentation on this in case you can find some hint that I couldn't see.
Restify documentation

RTCPeerConnection.createAnswer callback returns undefined object in mozilla for WebRTC chat

Following is my code to answer the incoming call:
var pc = connection.pc;
pc.setRemoteDescription(sdp,function() {
pc.createAnswer(function(answer) {
pc.setLocalDescription(answer,function() {
// code for sending the answer
})
})
})
The above code works fine for chrome, but when i run the same in mozilla, the answer obtained from pc.createAnswer callback is undefined. As a result of which it gives me following error:
TypeError: Argument 1 of RTCPeerConnection.setLocalDescription is not
an object.
The problem is you're not checking errors, specifically: not passing in the required error callbacks.
setRemoteDescription and setRemoteDescription require either three arguments (legacy callback style) or one (promises), but you're passing in two. Same for createAnswer minus one.
The browser's JS bindings end up picking the wrong overload, returning you a promise which you're not checking either, effectively swallowing errors.
Either add the necessary error callbacks:
var pc = connection.pc;
pc.setRemoteDescription(sdp, function() {
pc.createAnswer(function(answer) {
pc.setLocalDescription(answer, function() {
// code for sending the answer
}, function(e) {
console.error(e);
});
}, function(e) {
console.error(e);
});
}, function(e) {
console.error(e);
});
Or use the modern promise API:
var pc = connection.pc;
pc.setRemoteDescription(sdp)
.then(() => pc.createAnswer())
.then(answer => pc.setLocalDescription(answer))
.then(() => {
// code for sending the answer
})
.catch(e => console.error(e));
The promise API is available natively in Firefox, or through adapter.js in Chrome. See fiddle.
And always check for errors. ;)