Mock multiple getBoundingClientRect in one render for Jest - jestjs

Inside a useEffect hook I get the bounding top of masterRef. Then I compare masterRef's top with some divs in the parentRef. If a div's top is the same as the masterRef's top, a class is added, if not, it will be removed.
useEffect(() => {
const masterTop = masterRef.current.getBoundingClientRect().top;
let children = parentRef.current.children;
Array.from(children).forEach((child) => {
const childTop = child.getBoundingClientRect().top;
if (childTop !== divTop) {
child.classList.add("greyout");
} else {
child.classList.remove("greyout");
}
});
}
}, []);
I have found mocking getBoundingClientRect that returns only one value. To test the above, it requires returning at least two different values. How can this be done?

Related

Pagination testing via cypress JS

I need to test that when I select some model car, as a result, I have only that model in all pages. So basically I do pagination testing. But I do something wrong that it does not moves to another page although selectors are correct. Please tell me what I am doing wrong.
findItem("AUDI")
});
async function findItem(value) {
async function findInPage(index) {
let found = false;
cy.get("li.page-item:not(.page-pre):not(.page-next)").as("pages");
await cy.get("#pages")
.its("length")
.then(async (len) => {
if (index >= len) {
return false;
} else {
await cy.get("#pages")
.eq(index)
.click();
await cy.get("table tr > td:nth-child(5) p")
.each(async ($itemNameEl, index) => {
const itemText = $itemNameEl.text().toUpperCase();
cy.log('item ', itemText);
if (itemText.includes(value)) {
found = true;
await cy.wrap($itemNameEl).eq(index);
//cy.get('.chakra-text.css-0').should('include', value)
cy.get('.css-1b4k5p > .chakra-text.css-0')
.each(($el) => {
expect($el.text().toUpperCase()).to.include(value)
})
return false;
}
})
.then(() => {
if (!found) {
findInPage(++index);
}
});
}
});
}
findInPage(0);
}
A simple example without aliases, async functions, etc, just using recursion and a single NEXT PAGE button (that I see you have in your screen shot) would look like this (tested and working on bootstrap pagination examples):
it('Some test', () => {
cy.visit('/')
const findInPage = () => {
cy.get('li:has(a.page-link:has(span:contains(»)))').then((el) => {
// do your test
if (el.hasClass('disabled')) {
// on last page, break out
return
}
cy.wrap(el).click()
findInPage()
})
}
findInPage()
});
How this works: Look for li element which represents a single pagination next-page button, in bootstrap case it has child a tag which has child span tag which contains an icon ». Once you reach last page, the button get's disabled by adding .disabled class to li tag which is checked for on every page. Using this it doesn't matter if you have 3 or 33 pages and if some numbers are hidden with ...
Reference: https://glebbahmutov.com/blog/cypress-recurse/

Cypress get text value from an element

I am trying to get a text from an element with Cypress in the first test from the first domain and then type it in the second test in another domain, here is a code
I have to grab code from h4.
I implemented next part of code:
get studentCouponValue() {
return cy.get('h4').then(($span) => {
const couponValue = $span.text();
cy.log(couponValue);
})
}
in logs, I see the correct coupon's value, but when I am trying to type it into the field I get an error
The chain approach doesn't fit my expectation, cause i am going to use it in different tests.
Try this:
get studentCouponValue() {
return cy.get('h4').then(($span) => {
const couponValue = $span.innerText;
cy.log(couponValue);
})
}
i resolved
initStudentCouponValue() {
const self = this;
return cy.get('main > .container-fluid').find('h4').then((span) => {
self.couponValue = span.text();
cy.log('First log '+ self.couponValue);
return new Cypress.Promise((resolve) => {
return resolve(self.couponValue);
});
});
}
getStudentCouponValue() {
return this.couponValue;
}
in the test where we want to use value
let couponValue;
admin.initStudentCouponValue().then(() => {
couponValue = admin.getStudentCouponValue()
});
and later we can use
coupoValue
for inputs

Update prop for dynamically inserted element

New to react... Really banging my head against it with this one... I'm trying to figure out how to get a dynamically inserted component to update when the props are changed. I've assigned it to a parent state object but it doesn't seem to re-render. I've read that this is what's supposed to happen.
I was using ReactDOM.unmountComponentAtNode to re-render the specific elements I needed to, but it kept yelling at me with red text.
I need to hide "chat.message" unless the user has the authority to see it (server just sends empty string), but I still need to render the fact that it exists, and reveal it should the user get authentication. I'm using a css transition to reveal it, but I really need a good way to update the chat.message prop easily.
renderChats(uuid){
let userState = this.state.userStates.find(user => {
return user.uuid === uuid;
});
const children = userState.chats.map((chat) => {
let ChatReactElement = this.getChatMarkup(chat.cuid, chat.message, chat.status);
return ChatReactElement;
});
ReactDOM.render(children, document.getElementById(`chats-${this.state.guid}-${uuid}`));
}
getChatMarkup() just returns JSX and inserts Props... I feel like state should be getting passed along here. Even when I use a for-loop and insert the state explicitly, it doesn't seem to re-render on changes.
getChatMarkup(cuid, message, status){
return(
<BasicChatComponent
key={cuid}
cuid={cuid}
message={message}
status={status}
/>
);
}
I attempted to insert some code line this:
renderChats(uuid){
let userState = this.state.userStates.find(user => {
return user.uuid === uuid;
});
const children = userState.chats.map((chat) => {
let ChatReactElement = this.getChatMarkup(chat.cuid, chat.message, chat.status);
if(chat.status.hidden)
this.setState({ hiddenChatRE: [ ...this.state.hiddenChatRE, ChatReactElement ] }); // <== save elements
return ChatReactElement;
});
ReactDOM.render(children, document.getElementById(`chats-${this.state.guid}-${uuid}`));
}
and later in my code:
this.state.hiddenChatRE.every(ReactElement => {
if(ReactElement.key == basicChats[chatIndex].cuid){
ReactElement.props = {
... //completely invalid code
}
}
});
The only response I see here is my ReactDOM.unmountComponentAtNode(); approach...
Can anyone point me in the right direction here?
Although perhaps I should be kicking myself, I read up on how React deals with keys on their components. So there's actually a fairly trivial answer here if anyone comes looking... Just call your render function again after you update the state.
In my case, something like:
this.setState(state =>({
...state,
userStates : state.userStates.map((userstate) => {
if(userstate.uuid == basicChats[chatIndex].uuid) return {
...userstate,
chats: userstate.chats.map((chat) => {
if(chat.cuid == basicChats[chatIndex].cuid){
//
return {
cuid: basicChats[chatIndex].cuid,
message: basicChats[chatIndex].message,
status: basicChats[chatIndex].status
}
}
else return chat;
})
}
else return userstate;
})
}));
and then, elsewhere in my example:
this.state.userStates.map((userstate) => {
this.renderChats(userstate.uuid);
});
Other than the fact that I'd recommend using indexed arrays for this example to cut complexity, this is the solution, and works. This is because even though it feels like you'd end up with duplicates (that was my intuition), the uid on the BasicChatComponent itself makes all the difference, letting react know to only re-render those specific elements.

how to push data in array in typescript

I want to create a dynamic menu bar by fetching data from two collections (supcat and cat) then combining the two to create a new array which i will access on page load for menu but the push() is not working.
ngOnInit() {
this.cattest();}
cattest(){
var x;
this.supcatobj.fetchsupcat().subscribe(
(res)=>
{
if(res.length!=0)
{this.supcat=res;
for(let x=0;x<this.supcat.length; x++)
{
this.catobj.fetchallcat(this.supcat[x]["_id"]).subscribe(
(resp)=>
{
this.allcat=resp;
for(let y=0;y<this.allcat.length;y++)
{
}
this.testarr[x].push(this.supcat[x]["supcatname"],this.allcat);
}
);
}
}
}
);}
Instead of nesting subscribe() calls, I would try to compose separate observables for your two different collections and then use the combineLatest() operator to combine those into your desired array. It is hard to see exactly what you are working for, but conceptually it would be something like this:
const supcat$ = this.supcatobj.fetchsupcat().pipe(filter(cat => cat.length > 0));
const allCat$ = this.catobj.fetchallcat();
const combinedCats$ = combineLatest(supcat$, allCat$);
const res$ = combinedCats$.pipe(map(res => {
// do operation that involves both data sets
});
Remember that map() will return a new array. This way you will only need to subscribe to the one variable, and if you put it at the class level you could use the async pipe (|) in your template so it will unsubscribe automatically.

React-native and Redux healthy way to call actions on props change

I've been using react-native with redux for a while, and the way i learn to call actions when something change on prop is using the componentWillReceiveProps, but when I use it I need to pass between if's and some times it goes to the wrong if, then I need to add more stuff to prevent it.
Here's an example I have done. I know this is not the best way to do it, but it is what I could think of.
componentWillReceiveProps(newProps) {
if(Object.keys(newProps.selected_product).length > 0) {
if(Object.keys(this.props.current_location).length > 0 || Object.keys(newProps.current_location).length > 0) {
this._handleNextPage(2);
this.props.verifyProductById(newProps.selected_product, newProps.current_location, this.props.token);
} else {
this.props.statusScanner(false);
this._handleNextPage(1);
}
} else if(Object.keys(newProps.historic_product_confirm).length > 0) {
if(newProps.historic_product_confirm.location._id == newProps.current_location._id)
this.props.handleModalConfirmPrice(!this.props.modal_confirmPrice_status)
} else if(newProps.scanResult != "") {
this.props.statusScanner(false);
if(Object.keys(newProps.current_location).length > 0) {
this._handleNextPage(2);
} else {
this._handleNextPage(1);
}
} else {
this._handleNextPage(0);
}
}
What I need is a healthy way to call my actions when the props change.
Edit:
Here i have the full OfferScene and an action file example:
OfferScene:
https://gist.github.com/macanhajc/0ac98bbd2974d2f6fac96d9e30fd0642
UtilityActions:
https://gist.github.com/macanhajc/f10960a8254b7659457f8a09c848c8cf
As mentioned in another answer, componentWillReceiveProps is being phased out, so I would aim for trying to eliminate it where possible. You'll be future-proofing your code and keeping your component logic more declarative and easy to reason about. As someone who has been responsible for (and been frustrated by) lifecycle method abuse like this, here are some things that have helped me.
Remember that when using redux-thunk, along with passing dispatch as the first argument, you can also pass getState as the second. This allows you to access state values in your action logic instead of bringing them into your component's props and adding clutter. Something like:
export const ExampleAction = update =>
(dispatch, getState) => {
const { exampleBool } = getState().ExampleReducer
if (exampleBool) {
dispatch({
type: 'UPDATE_EXAMPLE_STATE',
update
})
}
}
Using async/await in action logic can be a lifesaver when your action depends upon fetched results from an API call:
export const ExampleAction = () =>
async (dispatch, getState) => {
const { valueToCheck } = getState().ExampleReducer
, result = await someAPICall(valueToCheck)
.catch(e => console.log(e))
if (result.length > 0) {
dispatch({
type: 'UPDATE_EXAMPLE_STATE',
update: result
})
}
}
For cases where your component's rendering behavior depends upon certain state values after your state has been updated, I highly recommend reselect. A very basic example would be something like:
component.js
import React, { Component, Fragment } from 'react'
import { connect } from 'react-redux'
import { shouldDisplayItems } from '../selectors'
import MyListviewComponent from './myListview'
class ItemList extends Component {
render() {
const { shouldDisplayItems, items } = this.props
return (
<>
{shouldDisplayItems && <MyListviewComponent items={items} />}
</>
)
}
}
const mapStateToProps = ({ ListItems }) => shouldDisplayItems(ListItems)
export default connect(mapStateToProps)(ItemList)
selectors.js:
(Assuming your ListItems reducer has the params items and visibilityFilter)
import { createSelector } from 'reselect'
export const shouldDisplayItems = createSelector(
[state => state],
({ items, visibilityFilter }) => {
return {
shouldDisplayItems: visibilityFilter && items.length > 0,
items
}
}
)
I should mention that another option would be using higher-order components, but it can be tricky to use this approach before having a good grasp on how to keep too much imperative logic out of your components (I learned this the hard way).
I agree with #AnuragChutani and #Goldy in terms of clarity of the code; break it down some more into more components or functions.
Now after some review of your componentWillReceiveProps function, it is definitely not specific enough to narrow down exactly which prop changes. If any connected redux variable changes, the componentWillReceiveProps function will be invoked each time.
So e.g. if 'token' or 'selected_product' updates, componentWillReceiveProps will be triggered, even though you did not want it to trigger for token updates.
You can use a comparison for a specific variable update in the props.
E.g Using lodash
if(!_.isEqual( nextProps.selected_product, this.props.selected_product ))
// if props are different/updated, do something
Secondly, you can call actions/callbacks in your actions to narrow down navigation.
E.g.
takePicture = (camera, options){
...
//on success
dispatch(handleModalConfirmPrice())
...
}}

Resources