I have the below function for login, which would console "successful" on success and "Failed" for files ones.
function login() {
return new Promise(function (resolve, reject) {
var username = element(by.name('txtLogin'));
var password = element(by.id('txtPassword'));
var signInButton = element(by.id('btnSignIn'));
for (var i = 0; i < testData.length; i++) {
if (testData[i].env === appConfig) {
username.sendKeys(testData[i].user);
password.sendKeys(testData[i].pass);
signInButton.click();
console.log("Successfully Clicked on the Sign In Button!");
break;
}
}
browser.getTitle().then(function (title) {
if (title == "Page Title") {
resolve("Successfull");
} else {
reject("Failed");
}
});
});
};
And the following test
describe('Login Scenarios', function () {
beforeEach(function () {
login();
});
it('Valid Credentials, Should login successfully from util class', function () {
console.log('Successfully logged in!');
});
});
I am seeing very strange behavior here. This line executes and consoles output even before the page is fully loaded.
console.log("Successfully Clicked on the Sign In Button!");
and the below code never gets executed.
browser.getTitle().then(function (title) {
if (title == "Page Title") {
resolve("Successfull");
} else {
reject("Failed");
}
});
And in the end i see the following error.
failed: error while waiting for protractor to sync with the page: "cannot re ad property '$$testability' of undefined"
I am pretty sure, i have messed up something here. But unable to figure out what's wrong that am doing here.
login returns a promise, but you're not signalling to Jasmine/Protractor that it needs to wait for it to finish. You can do this using the done callback:
beforeEach(function (done) {
login().then(function () {
done();
});
});
See the Jasmine documentation (which seems to have been inexplicibly hidden away in their new website layout...) for more info.
Related
I am trying to retrieve the access token from an API (https://github.com/Axosoft/node-axosoft/)
To receive an access token we have to follow this process:
var axo = nodeAxosoft(axosoftUrl, credentials);
axo.Api.getLoginUrl(function(url) {
// open browser using authorizationUrl and get code parameter from
//redirected Url after login
var code = 'code received from redirect';
axo.Api.exchangeCodeForToken(code);
});
As I did not understood exactly how to get the code following that example nor what is the url parameter on getLoginUrl, I did it on my own.
I have a login route that redirects the user to the axosoft website for authentication and redirects the user to the /authorization-process route on my application.
On the /authorization-process I get the code returned by the login and call a function that should get the access token by calling:
axo.Api.exchangeCodeForToken(code);
Code:
var axosoft_code = req.query.code;
console.log(axosoft_code);
var token = request.exchangeAuthCodeForAccessToken(axosoft_code)
.then(function(token)
{
res.send(token);
})
The Method:
var connection = nodeAxosoft(client_url, credentials);
return new Promise(function(resolve, reject){
console.log("CODE: ", axosoft_code)
var token = connection.Api.exchangeCodeForToken(axosoft_code, function(token){
console.log(token);
resolve(token)
})
The problem is that returns null
I had a look at the API lib api.js and found that:
https://github.com/Axosoft/node-axosoft/blob/master/lib/api.js
function exchangeCodeForToken(code, callback) {
_credentials.code = code;
_access_token = '';
_authenticateCredentails(function (err) {
if (!err) {
callback(null, _access_token);
} else {
callback(err);
}
})
}
So I have two questions:
Does anyone has an Idea what am I doing wrong?
What would be necessary to code the callback function?
The method expects a callback function but I don't really know how to do it.
EDIT:
return new Promise(function(resolve, reject){
var token = connection.Api.exchangeCodeForToken(axosoft_code, function(response,err){
if(!err){
console.log("token",response)
resolve(token);
}
else{
console.log("error",err)
resolve(token);
}
});
})
OR
var token = connection.Api.exchangeCodeForToken(axosoft_code, function(response,err){
if(!err){
console.log("token",response.body)
return response.body;
}
else{
console.log("error",err)
return err;
}
});
I am giving to my callback function two args (response and err), my problem is that I am falling at the else condition.
The output of err is something similar to a token though the documentation here shows that it should be like that:
{
"error" : "invalid_request",
"error_description" : "One or more parameters are missing: client_secret"
}
Another point is that the page is frozen waiting for something to happen but nothing happens.
Given that this is the input:
function exchangeCodeForToken(code, callback) {
_credentials.code = code;
_access_token = '';
_authenticateCredentails(function (err) {
if (!err) {
callback(null, _access_token);
} else {
callback(err);
}
})
}
You should format your call as:
exchangeCodeForToken(axosoft_code, function(err, response) {
if (err) {
// Deal with error
} else {
// Deal with response
}
}
Node functions often pass through error variables first so that you have to receive them, which is considered good practice.
I'm trying to start a windows service from a node script. This service has a bad habit of hanging and sometimes requires a retry to start successfully. I have a promise while loop setup (Please feel free to suggest a better way). The problem I'm having, is with each loop the sc.pollInterval output writes duplicate results in the console. Below is an example of the duplicate content I see in the console, this is after the second iteration in the loop, i'd like it to only display that content once.
sc \\abnf34873 start ColdFusion 10 Application Server
sc \\abnf34873 queryex ColdFusion 10 Application Server
SERVICE_NAME: ColdFusion 10 Application Server
TYPE : 10 WIN32_OWN_PROCESS
STATE : 2 START_PENDING
(NOT_STOPPABLE, NOT_PAUSABLE, IGNORES_SHUTDOWN)
WIN32_EXIT_CODE : 0 (0x0)
SERVICE_EXIT_CODE : 0 (0x0)
CHECKPOINT : 0x0
WAIT_HINT : 0x7d0
PID : 0
FLAGS :
SERVICE_NAME: ColdFusion 10 Application Server
TYPE : 10 WIN32_OWN_PROCESS
STATE : 2 START_PENDING
(NOT_STOPPABLE, NOT_PAUSABLE, IGNORES_SHUTDOWN)
WIN32_EXIT_CODE : 0 (0x0)
SERVICE_EXIT_CODE : 0 (0x0)
CHECKPOINT : 0x0
WAIT_HINT : 0x7d0
PID : 13772
FLAGS :
Here is the code I have. Basically, I'm going to try to start the service 3 times. If it doesn't, then I throw and error. One thing to note, when I attempt to start the service, but it's stuck in 'Start_pending' state, I kill the process and then try to start it again.
var retryCount = 0;
// Start the colfusion service
gulp.task('start-coldfusion-service', function(done) {
var serviceStarted = false;
console.log("Starting coldfusion service..");
// This says we're going to ask where it's at every 30 seconds until it's in the desired state.
sc.pollInterval(30);
sc.timeout(60);
retryCount = 0;
tryServiceStart().then(function(result) {
// process final result here
done();
}).catch(function(err) {
// process error here
});
});
function tryServiceStart() {
return startService().then(function(serviceStarted) {
if (serviceStarted == false) {
console.log("Retry Count: " + retryCount);
// Try again..
return tryServiceStart();
} else {
return result;
}
});
}
function startService() {
return new Promise(function(resolve, reject) {
var started = true;
// Make sure the coldfusion service exists on the target server
sc.query(targetServer, { name: 'ColdFusion 10 Application Server'}).done(function(services) {
// if the service exists and it is currentl stopped, then we're going to start it.
if (services.length == 1) {
var pid = services[0].pid;
if (services[0].state.name == 'STOPPED') {
sc.start(targetServer, 'ColdFusion 10 Application Server')
.catch(function(error) {
started = false;
console.log("Problem starting Coldfusion service! error message: " + error.message);
console.log("retrying...");
retryCount++;
if (parseInt(retryCount) > 2) {
throw Error(error.message);
}
})
.done(function(displayName) {
if (started) {
console.log('Coldfusion service started successfully!');
}
resolve(started);
});
} else if (services[0].state.name == 'START_PENDING') {
kill(pid, {force: true}).catch(function (err) {
console.log('Problem killing process..');
}).then(function() {
console.log('Killed hanging process..');
resolve(false);
});
}
} else {
console.log("Could not find the service in a stopped state.");
resolve(false);
}
});
});
}
Not too sure why you get duplicate results in the console, however below are some ideas on how the code might be better written, chiefly by promisifying at the lowest level.
Sticking fairly closely to the original concept, I ended up with this ...
Promisify sc commands
sc commands return something which is promise-like but with a .done() method that does not, in all probability, possess the full power of a genuine .then()
promisify each command as .xxxAsync()
by adopting each command's .done as .then, Promise.resolve() should be able to assimilate the promise-like thing returned by the command.
;(function() {
commands.forEach(command => {
sc[command].then = sc[command].done;
sc[command + 'Async'] = function() {
return Promise.resolve(sc[command](...arguments));
};
}).
}(['start', 'query'])); // add other commands as required
gulp.task()
promise chain follows its success path if service was opened, otherwise its error path
no need to test a result to detect error conditions in the success path.
gulp.task('start-coldfusion-service', function(done) {
console.log('Starting coldfusion service..');
// This says we're going to ask where it's at every 30 seconds until it's in the desired state.
sc.pollInterval(30);
sc.timeout(60);
tryServiceStart(2) // tryServiceStart(maxRetries)
.then(done) // success! The service was started.
.catch(function(err) {
// the only error to end up here should be 'Maximum tries reached'.
console.err(err);
// process error here if necessary
});
});
tryServiceStart()
orchestrate retries here
function tryServiceStart(maxRetries) {
return startService()
// .then(() => {}) // success! No action required here, just stay on the success path.
.catch((error) => {
// all throws from startService() end up here
console.error(error); // log intermediate/final error
if(--maxRetries > 0) {
return tryServiceStart();
} else {
throw new Error('Maximum tries reached');
}
});
}
startService()
form a fully capable promise chain by calling the promisified versions of sc.query() and sc.start()
console.log() purged in favour of throwing.
thrown errors will be caught and logged back in tryServiceStart()
function startService() {
// Make sure the coldfusion service exists on the target server
return sc.queryAsync(targetServer, { name: 'ColdFusion 10 Application Server'})
.then((services) => {
// if the service exists and it is currently stopped, then start it.
if (services.length == 1) {
switch(services[0].state.name) {
case 'STOPPED':
return sc.startAsync(targetServer, 'ColdFusion 10 Application Server')
.catch((error) => {
throw new Error("Problem starting Coldfusion service! error message: " + error.message);
});
break;
case 'START_PENDING':
return kill(services[0].pid, { 'force': true })
.then(() => {
throw new Error('Killed hanging process..'); // successful kill but still an error as far as startService() is concerned.
})
.catch((err) => {
throw new Error('Problem killing process..');
});
break;
default:
throw new Error("Service not in a stopped state.");
}
} else {
throw new Error('Could not find the service.');
}
});
}
Checked only for syntax error, so may well need debugging.
Offered FWIW. Feel free to adopt/raid as appropriate.
I have found another npm package called promise-retry that seems to have addressed the issue I was having. At the same time, I believe it made my code a little more clear as to what it's doing.
gulp.task('start-coldfusion-service', function(done) {
var serviceStarted = false;
console.log("Starting coldfusion service..");
// Since starting a service on another server isn't exactly fast, we have to poll the status of it.
// This says we're going to ask where it's at every 30 seconds until it's in the desired state.
sc.pollInterval(30);
sc.timeout(60);
promiseRetry({retries: 3}, function (retry, number) {
console.log('attempt number', number);
return startService()
.catch(function (err) {
console.log(err);
if (err.code === 'ETIMEDOUT') {
retry(err);
} else if (err === 'killedProcess') {
retry(err);
}
throw Error(err);
});
})
.then(function (value) {
done();
}, function (err) {
console.log("Unable to start the service after 3 tries!");
process.exit();
});
});
function startService() {
var errorMsg = "";
return new Promise(function(resolve, reject) {
var started = true;
// Make sure the coldfusion service exists on the target server
sc.query(targetServer, { name: 'ColdFusion 10 Application Server'}).done(function(services) {
// if the service exists and it is currentl stopped, then we're going to start it.
if (services.length == 1) {
var pid = services[0].pid;
if (services[0].state.name == 'STOPPED') {
sc.start(targetServer, 'ColdFusion 10 Application Server')
.catch(function(error) {
started = false;
errorMsg = error;
console.log("Problem starting Coldfusion service! error message: " + error.message);
console.log("retrying...");
})
.done(function(displayName) {
if (started) {
console.log('Coldfusion service started successfully!');
resolve(started);
} else {
reject(errorMsg);
}
});
} else if (services[0].state.name == 'START_PENDING') {
kill(pid, {force: true}).catch(function (err) {
console.log('Problem killing process..');
}).then(function() {
console.log('Killed hanging process..');
reject("killedProcess");
});
} else {
// Must already be started..
resolve(true);
}
} else {
console.log("Could not find the service in a stopped state.");
resolve(false);
}
});
});
}
We are developing alexa skill using alexa-app, in one of our intent we are trying to fetch albums from facebook and on success/failure we want alexa to respond accordingly. But intent is not waiting for FB call to complete. Below is the code snippet we are using:
function fetchAlbums(){
return new Promise(resolve =>{
FB.api("/me/albums", function (res) {
if (res && !res.error) {
// If we have data
if (res.data) {
resolve("Got Albums");
} else {
// REPORT PROBLEM WITH PARSING DATA
resolve("Error getting albums");
}
} else {
// Handle errors here.
resolve("Error hitting endpoint");
}
});
});
}
alexaApp.intent("readFeedIntent", {
"utterances": [
"read my facebook feed", "read facebook feed", "read feed"
]
},
function(request, res1) {
// Again check if we have an access token
if(request.hasSession() && !accessToken){
accessToken = request.data.session.user.accessToken;
FB.setAccessToken(accessToken);
}
if (accessToken) {
var session = request.getSession();
fetchAlbums().then(function(data){
console.log(data);
res1.say(data);
});
} else {
res1.say(noAccessToken, tryLaterText).send();
}
});
It is not throwing any errors, but Alexa is not speaking the anything where I can see the response in the console logs.
If I add: res1.say("Whatever!") at the end of function, then Alexa will speak 'Whatever' in response to this intent.
Got it solved my myself:
instead of this:
fetchAlbums().then(function(data){
console.log(data);
res1.say(data);
})
you have to return it, like:
return fetchAlbums().then(function(data){
console.log(data);
res1.say(data);
})
On page load I request the user to allow notifications like so:
document.addEventListener('DOMContentLoaded', function () {
if (Notification.permission !== "granted")
Notification.requestPermission();
});
I have then wrote a function to send notifications to the user which works fine:
function push(title, message, location) {
if(!Notification) { return; }
if (Notification.permission !== "granted") { return; }
var notification = new Notification(title, {
icon: 'http://cdn.sstatic.net/stackexchange/img/logos/so/so-icon.png',
body: message,
});
notification.onclick = function () {
window.open(location);
};
}
Used like this:
push('Test', 'Test Message', 'http://example.com');
I then have a PHP script, for the time being it is static and returns the same thing for testing soon to be database driven.
session_start();
header('Content-Type: application/javascript');
if(!isset($_SESSION['notify'])) {
echo json_encode(array(
'Title' => 'Welcome to our Site',
'Message' => 'You are current recieving notifications.',
'Location' => 'http://example.com/',
'State' => true
),true);
$_SESSION['notify'] = true;
} else {
echo json_encode(array(
'State' => false
), true);
}
This works fine too when the page is directly accessed. The issue occurs in my AJAX request:
setInterval(function() {
$(document).ready(function() {
$.post('../notify/get_notify.php').done(function(response) {
if(response.State) {
push(response.Title, response.Message, response.Location);
}
console.log(response);
});
});
}, 5000);
The issue is here! When I check the network, every 5 seconds a request is being made and the response is coming back. However, nothing is logged in the console and the push() method is not executed.
Any help would be greatly appreciated.
I found the issue, due to the scope of the $.post() I was unable to retrieve anything, so instead I returned the value of the response like this:
setInterval(function() {
$(document).ready(function() {
response = $.post('../notify/get_notify.php').done(function(response) {
return response;
});
if(response.State) {
push(response.Title, response.Message, response.Location);
}
});
}, 5000);
I am trying to display notifications with jQuery locally on page load. The notification is showing correctly in Firefox, Firefox Developer, and Chrome. The notification is not appearing in Safari despite allowed in notification preference settings.
Similar code is working from MDN site https://developer.mozilla.org/en/docs/Web/API/notification.
Snippet is below.
// Display a sample notification
if (window.Notification) {
return $(".au-notifications-page").show(function() {
var notification;
notification = new Notification(
'Success Text', {
//tag: $("[name=tag]").val(),
body: 'Success Message',
iconUrl: 'img/avatar-male.png',
icon: 'img/avatar-male.png'
});
return notification.onclick = function() {
notification.close();
window.open().close();
return window.focus();
};
});
};
Complete code is below.
$(document).ready(function () {
// Request permission on site load
Notification.requestPermission().then(function(result) {
if (result === 'denied') {
//alert('denied');
$(".au-notif-disabled-header").removeClass('hide');
$(".au-notif-disabled-header .btn").addClass('hide');
return;
}
if (result === 'default') {
//alert('ignored');
$(".au-notif-disabled-header").removeClass('hide');
return;
}
//alert('granted');
$(".au-notif-disabled-header").addClass('hide');
});
// Request permission with button
$('.au-notif-disabled-header .btn').click(function () {
Notification.requestPermission().then(function(result) {
if (result === 'denied') {
$(".au-notif-disabled-header").removeClass('hide');
$(".au-notif-disabled-header .btn").addClass('hide');
return;
}
if (result === 'default') {
$(".au-notif-disabled-header").removeClass('hide');
return;
}
$(".au-notif-disabled-header").addClass('hide');
});
});
$( ".au-notification-icon" ).hover(
function() {
$(".au-notifications-menu .au-notif-msg-realtime").slideDown();
$('.au-notification-icon .badge').html("2");
}, function() {
$(".au-notifications-menu .au-notif-msg-realtime").slideUp();
$('.au-notification-icon .badge').html("1");
}
);
//To show notification received while on notifications page
$(".au-notif-msg-realtime").hide();
//$(".au-notifications-page .au-notif-msg-realtime").slideDown();
$(".au-notifications-page .au-notif-msg-realtime").slideDown({
complete: function(){
$('.au-notification-icon .badge').html("2");
$('head title').html("(2) Notifications");
}
});
// Display a sample notification
if (window.Notification) {
return $(".au-notifications-page").show(function() {
var notification;
notification = new Notification(
'Success Heading', {
body: 'Success Text',
iconUrl: 'img/avatar-male.png',
icon: 'img/avatar-male.png'
});
return notification.onclick = function() {
notification.close();
window.open().close();
return window.focus();
};
});
};
});
EDIT 1: Safari throws this exception
undefined is not an object (evaluating 'Notification.requestPermission().then')
You have to use a callback function for Safari, since it doesn't return a Promise.
According to MDN:
This uses the promise-version of the method, as supported in recent
implementations (Firefox 47, for example.) If you want to support
older versions, you might have to use the older callback version,
which looks like this:
Here's the sample code they gave:
Notification.requestPermission(function (permission) {
// If the user accepts, let's create a notification
if (permission === "granted") {
var notification = new Notification("Hi there!");
}
});
To support Safari notifications, this is what I ended up with:
try {
Notification.requestPermission()
.then(() => doSomething())
} catch (error) {
// Safari doesn't return a promise for requestPermissions and it
// throws a TypeError. It takes a callback as the first argument
// instead.
if (error instanceof TypeError) {
Notification.requestPermission(() => {
doSomething();
});
} else {
throw error;
}
}
A better solution is to wrap the results in a Promise and then (no pun intended) run your code. This code works on all browsers (including Safari) and without a complicated if block (concept is discussed in detail in this question)
Promise.resolve(Notification.requestPermission()).then(function(permission) {
// Do something
});
This works because Promise.resolve does nothing to a Promise, but will convert the Safari requestPermission() to a Promise.
Note that iOS Safari still does not support the Notification API so you will need to check if it is available first
To return a promise which doesn't resolve until the user grants or denies permission to show notifications:
if (!permissionPromise && Notification.permission === 'granted' ) {
permissionPromise = Promise.resolve(Notification.permission);
}
if (!permissionPromise) {
permissionPromise = new Promise(function (resolve, reject) {
// Safari uses callback, everything else uses a promise
var maybePromise = $window.Notification.requestPermission(resolve, reject);
if (maybePromise && maybePromise.then) {
resolve(maybePromise);
}
});
}
return permissionPromise;