How to get length by children collactions with Cypress? - reactjs

I use Cypress.io with ReactJs
i have the html structure
<ul class="wrapper">
<li class="class-1">1</li>
<li class="class-2">2</li>
<li class="class-3">3</li>
<li class="class-4">4</li>
<li class="class-5">5</li>
</ul>
How can i get length of li elems?
like this document.querySelector(.wrapper > li).length
cy.get('.wrapp > li').length does not work

Like it is said in Cypress docs
cy.get('.wrapper li') // this yields us a jquery object
.its('length')
.then(length =>{
//code that uses the length
})

You can use something like:
cy.get('.wrapper').find('li').its('length').then((len) => {
//Do stuff with length
expect(len).to.equal(5)
})

Related

Puppeteer how to click the child element of 'next sibling'

I want to select and click the specified <a> of next sibling, html code
below:
<ul>
<li>
<a></a>
</li>
<li>
<a></a>
</li>
<li class='abc'>
<a></a>
</li>
<li>
<a></a> <!-- I want to click this link -->
</li>
</ul>
the <ul> is changing, but the <li class='abc'> is changeless
and use the code below:
let tag = await page.evaluate(() => {
return document.querySelector('li.abc').nextSibling.a
})
console.log(tag?'Y': 'N')
// await page.click(tag)
then it output 'N'
Can someone help me fix this error?
Thanks in advance!
Got this answer from Dave Batiste(user of puppeteer.slack.com),
Thanks to #Vaviloff point that: I should post this answer.
Use the code page.click('.abc + li > a') can click the specified <a> that I want to,
and Dave Batiste(user of puppeteer.slack.com) give more info as below:
If you do want a handle that you can call methods on, I think you'd be interested in > page.$...
const handle = await page.$(selector);
handle.click(options);

AngularJS - Change scope with one of the iterations of a ng-repeat

I have a project with Angular where I don't want to use a select element with ng-options, so I made up a list with different options in order to select one of them.
<div class="countrylist">
<ul>
<li ng-repeat="c in shippingCountry"><p>{{c.name}}</p></li>
</ul>
</div>
So the option selected would modify another element where the chosen option would be displayed.
<div>
<ul>
<li>{{selectedCountry}}</li>
</ul>
</div>
In order to do that, I would need to pass the data from the chosen option of the first element into the 2nd one. I have tried doing something like this
<li ng-repeat="c in shippingCountry" ng-click="selectedCountry = {{c}}"><p>{{c.name}}</p></li>
with no success.
You can check the plunker here Thanks in advance
I suggest you to use a function over there like this in the DEMO
<li ng-repeat="c in shippingCountry" ng-click="Click(c)"><p>{{c.name}}</p></li>
Having this method in your controller
$scope.Click=function (c)
{
$scope.select=c;
}
It creates child scope for each iteration, so explicitly refer parent scope:
Change like this,
<ul>
<li ng-repeat="c in shippingCountry" ng-click="$parent.selectedCountry = c"><p>{{c.name}}</p></li>
</ul>
DEMO
I've fixed your plunker here. It would be better to use methods in scope for this operations because they work in current scope, not in child
<li ng-repeat="c in shippingCountry" ng-click="selectCountry(c)">
<p>{{c.name}}</p>
</li>
// .html
<div>
<ul>
<li>{{selectedCountry.item}}</li>
</ul>
</div>
<div class="countrylist">
<ul>
<li ng-repeat="c in shippingCountry" ng-click="selectedCountry.item = c"><p>{{c.name}}</p></li>
</ul>
</div>
// controller
$scope.selectedCountry = {
item: $scope.shippingCountry[0]
};
Example

Expression in ng-if of AngularJS

Consider the following snippet
ng-if not working
<div class="row">
<div class="col-md-8">
<ul>
<li ng-repeat="bigL in bigLs">
<span ng-if="isObj(bigL)">{{bigL.note}}</span>
<ul ng-if="bigL instanceof Array">
<li ng-repeat="bigLl in bigL">
{{bigLl}}
</li>
</ul>
</li>
</ul>
</div>
</div>
ng-if working
<div class="row">
<div class="col-md-8">
<ul>
<li ng-repeat="bigL in bigLs">
<span ng-if="isObj(bigL)">{{bigL.note}}</span>
<ul ng-if="isArr(bigL)">
<li ng-repeat="bigLl in bigL">
{{bigLl}}
</li>
</ul>
</li>
</ul>
</div>
</div>
Controller
$scope.isArr = function(bigL){
return bigL instanceof Array;
};
I use ng-if to determine whether a nested ul is required to create (by determinate different data type inside the array bigLs), I come to a situation that ng-if cannot evaluate bigL instanceof Array, I then move this snippet inside a function, with the same context, the ng-of works properly, but still cannot understand why it is a need to wrap the expression inside a function instead of running it directly inside the ng-if.
Appreciate for any clarification, thanks!
I'm not exactly sure of the problem, but there are several things that have bad smells in your code:
Don't use 'instanceof Array', ever. It won't work in an angular expression.
Instead, use angular.isArray(). This will only work in javascript by adding a method to your scope.
So, you would want to do something like this:
Controller:
...
$scope.hasChildren = function(bigL1) {
return angular.isArray(bigL1);
}
Template:
...
<ul ng-if="hasChildren(bigL)">
...
As a bonus, it becomes much easier to unit test this code.

Remove parent li after click child image

I want to remove the parent li when I click on the child image using angular. Here's what I have so far:
View
<ul class="cg-tag-list">
<li ng-repeat="tag in list">
<span>{{tag}}</span>
<img src="" ng-click="fnRemoveTag()"/>
</li>
</ul>
JS
//Remove Tag
$scope.fnRemoveTag = function () {
// Put Code here
}
pass the tag to the fnRemoveTag function;
<ul class="cg-tag-list" >
<li ng-repeat="tag in list">
<span>{{tag}}</span>
<img src="" ng-click="fnRemoveTag(tag)"/>
</li>
</ul>
$scope.fnRemoveTag = function (tag) {
// get the index of the tag which we are going to remove
var index = $scope.list.indexOf(tag);
// remove that tag from the `list` array
$scope.list.splice(index, 1);
//this will automatically update the dom for you
}
here is a DEMO
and note that you can pass the $index of the instead of tag like
<img src="" ng-click="fnRemoveTag($index)"/>
and remove the element in controller as,
$scope.fnRemoveTag = function(index) {
// remove that tag from the `list` array
$scope.list.splice(index, 1);
//this will automatically update the dom for you
}
example DEMO
if you don't use the orderBy in the ng-repeat then use the $index other vise use the tag, because if you use orderBy it will sort the array according to sort description and when you pass the $index it may not the correct index of $scope.list.
for EX:
say you have array like $scope.list = [3, 2, 1]; and you need to ng-repeat this with order by
<li ng-repeat="tag in list | orderBy:tag">
after the orderBy, ng-repeat will repeat the sorted array, but actually $scope.list remain as before & it will not gonna sort only the repeat order gets change.
so $index is the ng-repeat's index and its not represent the index of $scope.list, if you do not use the orderBy then both will be same.
then when you try to remove from the $scope.list with the passed $index it will may not remove the correct element.
see this DEMO
try to remove first one and note that it actually remove the last one. because we pass the $index as 0 to the function and remove the 0th index element of the $scope.list which has value of 3.
hope that makes sense.
View
<ul class="cg-tag-list" >
<li ng-repeat="tag in list">
<span>{{tag}}</span>
<img src="" ng-click="fnRemoveTag(tag)"/>
</li>
</ul>
JS
//Remove Tag
$scope.fnRemoveTag = function (listItem) {
var index = $scope.list.indexOf(listItem);
if (index > -1) {
$scope.list.splice(index, 1);
}
}
if you have jQuery you can use this:
var li = $(this).closest("li");
li.parent().remove(li);
Simply put $index as function parameter then use array splice method.
View
<ul class="cg-tag-list" >
<li ng-repeat="tag in list">
<span>{{tag}}</span>
<img src="" ng-click="fnRemoveTag($index)"/>
</li>
</ul>
Javascript (don't use orderBy filter in this purpose)
$scope.fnRemoveTag = function (index) {
$scope.list.splice(index, 1);
}

AngularJS , small trick

I've got one question . Is there anybody how has idea how to invoke function in controller only for the first li object in ul when ul has a ng-repeat?
Lets say ive got function in my controller like:
var init = function () {
var a = this;
};
And my ul repeater seams like lets say:
<ul ng-repeat="up in userpartners" ng-click="setSelected(this);">
<li>
{{up.id}}
</li>
</ul>
And I want to invoke init function with parameter 'this' which will be first 'li' item. Only for the first li i want to set something.
You can do it like this:
<ul ng-repeat="up in userpartners" ng-init="init(up, $first);">
<li>
{{up}}
</li>
</ul>
And in the controller:
$scope.init = function (item, isFirst) {
if (isFirst) {
alert(item);
}
};
Fiddle Example
See here check if $index is zero or $first is true.
But there's already a single LI inside your UL.
I believe you mean:
<ul>
<li ng-repeat="up in userpartners" ng-init="$first && setSelected(up)">
{{up.id}}
</li>
</ul>
or
<ul ng-init="setSelected(userpartners[0])">
<li ng-repeat="up in userpartners">
{{up.id}}
</li>
</ul>
See the difference? We want to repeat the children, not the whole list.
Although I believe it wouldn't be a good practice to have this call on a view. I'd put it inside a controller, the call itself.

Resources