Cypress do something after click - javascript

I have multiple components that contains react-select. I want to iterate through the components and get the values that are in react-select menu.
I tought It should work if I do it like this
cy.get(".flight-segment-times .css-10nd86i")
.each(($select) => {
$select.click({ force: true });
cy.get(".css-11unzgr").contains("option")
})
But this doesn't work since the .css-11unzgr class appears only when it's parent element is clicked. However if I call .click() on $select element then dropdown menu won't even appear, if I call .click({multiple: true}) directly on cy.get(".flight-segment-times .css-10nd86i") then every dropdown will sequentially open, but I need to be able to do something between those click actions.

cy.each() (and other jquery-based commands) pass a jQuery collection to the callback, thus the .click() you are calling is actually a jQuery method, not a cypress command.
What you want to do is to cy.wrap() it first:
cy.get(".flight-segment-times .css-10nd86i")
.each(($select) => {
cy.wrap($select).click({ force: true });
cy.get(".css-11unzgr").contains("option");
});

just make it like two actions:
cy.get(".flight-segment-times .css-10nd86i")
.click({ force: true })
cy.get(".css-11unzgr")
.contains("option")
But since you have problems with actually opening the React-Select component you are probably clicking the wrong component. What I do is find the class that holds the 'onMouseDown' property (that class is in our application called 'react-select__control'. If I click that element it does open.
To find out which class has the 'onMouseDown' property I use the React Developer Tools: https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi

Related

Adding dynamic eventlistners in VueJS3/Nuxt app

Im currently struggling with something where i want to add a data attribute to a component and then based on when NUXT is loaded have a click event bind to all nodes that have this data attribute. Im not using v-on because i want to have this separated from current Vue logic. So example:
component a.vue is tagged with data-element
<a href="/somelink" data-element>link</a>
component b.vue also has HTM elements tagged with data-element
<button data-element>link</button>
When the app loads i need to then loop through all data-element and bind an eventlistner to them.
I tried the above method and that works to some degree but fails when reactivity sets in and the DOM is updated. I checked mixins (not recommended using VueJS3), used composition API, used a mutation observer that checked the DOM status for changes and based on new elements loaded it ads click events, looked at hooks etc but now im getting confused at what is the best way to proceed. Some solutions work to some extend but feels hacky. Or is there a completely different approach i am missing.
To be Sure that the DOM has been initialized you need to set your event listeners on "mounted" lifecycle
mounted() {
const elements = [...document.querySelectorAll('[data-element]')];
elements.forEach(element => {
element.addEventListener('click', () => { /* your code here. */ })
})
}

byRole is not returning the DOM element

I am migrating some of my unit test cases which were previously written using Jest and Enzyme to React Testing Library. I am using Material UI's Select component and I know that, in order to open the dropdown, we have to trigger the mouseDown event on the corresponding div. Here is how I did it in Enzyme (working):
wrapper.find('[role="button"]').simulate('mousedown', { button: 0 });
I am trying to achieve the same using React Testing Library in the following manner, which is not working:
const { container, getAllByRole, getByRole } = renderComponent(mockProps);
fireEvent.mouseDown(getByRole('button')); // trigger the mouseDown on div having role=button
After this I am trying to access the listbox element which is ul element:
getByRole('listbox')
which throws an error and says:
TestingLibraryElementError: Unable to find an accessible element with the role "listbox"
There are no accessible roles. But there might be some inaccessible roles. If you wish to access them, then set the `hidden` option to `true`. Learn more about this here: https://testing-library.com/docs/dom-testing-library/api-queries#byrole
I have verified and the ul element is visible (neither it nor its parent have display none or visibility hidden)
UPDATE - 1
I have tried all the following approach to wait for element to appear in DOM:
waitFor
findByRole instead of getByRole
in both the case it throws error that
Unable to find role="listbox"
What is wrong?
If the element is not accessible at the first render this error could appear, you can try passing the option hidden to the options key in the second argument of the getByRole, if this not works you can try using findByRole, that method wait for the element, and if you want to be sure of the visibility of the element you can try adding a waitFor inside of the test.
getByRole('listbox', { options: { hidden: true } });
Make sure animations are not compromising your results. If the animations change opacity or display and they did not finish before assertion from jest... it is likely to throw an error.
I found the root cause of this issue. I am using the Select component from MUI in disablePortal mode, which make the menu list to be rendered inside the parent component instead of document body. But while MUI does that, it doesn't remove the aria-hidden attribute from the parent component's div, and because of that testing library is not able to locate the listbox (ul) element inside.
There is an issue reported here:
https://github.com/mui/material-ui/issues/19450
So as a work around, I passed the data-testid to the menu component (MenuListProps) and using it to get the listbox.

How to detect when DOM is ready after *ngIf condition is set to true

I have an Angular 2 (Ionic 2) application where I hide some elements using an *ngIf if the network is offline, and show another button, "retry".
When I click the retry button, if the network is back I then want to reshow the elements hidden by the *ngIf. Also I then need to reference some of them via #ViewChild (e.g. an Ionic 2 slider to get it's current index). My problem is that these elements are not all defined when I try to get a reference to them in the same function call that sets my property bound to the *ngIf back to true.
I have tried calling from within a setTimeout but this does not seem to be very safe - some DOM elements are back but not all.
Is there a way to know then the DOM is ready with all the elements that were hidden by the *ngIf, or do I need to find some other way of doing this (not using the *ngIf, so elements are not actually removed).
Thanks in advance for any suggestions!
You should be able to do what you want by looking at the onStable event emitted via the NgZone:
In the component template:
<button (click)="showFoo = !showFoo">toggle</button>
<div *ngIf="showFoo" #foo >foo</div>
In your component class
#ViewChild('foo') foo: ElementRef;
showFoo = false;
constructor(private zone: NgZone) {
zone.onStable.subscribe(() => {
console.log(this.foo);
// this will log either undefined or an ElementRef as you toggle
});
}
Here's a plunk showing it working: https://plnkr.co/edit/jKSaEI0XUcJQehZnJTxB
I think you are looking to understand this,
I think the ngAfterViewInit() would help you with this,
ngAfterViewInit() Respond after Angular initializes the component's
views and child views.
Called once after the first ngAfterContentChecked().
A component-only hook.

How to open child node in JsTree when child nodes do not exist until parent is expanded?

I have a parent with multiple children nodes. The problem is if I want to open a specific node I call $("#jstree").jstree("open_node", $('#node_27'));. The problem is that the parent node is not open so the $('#node_27') returns an empty array.
If I have the child ID how can I open it when they are not added to the DOM until the parent is opened?
Apparently if you want to "deep open" a node, you would need to use
$("#jstree1").jstree("open_node", ['rootId','childId','childChildId','yourNodeId']);
However, there is a method called _open_to that already does that automatically, so you can do:
$("#jstree1").jstree("_open_to", 'yourNodeId'); //j1_5 in your case
And it will open whatever it needs to so as to reach the node you want
The _open_to method is a private one and is not supposed to be used directly. Though working right now it may change in the future, e.g. when the jsTree author decides to change the internal mechanics of his library.
To open parents down to the node you can simply use
$('#jstree1').jstree('select_node', 'node_27' );
As a bonus you will get the target node selected.
If you use ajax to populate tree, there is lazy loading of child nodes. You must first use 'load_node' to load parent node and then use 'select_node' for children node.
$('#jstree').on('refresh.jstree', function () {
$('#jstree').jstree(true).load_node('parent-id', function () {
$('#jstree').jstree().deselect_all();
$('#jstree').jstree('select_node', 'child-id');
});
});
$('#jstree').jstree(true).refresh(false, false);

Recursive jQuery function to amend select option values

I have a form that I am trying to alter with jQuery. Basically, my form has two elements and I need to change the value of the first option in each of them. However, there is an "add more" option that uses AJAX to dynamically generate another element that also needs changed. This add more button can be clicked an unlimited amount of times.
Right now I have this:
$(document).ready(function(){
$("#myname-0-field option:first").val("None");
$("#myname-1-field option:first").val("None");
});
This works fine, but once the "add more" button is clicked, I have more elements called "#myname-2-field", "#myname-3-field", "#myname-4-field" etc. These obviously aren't affected by adding another line into my jQuery as the document has already loaded when they are added.
So the real question is, can someone point me in the right direction of writing a function that can react when the new element is added and change it. If possible, I'm also looking for the function to be aware and look for "#myname-X-field option:first" for tidyness.
use live() function
Then using each function set value
From the jQuery API look live function
Maybe you could add class to your element, so that finding particular element would be easier and it would not add event to other similar elements.
In the example I have a Li with class
$('li.myClass').live('click', function() {
$(this).val(); // this is the getter for clicked value
$(this).val("some_value_here"); // this is the setter for clicked value
});
Now you can add more elements (that has myClass class) and it will have a click event.
Btw. if you know that all elements are inside some container (div for example) then you can write more efficient jQuery using delegate.
$('#container_id').delegate('li.myClass', 'click', function () {
});
This is more efficient because it looks your new elements only under "containter" not from the whole DOM structure.

Categories