How to check alphabet sorting with cypress - sorting

I have a 2 variables and want to checking alphabetically sort for its.
This is my code
cy.get('.list-item').eq(1)
.find('.activity-name span')
.invoke('text')
.then(text => {
const first = text;
cy.get('.activity').click();
cy.get('.list-item').eq(1)
.find('.activity-name span')
.invoke('text')
.then(text => {
const second = text;
// Here I want to check if the result of first element is equal second-variable
});
});
Pls help me. How can I do it with cypress

Given the list below
<ul>
<li class="list-item">Alaska</li>
<li class="list-item">Colorado</li>
<li class="list-item">Delaware</li>
</ul>
Here is how to check if it's sorted
it('should be sorted', () => {
cy.visit('/')
cy.get('.list-item')
.then($items => {
return $items.map((index, html) => Cypress.$(html).text()).get()
})
.should('deep.eq', ['Alaska', 'Colorado', 'Delaware'])
})

For the record, I couldn't have a hardcoded list here, so I used the above (from #Hung Tran) to have a more generic solution:
cy.get('.list-item')
.then(items => {
const unsortedItems = items.map((index, html) => Cypress.$(html).text()).get();
const sortedItems = unsortedItems.slice().sort();
expect(unsortedItems, 'Items are sorted').to.deep.equal(sortedItems);
});
It works well on Cypress 5.3.0

Related

cypress each method get a child elements href

I'm using an each method in cypress to find a list of li elements.
What I'm after is to find a child inside the li and test its href value.
The child is an a tag.
Here is what I have so far:
it("should have ..... ", () => {
cy.get("ul > li")
.each((el, index, list) => {
expect(el.text()).to.be.oneOf(["hello", "world"]);
const a = cy.wrap(el).find("a");
expect(a.attr("href")).to.be.oneOf(["/hello", "/world"]);
})
.then((list) => {
expect(list).to.have.length(2);
});
});
Error: a.attr is not a function
Don't wrap the el. If you do, it becomes a Cypress command which is always async.
You would have to do
cy.wrap(el).find("a").then(a => {
expect(a.attr("href")).to.be.oneOf(["/hello", "/world"]);
})
But find works for jQuery as well.
const a = el.find("a")
expect(a.attr("href")).to.be.oneOf(["/hello", "/world"]);
The problem is you're storing the results of a Cypress (Promise-like) Chainable. Also using an expect inside each won't benefit from the retry-ability provided by should.
it("should have ..... ", () => {
cy.get("ul > li")
.should("have.length", 2)
.each(($el) => {
cy.wrap($el)
.invoke("text")
.should("be.oneOf", ["hello", "world"])
cy.wrap($el)
.find("a")
.invoke("attr", "href")
.should("be.oneOf", ["/hello", "/world"])
});
});

cypress .each() .wrap() .invoke('text') takes long time

I am trying to check the items shown in my table for some assertions. My way is to put all of the items in an array and then iterate over that array.
My problem: All assertions already passed but the cypress runner still takes a lot of time to finish the cy.wrap(.invoke(text)) jobs.
Since this is a very core command of my cypress tests it would be great to have a more efficient function.
My command:
cy.get('table tbody').within(() => {
cy.get('tr').each((tr) => {
cy.wrap(tr.children().eq(index)).invoke('text').then((text) => {
text = text.trim();
arrayWithValuesOfTheList.push(text);
});
})
.then(() => {
//in here all the (quickly passing) assertions are...
});
});
Thanks for any help in advance. I appreciate you all!
You can avoid wrapping the value, will give some increase in speed but it's hard to say what is the slowest part.
const arrayWithValuesOfTheList = []
cy.get('table tbody tr')
.each($tr => {
arrayWithValuesOfTheList.push($tr.children().eq(index).text())
})
.then(() => {
//in here all the (quickly passing) assertions are...
})
})
You can do something like this. It gets the tr values one by one and matches in against a regex pattern.
cy.get('table tbody tr').each(($ele) => {
cy.wrap($ele.text().trim())
.should('match', /myregexp/)
.and('not.include', 'some text')
})
If you want to assert on individual cells, using .each($cell => {...}) is fine but if you want whole-column assertions (e.g sorted, unique-values) it gets difficult with .each().
To build something adaptable for various tests, take a look at the pattern here Sorting the table.
The idea is to create helper functions using .map() to selected table rows and columns.
const { _ } = Cypress
// helpers, reusable
const getColumn = (colIndex) => {
return (rows$) => {
const children$ = _.map(rows$, 'children')
return _.map(children$, `[${colIndex}]`)
}
}
const toStrings = (cells$) => _.map(cells$, 'textContent')
const toNumbers = (texts) => _.map(text, Number)
cy.get('table tbody tr') // rows of table
.then(getColumn(1)) // extract 2nd column
.then(toStrings) // get the text value
.then(toNumbers) // convert if assertions require numeric values
// whole-column assertion example
.should(values => {
const sorted = _.sortBy(values)
expect(values, 'cells are sorted 📈').to.deep.equal(sorted)
})
// individual value assertion
.should(values => {
values.forEach(value => {
expect(value).to.be.lt(100)
})
})
Addressing performance issue
If performance is poor, you can reduce the number of process steps at the Cypress command level by using jQuery-only commands.
This will avoid adding commands to the Cypress queue which is likely to be the slowest part
const arrayWithValuesOfTheList = []
cy.get('table tbody tr td:nth-child(2)') // 2nd col of each row
.then($tds => { // only jQuery methods inside
$tds.text((index, cellText) => {
arrayWithValuesOfTheList.push(cellText.trim())
})
})
.then(() => {
//in here all the (quickly passing) assertions are...
})
})

How to use init() in multiple x-data spreads?

I've split up my methods into two functions in a separate JS file. Both parts need x-init, but only the second part's init() method is triggered:
<div
x-data="{
...part1(),
...part2(),
}">
<p>Check the console</p>
</div>
document.addEventListener('alpine:init', () => {
Alpine.data('part1', () => ({
init(){
// Not triggered
console.log("Part 1 init");
}
})
)});
document.addEventListener('alpine:init', () => {
Alpine.data('part2', () => ({
init(){
console.log("Part 2 init");
}
})
)});
Codepen
Can we have 2 init()s in one x-data?
The simplest solution here, if it's an option, would be to use two separate
DOM elements:
<div x-data="part1()">
<div x-data="part2()">
<p>Check the console</p>
</div>
</div>
However, I understand this might not be desirable.
Due to the way the object spread operator works, your init method from part1 is being overwritten with the one from part2 before AlpineJS even sees it. If you plan to do a lot of this type of merged data object, you might consider writing a helper such as:
Alpine.magic('merge', (...inputs) => inputs.reduce((state, next) => {
const prevInit = typeof state.init === 'function' ? state.init : () => {};
return {
...state,
...next,
init() {
prevInit.call(this);
next.init.call(this);
}
};
}, {});
<div x-data="$merge(part1(), part2())">...</div>
Depending on what you're doing, you might consider writing this as a directive instead. At least as of Alpine v3, it's possible to add multiple x-init directives as long as they're namespaced. For example:
<span x-init.foo="console.log('foo')" x-init.bar="console.log('bar')"></span>
You would implement this as:
document.addEventListener("alpine:init", () => {
Alpine.directive('part1', (el, {expression}) => {
Alpine.bind(el, {
['x-init.part1']() {
console.log('part 1 init');
}
})
});
Alpine.directive('part2', (el, {expression}) => {
Alpine.bind(el, {
['x-init.part2']() {
console.log('part 2 init');
}
})
});
});
You might end up with issues using this approach, though. In general simpler is better.

cypress - how to compare href to anchor text

I'm wondering how to test using cypress libary if the href attribute include the text from anchor tag. The code is as follow:
I have a div with a lot of anchors with data attribute.
<div>
#hash1
#hash2
#hash3
// and many more
</div>
I stared testing with check if each element have "#", href attribute with "/tag/" :
describe('Test', () => {
it('should navigate to link from hashtag', () => {
cy.visit('/');
cy.get('[data-cy="hashtag"]').each((item) => {
cy.wrap(item).contains('#').should('have.attr', 'href').and('contain', '/tag/');
})
})
});
but how can I check if href include the displayed text from a tag?
Once you've yielded the element from your cy.get(), you can reference it as a jQuery object. In this case, we'll want to get the text.
describe('Test', () => {
it('should navigate to link from hashtag', () => {
cy.visit('/');
cy.get('[data-cy="hashtag"]').each((item) => {
cy.wrap(item).contains('#').should('have.attr', 'href', `/tag/${item.text().replace('#', '')}`)
})
})
});
If you simply wanted to see if the href contains the text, you could use the jQuery attr function.
describe('Test', () => {
it('should navigate to link from hashtag', () => {
cy.visit('/');
cy.get('[data-cy="hashtag"]').each((item) => {
expect(item.text()).to.contain('#');
expect(item.attr('href')).to.contain(item.text().replace('#', ''));
})
})
});

How to add the values which has same name in angular8 using rxjs?

html code:
here i am calling kgs like 10kgs or 20kgs etc.. i would like to add the values which has same name as a parameter. this code is not working for me.
<p>Total Materials</p>
<div class="add-div">
<div *ngFor="let item of allMaterials">
<div class="file-name">
<div>{{item.title}}</div>
<div>{{item.wt_in_kgs_per_unit}}{{item.uom}}</div>
</div>
</div>
</div>
component.ts:
updateMaterials(){
this.allMaterials = []
this.materialList.map((item : any) => {
this.allMaterials =[...this.allMaterials, ...item]});
console.log(this.allMaterials);
=> from this line the code is not working...
const x = from(this.allMaterials).pipe(map(data => {
data.groupBy((x:any) => x.title)
.flatMap(group => {
return group.reduce((acc,currentValue)=>{
acc.wt_in_kgs_per_unit = acc.wt_in_kgs_per_unit + currentValue.wt_in_kgs_per_unit;
return acc;
})
})
.subscribe(d => this.groupedData = d);
})
)
console.log(x)
console.log(this.allMaterials);
this.allMaterials = []
this.materialList.map((item : any) => {
this.allMaterials =[...this.allMaterials, ...item]});
console.log(this.allMaterials);
is this your way of copying the array materialList to allMaterials? It is a rather inefficient way when you could just use
this.allMaterials = [...this.materialList];
The next mess looks like a strange attempt to use rxjs to group something that is already an array. What are you expecting x to be? A subscription?
You could try
from(this.allMaterials).pipe(
groupBy((x:any) => x.title),
map(group => group.reduce((acc,currentValue)=> {
acc.wt_in_kgs_per_unit = acc.wt_in_kgs_per_unit + currentValue.wt_in_kgs_per_unit;
return acc;
}))
But I would just work with the array outside of rxjs.

Resources