Pagination testing via cypress JS - node.js

I need to test that when I select some model car, as a result, I have only that model in all pages. So basically I do pagination testing. But I do something wrong that it does not moves to another page although selectors are correct. Please tell me what I am doing wrong.
findItem("AUDI")
});
async function findItem(value) {
async function findInPage(index) {
let found = false;
cy.get("li.page-item:not(.page-pre):not(.page-next)").as("pages");
await cy.get("#pages")
.its("length")
.then(async (len) => {
if (index >= len) {
return false;
} else {
await cy.get("#pages")
.eq(index)
.click();
await cy.get("table tr > td:nth-child(5) p")
.each(async ($itemNameEl, index) => {
const itemText = $itemNameEl.text().toUpperCase();
cy.log('item ', itemText);
if (itemText.includes(value)) {
found = true;
await cy.wrap($itemNameEl).eq(index);
//cy.get('.chakra-text.css-0').should('include', value)
cy.get('.css-1b4k5p > .chakra-text.css-0')
.each(($el) => {
expect($el.text().toUpperCase()).to.include(value)
})
return false;
}
})
.then(() => {
if (!found) {
findInPage(++index);
}
});
}
});
}
findInPage(0);
}

A simple example without aliases, async functions, etc, just using recursion and a single NEXT PAGE button (that I see you have in your screen shot) would look like this (tested and working on bootstrap pagination examples):
it('Some test', () => {
cy.visit('/')
const findInPage = () => {
cy.get('li:has(a.page-link:has(span:contains(»)))').then((el) => {
// do your test
if (el.hasClass('disabled')) {
// on last page, break out
return
}
cy.wrap(el).click()
findInPage()
})
}
findInPage()
});
How this works: Look for li element which represents a single pagination next-page button, in bootstrap case it has child a tag which has child span tag which contains an icon ». Once you reach last page, the button get's disabled by adding .disabled class to li tag which is checked for on every page. Using this it doesn't matter if you have 3 or 33 pages and if some numbers are hidden with ...
Reference: https://glebbahmutov.com/blog/cypress-recurse/

Related

Mock multiple getBoundingClientRect in one render for Jest

Inside a useEffect hook I get the bounding top of masterRef. Then I compare masterRef's top with some divs in the parentRef. If a div's top is the same as the masterRef's top, a class is added, if not, it will be removed.
useEffect(() => {
const masterTop = masterRef.current.getBoundingClientRect().top;
let children = parentRef.current.children;
Array.from(children).forEach((child) => {
const childTop = child.getBoundingClientRect().top;
if (childTop !== divTop) {
child.classList.add("greyout");
} else {
child.classList.remove("greyout");
}
});
}
}, []);
I have found mocking getBoundingClientRect that returns only one value. To test the above, it requires returning at least two different values. How can this be done?

Calculate sum of values in Cypress

I am using BDD/Cucumber with Cypress. I want to calculate the sum of some rows of table.
This is my step definition:
And("I add up all the total on hand",()=>{
const sumOnHand = itemListPage.totalOnHandAmsterdam()+itemListPage.totalOnHandDelft()
cy.log(sumOnHand)
})
And this is my js page:
totalOnHandAmsterdam() {
cy.get(':nth-child(2) > .dx-grandtotal > span').invoke('text').then(text =>{
const ttOnHandAmst = text
return ttOnHandAmst;
})
}
totalOnHandDelft() {
cy.get(':nth-child(11) > .dx-grandtotal > span').invoke('text').then(text =>{
const ttOnHandDelft = text
return ttOnHandDelft;
})
}
But this is the output of the calculation:
Any ideas on how can I sum up this value is appreciated.
You can't use the results of totalOnHandAmsterdam() and totalOnHandDelft() directly in a summation because
they don't return anything (the return inside .then(text => does not return the value from the function).
Cypress commands don't return values, they add the values to the command queue
You can do it like this
totalOnHandAmsterdam() {
return cy.get(':nth-child(2) > .dx-grandtotal > span')
.invoke('text').then(parseInt)
}
totalOnHandDelft() {
return cy.get(':nth-child(11) > .dx-grandtotal > span')
.invoke('text').then(parseInt)
}
And("I add up all the total on hand", () => {
itemListPage.totalOnHandAmsterdam().then(ams => // get value on command queue
itemListPage.totalOnHandDelft().then(delft => // get other value
const sumOnHand = ams + delft;
cy.log(sumOnHand)
})
})
})
The key to accessing command return values is using .then() after the command.
It's annoying but necessary because Cypress ensures that the web page has received data from the server before evaluating the element text.
Since the test runs faster than web page fetches data, it can easily evaluate the text before the page is fully populated.
You have to convert your texts to numbers and then add it. You can simply add + in front of the number to convert them into Integers. Also I have added trim() in case your strings have any unwanted spaces.
And('I add up all the total on hand', () => {
const sumOnHand =
+itemListPage.totalOnHandAmsterdam().trim() + +itemListPage.totalOnHandDelft().trim()
cy.log(sumOnHand)
})
You could set the function results as aliases.
Since the code is asynchronous, access it within cy.then().
totalOnHandAmsterdam() {
cy.get(':nth-child(2) > .dx-grandtotal > span')
.invoke('text')
.then(parseInt)
.as('amsterdamTotal') // alias will set this.amsterdamTotal
}
totalOnHandDelft() {
return cy.get(':nth-child(11) > .dx-grandtotal > span')
.invoke('text')
.then(parseInt)
.as('defltTotal') // alias will set this.delftTotal
}
And("I add up all the total on hand", function() { // use function() to access "this"
po.totalOnHandAmsterdam()
po.totalOnHandDelft()
cy.then(() => {
const sumOnHand = this.amsterdamTotal + this.defltTotal;
cy.log(sumOnHand)
})
})

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

Tiptap how to create a paragraph (p) on Shift-Enter, instead of a br?

Using TipTap, I'm trying to avoid adding a <br />, but create a <p></p> instead, with the focus inside that <p>|</p> when the user hit shift-Enter but I can't make it work.
Here's what I did so far:
new (class extends Extension {
keys () {
return {
'Shift-Enter' (state, dispatch, view) {
const { schema, tr } = view.state
const paragraph = schema.nodes.paragraph
console.log(tr.storedMarks)
const transaction = tr.deleteSelection().replaceSelectionWith(paragraph.create(), true).scrollIntoView()
view.dispatch(transaction)
return true
}
}
}
})()
How can I do this?
I don't know if this is still relevant but as I was looking for the same thing, I found two ways to make this work.
NOTE:
I'm using tiptap v2, if that's not a problem, then:
I overrode the HardBreak extension, since it's the one that use the Shift-Enter keybinding. It looks something like;
const CustomHardBreak = HardBreak.extend({
addKeyboardShortcuts() {
return {
"Mod-Enter": () => this.editor.commands.setHardBreak(),
"Shift-Enter": () => this.editor.commands.addNewline(),
};
},
});
And used it like so;
editor = new Editor({
extensions: [
customNewline,
CustomHardBreak,
]
});
Use the default editor command createParagraphNear. E.g this.editor.commands.createParagraphNear()
I tried creating a custom extension from your code and ended up with something similar to the command above, i.e;
export const customNewline = Extension.create({
name: "newline",
priority: 1000, // Optional
addCommands() {
return {
addNewline:
() =>
({ state, dispatch }) => {
const { schema, tr } = state;
const paragraph = schema.nodes.paragraph;
const transaction = tr
.deleteSelection()
.replaceSelectionWith(paragraph.create(), true)
.scrollIntoView();
if (dispatch) dispatch(transaction);
return true;
},
};
},
addKeyboardShortcuts() {
return {
"Shift-Enter": () => this.editor.commands.addNewline(),
};
},
});
And added this as an extension in my editor instance.
PS:
They both work, almost exactly the same, I haven't found a difference yet. But there's somewhat of a 'catch' if you would call it that; Both these methods don't work on empty lines/nodes, a character has to be added before the cursor for it to work, any character, even a space.
In TipTap 2.0 I am able to use this custom extension:
const ShiftEnterCreateExtension = Extension.create({
addKeyboardShortcuts() {
return {
"Shift-Enter": ({ editor }) => {
editor.commands.enter();
return true;
},
};
},
});
To make shift + enter behave like enter.
In my case I actually wanted enter to do something different. So I use prosemirror events to set a ref flag on whether shift was pressed. Than I check that flag under the "Enter" keyboard event -- which could be triggered normally or through the shift + enter extension.

Using a custom filter button to filter a list, works on first click but subsequent clicks don't work as well! SPFX

I have 2 SP lists (A and B).
List A has filter buttons next to each list item. When a user clicks a button it should filter List B, only showing the related items.
List A has an Id column which List B matches it's column (MasterItems) with List A's Id.
Here's the code I'm using:
public _getListItems() {
sp.web.lists.getByTitle("ListA").items.get().then((items: any[]) => {
let returnedItems: IListAItem[] = items.map((item) => { return new ListAItem(item); });
this.setState({
Items: returnedItems,
ListAItems: returnedItems,
});
});
sp.web.lists.getByTitle("ListB").items.get().then((items: any[]) => {
let returnedItems: IListBItem[] = items.map((item) => { return new ListBItem(item); });
this.setState({
ListBItems: returnedItems, //This brings in the items from ListB so they can be filtered on this.state.ListB when clicked
});
});
}
private _editItem = (ev: React.MouseEvent<HTMLElement>) => {
this._getListItems(); //This attempts to reset the list when another filter is clicked, but is half working!
const sid = Number(ev.currentTarget.id);
const sid2 = 'DIBR'+sid;
let _item = this.state.ListBItems.filter((item) => { return item.MasterItem == sid2; });
if (_item && _item.length > 0) {
sp.web.lists.getByTitle("ListB").items.get().then((items: any[]) => {
let returnedItems: IListBItem[] =
items.filter(i => _item.some(other => other.Id === i.Id)).map(
(item) => new ListBItem(item)
);
this.setState({
ListBItems: returnedItems,
});
});
}
}
The problem is that when the button is clicked next to an item, it filters correctly on first click!
but if filtered again on the same or different item it will sometimes unset the filter and mix results, other times it will filter correctly. So I'm suspecting I've made a state problem here, but can't seem to discover why.
Regards,
T
UPDATE: I've added a clear filter button which makes things work, but would like the user to be able to click on filter to filter instead of having to clear it each time.
I am doing the same in my SharePoint list
so basically I always set the clear filter function before the filter function,
for example:
function myFilter(){
//my filter code goes here
}
function clearFilter(){
//the clear filter code goes here
}
lets say you are running the function on an item select or a button click or text input change, set the clear filter to run before the filter.
function funcGroup{
clearFilter();
setTimeout(() => {
myFilter();
}, 300);
}
or
function funcGroup{
setTimeout(() => {
clearFilter();
}, 300);
myFilter();
}
I am using this scenario with my SharePoint lists and its working perfect...

Resources