data: 'Failed to deserialize params.x - BINDINGS: mandatory field missing at position 47' - coded-ui-tests

i'm using gauge automated ui tests with taiko integration written in javascript.
I cant find a way to solve my problem. Here is the error i got.
message: 'Invalid parameters',
data: 'Failed to deserialize params.x - BINDINGS: mandatory field missing at position 47'
}
}
Error Message: transport is closing
Stacktrace:
Here is my step_implementation.js file
//this step works fine
step("Go to <item>", async(item) => {
await goto(item);
await click($(".fancybox-close-small"))
})
//this step works fine
step("Search for <item>", async(item) => {
await click(textBox(""));
await focus($(".pw-search-input"));
await write(item);
})
//this step does not working, my tests fail while last step accomplished.
step("Go to Product Detail <item> By Index", async(item)=>
{
click(($(".pw-autocomplete-product-suggestion").elements())[item]);
})
step("Select the size <item> by index", async(item)=>
{
click(dropDown({'name':'options'}).elements()[item]);
click(await $(".detail__add2cart"));
})
This my example.spec file
## Go to site
//lets say example.com
* Go to "example.com"
* Search for "ERKEK SPOR AYAKKABI KİNETİX"
* Go to Product Detail "1" By Index

click is an async function and hence has to be awaited, try updating the steps as below
step("Go to Product Detail <item> By Index", async(item)=>
{
await click(($(".pw-autocomplete-product-suggestion").elements())[item]);
})
step("Select the size <item> by index", async(item)=>
{
await click(dropDown({'name':'options'}).elements()[item]);
await click(await $(".detail__add2cart"));
})

Related

Cypress data-testid not found

I am running a cypress test on a remote web app and the cy.get method fails to capture elements based on their data-testid .
This is a sample of the tests that I want to run :
describe('test: deny', () => {
it('I can deny', () => {
cy.visit('https://www.deepskydata.com/');
cy.wait(1000)
cy.get("[data-testid='uc-accept-all-button']").click();
});
});
Here the button with data-testid='uc-accept-all-button' is not found by cy.get.
Also cy.get('button') doesn't work despite that there are multiple button elements in the DOM.
Has anyone ever encountered a similar issue ?
The button you want to click is for privacy settings.
The problem is this section of the page isn't consistently displayed, once you click the "Accept All" button it may not display on the next run.
To avoid the problem, you should poll for the button inside the shadow root container element before attempting to click.
function clickPrivacySettingsButton(attempt = 0) {
// Poll for 4 seconds
if (attempt === 40) {
cy.log('No Accept All button to click')
return
}
cy.get('#usercentrics-root', {log:false}).then($privacySettings => {
const prvivacyShadow = $privacySettings.shadowRoot // get shadow root
const $acceptAllButton = Cypress.$(prvivacyShadow)
.find('[data-testid="uc-accept-all-button"]') // look for button
if ($acceptAllButton.length === 0) {
cy.wait(100, {log:false})
clickPrivacySettingsButton(++attempt)
} else {
cy.get('#usercentrics-root')
.shadow()
.find('[data-testid="uc-accept-all-button"]')
.click()
cy.log('Dismissed Accept All button')
return
}
})
}
cy.visit('https://www.deepskydata.com/')
clickPrivacySettingsButton()
Notes
You cannot just use Cypress includeShadowDom setting, because the shadow root may or may not exist.
You will have to apply cy.wait() in small increments to effectively poll for the Privacy Settings section.
I could see that there is a Shadow DOM in your app. TO make sure cypress traverses through the shadow DOM, in your cypress config file write:
includeShadowDom: true
Your cypress.config.js should look like this:
const { defineConfig } = require("cypress");
module.exports = defineConfig({
e2e: {
...
},
...
includeShadowDom: true
})
Then in your test, you can directly write:
describe('test: deny', () => {
it('I can deny', () => {
cy.visit('https://www.deepskydata.com/')
cy.get("[data-testid='uc-accept-all-button']").should('be.visible').click()
})
})
Avoid using cy.wait() as these can make the tests flaky. Instead use a visible assertion to first check that the element is visible and then click on it.
You may want to have a test to check the banner exists for a user meeting certain conditions.
Otherwise, you can bypass this modal, with correct permissions of course, by making a making a graphQL mutation saveConsents, so you will not need to making any checks at the UI level.
Note: you should choose your method of recursing and allow failOnStatusCode.
cy.request({
method: 'POST',
url: 'url-endpoint',
auth: 'any-authorization',
body: {
operationName: 'saveConsents',
query: 'graphQL mutation query'
}
variables: 'variables-if-needed',
failOnStatusCode: false, // to allow recurse
})

How to make a new embed when the first embed reaches the description limit (4096)

I have added the logs case for my warn slash command, but I have a problem.. that problem is that if the embed description reaches the limit, i get an error and thats not what I want.
So basically, I want the a new embed to be created as like a "second page", and I can use my pagination function to help with navigating between pages and so on. I just don't exactly know how to do that or how to get started.
I am asking for some assistance here because my goal is to have a functional "warning logs" embed with buttons to navigate through the pages if there are more than one like most users will have.
case "logs": {
const buttonPages = require("../../functions/pagination");
const user = interaction.options.getUser("user");
const userWarnings = await warnSchema.find({ Guild: interaction.guild.id, User: user.id });
if (!userWarnings?.length) return interaction.reply({ content: `\`${user.tag}\` does not have any warnings.`, ephemeral: true });
const embedDescription = userWarnings.map((warn) => {
const moderator = interaction.guild.members.cache.get(warn.Moderator);
return [
`<:CL_Shield:937188831227183135> Warn ID: ${warn.id}`,
`<:CL_ReplyContinued:909444370221137930> Moderator: ${moderator || "unknown"}`,
`<:CL_ReplyContinued:909444370221137930> User: ${user}`,
`<:CL_ReplyContinued:909444370221137930> Reason: \`${warn.Reason}\``,
`<:CL_Reply:909436090413363252> Date: ${warn.Date}`,
].join("\n");
}).join("\n\n");
const embed = new EmbedBuilder()
.setTitle(`${user.tag}'s warnings`)
.setDescription(embedDescription)
.setColor("#2f3136");
//const pages = [embed];
//buttonPages(interaction, pages);
await interaction.reply({ embeds: [embed] });
}
What you're trying to do is called pagination. Code is below, but here is how it works:
First what you've got to do is determine how many different embeds it'll be. We can do that with description.length/4096, but we need to round up so we'd use Math.ceil. The Array().keys() allows us to get a list of all the numbers up to it to iterate over. For example Array(3).keys() would give us [0, 1, 2] so we can iterate over it.
We then need to select which part of the description we want to send. We want to start at (i*4096) since all previous embeds have 4096 characters, and then we want it to be 4096 characters long so we simply end it at (i*4096)+4096).
You also need to consider that we cannot always use interaction.reply as interactions can only be replied to once, so we must use interaction.channel.send to send them all to the channel. But, we will want some sort of response to the interaction, so we send the first one as a response to the interaction, and all following ones to the channel.
Here's the code:
for (const i of Array(Math.ceil(embedDescription.length/4096)).keys()) {
const embed = new EmbedBuilder().setDescription(embedDescription.substring((i*4096), (i*4096)+4096))
if(i === 0) await interaction.reply({embeds: [embed]})
else await interaction.channel.send({embeds: [embed]})
}

Including dialogs or reusing dialogs from different file

I am trying to do begindialog from another another dialog js file. I am getting error.
<b>[onTurnError]: Error: DialogContext.beginDialog(): A dialog with an id of 'FollowUpDialog' wasn't found. </b>
this is dialog structure-
dialogs
orderstatus
orderstatus.js
index.js
FollowUp
followUp.js
index.js
i am trying to include FollowUp dialog in OrderStatus Dialog, similary i have other dialogs where i want to begin followup or orderstatus dialog. trying to reuse the dialogs.
One way to do use how we include the file in botjs amd to do adddialog same way i can include in otherfile. But it is redundant work. I am trying to avoid that. Can some one tell me better approach to include the dialog in different dialogs.
code:
Below code is from greeting.js
If you see line where i am doing begindialog.
return await step.beginDialog(ORDER_STATUS_DIALOG);
return await step.beginDialog(ENTITLEMENT_CHECK_DIALOG);
It is error. I am trying to include the dialog which is part of different JS files.
async step => {
if (step.context.activity.channelId == 'directline') {
const buttons = [{
type: ActionTypes.ImBack,
title: 'Repair Order Status',
value: symbolicString.ZEB_GR_STR_013
}, {
type: ActionTypes.ImBack,
title: 'Entitlement Check',
value: symbolicString.ZEB_GR_STR_014
}];
const card = CardFactory.heroCard('', undefined, buttons, {
text: symbolicString.ZEB_GR_STR_015
});
const reply = {type: ActivityTypes.Message};
reply.attachments = [card];
return await step.context.sendActivity(reply);
} else {
return await step.prompt(MENU_PROMPT, symbolicString.ZEB_GR_STR_028);
}
},
async step => {
if (step.context.activity.channelId != 'directline'){
console.log("step greeting dialog next step");
console.log(step);
console.log(step.context.activity);
if (step.context.activity.text == '1'){
return await step.beginDialog(ORDER_STATUS_DIALOG);
}else if (step.context.activity.text == '2'){
return await step.beginDialog(ENTITLEMENT_CHECK_DIALOG);
}
}
return await step.endDialog();
}
]));

Reason for different API responses to request in node and chrome?

So I have a bunch of tracks from Spotify's API and I want their genres (which Spotify doesn't give) so for every track I make an API call to Last FM to get their top tags. Now this works for most tracks, I have to match the track name and artist as strings to last fm:
Here's my problem:
I do like this (pseudo:ish code):
let promises = spotifyTracks
.map(track => rp({url: "http://lastfmapi.com/?artist="+track.artist+"?track="+track.name })
.then(response => {
track.genre = response.genre;
return track;
})
);
return Promise.all(promises).then(() => console.log('done!'));
Using request promise.
Now there a few tracks that currrently baffles me. Like 10 in 600. I get a response from lastFM saying:
{ error: 6, message: 'Track not found', links: [] }
To double check I printed the url used:
"http://lastfmapi.com/?artist="+track.artist+"?track="+track.name
Inside the then-call along with the response.
Now if I copied that url from my output and pasted it right into my chrome-browsers address-bar, the API finds the track!?!??!
the actual example
http://ws.audioscrobbler.com/2.0/?method=track.gettoptags&artist=pugh+rogefeldt&track=små+lätta+moln&autocorrect=1&api_key=141bed9ffc180dd9b07ac93b7e3b56d7&format=json
When it is called in my node-code I get
{ error: 6, message: 'Track not found', links: [] }
when called in the chrome address bar I get
{"toptags": {
"tag":
[
{
"count":100,
"name":"swedish",
"url":"https://www.last.fm/tag/swedish"
},
{
"count":100,
"name":"singer-songwriter",
"url":"https://www.last.fm/tag/singer-songwriter"
},
...
],
"#attr":{
"artist":"Pugh Rogefeldt",
"track":"Små lätta moln"
}
}
}
Anyone got any idea what could be the reason behind this discrepancy?
Chrome address bar will encode the string into URL for you, which will make your actual example become
method=track.gettoptags&artist=pugh+rogefeldt&track=sm%C3%A5+l%C3%A4tta+moln&autocorrect=1&api_key=141bed9ffc180dd9b07ac93b7e3b56d7&format=json
You should do the same thing in your node-code with encodeURIComponent

Vuetify - how to make pagination?

I want to use pagination from Vuetify framework for VueJS.
Pagination component from Vuetify:
<v-pagination
v-model="pagination.page"
:length="pagination.total / 5"
:total-visible="pagination.visible"
></v-pagination>
I want to execute a function when the user clicks on a button. I want to get the page number and then execute the function with this page number in parameter.
Code from getItems from methods:
this.pagination.page = response.body.page
this.pagination.total = response.body.total
this.pagination.perPage = response.body.perPage
Data:
data () {
return {
items: [],
pagination: {
page: 1,
total: 0,
perPage: 0,
visible: 7
}
}
}
checkout the docs on the events section. I found the input event to handle new page.
<v-pagination
v-model="pagination.page"
:length="pagination.pages"
#input="next"
></v-pagination>
and my next method:
next (page) {
api.getBillList(page)
.then(response => {
this.bills = response.data.content
console.log(this.bills)
})
.catch(error => {
console.log(error)
})
}
COMMENT:
Before you implement pagination, try to see if you really need it in the first place, or you can use alternatives:
https://slack.engineering/evolving-api-pagination-at-slack-1c1f644f8e12
https://dzone.com/articles/why-most-programmers-get-pagination-wrong
http://allyouneedisbackend.com/blog/2017/09/24/the-sql-i-love-part-1-scanning-large-table/
https://www.xarg.org/2011/10/optimized-pagination-using-mysql/
https://www.eversql.com/faster-pagination-in-mysql-why-order-by-with-limit-and-offset-is-slow/
**ANSWER:**
You can react on pagination.page change with watcher since pagination.page changes on button click, and then execute your method.
watch: {
"pagination.page": (newPage) => {
this.onPageChange(newPage);
}
}
Or react on component's input event:
<v-pagination
#input="onPageChange"
></v-pagination>
I arrived here after searching for an error I received trying to implement this pagination in my VueJS project: [Vue warn]: Invalid prop: custom validator check failed for prop "length"..
My problem, and it looks like a problem you may have in your question's example code, was my calculation of length was arriving at a decimal answer. For example, if I had 23 records and a page size of 5, it would return 4.6, giving me the error above. I had to wrap my calculation in a Math.ceil() to arrive at the appropriate value for length.
Hope this helps someone :)
<v-pagination
v-model="currPage"
:length="Math.ceil(arr.length / pageSize)"
:total-visible="6"
></v-pagination>

Resources