How Do I Traverse The DOM with Cypress Conditionally? - javascript

I would like to click the "View profile" button when the firstName of the user is equal to "Luke". There will be many users on one page.
HTML:
<div>
<div>
<a datcy="viewProfileButton">View profile</a>
</div>
<div>
<div>
<div>
<h5 datacy="firstName">Luke</h5>
</div>
</div>
</div>
</div>
Cypress Test:
cy.get('[datacy="firstName"]').contains($firstName)...
I've got to this point, but I am struggling with .parents() and other Cypress traversing options to move up the div, and then back down.

The .parent() and .children() function are pretty static and can be prone to cause flakiness if the structure of the HTML changes.
I would suggest setting up your HTML to allow for downward traversal from a higher level element.
In your case, if you have a common datacy attribute on the upper most <div> you can use cy.contains() and find() to do all the heavy lifting for you.
<!-- Added a second profile for example -->
<div datacy="profile">
<div>
<a datacy="viewProfileButton">View Luke's profile</a>
</div>
<div>
<div>
<div>
<h5 datacy="firstName">Luke</h5>
</div>
</div>
</div>
</div>
<div datacy="profile">
<div>
<a datacy="viewProfileButton">View Alice's profile</a>
</div>
<div>
<div>
<div>
<h5 datacy="firstName">Alice</h5>
</div>
</div>
</div>
</div>
// cy.contains returns the <div datacy='profile'> containing a child with the text 'Luke'
it('get link', () => {
cy.contains('[datacy="profile"]', 'Luke')
.find('a')
.should('have.text', "View Luke's profile");
cy.contains('[datacy="profile"]', 'Alice')
.find('a')
.should('have.text', "View Alice's profile");
});
Using cy.contains() to get parent element when child element contains text

Related

How can i continuesly using appenTo() when i click a button?

im trying to reverse div with Jquery which when i click a button the divs will reverse and switch place
<div class="player1">
<div class="player1-a">
<div class="pemain p1a">
<h4>Samsudin</h4>
</div>
<div class="cock 1a">
</div>
</div>
<div class="player1-b">
<div class="pemain p1b">
<h4>Joko</h4>
</div>
<div class="cock 1b">
</div>
</div>
</div>
<button class="btn btn-light score_plus" id="score_kiri"><h1>SCORE</h1></button>
whenever this button clicked the div p1b will move to div player1-a and so do div p1a will move to div player1-b.
Here's my jquery code that the divs only move once and dont move again when i click again.
$('#score_kiri').click(function() {
$('#player_kiri').val(i++);
$('.p1a').appendTo('.player1-b');
$('.1a').appendTo('.player1-b');
$('.p1b').appendTo('.player1-a');
$('.1b').appendTo('.player1-a');
$('.p1a').append('.player1-b');
$('.1a').append('.player1-b');
$('.p1b').append('.player1-a');
$('.1b').append('.player1-a');
});
Your issue is that you have hard-coded the elements to move, rather than use relative positions. Effectively saying "make it exactly like this" rather than "move the first one to the end" (which I believe is what you're trying to do).
You can select the first one various ways, here's one:
$(".player1 > div").first()
Using .appendTo(".player1") with this will move the element to the end - so by always moving the first to the end you get your "continuously appendTo". If you have 3, then first will move to the end each time.
This is slightly different from "switching places" but has the same effect when only 2.
Updated snippet:
$("#btn").click(() =>
$(".player1 > div").first().appendTo(".player1")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="player1">
<div class="player1-a">
<div class="pemain p1a">
<h4>p1a Samsudin</h4>
</div>
<div class="cock 1a">1a
</div>
</div>
<div class="player1-b">
<div class="pemain p1b">
<h4>p1b Joko</h4>
</div>
<div class="cock 1b">
1b
</div>
</div>
</div>
<button id=btn>
click me
</button>

add data- attributes by reading values from DOM

I have the below html structure and I'm trying to get the text of h tags and add as a data- attribute in the corresponding tags:
<div class="content">
<div class="body">
<h1>foo</h1>
<p>para-test1</p>
<p>para-test2</p>
<div class="link">
anchor1
anchor2
anchor3
</div>
</div>
</div>
<div class="content">
<div class="body">
<h1>bar</h1>
<p>para-test3</p>
<div class="link">
anchor4
</div>
</div>
</div>
So 'foo' should be set as a data attribute value for anchor 1,2,3 elements and
'bar' should be set as a data attribute value for anchor 4. Something like this:
<a data-custom="foo" href="#">anchor1</a>
<a data-custom="foo" href="#"">anchor2</a>
<a data-custom="foo" href="#">anchor3</a>
<a data-custom="bar" href="#">anchor4</a>
I tried to iterate over the elements and I'm struck at the second loop.
$(".content .body").each(function() {
$(this).find(".link").attr("data-hN", $(this).next(":header").text());
});
You have an extra double quote two times in your HTML. But, fixing that and foregoing JQuery (which is overkill for such a trivial task), see comments inline below:
// Loop over the links
document.querySelectorAll("div.link > a").forEach(function(item){
// Set the current link data-custom attribute to the nearest
// .body ancestor and the first heading element within that text
item.dataset.custom = item.closest(".body").querySelector(":first-child").textContent;
console.log(item);
});
<div class="content">
<div class="body">
<h1>foo</h1>
<p>para-test1</p>
<p>para-test2</p>
<div class="link">
anchor1
anchor2
anchor3
</div>
</div>
</div>
<div class="content">
<div class="body">
<h1>bar</h1>
<p>para-test3</p>
<div class="link">
anchor4
</div>
</div>
</div>

How To Display A Unique Element Based On A Click Event In Angular 6

So I have two files. An app.component.ts file and an app.component.html file.
Below is an image example of what I want to achieve in my User Interface.
If you look at the photo, you'll see an action icon by the right displaying a nav box element.
I tried achieving something similar in my angular application and this is what I get any time I click on the action Icon:
All the nav boxes display on the UI when I click on the icon. This is because the nav element is inside a *ngFor loop.
Here is my code in the app.component.html file:
<div class="row" *ngFor="let picture of pictures">
<div class="cell">
<p>{{picture.name}}</p>
<p class="med-text meta space-text">{{picture.ticketid}}</p>
</div>
<div class="cell"><p class="meta">{{picture.email}}</p></div>
<div class="cell-photo"><p class="meta">{{picture.ticket_type}}</p></div>
<div class="cell-large">
<div class="toggle on">
<div class="toggle"></div>
</div>
</div>
<div class="small">
<div>
<div class="toggle-on" (click)="toggleActionNav()">
<div class="action"></div
</div>
<!--- nav-Element Starts ---->
<nav class="dropdown-list w-dropdown-list w--open" *ngIf ="actionNav">
<div>View Pictures</div>
<div >Edit Pictures</div>
</nav>
<!--- nav-Element Ends ---->
</div>
</div>
</div>
And here is the code on my app.component.ts file:
actionNav: boolean;
toggleActionNav() {
this.actionNav = !this.actionNav;
}
My question goes thus. How do I get the nav elements to display uniquely and not all displaying together at the same time, just like it is in the first photo?
All responses will be deeply appreciated. Thanks
You can, for example, add a showActionNav property to your picture object. Then you can do (click)="picture.showActionNav = !picture.showActionNav" to toggle it and *ngIf ="picture.showActionNav" to display it.
simplest answer would be just set flag,
for example, actionNavOpen in your object:
(click)="picture.actionNavOpen = !picture.actionNavOpen"
in navs ngIf do: *ngIf ="picture.actionNavOpen"
of course if you're heavily typed this will give you some errors.
so few tips what to do next:
toggling of this flag (actionNavOpen) should be via method
in this method you can also set all others flag in pictures to false
just remember you don't need to pre-set this flag to falsy - it's already falsy while you get this data from rest api
to remove type errors you should extend returning model (interface) from rest api with your model which has this particular flag (actionNavOpen)
if you could show the before and after image will help to understand the problem. Here the problem statement given , when clicking the icons overlap or design itself is overlapping ?
actionNav variable shouldn't be in component's scope. It should be part of picture object which you are using in the ngFor.
And, pass the picture object along with index to the toggleActionNav method to change the actionNav value.
Below is what you need.
<div class="row" *ngFor="let (picture, index) of pictures">
<div class="cell">
<p>{{picture.name}}</p>
<p class="med-text meta space-text">{{picture.ticketid}}</p>
</div>
<div class="cell">
<p class="meta">{{picture.email}}</p>
</div>
<div class="cell-photo">
<p class="meta">{{picture.ticket_type}}</p>
</div>
<div class="cell-large">
<div class="toggle on">
<div class="toggle"></div>
</div>
</div>
<div class="small">
<div>
<div class="toggle-on" (click)="toggleActionNav(picture, index)">
<div class="action"></div>
</div>
<!--- nav-Element Starts ---->
<nav class="dropdown-list w-dropdown-list w--open" *ngIf="picture.actionNav">
<div>View Pictures</div>
<div>Edit Pictures</div>
</nav>
<!--- nav-Element Ends ---->
</div>
</div>
</div>
And the toggle method needs to be like:
toggleActionNav(picture, index) {
//disable all the actionNavs to false
this.pictures.map(pictureObj => pictureObj.actionNav = false)
this.pictures[index] = true
}

Dynamic button click hide and show functionality in Meteor

I have hide and show functionality on dynamically generated cards after form submission.
{{#each newaction}}
<div class="workflowcard">
<div class="module-card-small">
<div class="res-border"></div>
<div class="card-img">{{team}}</div>
<div class="res-content">
<div class=" newaction-name">{{action_title}}</div><hr>
<div class="newaction-des">{{description}}</div>
<!-- <div class=" due-on">Due on:{{d_date}}</div><hr>-->
</div>
<div class="due">
Due on:
<div>
<div class="day-stamp">{{weekday d_date}}</div>
<div class="date-stamp">{{date d_date}}</div>
<div class="month-stamp">{{month d_date}}</div>
</div>
</div>
{{> actioncardsubcontent}}
</div>
<div class="btn-box">
<button type="button" class="cancelsub">Hide Option</button>
<button type="submit" class="createbtnsub">Show Options</button>
</div>
</div>
</div>
{{/each}}
</div>
</div>
<template name="actioncardsubcontent">
<div class="subcontent" >
<div class="modulepath"><div>{{module_list}}</div></div>
<div class="linkto"><div>Linked To: <div class="linkto-color">{{link}}</div></div></div>
<div class="description"><div>Notes:<br>{{description}}</div></div>
</div>
</template>
When I click on show options button the action card subcontent is displaying and when I click on hide option it is hiding.
The problem is, the hide and show functionality is applying for all the cards which are creating dynamically at a time when I click on single card. I understand the reason is I have given the class name for the buttons. So how to stop that and make it work to current target.
Here is my JS:
Template.actioncardsubcontent.rendered = function(){
this.$(".subcontent").hide();
};
Template.workflow.events({
"click .createbtnsub":function(){
$('.subcontent').show();
},
"click .cancelsub":function(){
$('.subcontent').hide();
}
What you need to do is give a unique ID for each iteration to your cards div class attribute.
in JSTL usually has indexId attribute. You can use that and set your class.
Sample:
{{#each newaction indexId="i"}}
{{/each}}
<div class="btn-box">
<button type="button" class="cancelsub<%=i%>">Hide Option</button>
<button type="submit" class="createbtnsub<%=i%>">Show Options</button>
</div>
In your javascript:
use i to hide.
You have a couple of options here. The one I prefer is to put everything in the {{#each}} into its own template. If you do that, you can put the button click events inside of the child template's event which makes it much simpler to manipulate your data. An example:
Templates:
<template name='workflow'>
{{#each newaction}}
{{> card}}
{{/each}}
</template>
<template name='card'>
<div class="workflowcard">
<div class="module-card-small">
<div class="res-border"></div>
<div class="card-img">{{team}}</div>
<div class="res-content">
<div class=" newaction-name">{{action_title}}</div><hr>
<div class="newaction-des">{{description}}</div>
</div>
<div class="due">
Due on:
<div>
<div class="day-stamp">{{weekday d_date}}</div>
<div class="date-stamp">{{date d_date}}</div>
<div class="month-stamp">{{month d_date}}</div>
</div>
</div>
{{> actioncardsubcontent}}
</div>
<div class="btn-box">
<button type="button" class="cancelsub">Hide Option</button>
<button type="submit" class="createbtnsub">Show Options</button>
</div>
</div>
</div>
</template>
Javascript (template.$ docs):
Template.cards.events({
"click .createbtnsub":function(event, template){
template.$('.subcontent').show();
},
"click .cancelsub":function(event, template){
template.$('.subcontent').hide();
}
});
-- OR --
You can do a better DOM query.
Without more info on what is actually in the actioncardsubcontent template or proper testing, this is a best guess on what you're trying to find. You should be able to tweak this query to meet your specific needs easily enough through trial and error. Please read the jQuery Traversing docs as it should clear this up a bit for you.
Template. workflow.events({
"click .createbtnsub":function(event, template){
$(event.target).siblings('.due').find('.subcontent').show();
},
"click .cancelsub":function(event, template){
$(event.target).siblings('.due').find('.subcontent').hide();
}
});

Toggle a repeated class in jQuery

I have this HTML code:
<div id="content">
<div class="profile_photo">
<img style="float:left;margin-right:7px;" src="http://gravatar.com/avatar/53566ac91a169b353a78b329bdd35c95?s=50&d=identicon" class="profile_img" alt="{username}"/>
</div>
<div class="container" id="status-#">
<div class="message">
<span class="username">{username} Debugr Rocks!
</div>
<div class="info">24-oct-2010, 14:05 GMT · Comment (5) · Flag · Via Twitter
</div>
<div class="comment_container">
<div class="profile_photo">
<img style="float:left;margin-right:7px;" src="http://gravatar.com/avatar/53566ac91a169b353a78b329bdd35c95?s=32&d=identicon" class="profile_img" alt="{username}"/>
</div>
<div class="comment_message">
<span class="username">{username}</span> Debugr Rocks! XD
</div>
<div class="comment_info">24-oct-2010</div>
</div>
</div>
<div class="profile_photo">
<img style="float:left;margin-right:7px;" src="http://gravatar.com/avatar/53566ac91a169b353a78b329bdd35c95?s=50&d=identicon" class="profile_img" alt="{username}"/>
</div>
That is repeated two or more times. What I want to do, is to when I click the "Comments (5)" link, the class "comment_container" appears, but only the one in the same "container" class.
It's this possible?
You can use .closest() to go up to the .container then .find() to look inside it, like this:
$(".toggle_comment").click(function() {
$(this).closest(".container").find(".comment_container").show();
});
You can try it here, if you're curious about finding other things relative to this here's a full list of the Tree Traversal functions.
As an aside, there's an error in your HTML that needs correcting, this:
<span class="username">{username} Debugr Rocks! </div>
Should be:
<span class="username">{username} Debugr Rocks! </span>

Categories