Jimp: How to fix 'TypeError: Cannot read property of 'lineHeight' of undefined - node.js

I'm trying to use jimp to add text to an image. Where am I going wrong in the print function to receive this error.
let fileName = 'botImageTemplate.png';
let fontPerson = Jimp.loadFont(Jimp.FONT_SANS_128_BLACK).then(font => font);
Jimp.read(fileName)
.then(image => {
return image
.print(fontPerson, 50, 50, {
text: "This is my text",
alignmentX: Jimp.HORIZONTAL_ALIGN_CENTER,
alignmentY: Jimp.VERTICAL_ALIGN_MIDDLE
}, 40, 40)
.write('botTesting.png')
})
.catch(err => {
console.error(err);
});
The error message I keep getting is "TypeError: Cannot read property of 'lineHeight' of undefined"
What do I have to change to get this to work?

i dont know jimp, but can you try to add arguments: maxWidth and maxHeight.
For me it seems there are missing some properies.
https://www.npmjs.com/package/jimp

Related

Display images from Node (MERN)

I’m building a MERN App and trying to display image saved in MongoDB on the fronted. I use Multer and Sharp to process images when saved to database like so:
const buffer = await sharp(req.file.path)
.resize({ width: 160, height: 160 })
.png()
.toBuffer();
group.groupImgUrl = Buffer.from(buffer, "binary").toString("base64");
await group.save();

when I console.log(group.groupImgUrl), it gives me the following output: new Binary(Buffer.from("6956….73d3d", "hex"), 0)
On the frontend side, I receive the groupImgUrl in different formats, sometimes in typed array, sometimes in string. when the response is in shape: groupImgUrl: { type: Buffer; data: Array }, I can transform the typed array and display it properly:
let base64Str = "";
if ("data" in group.groupImgUrl) {
response.group.groupImgUrl.data.forEach((index: number) => {
base64Str += String.fromCharCode(index)
})
}
<img src = `data:image/png;base64,${src}` />
However, when I receive the groupImgUrl in string format, it’s somehow tampered and cannot display correctly using src = data:image/png;base64,${src}
My question is, how come I receive the same image in different formats, and how to display them properly?

Cypress get text value from an element

I am trying to get a text from an element with Cypress in the first test from the first domain and then type it in the second test in another domain, here is a code
I have to grab code from h4.
I implemented next part of code:
get studentCouponValue() {
return cy.get('h4').then(($span) => {
const couponValue = $span.text();
cy.log(couponValue);
})
}
in logs, I see the correct coupon's value, but when I am trying to type it into the field I get an error
The chain approach doesn't fit my expectation, cause i am going to use it in different tests.
Try this:
get studentCouponValue() {
return cy.get('h4').then(($span) => {
const couponValue = $span.innerText;
cy.log(couponValue);
})
}
i resolved
initStudentCouponValue() {
const self = this;
return cy.get('main > .container-fluid').find('h4').then((span) => {
self.couponValue = span.text();
cy.log('First log '+ self.couponValue);
return new Cypress.Promise((resolve) => {
return resolve(self.couponValue);
});
});
}
getStudentCouponValue() {
return this.couponValue;
}
in the test where we want to use value
let couponValue;
admin.initStudentCouponValue().then(() => {
couponValue = admin.getStudentCouponValue()
});
and later we can use
coupoValue
for inputs

TypeError: inspect(...).addField is not a function

var embed = new Discord.MessageEmbed()
.setColor('#f03824')
.setTitle('Evaluated')
.addField(`To Eval`, `\`\`\`${command}\`\`\``)
.addField('Evaled', `\`\`\`js\n${inspect(evaled, { depth: 0})}\`\`\``
.addField(`Type Of`, `\`\`\`${typeof(evaled)}\`\`\``))
message.channel.send(embed)
How do i fix this error?
TypeError: inspect(...).addField is not a function
You're missing a closing parentheses ) at the end of the line
.addField('Evaled', `\`\`\`js\n${inspect(evaled, { depth: 0})}\`\`\``
Add it and it should work:
.addField('Evaled', `\`\`\`js\n${inspect(evaled, { depth: 0})}\`\`\``)

TypeError: Cannot read property 'AccountToken' of undefined

Trying to run the following test for a select component that is being import 'react-select'
I have the click events running fine, but onChange event is displaying an error:
TypeError: Cannot read property 'Accounts' of undefined
Here is where the error is located, line :
setReport = () => {
let data = this.state.Account.accountBody;
this.setState({
AccountToken: data.AccountToken,
Render();
<Select
className={'field-input-select margin-right'}
id='accounts-id-test'
value={this.state.Account}
onChange={(e) => { this.setState({Account: e}, ()=>{this.setReport()}) }}
Using mount :
it("should check button click event - Select ", () => {
baseProps.onClick.mockClear();
wrapper.find('CLASS YOUR TESTING').setState({
contactOptions:[],
Account:[[""]],
accountOptions: [{ value: 'test', label: 'label test', accountBody: "test account" }],
showRequired: false,
loading:false
});
wrapper.update()
// wrapper.update()
wrapper.find('CLASS YOUR TESTING').find('Select').at(0).props().onChange({
contactBody:{
Accounts:[]
}})
onChange works at(0) and not at (1) . Why ?
The error means that:
let contactBody = contact.contactBody;
is undefined so now you should try to debug with console.log or other ways and see why it is undefined and find a Solution
Able to figure out
wrapper.find('CLASS YOUR TESTING').find('Select').at(1).props().onChange({
accountBody:{
LOA:{
Documents:{
Signers:[],
}
}
}
})

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;
}

Resources