Cypress unable to select multiselect with loop - javascript

I am writing a React application that I'm testing with Cypress. In my integration tests I've written a cypress command that takes an array of strings, in order to select items in a multi-value select box.
The code I'm using looks roughly like this:
Cypress.Commands.add("command", (options) => {
options.forEach((option) => {
cy.get("div[data-g-portal-id='1']") // this is the container for the select box dropdown
.find("div[role='menubar']")
.contains(option)
.click({ force: true });
});
}
I've tried various iterations of this, including things like cy.wrap and .each. Whatever I do, when the array contains more than one item, it clicks one item, the item is marked as selected successfully, then it clicks the other item, marks it as selected, but the first item loses its selection state. Its as if the internal state of the component never really got the initial change.
I've confirmed this is not a bug in my application; when testing it manually, the multi-select works fine. But Cypress just doesn't want to know. Any help would be much appreciated.

The v2.grommet.io/select uses a "portal" to display the dropdown options.
The portal is just a div that's appended to body when the dropdown opened and removed again when an option is clicked.
Problem is, there isn't a property to connect the dropdown and portal containing the options. There may be other portals present e.g the Layer component uses a portal.
The options portal we need to target will always be the last portal on the page (since we just opened the dropdown). In the custom command, applying the .last() command will select the options portal.
Cypress.Commands.add("multiselect", (options) => {
options.forEach((option) => {
cy.get(`[data-g-portal-id]`)
.last() // just opened the dropdown so option are in the last portal
.find("div[role='menubar']")
.contains(option)
.click();
});
});
cy.get('input[name="my-select"]')
.click(); // open dropdown
.multiselect(['First', 'Third']);
cy.get('input[name="my-select"]')
.click(); // close dropdown
cy.get('input[name="my-select"]')
.should('have.value', 'multiple')
The test ends with dropdown displaying the text "multiple".
Configuration of the Select,
<Select
multiple // allow multiple selected options
closeOnChange={false} // do not close the dropdown between selections
name="my-select"
options={myOptions}
...
/>

Related

Deselect top option in <select> - bootstrap multiselect

I have a bootstrap multiselect html element that i fill with data from a web service call that gets a list of regions in New Zealand. I need to load those options into the <select> and not select the top option by default.
I have tried everything that is done via the bootstrap multiselect built in functions and options. Nothing will work. So i am resorting to vanilla javascript or vanilla jquery to go straight into the plain html and deselect the first option.
All work on this can be done in this jsfiddle
I had to copy paste a minified external resource (xml2json.min.js) into the top of the javascript editor because it wasn't playing nicely when I tried to add it as an external resource.
I have tried this:
$("ul.multiselect-container").find("li").removeClass("active");
And it doesn't work. It removes the active class but doesn't deselect the option. How do I deselect the option?
I have tried this:
$("ul.multiselect-container").find('input[type="radio":checked]').prop('checked', false);
It doesn't deselect the option.
Please see if you can deselect the top option of the select.
So the jsfiddle at url same as above but ending in 182 is my refactoring to try and make it easier for people to understand but it didn't end up working correctly on my friends laptop.
When using the plugin as a <select multiple=true> it doesn't select the top option by default. However I need that same behaviour with a normal <select>
I just had a look in the plugin, And here is what I understood, To remove default selection you must have your selection set as multiple='multiple', This is when you can see a Checkbox in the drop down rather than radio buttons.
If you do not put this multiple option set then you will end up in having a radio button and this will set the first value by default. Here is the Fiddle which doesnt not select any default value. I just added the option multiple='multiple' to your dynamic select tag.
To have the option multi select set and also be able to select only one at a time use the below code. You need to replace your code block with this one
$("body").prepend("<select id=\"a-select\" multiple='multiple' >"
+ optionsText +
"</select>");
$('#a-select').multiselect({
on: {
change: function(option, checked) {
alert('sdd');
var values = [];
$('#a-select').each(function() {
if ($(this).val() !== option.val()) {
values.push($(this).val());
}
});
$('#a-select').multiselect('deselect', values);
}
}
});
Logic taken from http://davidstutz.github.io/bootstrap-multiselect/#further-examples, see the 5th example from here which says Simulate single selections using checkboxes.
I have redefined the method "deselect" as follows.
this.deselect = function() {
$('input[type="radio"]:checked').attr('checked', false);
$(".multiselect-selected-text").text("None selected");
}
JsFiddle Demo
Add an empty option to the top of the select element.

Strange behavior to searchable JQuery Connected Sortable. Need fix

I am using a connected sortable that saves wonderfully via asp.net back-end on a button click. Additional features include the ability to search each of the lists. This works well until you do the drop and drag of an item from one list to another. When I do the search of the one list using JavaScript to search items in the one list, it still thinks that it is a part of the first list. Has anyone seen this behavior? and do you know of a possible fix to make item permanently part of the said dragged upon list within the DOM. This behavior is in FF, IE, Chrome, etc.
Now I do have buttons on this list that move items from one list to the other based on them being selected then button clicked, it is then a part of the second list using JQuery append();. This makes the item a permanent part of the second list's DOM and is able to be searched upon within that list.
So here is my "Ah-Ha" moment. It was doing what it was supposed to do looking for the item and showing it and if it was not the item it wouldn't show it. however when the item moved to the other column using sortable it still had the same class as the first column. I needed to make that switch.
The answer is here as follows...
http://jsfiddle.net/6jmLvysj/
$(input)
.change(function () {
var filter = $(this).val();
if (filter) {
//here is my DAH moment it is looking for this class so when the user was typing in the first search input it was looking for item it would be searching for it is the second column--give myself a Gib slap--
$(list).find(".ui-state-default:not(:Contains(" + filter + "))").hide();
$(list).find("ui-state-default:Contains(" + filter + ")").show();
} else {
$(list).find(".ui-state-default").show();
}
return false;
})
.keyup(function () {
$(this).change();
});
then this
//so here is where I changed this code when the user brings the item over then it changes it to the proper respected class depending where the user drops it so it can be searched.
stop: function (event, ui) {
if (ui.item.hasClass("ui-state-default")) {
ui.item.attr('class', 'ui-state-highlight');
}
else {
ui.item.attr('class', 'ui-state-default');
}

Jquery Chosen is updating cascading selects on step behind native select boxes

I am using jquery chosen on 4 select boxes that are being populated via database.
The select box ids are: #Year_395, #Make_395, #Model_395, #Trim_395.
I am using the following script to "cascade" them so that the second select box's options depend on what option has been selected in the first, the options for the third are dependent on the second selection etc.
function cascadeSelect(parent, child){
var childOptions = child.find('option:not(.static)');
child.data('options',childOptions);
parent.change(function(){
var parentValue = (this.value).replace(" ", "_");
childOptions.remove();
child
.append(child.data('options').filter('.sub_' + parentValue))
.change();
})
childOptions.not('.static, .sub_' + parent.val()).remove();
}
The native select boxes are cascading correctly. The problem is that when I implement jQuery Chosen, the new select boxes update, but do so one step behind the native boxes. For now I am using the below code to update the options for jQuery Chosen's replacement select box display. This should cause jQuery Chosen to update #Trim_395 as soon as an option for #Model_395 has been selected.
$("#Model_395").chosen().change(function(){
$("#Trim_395").trigger('chosen:updated');
});
Here is the link to the build site:
You will see that if you select your year, make, and model, no trim options will be available, as if you have yet to select the model. If you then select another model, the options for the first model you selected will be displayed. Selecting a third model will display the trim options for the second, etc.
I figured it out on my own. jQuery Chosen was updating before the hidden select boxes had time to update so:
$("#Model_395").change(function(){
wto = setTimeout(function() {
$("#Trim_395").trigger('chosen:updated');
}, 500);
});
Solved the problem. Thanks to anyone who looked :)

onChange not sufficient to trigger query from Dojo Combobox

this is closely related to this post:
Is there an onSelect event or equivalent for HTML <select>?
...but specifically for the Dojo ComboBox..(I am using Dojo w/ ArcGIS JSAPI).
I have three combo boxes, which populate the successors using queries triggered by the selections from the dropdown list. My problem is that if the second combobox selection remains the same, the query is not triggered. I have tried using onblur and several other events instead of onchange, and tried to reset the value of the selection in JS, but neither has worked. The value is reset but the combobox still acts as if the same value is there, so onchange does not work. I tried many of the methods in the link above, but none worked for me. As far as I can tell, my comboboxes do not generate any selectedIndex.
Any suggestions? Thanks, J
All three dijits dijit/form/Select, dijit/form/FilteringSelect and dijit/form/ComboBox subclass dijit/_HasDropDown and that adds them a property dropDown:
// dropDown: [protected] Widget
// The widget to display as a popup. This widget *must* be
// defined before the startup function is called.
dropDown: null
What you want is to listen on this dropDown widget. The problem is that in the case of ComboBox and FilteringSelect this widget dijit/form/_ComboBoxMenu gets instantiated lazily, i.e. when you open popup for the first time. So you need to hook to opening dropDown in the first place and then add onClick event listener to the dropDown:
var signal = aspect.after(comboBox, "openDropDown", function() {
comboBox.dropDown.on("click", function(node) {
console.log("value:", comboBox.get("value"));
console.log("selectedIndex:", domAttr.get(node, "item")); // <= this is not an identifier
}
signal.remove(); // remove aspect so it called only once
}
It's a bit easier when working with dijit/form/Select, because dropDown exists and you can listen to onExecute on its dropDown dijit/Menu immediately:
select.dropDown.on("execute", function() {
setTimeout(function() {
console.log("value:", select.get("value"))
});
});
See all three in action at jsFiddle: http://jsfiddle.net/phusick/Hp5jr/

How to create dynamic select field with blank option and unfiltered state

I need to create a dynamic select field in Rails 3.2, where the options available in the second fields (states) depends on the value of the first (country). I've referred to the revised version of this Railscast, and am using the following code:
jQuery ->
$('#person_state_id').parent().hide()
states = $('#person_state_id').html()
$('#person_country_id').change ->
country = $('#person_country_id :selected').text()
escaped_country = country.replace(/([ #;&,.+*~\':"!^$[\]()=>|\/#])/g, '\\$1')
options = $(states).filter("optgroup[label='#{escaped_country}']").html()
if options
$('#person_state_id').html(options)
$('#person_state_id').parent().show()
else
$('#person_state_id').empty()
$('#person_state_id').parent().hide()
I need to make two changes to this code, which I think should be pretty straightforward for someone with stronger javascript skills than I have.
In the filtered list, I need to include a blank option. Currently selecting a country results in the first state state in the filetred list being selected. I need to leave the prompt "please select". How can I do this?
EDIT
SMathew's suggestions helped here. I'm using $('#person_state_id').html(options).prepend('<option></option>') which, together with a prompt attribute on the html tag, acheives the required result.
If no country is selected (ie the else statement) person_state_id should contain a complete, unfiltered list of all states. I've tried:
else
$('#person_state_id').html(states)
But this is not behaving as expected. I'm having these issues.
If I select a country that has associated state records, #person_state_id options are correctly filtered (and with smathews suggestion, a prompt is included).
If I select a country with no associated state records, #person_state_id contains all states, a blank option in the markup, but the first state option is selected by default. (It should be empty, with a blank option selected by default and a prompt displayed).
If I clear the selection in #person_country_id, #person_state_id contains an unfiltered list of all states (correct), and an empty option in the markup (correct) but the first state record is selected by default (should be a prompt).
How can I resolve these issues?
Thanks
Try
...
if (options) {
$('#person_state_id').html(options).prepend('<option>Please select</option>').parent().show()
} else {
$('#person_state_id').html(states).prepend('<option>Please select a state</option>').parent().show()
}
To deal with my second problem, I added the following coffeescript
jQuery ->
resetCountry = ->
$('#person_state_id').select2 "val", "0"
$('#person_country_id').bind("change", resetCountry);
This, coupled with smathew's answer, seems to be working
(I'm using select2 to format my select fields. You'll need a different approach to set the value if not using select2)

Categories

Resources