GetByRole Unable to find an accessible element with the role "listbox" and name - reactjs

I have a Kendo dropdown on a react page as follows
<div className="col-lg-4 col-md-4 col-sm-4 col-xs-4 form-group">
<Label>CDN Code</Label>
<DropDownList
id="cdnCode-dropdown"
name="cdnCodeDD"
value={props.CdnCodeTxt}
onChange={handleChange}
data={componentState.cdnCodeList}
textField="text"
dataItemKey="id"
defaultItem={Constants.DefaultItem}
/>
</div>
and I have this react test code
test('renders cdncode dropdown', () => {
render(<ComponentDetailsContent />);
const element = screen.getByRole("listbox", {
name: "cdnCodeDD",
});
expect(element).toBeInTheDocument();
});
But when I run this test I get this error
TestingLibraryElementError: Unable to find an accessible element with the role "listbox" and name "cdnSicCodeDD"
It then goes on to list all the accessible roles and the name is blank for all of them.
Name "":
<span
aria-activedescendant="option-01abe0e8-1cc2-4ca2-ac35-d2a04f7dda62--1"
aria-expanded="false"
aria-haspopup="true"
aria-owns="0f6115c2-49f9-4430-bb2e-09841d34807b"
class="k-dropdown-wrap k-state-default"
id="cdnCode-dropdown"
role="listbox"
tabindex="0"
/>
Why is that even though my dropdown clearly has a name? I have also tried with "/cdncode/i" and I get the same error. I can't install Testing Playground plugin at work otherwise I would be able to tell the correct query for testing. Any alternatives that don't require chrome plugins?
Also, if I list out all the listboxes using GetAllByRoles I get the following array which contains the dropdown with the correct name?
[...
<select aria-hidden="true" name="cdnCodeDD" style="opacity: 0; width: 1px; border: 0px; z-index: -1; position: absolute; left: 50%;" tabindex="-1"><option value="[object Object]" /></select></span>]

Related

How to check the Radio Button in Cypress. The regular check() is not working

I bumped into a problem with Cypress. I just couldn't check the radio button.
I have 7 questions with Yes or No radio buttons. By default, all buttons are checked to "NO". I need to check the "YES" options, it will send an API call to the backend and continue to do magic.
The html code for YES looks like this:
<input type="radio" id="options[0]radiovalue-true" class="form-control is-action-field form-check-input" value="true" style="opacity: 0; cursor: pointer; height: 0px; width: 0px;">
The html code for NO looks like this:
<input type="radio" id="options[0]radiovalue-false" class="form-control is-action-field form-check-input" value="false" checked="" style="opacity: 0; cursor: pointer; height: 0px; width: 0px;">
By default the NO radio button has property: checked: true , the YES radio button has property: checked: false
This is my tries:
cy.get('[class="sc-fzoJMP"]')
.find('.list-group-item')
.then((item) => {
cy.wrap(item)
.eq(0)
.should('contain', 'Is this opportunity?')
.find('input[type="radio"]')
.then((radioButtons) => {
cy.wrap(radioButtons).eq(0).check({ force: true }).should('be.checked');
});
I used different locators, tried ('[type="radio"]'), (':radio'), ('#options[0]radiovalue-true') I used different check methods: cy.get(':radio').click({force: true}) , cy.get(':radio').check('true', {force: true}) and tried to move code with checking radio buttons out of the loop but this is something else. It doesn't check the radio button but can verify that it's not checked.
I found one solution to use invoke property:
cy.wrap(radioButtons).eq(0).invoke('prop', 'checked', true).should('be.checked');
cy.wrap(radioButtons).eq(1).invoke('prop', 'checked', false).should('not.be.checked');
But it's checking "YES" radio button but it's not unchecking "NO" radio button and it doesn't send API call to the backend as supposed to do.
This is full HTML code for YES and NO radio buttons:
<div class="d-flex ">
<div class="mr-4" style="position: relative;">
<div class="form-check">
<input type="radio" id="options[0]radiovalue-true" class="form-control is-action-field form-check-input" value="true" style="opacity: 0; cursor: pointer; height: 0px; width: 0px;">
<label for="options[0]radiovalue-true" class="ml-0 form-check__radio form-check-label">
<svg viewBox="0 0 24 24" style="height: 20px; min-height: 20px; width: 20px; min-width: 20px; opacity: 1;">
<title>Select Option</title>
<circle cx="12" cy="12" r="9" stroke="#666666" stroke-width="2" fill="white"></circle>
</svg>
<p class="sc-fznyAO hdDwJr ml-1 typography-body ">Yes</p>
</label>
</div>
</div>
<div class="mr-4" style="position: relative;">
<div class="form-check">
<input type="radio" id="options[0]radiovalue-false" class="form-control is-action-field form-check-input" value="false" checked="" style="opacity: 0; cursor: pointer; height: 0px; width: 0px;">
<label for="options[0]radiovalue-false" class="ml-0 form-check__radio form-check-label">
<svg viewBox="0 0 24 24" style="height: 20px; min-height: 20px; width: 20px; min-width: 20px; opacity: 1;">
<title>Selected Option</title>
<circle cx="12" cy="12" r="9" stroke="var(--dcp-color-primary)" stroke-width="2" fill="none"></circle>
<circle cx="12" cy="12" r="5" fill="var(--dcp-color-primary)"></circle>
</svg>
<p class="sc-fznyAO hdDwJr ml-1 typography-body ">No</p>
</label>
</div>
<div class="action-field-wrapper is-action-field"></div>
</div>
</div>
The id's that have been assigned contain characters that don't work with # notation, but you can use attribute notation
cy.get('.list-group-item')
.eq(0)
.find('[id="options[0]radiovalue-true"]') // query id as an attribute
.click()
cy.get('.list-group-item')
.eq(0)
.find('[id="options[0]radiovalue-false"]')
.click()
Specifically, the [] in the ids stops '#options[0]radiovalue-true' from working.
With the expanded fragment, this works
cy.get('input[id="options[0]radiovalue-true"]')
.click({force:true})
.should('be.checked')
You need force:true because of the style opacity:0 and width & height 0.
This means the input is effectively hidden, and there is probably a better way to test this - perhaps clicking on the SVG icons.
You can use the IDs and click to select the 'Yes' or 'No' radio buttons.
cy.get('.list-group-item')
.eq(0)
.find('#options[0]radiovalue-true')
.click() //Clicks Yes
cy.get('.list-group-item')
.eq(0)
.find('#options[0]radiovalue-false')
.click() //Clicks No

How to use a variable in ng-if conditions?

I hope somebody can help me with this.
I'm building a messaging mobile application and i want to have my messaging style more interactive using this ng-if conditions but i'm having trouble using a variable outside my scope. "xyz" is my variable containing the email of the currentUser. Im using this xyz to make condition with the email inside my array. If the email of the message is equal to the currentUser which is the "xyz" variable the messages will be placed right and if not the messages will be placed left.
<div ng-repeat="m in messages">
<div ng-if="m.email === xyz" id="getCss" class="chat self">
<div class="user-photo"><img src="css/Image/image.png" style=" width: 60px; height: 60px;"></div>
<div class="chat-message">
<p class="mailcss">{{m.email}}</p>
<div class="messcss">{{m.message}}</div>
</div>
</div>
<div ng-if="m.email !== xyz" id="getCss" class="chat friend">
<div class="user-photo"><img src="css/Image/image.png" style=" width: 60px; height: 60px;"></div>
<div class="chat-message">
<p class="mailcss">{{m.email}}</p>
<div class="messcss">{{m.message}}</div>
</div>
</div>
</div>
If i use string like 'lenard#gmail.com' it works. I don't want to have it that way because it is only good for the user that owns the email how about the other users? Is there any way to solve this?
Thanks in advance. Happy coding!

is there a way to place material icon as input background?

Is there a way to place material icon in an input filed as background at the right?
I know I can place an image as input filed background but material icons are not images and I think these are created by fonts.
this is what I am trying but no luck
<input type="search" class="global-search"
ng-model="vm.searchText"
ng-keyup="$event.keyCode == 13 ? vm.search() : null"
placeholder="I can help you to find anything you want!"/>
<a ng-click="vm.search()">
<i class="material-icons global-search-icon"></i> <!--search-->
</a>
the icon <i class="material-icons global-search-icon"></i> need to be the background image of <input type='text'/>
You can wrap your input and icon in a div:
<div id="divWrapper">
<input type="search" class="global-search"
ng-model="vm.searchText"
ng-keyup="$event.keyCode == 13 ? vm.search() : null"
placeholder="I can help you to find anything you want!"/>
<i class="material-icons global-search-icon"></i>
</div>
And add this styles:
#divWrapper{
display:inline;
position: relative;
}
#divWrapper i {
position: absolute;
left: 0;
}
.global-search:focus + i{
display: none;
}

embedded stylesheet with angular-defined values

I am repurposing the HTML/CSS for this survey widget.
The CSS is built for a fixed number (5) of options (radio buttons), but I can dynamicize it using ng-repeat so that it can hold anywhere from 3 to 9 options, as the data requires.
The CSS needs to be dynamic as well to account for a variable number of options.
<div class="wrap">
<div class="widget-wrapper likert-scale-widget" action="">
<ul class='likert1'>
<li ng-repeat="option in item.response.options>
<input type="radio" name="likert" ng-value="option.value">
<label>{{option.label}}</label>
</li>
</ul>
</div>
</div>
The CSS makes extensive use of pseudo-classes:
div.likert-scale-widget .likert1:before {
top: 11px;
left: 9.5%;
display: block;
width: 78%;
}
You can't put pseudo-classes directly inline, so I can't do this:
<div class="likert-scale-widget">
<div class="likert1" style="width:78%">
So what I have to do is embed my styles in a element at the top of the page. Then I can target it with Angular:
<style>
div.likert-scale-widget .likert1:before {
top: 11px;
left: {{ 12 - item.response.options.length*1.1 }}%;
display: block;
width: {{ 74 + item.response.options.length }}%;
}
</style>
Problem is, this is very non-standard markup. My GUI hates the multiple use of parentheses.
Is there a better way of doing this?
You can use ng-style. Looks really hackish, but if your styles are dynamic towards the content, then I guess its justifiable.
Declare a variable to get the entire styles in a key value format.
$scope.likerStyle = {
'top': '11px';
'left': (12 - item.response.options.length*1.1) + '%'; // '+' automatically stringify this
'display': 'block';
'width': (74 + item.response.options.length) +'%';
};
Then at your markup, use this:
<div class="wrap">
<div class="widget-wrapper likert-scale-widget" action="">
<ul class='likert1' ng-style='likerStyle'>
<li ng-repeat="option in item.response.options>
<input type="radio" name="likert" ng-value="option.value">
<label>{{option.label}}</label>
</li>
</ul>
</div>

overflow-y not working with ng-if in Internet Explorer with Angular

I am using Angular 1.2.16 and testing with IE 11.
In my app, in settings.html, I have the following section:
<section class='scrollable'>
<section class='hbox stretch'>
<aside class="aside-md bg-white b-r" data-ng-controller='RecipientsCtrl'>
<div data-ng-if='inputMode' data-ng-include="'partials/recipient-form.html'"/>
</section>
</section>
where 'scrollable' class is defined as:
.scrollable {
overflow-x: hidden;
overflow-y: auto;
}
and recipient.form:
<div class="panel-body">
<form name='rform' ng-submit='addRecipient(rform)' novalidate='novalidate' role="form">
<label>Recipient's Name</label>
<input type="text" class="form-control" ng-model='recipient.name' name="name" placeholder="Enter name" required='', data-mongoose-error=''/>
....
<button type="submit" class="btn btn-sm btn-success">Add</button>
</form>
</div>
A button click puts the controller in 'inputMode'. This works fine with Chrome, Safari and Firefox. In IE, when you click the button to get into the 'inputMode' it seems like nothing happens, but when you resize the window a bit you see the form.
The following makes it work in IE:
Remove the 'scrollable' class from the section (not an option as I need that)
Display the form directly without the 'inputMode' condition (not an option)
I tried ng-show as well at no avail.
Any ideas?
Thanks.
Changing the css seems to have fixed it:
.scrollable {
overflow-x: hidden;
overflow-y: auto;
min-height: 100%;
}

Resources