QuerySelectorAll in React Testing Library? - reactjs

Question:
I have multiple dropdowns and I am checking to see if any of them are open. How can i do this in React testing library? (I'm going through bunch of tabIndexes and checking through them)
Issue:
container.querySelectorAll isn't possible in react testing library.
Code:
it('should not expand dropdown for multiple view', () => {
const { container } = render(
getMockedComponent()
)
expect(container).toBeVisible()
container
.querySelector('div[tabindex]').forEach(eachAccordian => {
expect(eachAccordian).toHaveAttribute('aria-expanded', 'false')
})
})
How can i check all the nodes using React testing library?

You can do this by using querySelectorAll instead of querySelector.
container
.querySelectorAll('div[tabindex]').forEach(eachAccordian => {
expect(eachAccordian).toHaveAttribute('aria-expanded', 'false')
})

You might want to use the React Testing Library queries instead of querySelector. queryAllBy should probably get you what you need, where you can select anything with a certain data-test-id or role, and check their attributes from there!

It seems "getByRole" solves many querySelector requirements.
Roles: https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques#roles

Related

How to detect if the React app is actually being viewed

So I have created an app that shows data in realtime obtaining it from devices.
However, I want to make my server not obtain data when nobody is viewing the app.
So essentially I need some way to determine whether the app is currently being viewed, regardless of if it's desktop or mobile, this includes tab is on focus where the app is opened and that is what the user is currently viewing, and there is nothing on top of the browser, so browser opened on the correct tab, but user has explorer on top of it doing something entirely different, this for my case should be false, and for mobile, the same thing including if device is locked (screen off).
The reason for trying to do that, is to reduce the load on the devices, so that data is being requested, only when there is someone to view it.
From what I have researched I found out about the focus and blur events, but I was unable to make it work, and I don't even know if that is the correct approach, but what I have tried is:
Adding event listeners to the window in the App component:
function App() {
useEffect(() => {
window.addEventListener("focus", () => { console.log("viewed")});
window.addEventListener("blur", () => { console.log("hidden")});
})
}
Adding them as props to the App component within index.js:
<App onFocus={() => {console.log("viewed")}} onBlur={() => {console.log("hidden")}}/>
Neither had any kind of effect, I didn't get either of the console outputs.
Is that even the correct approach?
I would add a socket connection to the app. Then the server would be able to know if there are at least X persons connected and act accordingly.
I would suggest you to try socket for this kind of connection tested, but since you also wanna reduce the load for the user, testing if the user is focused in the browser is the way to go.
To achieve it, I won't add this code inside the React component because of the nature of React as all of its components are rendered inside the <div id="root"></div>, other parts of the html page will still be unaffected by this mechanism. So what you probably want to do is to add the code in the index.html and use window.userFocused to pass the value into React from the index
Edit: added focus/blur script
<script>
window.addEventListener("focus", function(event)
{
console.log("window is focused");
window.userFocused = true;
}, false);
window.addEventListener("blur", function(event)
{
console.log("window is blurred");
window.userFocused = false;
}, false);
</script>
So I ended up solving it with pretty much the same code as initiallywith a few slight modifications, I still used an useEffect hook:
const onFocusFunction = () => {
// do whatever when focus is gained
};
const onBlurFunction = () => {
// do whatever when focus is lost
};
useEffect(() => {
onFocusFunction();
window.addEventListener("focus", onFocusFunction);
window.addEventListener("blur", onBlurFunction);
return () => {
onBlurFunction();
window.removeEventListener("focus", onFocusFunction);
window.removeEventListener("blur", onBlurFunction);
};
}, []);
The best way is to use document.
document.onvisibilitychange = () => {
console.log(document.hidden)
}

How to set value of React Material UI Slider via Cypress?

I'm using slider from here: https://material-ui.com/components/slider/
Trying to set it's value like:
cy.get("[data-cy=slider-population]")
.as("range")
.invoke("val", val)
.trigger("change");
However it's not moving. Anyone manage to get this working?
To make it a bit simpler for future viewers, #kevin's answer points you to all the right places to extract a working test, however here's everything without their demo code:
<Slider data-cy="slider-population" />
First, you need to add this command:
Cypress.Commands.add("reactComponent", {
prevSubject: "element"
}, ($el) => {
if ($el.length !== 1) {
throw new Error(`cy.component() requires element of length 1 but got ${$el.length}`);
}
// Query for key starting with __reactInternalInstance$ for React v16.x
const key = Object.keys($el.get(0)).find((key) => key.startsWith("__reactFiber$"));
const domFiber = $el.prop(key);
Cypress.log({
name: "component",
consoleProps() {
return {
component: domFiber,
};
},
});
return domFiber.return;
});
Then in your test if you want to set the minimum to 0, and the max to 15:
cy.get('[data-cy="slider-population"]')
.reactComponent()
.its("memoizedProps")
.invoke("onChange", null, [0, 15]);
There is an example in the Cypress Real World App, a payment application to demonstrate real-world usage of Cypress testing methods, patterns, and workflows which demonstrates this for it's purposes.
cy.setTransactionAmountRange is a command built to interact with the Material UI Slider.
It uses another Cypress custom command, cy.reactComponent to gain access to the component internals and allow methods like onChange to be invoked directly on the component instance.
Here's a spartan riff on the solution provided by Shannon Hochkins.
Usage:
cy.get("[data-cy=slider-population]")
.setSlider([0,15])
Command:
Cypress.Commands.add('setSlider', { prevSubject: 'element' },
(subject, value) => {
const key = Object.keys(subject.get(0))
.find((key) =>
key.startsWith("__reactFiber$"))
const fiberNode = subject.prop(key)
fiberNode.return.memoizedProps.onChange(null, value)
return subject
})
Typescript:
declare namespace Cypress {
interface Chainable {
setSlider(value: number | number[]): Chainable<void>
}
}

ReactTable and custom server side data update

Has anybody ever used this awesome react components processing server side data?
The solution given here is excellent if you don't need to manually update the data.
I would need to refresh the data not only when changing page/pageSize/sorting/filtering, but also after some intervalled time, to see if data got changed.
Also I have an extension of the table that allows the user to do a full text search on all columns so I would need to update the data when the user changes the content of the custom search box too.
I had the same problem as yours and I started looking for asynchronous tests with ReactJs and I found this article that was very useful. The key part was to use "update" method on the Enzyme component, like below:
const items = ['die Straße', 'die Adresse', 'die Nationalität'];
jest.useFakeTimers();
describe('<DelayedList />', () => {
test('it renders the items with delay', () => {
const component = shallow(
<DelayedList items={items} />
);
jest.runAllTimers();
component.update(); // <--- force re-render of the component
expect(component.state().currentIndex).toEqual(items.length);
expect(component).toMatchSnapshot();
});
For more information, see this medium article.

React reposinveness + bootstrap

React responsiveness
I just wanted to know how the responsiveness in to the react is possible ?
There are different ways to achieve responsive like
css-grid bootstrap-grid react-bootstrap
could some let me know what is the preferred way to do the responsive into react?
Regards,
Just use plain JS, that's the beauty of React. I personally do this:
class Responsive extends Component {
state = { isMobile: false };
componentDidMount() {
// use css media quries
this.isMobile = window.matchMedia("(max-width: 767px)");
// test on first run
this.handleIsMobile({ matches: this.isMobile.matches });
// listen to changes after first run
this.isMobile.addListener(this.handleIsMobile);
}
componentWillUnmount() {
// stop listening when unmounted
this.isMobile.removeListener(this.handleIsMobile);
}
handleIsMobile = ({ matches }) => this.setState({ isMobile: matches });
render() {
if (this.state.isMobile) return <div>MOBILE</div>;
return <div>DESKTOP</div>;
}
}
First refer this, Bootstrap webpack
This is one way of using bootstrap in reactjs projects.
The advantage of this is that u get access to latest bootstrap css.
The disadvantage of using this is its complicated to use jquery plugins in reactjs. Hovewr if you are only interested in css, then its perfect.
Using this u can directly use bootstrap classes from documentation and get more control.
Just use className instead of class
If u want simple setup , then u can use reactstrap link
Other frameworks include react-bootstrap link

Best way to generate role based sidebar navigation with React Router

What is the best way to handle role a based navigation sidebar with React Router? Right now I am repeating myself by having two sidebars. I was thinking about mapping over to generate but didn't know if React Router had some functionality already to generate Links based on a Role condition? If not I am checking if there is a better approach than something like this this below?
Currently I am just hardcoding the Sidebar links instead of dynamically generating.
```
const routes = _.map(props.links, (link) => {
if(someRoleOnUser in link.roles) {
return (<div><Link .... /></div>);
}
})
```

Resources