How to get the element with the testid using react testing library? - reactjs

Hi i want to access the first div with class "icon" using getByTestId.
below is how the DOM looks,
<div class="parent">
<div data-testid="add">
<div class="info"></div>
<div class="action">
<div class="icon"/> //want to access this
</div>
</div>
</div>
<div class="parent">
<div data-testid="add">
<div class="info"></div>
<div class="action">
<div class="icon"/>
</div>
</div>
</div>
As you see from above there are multiple elements with testid "add" how can i get the first add div and access its child div with class icon.
i have tried
const element = queryAllByTestId('add')[0].querySelector(
'div'
)[1];
this gives element undefined.
could someone help me with this. thanks.

getAllBy
getAllBy* queries return an array of all matching nodes for a query,
and throw an error if no elements match.
queryAllBy
queryAllBy* queries return an array of all matching nodes for a
query, and return an empty array ([]) if no elements match.
Query Cheetsheet
Use the query variant so as not to throw in a test if no matches found, and use querySelectorAll since querySelector returns only the first element that matches, or null.
const element = queryAllByTestId("add");
if (element.length) {
let divs = element[0].querySelectorAll("div");
// ...get the div node you need
}
You can even specify divs with a specific class
const element = queryAllByTestId("add");
if (element.length) {
let divs = element[0].querySelectorAll("div.icon");
// ...get the div node you need
}

Related

React - render after single item modification in array

My problem goes like this:
I'm rendering an array of items using "map".
Each rendered item also includes a toggle button component that i created. You can refer this as regular checkbox.
The checkbox value will decided if the rendered item will have a specific css class, with transitions.
Here come's the problem.
On state change, i update the whole list. Therefore, the whole list is being re-rendered. Therefore, i get the relevant item with the relevant boolean value. Despite that, this item isn't modified, it's re-rendered, so the css doesn't change from A to B, it's just getting a new css class instead the previous, so the TRANSITION DOESN'T WORK.
The rendered array:
{this.state.extensions.map((curr,index)=>{
return <div className="single-extension" key={'extension-' + Utils.guid()}>
<div className="toggle-area">
<ToggleButton onChange={(value)=>{
this.handleAssociateExtension(index,value);
}} active={curr.associated}/>
</div>
<div className={ClassNames({'details':true, 'on': curr.associated})}>
<div className="link-text">{curr.link_text}</div>
<div className="description">{curr.description}</div>
<div className="landing-page">{curr.landing_page}</div>
</div>
</div>
})}
The update function:
handleAssociateExtension(index, value) {
let extensions = Utils.cloneObject(this.state.extensions);
if (extensions[index]) {
extensions[index].associated = value;
}
this.setState({extensions: extensions});
}
Thank you in advance! :)

Removing element from ng-repeat does not allow to remove or hide element from DOM

I have below code to display records using ng-repeat:
<div class="gallery">
<div ng-cloak
ng-repeat="photo in photos| orderBy:'-id'"
ng-mouseLeave = "delete_btn = !delete_btn"
ng-mouseEnter = "delete_btn = !delete_btn"
class="gallery_block"
id="photo_block_[[photo.id]]">
<span title="delete photo" ng-show="delete_btn" class="delete_btn_span" rel="[[photo.id]]" id="delete_photo_[[photo.id]]">
<img src="{{asset('frontend/images/cross_icon.png')}}">
</span>
<p>
<a href="javascript:;">
<img ng-click="showImagePopup([[photo.path_popup_thumbnail]]);" src={{$public_path}}./image.php?width=149&height=109&cropratio=2:1.4&image=[[photo.path_popup_thumbnail]] alt="">
</a>
{{--<span> </span>--}}
</p>
</div>
</div>
I have to delete records for that I first have to call a function using $http and then I remove element from photos array:
var index = $scope.photos.indexOf($('#photo_block_'+id));
$scope.photos.splice(index, 1);
Then I remove element from DOM:
$('#photo_block_'+id).remove();
Update
I have called deletePhoto function using jqyery like below:
$(".gallery").on('click','span.delete_btn_span',function()
{
$scope.deletePhoto($(elem), $(elem).attr('rel'));
});
deletePhoto function further do all the work of deleting elements etc.
But it does not remove element. Where as all the selectors are fine.
Is removing element from photos array causing problem?
With angular JS, you have to deal with data. So you should put an ng-click="delete(photo)" on your span.
and create a method in your controller :
$scope.delete = function(photo) {
// delete froms scope.photos here...
}
to delete the photo from the list.
Your code does not work because $scope.photos.indexOf($('#photo_block_'+id)) surely return -1, because you're looking for $('#photo_block_'+id), which is a jquery object in a list of simple json object (i suppose).
So the splice method does anything, and the $scope.photos list remain unchanged, so the view is not changed, because it's build from the list by the ng-repeat directive.
ps: you should never manipulate dom element from controller, if you need to do that, you should create directive.

How to locate different elements present under single Div tag

Please check the below sample code.
<html>
<body
<div id="HOTELS">
<div class="sub-classes">
<label class="control-label go-right">Location</label>
</div>
<div class="sub-classes">
<span class="size13 go-right">
<b>Adults</b>
</span>
</div>
</div>
<body>
<html>`
---------Web Driver Code--------------------
WebDriver wdriver = new FirefoxDriver();
List<WebElement>elements=wdriver.findElements(By.cssSelector("div[class='sub-classes'] label"));
for(WebElement located_element : elements)
{
system.out.println(located_element.getText());
}
I am trying to locate Label tag and span tag but Didn't find any way except for defining specific location xpath separately for both the elements. I want to do it using cssSelector.
Try this selector...
List<WebElement>elements=driver.findElements(
By.cssSelector(
"div[class='sub-classes']>label, div[class='sub-classes']>span"
));
This will select all labels and spans with <div class="sub-classes"> as its direct parent.
If the label/span are not directly under this div, then replace > with (space)
List<WebElement>elements=driver.findElements(
By.cssSelector(
"div[class='sub-classes'] label, div[class='sub-classes'] span"
));
Try with below logic.
Get all elements with required class
Iterate each element to get inner tags / texts.
List<WebElement>elements=driver.findElements(By.cssSelector("div[class='sub-classes']"));
for(WebElement located_element : elements){
System.out.println(located_element.getText());
System.out.println(located_element.findElement(By.xpath("./*")).getTagName());
}
output of above code will be
Location
label
Adults
span

Append to existing JSX instead of using if-else conditional logic

In my react classes, I often find myself having to use conditional logic to decide what to render. The problem with such an approach is that it leads to a lot of redundant markup. Here is an example:
if(this.props.quotes) {
return (
<div className="card">
<div className="item-1">{this.props.header}</div>
<div className="item-2">Add content...</div>
<i className="fa fa-quote-left"></i>
<i className="fa fa-quote-right"></i>
</div>
);
}
else {
return (
<div className="card">
<div className="item-1">{this.props.header}</div>
<div className="item-2">Add content...</div>
</div>
);
}
The only difference between the two HTML components is that one has two extra font-awesome elements. Ideally, you would want to use some base markup, and append content to it based on the result of the conditional.
I tried the following approach where I put the HTML content into an array and pushed the extra HTML elements in if the condition this.props.quotes was met:
var cardContent = [
<div className="item-1">{this.props.header}</div>,
<div className="item-2">Add content...</div>
];
if(this.props.quotes) {
cardContent.push(<i className="fa fa-quote-left"></i>);
cardContent.push(<i className="fa fa-quote-right"></i>);
}
return (
<div className="card">
{cardContent}
</div>
);
This introduces a new problem, mainly that React complains about missing key props in the array:
Warning: Each child in an array or iterator should have a unique "key" prop.
In this context, it doesn't make sense to give the elements keys since 3/4 of the content is static (the only non-static element is {this.props.header}).
Is there a better way of appending to existing JSX than the method I outlined above? I don't want to suppress all unique key prop warnings since it is still valid in the case of mapping. Is it better to just accept the redundant HTML approach?
When you render an Array of JSX elements, each one must have a key property on them. As you construct your array, you can do something like
cardContent.push(<div className="item-1" key="item-1">..</div>)
cardContent.push(<div className="item-2" key="item-2">..</div>)
I also want to mention for the example you've described, your elements are simple enough that having an inline condition rather than having two blocks that you conditionally return
return (
<div className="card">
<div className="item-1">{this.props.header}</div>
<div className="item-2">Add content...</div>
{this.props.quotes && <i className="fa fa-quote-left"></i>}
{this.props.quotes && <i className="fa fa-quote-right"></i>}
</div>
)

Using AngularJS Jqlite, how can I find the first child of an element that is of a certain element type?

In my AngularJS directive, I want to apply focus to the first child of type <pre> of the last child of type <div>.
That is if my document looks like this:
<div main-div>
<div>
</div>
<div>
<div>
<pre></pre>
</div>
</div>
<div>
<div>
<pre the-one></pre>
</div>
</div>
the <pre> it should select is the last one, with an attribute the-one.
Is there any way to do this?
if you have the structure above as a jqLite element (however you get it a hold of it), you could do the following:
var divs = element.children().find("div");
var theOne;
if (divs.length > 0){
theOne = angular.element(divs[divs.length-1]).find("pre");
}

Resources