NightwatchJS not passing variable - javascript

I've separated the variables for scoping purposes, I'm able to fill out all the variables with the respective DOM elements from url1 but when I try to pass them to url2 fields, they get set to undefined as if the variable was never set. Yet the console.log works... I've passed also a function to .setValue('#address-serviceability-form', '', '', function(e){ // set the value here}) but still I get undefined... Is this variable scoping issue?
describe('Ecosia.org Demo', function() {
var address ='';
var streetNumber = '';
var city = '';
var stateAndZip = '';
var state = ''
var zip = '';
before(browser => browser.url('url1.com'));
test('Extract random address', function (browser) {
browser
.getText('.rand_large li:first-child', function(result) {
address = result.value.split("\n");
streetNumber = address[0];
console.log('streetNumber', streetNumber) //this works and logs correctly
city = address[1].split(",")[0];
stateAndZip = address[1].split(",")[1];
state = stateAndZip.split(" ")[1];
zip = stateAndZip.split(" ")[2];
})
.url('url2.com')
.waitForElementVisible('form')
.setValue('#address-serviceability-form', streetNumber) // this is set to undefined
.pause(5000)
});
after(browser => browser.end());
});

This looks to me to be a problem with your understanding of NodeJS callbacks.
You should be able to fix this using async/await. Your code is not waiting for the callback to be resolved and the value is not assigned before being used in set value function. you could try something like below.
test('Extract random address', async function(browser) {
await browser
.getText('.rand_large li:first-child', function(result) {
address = result.value.split("\n");
streetNumber = address[0];
console.log('streetNumber', streetNumber) //this works and logs correctly
city = address[1].split(",")[0];
stateAndZip = address[1].split(",")[1];
state = stateAndZip.split(" ")[1];
zip = stateAndZip.split(" ")[2];
})
browser.url('url2.com')
.waitForElementVisible('form')
.setValue('#address-serviceability-form', streetNumber) // this is set to undefined
.pause(5000)
});

Related

Property of object inside indexedDB showing a value, when retrieved showing another

In chrome "Application" -> IndexedDB I see the property value of an object with value "inactive",
on retrieving that record, the property shows with value "active".
This is the function that does the retrieving:
function retrievesClientsFrom(databaseWithThisName, thisVersion, thisObjectStoreName) {
var requestToOpenDatabase = indexedDB.open(databaseWithThisName, thisVersion);
requestToOpenDatabase.onsuccess = function(event) {
var theDatabaseObject = requestToOpenDatabase.result;
//gets a transaction object
var theTransactionObject = theDatabaseObject.transaction(thisObjectStoreName, "readonly");
//gets the object store with that name
var theObjectStore = theTransactionObject.objectStore(thisObjectStoreName);
//gets all objects inside the objectStore sent to the function
var requestToGetContentOfAnObjectStore = theObjectStore.getAll();
requestToGetContentOfAnObjectStore.onsuccess = function(event) {
arrayOfRetrievedClients = requestToGetContentOfAnObjectStore.result;
//here I get the wrong value (I see it in chrome console).
console.log("arrayOfRetrievedClients: o%",arrayOfRetrievedClients);
};
};
requestToOpenDatabase.onerror = function(event) {
window.alert("There was a problem reading the database:
"+requestToOpenDatabase.error);
};
}
I have absolutely no idea what to do. Any ideas?

How to use try and catch if the user enters wrong city name with an Weather Api search?

I want an alert to pop-up when the user enters an invalid city name.
Tried using if-else and try-catch method, but not working. I am using the Weather API to get the weather and temperature.
const getWeather = () => {
let cityName = $("#cityName").val();
let apiCall = `http://api.openweathermap.org/data/2.5/weather?q=${cityName}&mode=json&units=metric&appid=${
CONSTANTS.appId
}`;
$.getJSON(apiCall, weatherData => {
let cityName = weatherData.name;
let countryName = weatherData.sys.country;
let description = weatherData.weather[0].description;
let tempMin = weatherData.main.temp_min;
let tempMax = weatherData.main.temp_max;
$("#city").text(cityName);
$("#detail").text(description);
$("#country").text(countryName);
$("#mintemp").html(`Minimum: ${tempMin}<span>℃</span>`);
$("#maxtemp").html(`Maximum: ${tempMax}<span>℃</span>`);
});
};
Whenever an invalid input is entered I get this message in the console.
GET http://api.openweathermap.org/data/2.5/weather?q=sdx&mode=json&units=metric&appid=d568dc579415266146ede4b4f9de029b 404 (Not Found)
jquery-3.4.1.js:9837
I don't want the error to appear, but instead use the error to display a message stating invalid user input.
the entire code link is https://github.com/Shefali-Upadhyay/weather-app
In this case, you don't need a try...catch block as jQuery handles everything for you but you need to sniff onto the HTTP Status Code. Don't use $.getJSON(), instead use $.ajax().
If you still want to use $.getJSON(), you can use the .fail() chained method this way:
let cityName = $("#cityName").val();
let apiCall = `http://api.openweathermap.org/data/2.5/weather?q=${cityName}&mode=json&units=metric&appid=${CONSTANTS.appId}`;
$.getJSON(apiCall, weatherData => {
let cityName = weatherData.name;
let countryName = weatherData.sys.country;
let description = weatherData.weather[0].description;
let tempMin = weatherData.main.temp_min;
let tempMax = weatherData.main.temp_max;
$("#city").text(cityName);
$("#detail").text(description);
$("#country").text(countryName);
$("#mintemp").html(`Minimum: ${tempMin}<span>℃</span>`);
$("#maxtemp").html(`Maximum: ${tempMax}<span>℃</span>`);
}).fail(() => {
alert("City doesn't Exist!!");
$("#cityName").val("");
$("#city").text("");
$("#detail").text("");
$("#country").text("");
$("#mintemp").html("");
$("#maxtemp").html("");
});

Use of indexedDB returns 'undefined'

I'm trying to use indexedDB.
Some parts of my code works.
In the following example, the first function adds server in my DB, however in Chrome debug console there is an undefined message not related to any line. The server is already added though.
The second function puts records in an array, there is also an undefined message not related to any line.
If I do a console.log(servers); just before return servers; I can see the array content, however if I call the function somewhere else in my code, the returned object is undefined.
var dbName = 'myDBname',
dbServersStoreName = 'servers',
dbVersion = 1,
openDBforCreation = indexedDB.open(dbName, dbVersion);
openDBforCreation.onupgradeneeded = function(e) {
var db = e.target.result;
var objStore = db.createObjectStore(dbServersStoreName, { keyPath: "alias"
});
var index = objStore.createIndex("serversAlias", ["alias"]);
};
function addServerInDB(serverAlias,serverAddress,user,pwd){
var myDB = indexedDB.open(dbName, dbVersion);
myDB.onerror = function() {
var notification = document.querySelector('.mdl-js-snackbar');
notification.MaterialSnackbar.showSnackbar(
{message: 'Error while trying to access internal database'});
}
myDB.onsuccess = function(e) {
var db = e.target.result,
request = db.transaction([dbServersStoreName],
"readwrite").objectStore("servers")
.put({alias:''+serverAlias+'',
address:''+serverAddress+'', login:''+user+'',
passwd:''+pwd+''});
request.onsuccess = function(){
var notification = document.querySelector('.mdl-js-snackbar');
notification.MaterialSnackbar.showSnackbar(
{message: 'Server added'});
}
}
};
function listServersInDB(){
var myDB= indexedDB.open(dbName, dbVersion);
myDB.onerror = function() {
var notification = document.querySelector('.mdl-js-snackbar');
notification.MaterialSnackbar.showSnackbar(
{message: 'Error while trying to access internal database'});
}
myDB.onsuccess = function(e) {
var servers = new Array(),
db = e.target.result,
request = db.transaction(["servers"], "readwrite")
.objectStore("servers")
.openCursor();
request.onsuccess = function(e){
var cursor = e.target.result;
if(cursor){
servers.push(cursor.value);
cursor.continue();
}
return servers;
}
}
};
I do not understand where this undefined comes from and if that is why the listServersInDB() function doesn't work.
You need to learn more about how to write asynchronous Javascript. There are too many errors in your code to even begin reasoning about the problem.
Briefly, don't do this:
function open() {
var openDatabaseRequest = ...;
}
openDatabaseRequest.foo = ...;
Instead, do this:
function open() {
var openDatabaseRequest = ...;
openDatabaseRequest.foo = ...;
}
Next, you don't need to try and open the same database multiple times. Why are you calling indexedDB.open twice? You can open a database to both install it and to start using it immediately. All using the same connection.
Next, I'd advise you don't name the database open request as 'myDB'. This is misleading. This is an IDBRequest object, and more specifically, an IDBOpenRequest object. A request isn't a database.
Next, you cannot return the servers array from the request.onsuccess at the end. For one this returns to nowhere and might be source of undefined. Two this returns every single time the cursor is advanced, so it makes no sense at all to return return servers multiple times. Three is that this returns too early, because it cannot return until all servers enumerated. To properly return you need to wait until all servers listed. This means using an asynchronous code pattern. For example, here is how you would do it with a callback:
function listServers(db, callbackFunction) {
var servers = [];
var tx = db.transaction(...);
var store = tx.objectStore(...);
var request = store.openCursor();
request.onsuccess = function() {
var cursor = request.result;
if(cursor) {
servers.push(cursor.value);
cursor.continue();
}
};
tx.oncomplete = function() {
callbackFunction(servers);
};
return 'Requested servers to be loaded ... eventually callback will happen';
}
function connectAndList() {
var request = indexedDB.open(...);
request.onsuccess = function() {
var db = request.result;
listServers(db, onServersListed);
};
}
function onServersListed(servers) {
console.log('Loaded servers array from db:', servers);
}
When you call a function that does not return a value, it returns undefined. All functions in JavaScript return undefined unless you explicitly return something else.
When you call a function from the devtools console, and that function returns undefined, then the console prints out '-> undefined'. This is an ordinary aspect of using the console.
If you want to get a function that returns the list of servers as an array, well, you cannot. The only way to do that in a pretend sort of way, is to use an 'async' function, together with promises.
async function getServers() {
var db = await new Promise(resolve => {
var request = indexedDB.open(...);
request.onsuccess = () => resolve(request.result);
});
var servers = await new Promise(resolve => {
var tx = db.transaction(...);
var request = tx.objectStore(...).getAll();
request.onsuccess = () => resolve(request.result);
});
return servers;
}
One more edit, if you want to call this from the console, use await getServers();. If you do not use the top-level await in console, then you will get the typical return value of async function which is a Promise object. To turn a promise into its return value you must await it.
Clear and helpfull explanations, Thank you.
I open database multiple times beacause the first time is for checking if DB needs an upgrade and doing something if needed. I'll add 'db.close()' in each functions.
Then, I tried your exemple and the result is the same:
console.log('Loaded servers array from db:', servers); works
but return servers; Don't work.
And in console there is already an undefined without related line :
Screenshot

Calling a function as a variable

I've been going at a small snippet of code for like an hour or two now, but can't seem to figure out why my Javascript breaks down at the end of this code.
// Getting username from address bar and user ID from username
var siteHref = window.location.href;
var specifiedUser = siteHref.split('#');
var userName = specifiedUser[1];
var userURL = 'http://soundcloud.com/' + userName;
var idGetter = SC.get('/resolve', { url: userURL }, function(user) {
SC.get('/users/', function() {
var userInfo = new Array(3);
userInfo[0] = user.username;
userInfo[1] = user.id;
userInfo[2] = user.public_favorites_count;
console.log(userInfo);
console.log(userInfo[2]);
});
});
I've added the variables at the top for a bit of context. To explain this function passes the two parts of the array that I've specified into the console, which is perfect. Only when I call the variable is shows up as undefined?
I also tried wrapping this in an additional function and has no luck calling it.
Anyone have idea where I've gone wrong?
You are calling two asynchronous functions. The results you get in the second one are ONLY valid inside that callback function or in a function you call from there. You can't use them globally afterwards because the async functions have not yet finished yet and thus they are still undefined.
Plus, if you're asking about the userInfo variable, that goes out of scope as soon as the callback returns so it is not available anywhere else.
See the comments I added to your code:
// Getting username from address bar and user ID from username
var siteHref = window.location.href;
var specifiedUser = siteHref.split('#');
var userName = specifiedUser[1];
var userURL = 'http://soundcloud.com/' + userName;
var idGetter = SC.get('/resolve', { url: userURL }, function(user) {
SC.get('/users/', function() {
var userInfo = new Array(3);
userInfo[0] = user.username;
userInfo[1] = user.id;
userInfo[2] = user.public_favorites_count;
console.log(userInfo);
console.log(userInfo[2]);
// ** you must use userInfo here or call some other function
// ** and pass userInfo to it
});
});
// ** you cannot use userInfo here as it is both out of scope and
// ** has not yet been set
The idGetter value will contain whatever the first call to SC.get() returns which will NOT be the eventual asynchronous result. If you tell us what SC.get() is, then we might be able to help you understand what it returns.
If SC.get() is the SoundCloud function, then it appears that it returns nothing and thus that is why idGetter is undefined.
FYI, I confirmed here in the SoundCloud source that SC.get() does not return anything.
If all you're trying to do is to create a new function that contains all this code and will call a callback when the results are available, you can just define that function and then call it:
function getUserInfo(callback) {
// Getting username from address bar and user ID from username
var siteHref = window.location.href;
var specifiedUser = siteHref.split('#');
var userName = specifiedUser[1];
var userURL = 'http://soundcloud.com/' + userName;
SC.get('/resolve', { url: userURL }, function(user) {
SC.get('/users/', function() {
var userInfo = new Array(3);
userInfo[0] = user.username;
userInfo[1] = user.id;
userInfo[2] = user.public_favorites_count;
console.log(userInfo);
console.log(userInfo[2]);
// ** you must use userInfo here or call some other function
// ** and pass userInfo to it
callback(userInfo);
});
});
// ** you cannot use userInfo here as it is both out of scope and
// ** has not yet been set
}
// then, you can call it like this:
getUserInfo(function(userData) {
// you can use userData here
});
idGetter isn't a function, you can't call it. If you want it to be a function, write:
var idGetter = function () {
SC.get('/resolve', { url: userURL }, function(user) {
SC.get('/users/', function() {
var userInfo = new Array(3);
userInfo[0] = user.username;
userInfo[1] = user.id;
userInfo[2] = user.public_favorites_count;
console.log(userInfo);
console.log(userInfo[2]);
});
});
};
Then you can do idGetter() to run the function.
I can only assume that userInfo[2] is showing as undefined since it would not make since for userInfo to be undefined. If that is the case, then it is probable that user.public_favorites_count is undefined. Trying printing that out to the console, and when it shows up as undefined, you'll need to determine why that is happening. Without more information about what that object is, or the code returning that object, there's not much more help the community will be able to provide.
This seems to either be working, or is a step forward.
var idGetter = function () { SC.get('/resolve', { url: userURL }, function(user) {
SC.get('/users/', function() {
var userInfo = new Array(3);
userInfo[0] = user.username;
userInfo[1] = user.id;
userInfo[2] = user.public_favorites_count;
console.log(userInfo);
console.log(userInfo[2]);
});
});
};
var idGetter = idGetter();
Setting the variable before the function, and re-setting it after. Not sure if this is standard practise, but seems to work to some extent.

Error assigning value to JavaScript class property

I am getting an error while setting a class property in javascript class. I am using nodejs prompt module to get user input and setting it to the class property. But i am getting following error.
TypeError: Cannot read property 'resultAge' of undefined
I figured it out that it has something to do with synchronization, but i am not able to figure it out that how to implement it for this situation.
Also i want to prompt user again until he has entered a valid number (I can not use a do while loop, what might be the solution?)
var prompt = require("prompt");
var ageTotal = function(){
this.resultAge = 0;
this.getUserAge = function(){
prompt.start();
//i want to run this until valid input is entered
prompt.get(["age"], function(err, result){
//I know i have to convert userInput to int but thats for later
this.resultAge += result.age
});
}
}
ageTotal.prototype.displayTotalAge = function(){
return this.resultAge;
}
var a = new ageTotal();
a.getUserAge();
var age = a.displayTotalAge();
console.log(age); //This is running before the above function finishes
EDIT:
The problem setting resultAge is solved but now the problem is var age = a.displayTotalAge(); is evaluated after console.log(age) which results in 0;
You need to pass the scope of ageTotal into the prompt.get callback:
var ageTotal = function(){
this.resultAge = 0;
this.getUserAge = function(){
var that = this;
prompt.start();
prompt.get(["age"], function(err, result){
that.resultAge += result.age
});
}
}

Categories