Protractor - get browser title as string - string

I'd like to store initial device title, which is accessible via GUI in browser as page title, and reuse it later in the code. E.g. I want to change name of device if it is set to some particular name only, and to store an old device name in browser console log. Code might look like this:
var some_name = 'Some Name';
var title = browser.getTitle();
console.log(title);
if (title.equals('Some Name')){
some_name = 'Some Other Name';
}
setDeviceName(some_name);
unfortunately, to that protractor responds with
TypeError: title.equals is not a function
How is it possible to extract browser title as string?
Thanks.
UPDATE:
Thanks everyone, the solution due to igniteram1 is
var some_name = 'Some Name';
some_name = browser.getTitle().then(function(webpagetitle){
if (webpagetitle === 'Some Name'){
return 'Some Other Name';
}else{
return 'Some Name'
}
});
expect(some_name).toEqual('Some Other Name');

In addition to #Sudharshan Selvraj's answer,its good practice to use strict equality === instead of == and to return the value.
var some_name = 'Some Name';
some_name = browser.getTitle().then(function(webpagetitle){
if (webpagetitle === 'Some Name'){
return 'Some Other Name';
}else{
return 'Some Name';
}
});
expect(some_name).toEqual('Some Other Name');

If you aware of the new asyc/await you may use:
expect(await browser.getTitle()).toEqual("myCustomTitle");

In protractor whatever information you need from browser will be a promise and not a string. in order to fetch the value you need to resolve the promise. look at the below example.
var title = browser.getTitle();
title.then(function(webpagetitle){
if (webpagetitle == 'Some Name'){
some_name = 'Some Other Name';
}
})

.equals() is not a function in JavaScript, but it's a valid function in Java. In Java Script, we have '==', '===' as an alternative for performing equal operations.
var x=10
== compare only values of operands, not type of operands
example:
x==10 => true
x=="10" => true
=== compare both values and type of operands
example:
x===10 => true
x==="10" => false(type of x in int and "10" is string)
var someName;
browser.getTitle().then(function (webPageTitle) {
if (webPageTitle == 'Some Name'){
someName= 'Some Other Name';
}
});

I've got a simple function in protractor to get the title when in the promise it shows the title properly, but when I do console.log it gives :
Title Got is ManagedPromise::123 {[[PromiseStatus]]: "pending"}
describe('Protractor Demo App', function() {
it('should have a title', function(one) {
browser.waitForAngularEnabled(false);
browser.get('https://tasyah.com');
titleGot = browser.getTitle().then(function(promisedResult){
console.log("Title is : " + promisedResult);
return promisedResult;
})
console.log("Title Got is " + titleGot);
expect(titleGot).toEqual('Tasyah: Online Shopping For Fashion And Costume Jewellery');
console.log("Title Got is " + titleGot);
});
});
Any explanation on this is appreciated...
Thanks
Naveen

Page Object .po file
getTableRows() {
return element.all(by.css('[data-qa="qa-table"] tr'));
}
.e2e.spec file
<PROMISE-APPROCH>
obj.getTableRows().then((list)=> {
list.count().then((totalRows)=>{
expect(totalRows).toBe(10); // example
})
});
<ASYNC-AWAIT-APPROCH>
const rowsCount = await obj.getTableRows().count();
expect(rowsCount).toBe(10); // example

Related

Unable to run includes() method on string returned by fs.readFileSync()

having a bit of trouble:
let data = fs.readFileSync(pathToCsv, "utf8");
the value of data comes out to be:
clan,mem1,mem2,mem3,mem4,language,managerID,serverJoinedDate
pm,
pm
(through console.log())
but still data.toString().includes("pm") is false.
Here is my full code:
const filter = (m) => m.author.bot === false;
await ogMessage.author.dmChannel
.awaitMessages(filter, {
max: 1,
time: 60000,
})
.then((collected) => {
if (clans[parseInt(collected.toJSON()[0].content) - 1]) {
let clan = clans[parseInt(collected.toJSON()[0].content) - 1];
let data = fs.readFileSync(pathToCsv, "utf8");
console.log(typeof clan);
// let reg = new RegExp(clan, "g");
// let count = (data.match(reg) || []).length;
if (data.split(",").includes(clan)) {
ogMessage.author.send(
"People from this clan are already registered!\nPlease contact the hosts for help!"
);
return;
} else {
teamCheck = true;
}
} else {
ogMessage.author.send("Invalid Clan! Please try again!");
return;
}
})
.catch((collected) => {
try {
console.log("Error" + collected);
} catch (e) {
console.log(e);
}
});
if (teamCheck === false) {
return;
}
I have tried splitting the data, using regular expressions but nothing seems to work on the string returned by
readFileSync()
PS. I am making a discord bot.
In the source string you have pm with a space before it. That's why after calling split("," you end up with the element " pm" in the result array and "pm" is not equal to it.
You just need to trim spaces in all elements before searching some string in it
The problem was in the clans array.
I was defining it as such:
var clans = fs.readFileSync(pathToData, "utf8").split("\n");
but the problem was that the readFileSync() method added an "\r" after every string of the array. That is why it was not able to match the clan string to the data
So what worked was var clans = fs.readFileSync(pathToData, "utf8").split("\r\n");
Now, the array includes only the string, and the includes() method can find a match!

How to concat string in node js to call column

I wish to make call different column if user using different language, example in one collection I have description_en for English and description_ind for Indonesia.
The problem if I concat it is not working.
This is example code
let lang = req.params.lang;
order_status_description:element.orderstatus.description + lang,
this is my complete code
const MyOrderResource = (req,res,next)=>{
let lang = req.params.lang;
let arrayResult = [];
req.order.forEach(element => {
let arrayItem =[];
element.orderitem.forEach(item=>{
arrayItem.push({
_id:item._id,
product_id:item.product_id,
product_name:item.product_name,
description:item.description,
unit:item.unit,
price:item.price,
fprice:'Rp ' + new Intl.NumberFormat().format(item.price),
quantity:item.quantity,
amount:item.amount,
fprice:'Rp ' + new Intl.NumberFormat().format(item.amount),
});
});
arrayResult.push({
_id:element._id,
user_id:element.user_id,
date:element.date,
store_id:element.store_id,
store_name:element.profile[0].name,
store_address:element.profile[0].address,
total:element.total,
ftotal:'Rp ' + new Intl.NumberFormat().format(element.total),
order_status_code:element.orderstatus.code,
order_status_description:element.orderstatus.description + lang,
order_item:arrayItem,
});
});
res.status(201).json({
data:arrayResult,
status : 200,
})
}
module.exports = MyOrderResource;
For this code, I wish to call, column description_en, but the result is undefined
Thanks for helping
You can use something like
order_status_description : element.orderstatus["description" + lang]
In the posted snipet, it instructs to concat the value of key- 'element.orderstatus.description' to the value of key - 'lang'

undefined parameter received in Bixby function

I'm trying to process an utterance in the format "Get News from Impeachment Sage" where Impeachment Sage corresponds to an enum of publication names. Bixby is successfully understanding the utterance and trying to call my goal (GetNewsByName) but the trained Value is not arriving at the function. (This is based off the user persistence data example).
The operative portion of the function is thus:
function getNewsByName(altBrainsNames) {
// const name = "Impeachment Sage" //hard coded for testing
const url = properties.get("config", "baseUrl") + "altbrains"
console.log("i got to restdb.js and the url is ", url);
console.log("AltBrainsNames is", altBrainsNames)
const query = {
apikey: properties.get("secret", "apiKey"),
q: "{\"" + "name" + "\":\"" + name + "\"}"
// q: "{}"
}
console.log("query", query)
const options = {
format: "json",
query: query,
cacheTime: 0
}
const response = http.getUrl(url, options)
if (response) {
const content1 = response
// const altBrainsData = response[0][properties.get("config", "altbrainsName")]
// altbrainsData.$id = response[0]["_id"]
console.log('content1', content1);
console.log('identifier', content1)
return content1
} else {
// Doesn't exist
console.log('doesnae exist');
return
}
}
What is happening here where the Value is not reaching the function?
The Action model is:
action (GetNewsByName) {
description ("Get news data from remote Content db by searching on AltBrain name")
type (Calculation)
output (Content)
collect {
input (altBrainsNames) {
type (AltBrainsNames)
min (Required) max (One) //this means js must catch error when multiple names offered
}
}
}
We resolved this offline, just wanted to follow up on the public channel to any fellow Bixby developers seeing this question posted. The function that calls 'getNewsByName' needs to receive the input parameter. Once populated, the action worked successfully.

Bot framework (v4) Prompt choice in carousel using HeroCards not going to next step

I’m trying to use HeroCards along with a prompt choice in a carousel. So the options to be selected by the user are displayed as HeroCards. As soon as the user clicks in the button of a card it should goes to the next waterfall function.
Here is a working example in bot framework v3. It does work as expected.
const cards = (data || []).map(i => {
return new builder.HeroCard(session)
.title(`${i.productName} ${i.brandName}`)
.subtitle(‘product details’)
.text(‘Choose a product’)
.images([builder.CardImage.create(session, i.image)])
.buttons([builder.CardAction.postBack(session, `${i.id.toString()}`, ‘buy’)]);
});
const msg = new builder.Message(session);
msg.attachmentLayout(builder.AttachmentLayout.carousel);
msg.attachments(cards);
builder.Prompts.choice(session, msg, data.map(i => `${i.id.toString()}`), {
retryPrompt: msg,
});
Below I’m trying to do the same with bot framework v4 but it does not work. It never goes to the next function in my waterfall.
How can I do the same with v4?
…
this.addDialog(new ChoicePrompt(PRODUCTS_CAROUSEL));
…
const productOptions: Partial<Activity> = MessageFactory.carousel(
item.map((p: Product) =>
CardFactory.heroCard(
p.productName,
‘product details’,
[p.image || ''],
[
{
type: ActionTypes.PostBack,
title: ‘buy’,
value: p.id,
},
],
),
),
‘Choose a product’,
);
return await step.prompt(PRODUCTS_CAROUSEL, productOptions);
…
UPDATE:
Follow full code with the suggestion from #Drew Marsh
export class ProductSelectionDialog extends ComponentDialog {
private selectedProducts: Product[] = [];
private productResult: Product[][];
private stateAccessor: StatePropertyAccessor<State>;
static get Name() {
return PRODUCT_SELECTION_DIALOG;
}
constructor(stateAccessor: StatePropertyAccessor<State>) {
super(PRODUCT_SELECTION_DIALOG);
if (!stateAccessor) {
throw Error('Missing parameter. stateAccessor is required');
}
this.stateAccessor = stateAccessor;
const choicePrompt = new ChoicePrompt(PRODUCTS_CAROUSEL);
choicePrompt.style = ListStyle.none;
this.addDialog(
new WaterfallDialog<State>(REVIEW_PRODUCT_OPTIONS_LOOP, [
this.init.bind(this),
this.selectionStep.bind(this),
this.loopStep.bind(this),
]),
);
this.addDialog(choicePrompt);
}
private init = async (step: WaterfallStepContext<State>) => {
const state = await this.stateAccessor.get(step.context);
if (!this.productResult) this.productResult = state.search.productResult;
return await step.next();
};
private selectionStep = async (step: WaterfallStepContext<State>) => {
const item = this.productResult.shift();
const productOptions: Partial<Activity> = MessageFactory.carousel(
item.map((p: Product) =>
CardFactory.heroCard(
p.productName,
'some text',
[p.image || ''],
[
{
type: ActionTypes.ImBack,
title: 'buy',
value: p.id,
},
],
),
),
'Choose a product',
);
return await step.prompt(PRODUCTS_CAROUSEL, {
prompt: productOptions,
choices: item.map((p: Product) => p.id),
});
};
private loopStep = async (step: WaterfallStepContext<State>) => {
console.log('step.result: ', step.result);
};
}
PARENT DIALOG BELOW:
...
this.addDialog(new ProductSelectionDialog(stateAccessor));
...
if (search.hasIncompletedProducts) await step.beginDialog(ProductSelectionDialog.Name);
...
return await step.next();
...
MY BOT DIALOG STRUCTURE
onTurn()
>>> await this.dialogContext.beginDialog(MainSearchDialog.Name) (LUIS)
>>>>>> await step.beginDialog(QuoteDialog.Name)
>>>>>>>>> await step.beginDialog(ProductSelectionDialog.Name)
UPDATE
Replacing the ChoicePrompt with TextPromt (as suggested by Kyle Delaney) seems to have the same result (do not go to the next step) but I realised that if remove return from the prompt like this:
return await step.prompt(PRODUCTS_CAROUSEL, `What is your name, human?`); TO await step.prompt(PRODUCTS_CAROUSEL, `What is your name, human?`);
it does work but when I'm returning the original code with ChoicePrompt without return like this:
await step.prompt(PRODUCTS_CAROUSEL, {
prompt: productOptions,
choices: item.map((p: Product) => p.id),
});
I'm getting another error in the framework:
error: TypeError: Cannot read property 'length' of undefined
at values.sort (/xxx/Workspace/temp/13.basic-bot/node_modules/botbuilder-dialogs/lib/choices/findValues.js:84:48)
at Array.sort (native)
at Object.findValues (/xxx/Workspace/temp/13.basic-bot/node_modules/botbuilder-dialogs/lib/choices/findValues.js:84:25)
at Object.findChoices (/xxx/Workspace/temp/13.basic-bot/node_modules/botbuilder-dialogs/lib/choices/findChoices.js:58:25)
at Object.recognizeChoices (/xxx/Workspace/temp/13.basic-bot/node_modules/botbuilder-dialogs/lib/choices/recognizeChoices.js:75:33)
at ChoicePrompt.<anonymous> (/xxx/Workspace/temp/13.basic-bot/node_modules/botbuilder-dialogs/lib/prompts/choicePrompt.js:62:39)
at Generator.next (<anonymous>)
at /xxx/Workspace/temp/13.basic-bot/node_modules/botbuilder-dialogs/lib/prompts/choicePrompt.js:7:71
at new Promise (<anonymous>)
at __awaiter (/xxx/Workspace/temp/13.basic-bot/node_modules/botbuilder-dialogs/lib/prompts/choicePrompt.js:3:12)
this is the line:
// Sort values in descending order by length so that the longest value is searched over first.
const list = values.sort((a, b) => b.value.length - a.value.length);
I can see the data from my state is coming properly
prompt: <-- the data is ok
choices: <-- the data is ok too
Sometimes I'm getting this error too:
error: TypeError: Cannot read property 'status' of undefined
at ProductSelectionDialog.<anonymous> (/xxxx/Workspace/temp/13.basic-bot/node_modules/botbuilder-dialogs/lib/componentDialog.js:92:28)
at Generator.next (<anonymous>)
at fulfilled (/xxxx/Workspace/temp/13.basic-bot/node_modules/botbuilder-dialogs/lib/componentDialog.js:4:58)
at <anonymous>
at process._tickDomainCallback (internal/process/next_tick.js:228:7)
this line
// Check for end of inner dialog
if (turnResult.status !== dialog_1.DialogTurnStatus.waiting) {
You're using a ChoicePrompt, but when you call prompt you're only passing through an activity (the carousel). ChoicePrompt is going to try to validate the input against a set of choices that you should be passing in when you call prompt. Because you're not doing this, the prompt is not recognizing the post back value as valid and technically should be reprompting you with the carousel again to make a valid choice.
The fix here should be to call prompt with PromptOptions instead of just a raw Activity and set the choices of the PromptOptions to an array that contains all the values you expect back (e.g. the same value you set for the value of the post back button).
This should end up looking a little something like this:
Since you're providing the choices UX with your cards, you want to set the ListStyle on the ChoicePrompt to none
const productsPrompt = new ChoicePrompt(PRODUCTS_CAROUSEL);
productsPrompt.style = ListStyle.none;
this.addDialog(productsPrompt);
Then, set the available choices for the specific prompt:
return await step.prompt(PRODUCTS_CAROUSEL, {
prompt: productOptions,
choices: items.map((p: Product) => p.id),
});
Basically Drew Marsh was right.
I just would like to add some other details that I had to tweak to make it work. In case someone else is going crazy like I was. It could give some insights. It's all about how you handle the returns of nested dialogs.
First change. I had to transform the identifier of the Choice prompt into string:
{
type: ActionTypes.PostBack,
title: 'buy',
value: p.id.toString(),
},
and
return await step.prompt(PRODUCTS_CAROUSEL, {
prompt: productOptions,
choices: item.map((p: Product) => p.id.toString()),
});
Another problem that I found was in the parent dialog:
I was basically trying to do this:
if (search.hasIncompletedProducts) await step.beginDialog(ProductSelectionDialog.Name);
return await step.next();
Which makes no sense, then I changed it to:
if (search.hasIncompletedProducts) {
return await step.beginDialog(ProductSelectionDialog.Name);
} else {
return await step.next();
}
And then the final change in the parent of the parent dialog:
Before was like this:
switch (step.result) {
case ESearchOptions.OPT1:
await step.beginDialog(OPT1Dialog.Name);
break;
default:
break;
}
await step.endDialog();
Which again does not make sense since I should return the beginDialog or endDialog. It was changed to:
switch (step.result) {
case ESearchOptions.OPT1:
return await step.beginDialog(OPT1Dialog.Name);
default:
break;
}

node.js module for specific block of code

I have a specific block of code (the forEach loop) that I use all over the place in my code base, whenever I have an "item".
How can I refactor this into a module or a global function that I can call whenever I need to.
Here's the context:
Item.findById(itemid).populate('wants').exec(function(err, item){
item.wants.forEach(function(person){
person.avatar = gravatar.url(person.email, {size: 80, default: 'mm' });
});
//do some more stuff with item
});
I want to re-use this forEach loop in other places where I have an "item" object. I'm thinking I pass in the item object and return it again.
item.wants.forEach(function(person){
person.avatar = gravatar.url(person.email, {size: 80, default: 'mm' });
});
Something like:
var myLib = require('./lib/myLib');
...
item = myLib.doStuff( item );
Your ./lib/myLib.js file would look like this:
module.exports.doStuff = function(item) {
item.wants.forEach(function(person){
person.avatar = gravatar.url(person.email, {size: 80, default: 'mm' });
});
return item;
}
You can then require and use like you outlined already:
var myLib = require('./lib/myLib');
item = myLib.doStuff( item );

Resources