How to fix a Wait condition error? - protractor

I have a browser.wait() in an E2E test which is invoking a function defined in protractor.config.js file. When running the test, I get the following error:
Failed: Wait condition must be a promise-like object, function, or a Condition object
FYI - the function defined in protractor.config.js contains an If-condition which I need for two cases. When I remove the If-condition, the test runs fine. The function returns a: deferred.promise
What does this exactly means and how to fix it? Have been searching, but unfortunately can't anything related to it.
Function in protractor.config.js:
checkMail: function(user, subjectSent) {
const deferred = protractor.promise.defer();
var usermail;
var mailsubject;
var count = 0;
mailListener.on("mail", function(mail, seqno, attributes) {
var mailuid = attributes.uid;
var toMailbox = '[Gmail]/All Mail';
var i = ++count;
user = mail.to[0].address;
mailsubject = mail.subject;
if (i > 2) {
mailListener.stop();
return;
}
deferred.fulfill(mail);
});
if ((user === usermail) && (subjectSent === mailsubject)) {
return deferred.promise;
}
}
E2E it-function:
it("It should do xxxxxxx", (done) => {
browser.wait(browser.params.checkMail('user_email#yyyyy.com', 'Email subject'))
.then((email) => {
expect(email['subject']).toEqual("Email subject");
expect(email['headers'].to).toEqual( 'user_email#yyyyy.com' );
});
done();
});

Related

Why items are not being pushed in array

I am using MongoseDB in order to receive some information about an item. When i try to search for it, it finds it with no trouble, but for some reasons this function is not pushing them into my array. I think this might be because of some async functions and that the console.log() is triggered before any item is being pushed in there.
const getOrders = function(allOrders){
let promise = new Promise((succ, fail)=>{
let ordersTodisplay = []
for (let order of allOrders) {
if (!(order.orderId === null || order.orderItem === null)){
postMong.findById(order.orderItem, function (err, item) {
ordersTodisplay.push(item)
})
}
}
if(ordersTodisplay.length > 0){
succ(ordersTodisplay)
} else{
fail("no items")
}
})
return promise
}
router.get('/accountpage',function(req,res){
const userDB = req.session.username
if (userDB !== undefined && userDB){
userForm.findOne({ username : userDB }, function (err, user) {
const userOrders = user.userOrders;
if (userOrders.length > 1) {
getOrders(userOrders).then((result)=>{console.log(result)}, (fail)=>{console.log(fail)})
res.render('../view/accountpage',{username: userDB,orders: itemsToDisplay});
}
else{
res.render('../view/accountpage',{username: userDB,orders: "There are no orders"});
}
});
} else {
res.redirect("/login")
}
});
The result is : no items
You have to for the database call to complete and then push the data in the array like this, using async-await:
const getOrders = function(allOrders){
let promise = new Promise(async (succ, fail)=>{
let ordersTodisplay = []
for (let order of allOrders) {
if (!(order.orderId === null || order.orderItem === null)){
await postMong.findById(order.orderItem, function (err, item) {
ordersTodisplay.push(item)
})
}
}
if(ordersTodisplay.length > 0){
succ(ordersTodisplay)
} else{
fail("no items")
}
})
return promise
}
Your code is quite nested and that makes it hard to reason about what is happening.
To break down your code, you:
get a single user that has several order IDs referenced
load each order
respond with those orders (although you return itemsToDisplay that doesn't seem to be defined anywhere, so I'm a bit confused)
I'd try to capture that logical pattern in the code. A good trick is returning early to make the code less nested and interdependent:
router.get('/accountpage', function(req,res){
const userDB = req.session.username;
if (!userDB){
res.redirect("/login");
return;
}
loadUserOrders(userDB)
.then(function(orders) {
if(orders.length > 0) {
res.render('../view/accountpage', {username: userDB,orders: orders});
return;
}
// Note: consider returning just the empty array here, that already implies no orders
res.render('../view/accountpage', {username: userDB, orders: "There are no orders"});
})
.catch(function(error) {
//TODO: render error -- case not covered by your code
});
});
// Because this is an async function you can now await inside it, meaning no need for the callback syntax for mongoose
async function loadUserOrders(username) {
const user = await userForm.findOne({ username: username });
// Promise.all runs in parallel several promises and returns an array containing their results
// .map here turns the list of userOrders into a list of promises getting each order
return await Promise.all(user.userOrders.map((userOrder) => postMong.findById(userOrder.orderItem));
}
Notice how this code highlights that you are not explicitly handling the error case from loading orders.
You can further simplify this by using something like express-async-handler which will let your endpoint function be async as well:
const asyncHandler = require('express-async-handler');
router.get('/accountpage', asyncHandler(async function(req,res){
const userDB = req.session.username;
if (!userDB){
res.redirect("/login");
return;
}
// Note: there is no try-catch around loadUserOrders right now, so errors will result in a 500 courtesy of express-async-handler -- better than before
const orders = await loadUserOrders(userDB);
if(orders.length > 0) {
res.render('../view/accountpage', {username: userDB,orders: orders});
return;
}
// Note: consider returning just the empty array here, that already implies no orders
res.render('../view/accountpage', {username: userDB, orders: "There are no orders"});
}));
I think using async/await syntax all the way through leaves the code more consistent and easier to reason about than the mix of callbacks, new Promise and await that was suggested in another answer. In the end, the endpoint is not very complex, and I think the code can reflect that.

Kick command with reason

I created a kick command with reason, but it doesn't kick the member, I don't have any errors...
Also, why when I send the command, it deletes it?
I tried to fix it myself, but still doesn't work. Thanks for your help.
Here my code:
client.on('message', message => {
if(message.content.startsWith(prefix + "kick")) {
if(message.channel.type === 'DM') {
message.channel.send('This command can use only in guide');
return;
};
if(!message.member.hasPermission('KICK_MEMBERS')) {
const KickEmbed = new Discord.MessageEmbed()
.setColor("YELLOW")
.setAuthor(message.author.username)
.setDescription("Sorry, but you don't have the permission to use the kick command.")
message.channel.send(KickEmbed);
return;
};
let mentionMember = message.mentions.members.first();
if(!mentionMember) {
const ErrEmbed = new Discord.MessageEmbed()
.setColor('YELLOW')
.setAuthor(message.author.username)
.setDescription('**Usage:** `y!kick <#user> or ID` You need to mention an user!')
message.channel.send(ErrEmbed);
return;
};
let args = message.content.slice(prefix.length).trim().split(/ +/g);
if(!args.lenght) {
const ReasonError = new Discord.MessageEmbed()
.setColor('YELLOW')
.setAuthor(message.author.username)
.setDescription('Before kicking this member, you need to provide a reason of your kick.')
message.channel.send(ReasonError)
return;
};
let authorHighestRole = message.member.roles.highest.position;
let mentionHighestRole = mentionMember.roles.highest.position;
if(mentionHighestRole >= authorHighestRole) {
message.channel.send('You can`t kick members with equal or higher position');
return;
};
if(!mentionMember.kickable) {
message.channel.send('I have no permissions to kick this user');
return
};
mentionMember.kick()
.then(() => message.channel.send(`Kicked ${mentionMember.tag} with reason: ${args}`))
.catch(console.error);
}
}
);
Should look at docs before posting:
https://discord.js.org/#/docs/main/stable/class/GuildMember?scrollTo=kick
But anyways:
<Member>.kick(Reason), it's just a string you pass in.
Also args looks like an array so you can't just use args inside of a string, try args.join(" "). (Inside of message.channel.send("Kicked..."))
All in all here's the change:
So mentionMember.kick() => mentionMember.kick(args.join(" "))

AEM - How to tweak activation error message

We are working in an AEM 6.1 environment and have created an activation preprocessor that will stop pages from being activated if certain attributes are not set. That works great but we'd also like to change the error message that's displayed by the activation process when the preprocessor throws a ReplicationExcdeption. Can anyone point me to the code that actually displays the error message?
We overrided several functions in SiteAdmin.Actions.js. Copy it from libs folder /apps/cq/ui/widgets/source/widgets/wcm/SiteAdmin.Actions.js or use CQ.Ext.override
We need to override CQ.wcm.SiteAdmin.scheduleForActivation and CQ.wcm.SiteAdmin.internalActivatePage methods.
We do it with using the following code
CQ.wcm.SiteAdmin.internalActivatePage = function(paths, callback) {
if (callback == undefined) {
// assume scope is admin and reload grid
var admin = this;
callback = function(options, success, response) {
if (success) admin.reloadPages();
else admin.unmask();
};
}
preActionCallback = function(options, success, response) {
if (success) {
var responseObj = CQ.Util.eval(response);
if (responseObj.activation) {
CQ.HTTP.post(
CQ.shared.HTTP.externalize("/bin/replicate.json"),
callback,
{ "_charset_":"utf-8", "path":paths, "cmd":"Activate" }
);
} else {
CQ.wcm.SiteAdmin.preactivateMessage(responseObj);
}
}else{
CQ.Ext.Msg.alert(
CQ.I18n.getMessage("Error"), CQ.I18n.getMessage("Could not activate page."));
}
admin.unmask();
};
CQ.HTTP.get(
"/apps/sling/servlet/content/preActivateValidator.html?path=" + paths,
preActionCallback
);
};
This path /apps/sling/servlet/content/preActivateValidator.html (You can use any other link and extension) returns json with some info about messages, which are parsed in custom method and generates custom error messages CQ.wcm.SiteAdmin.preactivateMessage:
CQ.wcm.SiteAdmin.preactivateMessage = function(responseObj) {
var message = "";
var incorrectItems = responseObj.incorrectItems;
if (responseObj.countOfIncorrectItems > 1) message = message + "s";
if (responseObj.missingMetadata) {
message = message + "Please, set \"Programming Type\" for next videos:<br/>";
var missingMetadataPaths = responseObj.missingMetadata;
for(var i = 0; i < missingMetadataPaths.length; i++){
message = message + ""+missingMetadataPaths[i].path+"<br/>";
}
message += "<br/>";
}
if(message == ""){
message = "Unknown error.";
}
CQ.Ext.Msg.alert(
CQ.I18n.getMessage("Error"), CQ.I18n.getMessage(message));
}
So you can implement component or servlet which will verify your attributes and will generate JSON.

How to find if one of the element is present after the page is displayed

The protractor has to verify whether the error message is present in the page. So the error message can be either in "alert-message" class or in "fail-heading" class.
If i do like below it is successful only if the alert-message className is displayed. But sometimes i get the className as "fail-heading".
var waitforele_confirmation = by.className('alert-message');
browser.wait(function(){return browser.driver.isElementPresent(waitforele_confirmation)}, 60000);
expect(browser.driver.isElementPresent(waitforele_confirmation)).toBeTruthy();
So i want to dynamically check which element is present after the page is loaded. Also i want to use the browser.wait function when waiting for each element. I just did the below pseudo code just to understand.
while (i<120 and !found)
{
int i=0;
if (element(by.className('alert-message')).isPresent())
{
found = true;
}
else if (element(by.className('fail-heading')).isPresent())
{
found = true;
}
else
{
browser.sleep(500);
}
i++;
}
Can someone convert the pseudo code in protractor pls?
let EC = protractor.ExpectedConditions;
let alertMessageVisibility = EC.visibilityOf($('.alert-message'));
let failHeadingVisibility = EC.visibilityOf($('.fail-heading'));
browser.wait(EC.or(alertMessageVisibility, failHeadingVisibility), 60000, "Alert message or fail heading should become visible in 60 seconds, but it wasn't")
Below code will work as you expected:
var EC = protractor.ExpectedConditions;
var alertMessageElement = element(by.className('alert-message'))
var failHeadingElement = element(by.className('fail-heading'))
/*browser.wait() will be keep on checking for error messages until either
any one of the error message found or specified timeout limit reached*/
browser.wait(function(){
EC.visibilityOf(alertMessageElement).call().then(function(isAlertMessagePres
ent){
if(isAlertMessagePresnt){
return true;
}
})
EC.visibilityOf(failHeadingElement).call().then(function(isFailedHeadingPresent) {
if(isFailedHeadingPresent){
return true;
}
})
},10000,'No error message found within specified timeout of 10 seconds
');

How to get Meteor.Call to return value for template?

I've tried to understand this post regarding this concept, however, I'm failing to get it. I have the following simple setup:
/server/test.js
Meteor.methods({
abc: function() {
var result = {};
result.foo = "Hello ";
result.bar = "World!";
return result;
}
});
/client/myapp.js
var q = Meteor.call('abc');
console.log(q);
This structure returns to the console undefined.
If I change the myapp.js file to:
Meteor.call('abc', function(err, data) {
!err ? console.log(data) : console.log(err);
}
I receive the Object in my console.
Ideally this is what I'd like to be able to do, but it doesn't work, stating in the console: Cannot read property 'greeting' of undefined
/client/myapp.js
var q = Meteor.call('abc');
Template.hello.greeting = function() {
return q.foo;
}
Any help in passing the data from the server object into the template would be greatly appreciated. I'm still learning JavaScript & Meteor.
Thanks!
From the Meteor.call documentation:
On the client, if you do not pass a callback and you are not inside a stub, call will return undefined, and you will have no way to get the return value of the method. That is because the client doesn't have fibers, so there is not actually any way it can block on the remote execution of a method.
So, you'll want to do it like this:
Meteor.call('abc', function(err, data) {
if (err)
console.log(err);
Session.set('q', data);
});
Template.hello.greeting = function() {
return Session.get('q').foo;
};
This will reactively update the template once the data is available.
This happens because Npm.require has Async behavior. That's the reason that you have to write a callback for Meteor.call.
But there is a solution, just use install(mrt add npm) and you'll get a function named Meteor.sync(//...) with this you can do both games: sync and async in your Meteor.call().
Reference: http://www.sitepoint.com/create-a-meteor-app-using-npm-module/
You can get the return value of a Meteor method for use in a template by using a reactive variable. Check out the working demonstration on Meteorpad
I went for a ghetto solution. But, it works for me, which is what matters, to me. Below is my code, which, in concept, I think, solves OP's problem.
In the client's main.js:
Meteor.setInterval(function() {
confirmLogin();
}, 5000);
This runs the confirmLogin() function every five seconds.
The confirmLogin function (in the client's main.js):
function confirmLogin() {
Meteor.call('loggedIn', function (error, result) {
Session.set("loggedIn", result);
});
}
The loggedIn method (in the server's main.js):
loggedIn: function () {
var toReturn = false;
var userDetails = Meteor.user();
if (typeof userDetails["services"] !== "undefined") {
if (typeof userDetails["services"]["facebook"] != "undefined") {
toReturn = true;
}
}
return toReturn;
},
The relevant helper:
loggedIn: function () {
return Session.get("loggedIn");
}