Tetsing Swiper with Jest - javascript

I have this component and it is using swiper/react.
While writing test cases, I'm not able to mock the swiper event it is getting in the onSlideChange.
I'm not able to pass through the if condition in the onSlideChangeHandler function.
Can anybody help? thanks!
import { Swiper, SwiperSlide } from 'swiper/react';
export default function Abcxyz(props: PropsType) {
...
...
const onSlideChangeHandler = (swiper) => {
const activeSlideIndex = swiper.activeIndex;
const slides = swiper.slides;
if (slides[activeSlideIndex]?.id === 'hybrid printer bundle') {
visibleConfigOptionsStore.setVisibleConfigOptions(
slides[activeSlideIndex].id
);
}
};
return (
<Swiper
onSlideChange={(swiper) => onSlideChangeHandler(swiper)}
>
)
}
Tried few things but nothing is working at the moment.

Related

how to write a test for below component in react?

could you please help me in writing the test for below component?
import { string, node } from "prop-types"
import * as Styled from "./Banner.styled"
const Banner = ({ heading, button }) => (
<Styled.Banner>
<Styled.Heading>{heading}</Styled.Heading>
{button && button}
</Styled.Banner>
)
Banner.propTypes = {
heading: string.isRequired,
button: node,
}
Banner.defaultProps = {
button: null,
}
export default Banner
I need to write a test in react library to see if the imported component(button) in rendering.
Could you please help me ? I tried the following but I think this is wrong :) The first test passes, but the second for the button itself is wrong:(
import { render, screen } from "../../../test-utils"
import Banner from "../Banner"
const heading = "heading"
const button = "button"
describe(`Banner`, () => {
it(`renders Banner with default properties`, () => {
render(<Banner heading={heading} />)
expect(screen.getByText(heading)).toBeInTheDocument()
})
it(`renders Banner with default properties`, () => {
render(<Banner button={button} />)
expect(screen.getByText(button)).toBeInTheDocument()
})
})
The second test case fails because your Banner expects heading props as required.
describe(`Banner`, () => {
it(`renders Banner without default properties`, () => {
render(<Banner heading={heading} />)
expect(screen.getByText(heading)).toBeInTheDocument()
})
it(`renders Banner with default properties`, () => {
render(<Banner heading={heading} button={button} />)
expect(screen.getByText(button)).toBeInTheDocument()
})
})
Try giving heading props in the second one.

How to make addEventListener("click") working when language is switched in magento 2 project

I am working on a magento 2 project. The project has two languages. I have encountered a strange behavior where the click events on tabs work on mobile screen when I first load on a language. Once I switch the language, the click events do not seem to work. I have made a screen recording to demonstrate my problem visually.
Here you can find the video
The code for my component is below.
import { shape, string } from 'prop-types';
import React, { useEffect, useRef } from 'react';
import { useWindowSize } from '#magento/peregrine/lib/hooks/useWindowSize';
import { useStyle } from '#magento/venia-ui/lib/classify';
import CmsBlock from '#magento/venia-ui/lib/components/CmsBlock';
import defaultClasses from './footer.module.css';
const Footer = ({ classes }) => {
const isMobile = useWindowSize().innerWidth < 1024;
const initialized = useRef(false);
useEffect(() => {
const buttonElements = document.querySelectorAll('.col-links');
if (!initialized.current && isMobile) {
setTimeout(() => {
initialized.current = true;
for (let i = 0; i < buttonElements.length; i++) {
const handleClick = () => {
if (isMobile) {
buttonElements[i].classList.toggle('active-tab');
}
};
if (isMobile) {
buttonElements[i].addEventListener('click', handleClick);
}
if (initialized.current && !isMobile) {
buttonElements[i].classList.remove('active-tab');
}
}
}, 500);
}
});
const rootClasses = useStyle(classes, defaultClasses);
return (
<footer id="footer" className={rootClasses.root} >
<div className="footerWrapper">
<CmsBlock identifiers="footer" />
</div>
</footer>
);
};
export default Footer;
Footer.propTypes = {
classes: shape({
root: string
})
};

Jest test onClick for util function

Hey guys I am facing issue while trying to add test case for onClick in MetaDetails.tsx file
utils.js
export const handlePrintLabelButtonClick = (
e,
rmaNumber,
labelUrl,
getReturnLabel
) => {
const rmaList = [];
e.preventDefault();
if (!labelUrl) {
// some logic
} else {
// some logic
}
};
PrintLabel.tsx
import { DefaultButton } from "some path";
import { AnchorWrapper, ButtonWrapper } from "./index.styles";
export const PrintLabelButton = ({
target,
url,
type,
text,
onClickHandle
}: PrintLabelButtonProps) => {
return (
<ButtonWrapper>
<AnchorWrapper
href={url}
target={target}
type={type}
>
<DefaultButton
tabIndex="0"
onClick={onClickHandle}
data-test="print-label-button"
>
{text}
</DefaultButton>
</AnchorWrapper>
</ButtonWrapper>
);
};
MetaDetails.tsx
// Some lines of code
import { PrintLabelButton } from "./printLabel";
import { handlePrintLabelButtonClick } from "utils";
export const OrderMetaDetails = () => {
// some logic
return(
//Some React code
{showPrintLabelButton && (
<PrintLabelButton
onClickHandle={e =>
handlePrintLabelButtonClick(e, rmaNumber, labelUrl, getLabel)
}
url={labelUrl}
target="_blank"
type="printLabel"
text={intl.formatMessage(messages.printLabelText)}
/>
)}
// Some React code
)
}
What I've tried
MetaDetails.test.tsx
test("Order Meta Details Print Label Click", () => {
const handlePrintLabelButtonClick = jest.fn();
const wrapper = mountWithIntl(
<OrderMetaDetails
getLabel={() => {}}
info={/*some data*/}
intl={/*intl*/ }
/>
);
const component = wrapper.find(`[data-test="print-label-button"]`).hostNodes();
component.simulate("click")
expect(handlePrintLabelButtonClick).toHaveBeenCalled();
});
Jest throws the following error
Error: expect(jest.fn()).toHaveBeenCalled()
Expected number of calls: >= 1
Received number of calls: 0
When running the test coverage, in the function coverage I see that the "handlePrintLabelButtonClick" in MetaDetails.tsx is shown as uncovered. I have tried other approaches as well but none worked. I am new to unit testing. Can someone please guide as to what is the correct approach.
Note 1: I am using react/#emotion
Note 2: I have commented or written text such as "some logic" since there are lots of unwanted lines.
Your source code still uses the original handlePrintLabelButtonClick function, not the mock function that you created in the test.
You need to mock the actual exported function, for example using jest.mock:
import { handlePrintLabelButtonClick } from './utils.js';
const mockHandlePrintLabelButtonClick = jest.fn();
// Mock the module.
jest.mock('./utils.js', () => {
handlePrintLabelButtonClick: mockHandlePrintLabelButtonClick
}
describe('something', () => {
test("Order Meta Details Print Label Click", () => {
const wrapper = mountWithIntl(
<OrderMetaDetails
getLabel={() => {}}
info={/*some data*/}
intl={/*intl*/ }
/>
);
const component = wrapper.find(`[data-test="print-label-button"]`).hostNodes();
component.simulate("click")
expect(mockHandlePrintLabelButtonClick).toHaveBeenCalled();
});
})

NextJS routeChangeComplete doesn't trigger on _app.js

Here my ReactJS' snippet:
const LoaderModal = dynamic(
() => import("~/modal/Loader/Loader"),
{ ssr: false }
)
export default class MyApp extends Component {
state={
displayLoader:false
}
componentDidMount(){
let specificChangeStart= Router.events.on('routeChangeStart', () => {
console.log("routeChangeStart")
this.setState({displayLoader:true})
})
let specificComplete = Router.events.on('routeChangeComplete', () => {
console.log("routeChangeComplete")
this.setState({displayLoader:false})
})
let specificError= Router.events.on('routeChangeError',() => {
console.log("routeChangeError")
this.setState({displayLoader:false})
})
}
render(){
let { Component, pageProps }=this.props
let {displayLoader}=this.state
return(
<LayoutContextProvider >
<Component {...pageProps}/>
{
displayLoader &&
<LoaderModal/>
}
</LayoutContextProvider>
)
}
}
I am using nextjs version 9.1.4.
As you can see the Router's events are stored in the componentDidMount() component lifecycle's stage. I have stored them in variable to make them specific from other declarations in my app. I have also tried without assigning them, the both method fail so far.
How can I make the Router works, is there a subtlety to be inform of here?

useEffect hook misbehaves with setTimeout and state

I created a custom toast component in my exercise React application. It is working correctly until the moment I try to introduce an auto dismiss timeout functionality. Basically when you load a new toast it needs to dismiss itself after let say 5000ms.
If you want check the full code in my Github Repo that also have a live preview.
Easiest way to create toast is put invalid mail / password.
I believe I am doing something wrong with the useEffect hook or I am missing something. The problem is that when I am creating multiple toasts they disappear all at the same time. Also React is complaining that I didn't include remove as a dependency of the useEffect hook but when I do it becomes even worse. Can someone demystify why this is happening and how it can be fixed. I am a bit new to React.
Here is the file that creates a HOC around my main App component:
import React, { useState } from 'react';
import { createPortal } from 'react-dom';
import ToastContext from './context';
import Toast from './Toast';
import styles from './styles.module.css';
function generateUEID() {
let first = (Math.random() * 46656) | 0;
let second = (Math.random() * 46656) | 0;
first = ('000' + first.toString(36)).slice(-3);
second = ('000' + second.toString(36)).slice(-3);
return first + second;
}
function withToastProvider(Component) {
function WithToastProvider(props) {
const [toasts, setToasts] = useState([]);
const add = (content, type = 'success') => {
const id = generateUEID();
if (toasts.length > 4) {
toasts.shift();
}
setToasts([...toasts, { id, content, type }]);
};
const remove = id => {
setToasts(toasts.filter(t => t.id !== id));
};
return (
<ToastContext.Provider value={{ add, remove, toasts }}>
<Component {...props} />
{ createPortal(
<div className={styles.toastsContainer}>
{ toasts.map(t => (
<Toast key={t.id} remove={() => remove(t.id)} type={t.type}>
{t.content}
</Toast>
)) }
</div>,
document.body
) }
</ToastContext.Provider>
);
}
return WithToastProvider;
}
export default withToastProvider;
And the Toast component:
import React, { useEffect } from 'react';
import styles from './styles.module.css';
function Toast({ children, remove, type }) {
useEffect(() => {
const duration = 5000;
const id = setTimeout(() => remove(), duration);
console.log(id);
return () => clearTimeout(id);
}, []);
return (
<div onClick={remove} className={styles[`${type}Toast`]}>
<div className={styles.text}>
<strong className={styles[type]}>{type === 'error' ? '[Error] ' : '[Success] '}</strong>
{ children }
</div>
<div>
<button className={styles.closeButton}>x</button>
</div>
</div>
);
}
export default Toast;
Searching today for the solution I found it here
You will need to use useRef and its current property
Here is how I transformed the Toast component to work:
import React, { useEffect, useRef } from 'react';
import styles from './styles.module.css';
function Toast({ children, remove, type }) {
const animationProps = useSpring({opacity: .9, from: {opacity: 0}});
const removeRef = useRef(remove);
removeRef.current = remove;
useEffect(() => {
const duration = 5000;
const id = setTimeout(() => removeRef.current(), duration);
return () => clearTimeout(id);
}, []);
return (
<div onClick={remove} className={styles[`${type}Toast`]}>
<div className={styles.text}>
<strong className={styles[type]}>{type === 'error' ? '[Error] ' : '[Success] '}</strong>
{ children }
</div>
<div>
<button className={styles.closeButton}>x</button>
</div>
</div>
);
}
export default Toast;

Categories

Resources