I have a react code which is drag and resize of the height of div and it works perfectly fine and clickable div is resizer I need to write a unit test to test event listeners are called or not
useEffect(() => {
let mousePosition;
retriveAgentHistoryHeight();
const panel = dragableElement?.current as HTMLElement;
const resizerEle = resizer?.current as HTMLElement;
const setPanelHeight = dx => {
const newHeight = `${parseInt(getComputedStyle(panel, '').height) + dx}px`;
panel.style.height = newHeight;
saveAgentHistoryHeight(newHeight);
};
const resize = e => {
const dx = mousePosition - e.y;
mousePosition = e.y;
setPanelHeight(dx);
};
const touchResizeEvent = e => {
const y = parseInt(e.touches[0].clientY);
const dx = mousePosition - y;
mousePosition = y;
setPanelHeight(dx);
};
const mouseOnPressEvent = e => {
mousePosition = e.y;
document.addEventListener('mousemove', resize, false);
setResizeClass('active');
panel.style.userSelect = 'none';
};
const screenTouchEvent = e => {
mousePosition = parseInt(e.touches[0].clientY);
document.addEventListener('touchmove', touchResizeEvent);
setResizeClass('active');
panel.style.userSelect = 'none';
};
const mouseOnReleaseEvent = () => {
document.removeEventListener('mousemove', resize, false);
setResizeClass('');
panel.style.userSelect = 'auto';
};
const screenTouchEndEvent = () => {
document.removeEventListener('touchmove', touchResizeEvent);
setResizeClass('');
panel.style.userSelect = 'auto';
};
if (resizerEle) {
resizerEle.addEventListener('mousedown', mouseOnPressEvent);
document.addEventListener('mouseup', mouseOnReleaseEvent);
resizerEle.addEventListener('touchstart', screenTouchEvent);
document.addEventListener('touchend', screenTouchEndEvent);
}
return () => {
if (resizerEle) {
resizerEle.removeEventListener('mousedown', mouseOnPressEvent);
document.removeEventListener('mouseup', mouseOnReleaseEvent);
resizerEle.removeEventListener('touchstart', screenTouchEvent);
document.removeEventListener('touchend', screenTouchEndEvent);
}
};
}, []);
my test.js
it('should load verify load event of resizer on mouse release', async () => {
const {getByTestId } = render(<ContactHistoryHandledByAgent />);
const mouseOnPressEvent = jest.fn(a => a);
fireEvent.mouseUp(getByTestId('agent-history-resizer'));
expect(mouseOnPressEvent).toBeCalled();
})
expect(mouseOnPressEvent).toBeCalled(); is not working
I wanted to check event such as mouseOnPressEvent is called or not
You can assign jest.fn() to a constant and pass it as the event parameter to your Component. Then you specify with the simulate(event) method the event which will happen during the test. And at the end you check if it was called.
Here an example for a onClick test:
const onclick = jest.fn();
const component = render(<ContactHistoryHandledByAgent onClick={onclick} />);
component.simulate("click");
expect(onclick).toHaveBeenCalled();
Related
I have code written on react js, using canvas html 5. It is image eraser: it should properly erase front image and when only some persent of it left, it trigger another action(i haven not done another action yet). It works fine in browser, but when i choose adaptive for any smatphone, it stops working. I tried to change mouse events to touch events but it didn't help.
Here is the code:
import './DrawingCanvas.css';
import {useEffect, useRef, useState} from 'react';
const DrawingCanvas = () => {
const canvasRef = useRef(null);
const contextRef = useRef(null);
const [isDrawing, setIsDrawing] = useState(false);
const [persent, setPersent] = useState(100);
useEffect(() => {
const url = require('../../images/one.jpg');
const canvas = canvasRef.current;
const img = new Image();
img.src = url;
const context = canvas.getContext("2d");
img.onload = function () {
let width = Math.min(500, img.width);
let height = img.height * (width / img.width);
canvas.width = width;
canvas.height = height;
context.drawImage(img, 0, 0, width, height);
contextRef.current = context;
context.lineCap = "round";
context.lineWidth = 350;
contextRef.current.globalCompositeOperation = 'destination-out';
};
}, []);
useEffect(() => {
console.log(persent);
if(persent<=0.1) return alert('hello')
}, [persent]);
const startDrawing = ({nativeEvent}) => {
const {offsetX, offsetY} = nativeEvent;
contextRef.current.beginPath();
contextRef.current.moveTo(offsetX, offsetY);
contextRef.current.lineTo(offsetX, offsetY);
// contextRef.current.stroke();
setIsDrawing(true);
nativeEvent.preventDefault();
};
const draw = ({nativeEvent}) => {
if (!isDrawing) {
return;
}
const {offsetX, offsetY} = nativeEvent;
contextRef.current.lineTo(offsetX, offsetY);
contextRef.current.stroke();
nativeEvent.preventDefault();
let pixels = contextRef.current.getImageData(0, 0, canvasRef.current.width, canvasRef.current.height);
let transPixels = [];
for (let i = 3; i < pixels.data.length; i += 4) {
transPixels.push(pixels.data[i]);
}
let transPixelsCount = transPixels.filter(Boolean).length;
setPersent((transPixelsCount / transPixels.length) * 100);
};
const stopDrawing = () => {
contextRef.current.closePath();
setIsDrawing(false);
};
return (
<div>
<canvas className="canvas-container"
ref={canvasRef}
onMouseDown={startDrawing}
onMouseMove={draw}
onMouseUp={stopDrawing}
onMouseLeave={stopDrawing}
>
</canvas>
</div>
)
}
export default DrawingCanvas;
.canvas-container {
background-image: url("https://media.istockphoto.com/id/1322277517/photo/wild-grass-in-the-mountains-at-sunset.jpg?s=612x612&w=0&k=20&c=6mItwwFFGqKNKEAzv0mv6TaxhLN3zSE43bWmFN--J5w=");
}
Maybe it all can be done through touch events, but idk how to do that properly
I am trying to play an animation that comes from a blender model while simultaneously having the model auto-rotate from OrbitControls. However I am getting the model to be duplicated, one model is animated on top of a stationary copy of the model that is unanimated:
I've tried removing the orbit controls code and update however I am still getting this result. Any advice would be greatly appreciated.
import {
AmbientLight,
Color,
DirectionalLight,
Mesh,
AnimationMixer,
MeshPhongMaterial,
PerspectiveCamera,
Scene,
SphereBufferGeometry,
UniformsUtils,
Vector2,
WebGLRenderer,
sRGBEncoding,
PointLight,
Clock,
} from 'three';
import { cleanRenderer,
cleanScene,
removeLights,
modelLoader,
textureLoader, } from 'utils/three';
import { GLTFLoader, OrbitControls, RenderPixelatedPass } from 'three-stdlib';
import { render } from 'react-dom';
export const ScifiWorkerRobot = props => {
const theme = useTheme();
const [loaded, setLoaded] = useState(false);
const { rgbBackground, themeId, colorWhite } = theme;
const start = useRef(Date.now());
const canvasRef = useRef();
const mouse = useRef();
const renderer = useRef();
const camera = useRef();
const scene = useRef();
const lights = useRef();
const uniforms = useRef();
const material = useRef();
const geometry = useRef();
const sphere = useRef();
const reduceMotion = useReducedMotion();
const isInViewport = useInViewport(canvasRef);
const windowSize = useWindowSize();
const rotationX = useSpring(0, springConfig);
const rotationY = useSpring(0, springConfig);
const { measureFps, isLowFps } = useFps(isInViewport);
const cameraXSpring = useSpring(0, cameraSpringConfig);
const cameraYSpring = useSpring(0, cameraSpringConfig);
const cameraZSpring = useSpring(0, cameraSpringConfig);
const controls = useRef();
const animations = useRef();
const mixer = useRef();
const clock = useRef();
const animationFrame = useRef();
const mounted = useRef();
const sceneModel = useRef();
//Setting up initial scene
useEffect(() => {
//mounted.current = true;
const { innerWidth, innerHeight } = window;
mouse.current = new Vector2(0.8, 0.5);
renderer.current = new WebGLRenderer({
canvas: canvasRef.current,
antialias: false,
alpha: true,
powerPreference: 'high-performance',
failIfMajorPerformanceCaveat: true,
});
renderer.current.setSize(innerWidth, innerHeight);
renderer.current.setPixelRatio(1);
renderer.current.outputEncoding = sRGBEncoding;
camera.current = new PerspectiveCamera(54, innerWidth / innerHeight, 0.1, 100);
camera.current.position.z = 5;
camera.current.position.x = 0;
camera.current.position.y = 0;
scene.current = new Scene();
clock.current = new Clock();
const ambientLight = new AmbientLight(colorWhite, 0.9);
const dirLight = new DirectionalLight(colorWhite, 0.8);
dirLight.position.set(0,0,0);
const lights = [ambientLight, dirLight];
lights.forEach(light => scene.current.add(light));
controls.current = new OrbitControls(camera.current, canvasRef.current);
controls.current.enableZoom = true;
controls.current.enablePan = true;
controls.current.enableDamping = true;
controls.current.rotateSpeed = 0.5;
controls.current.autoRotate = true;
const loader = new GLTFLoader();
loader.load(veribot, function ( gltf ) {
scene.current.add( gltf.scene );
animations.current = gltf.animations;
mixer.current = new AnimationMixer(gltf.scene);
mixer.current.timeScale = 0.8;
animations.current.forEach((clip, index) => {
const animation = mixer.current.clipAction(clip);
animation.play();
});
}, undefined, function ( error ) {
console.error( error );
} );
return () => {
//mounted.current = false;
cancelAnimationFrame(animationFrame.current);
removeLights(lights);
cleanScene(scene.current);
cleanRenderer(renderer.current);
};
}, []);
useEffect(() => {
let animation;
const animate = () => {
animation = requestAnimationFrame(animate);
const delta = clock.current.getDelta();
if (mixer.current){
mixer.current.update(delta);
}
controls.current.update();
renderer.current.render(scene.current, camera.current);
measureFps();
};
animate();
return () => {
cancelAnimationFrame(animation);
};
}, [isInViewport, measureFps, reduceMotion, isLowFps, rotationX, rotationY]);
return (
<Transition in timeout={2000}>
{visible => (
<canvas
aria-hidden
className={styles.canvas}
data-visible={visible}
ref={canvasRef}
{...props}
/>
)}
</Transition>
);
};
I wanted to test that a count increases whenever I clicked on a button, but it seems not to work. Please Help! Here is the code...
describe('checkbtn', () => {
it('onClick', () => {
const { queryByTitle } = render(<Counter />);
const { queryByTitle } = render(<Counter />);
const btn = queryByTitle('button1');
const count = queryByTitle('count');
expect(count.innerHTML).toBe(count.innerHTML);
fireEvent.click(btn);
expect(count.innerHTML).toBe(count.innerHTML + 1);
})
})
First of all you expect some state to equal the same state + 1 here:
expect(count.innerHTML).toBe(count.innerHTML + 1);
It's the same as to write
const x = 2;
expect(x).toBe(x+2)
Second is that you try to add number to string which will result in not what you expect.
What you should do is to write explicit values in your test:
describe('checkbtn', () => {
it('onClick', () => {
const { queryByTitle } = render(<Counter />);
const btn = queryByTitle('button1');
const count = queryByTitle('count');
expect(count.innerHTML).toBe('1');
fireEvent.click(btn);
expect(count.innerHTML).toBe('2');
})
})
I am currently learning React and specifically hooks. I am trying to create a canvas where I am using the hook 'useRef'. I get this point that whenever I have to create a reference to my Canvas I can use the 'useRef' hook and by can also use it to point to other canvas properties.
My question is in the code below I have created a component named "UseWBoard" and I want to pass my ctxReference.current value to the "Marker" component.
Here is the code:
import React, { useRef, useEffect } from "react";
const UseWBoard = () => {
const canvasReference = useRef(null);
const ctxReference = useRef(null);
let initPaint = false;
useEffect(() => {
const canvas = canvasReference.current;
const ctx = canvas.getContext("2d");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
ctx.lineCap = "round";
ctx.lineWidth = "25";
ctx.strokeStyle = "black";
ctxReference.current = ctx;
}, []);
function startDraw({ nativeEvent }) {
const { offsetX, offsetY } = nativeEvent;
ctxReference.current.beginPath();
ctxReference.current.moveTo(offsetX, offsetY);
initPaint = true;
}
function stopDraw() {
ctxReference.current.closePath();
initPaint = false;
}
function sketch({ nativeEvent }) {
if (!initPaint) {
return;
}
const { offsetX, offsetY } = nativeEvent;
ctxReference.current.lineTo(offsetX, offsetY);
ctxReference.current.stroke();
}
function Marker() {
ctxReference.current.lineCap = "round";
ctxReference.current.lineWidth = "1";
ctxReference.current.strokeStyle = "black";
}
return (
<div>
<button onClick={Marker}>abc</button>
<canvas
ref={canvasReference}
onMouseDown={startDraw}
onMouseUp={stopDraw}
onMouseMove={sketch}
></canvas>
</div>
);
};
const Mark = (ctxReference) => {
ctxReference.current.lineCap = "round";
ctxReference.current.lineWidth = "1";
ctxReference.current.strokeStyle = "black";
};
export { UseWBoard, Mark };
Thanks in advance.
I have a React Native component that makes an Image.getSize request for each image in the component. Then within the callback of the Image.getSize requests, I set some state for my component. That all works fine, but the problem is that it's possible for a user to transition away from the screen the component is used on before one or more Image.getSize requests respond, which then causes the "no-op memory leak" error to pop up because I'm trying to change state after the component has been unmounted.
So my question is: How can I stop the Image.getSize request from trying to modify state after the component is unmounted? Here's a simplified version of my component code. Thank you.
const imgWidth = 300; // Not actually static in the component, but doesn't matter.
const SomeComponent = (props) => {
const [arr, setArr] = useState(props.someData);
const setImgDimens = (arr) => {
arr.forEach((arrItem, i) => {
if (arrItem.imgPath) {
const uri = `/path/to/${arrItem.imgPath}`;
Image.getSize(uri, (width, height) => {
setArr((currArr) => {
const newWidth = imgWidth;
const ratio = newWidth / width;
const newHeight = ratio * height;
currArr = currArr.map((arrItem, idx) => {
if (idx === i) {
arrItem.width = newWidth;
arrItem.height = newHeight;
}
return arrItem;
});
return currArr;
});
});
}
});
};
useEffect(() => {
setImgDimens(arr);
return () => {
// Do I need to do something here?!
};
}, []);
return (
<FlatList
data={arr}
keyExtractor={(arrItem) => arrItem.id.toString()}
renderItem={({ item }) => {
return (
<View>
{ item.imgPath ?
<Image
source={{ uri: `/path/to/${arrItem.imgPath}` }}
/>
:
null
}
</View>
);
}}
/>
);
};
export default SomeComponent;
I had to implement something similar, I start by initialising a variable called isMounted.
It sets to true when the component mounts and false to when the component will unmount.
Before calling setImgDimens there's a check to see if the component is mounted. If not, it won't call the function and thus will not update state.
const SomeComponent = (props) => {
const isMounted = React.createRef(null);
useEffect(() => {
// Component has mounted
isMounted.current = true;
if(isMounted.current) {
setImgDimens(arr);
}
return () => {
// Component will unmount
isMounted.current = false;
}
}, []);
}
Edit: This is the answer that worked for me, but for what it's worth, I had to move the isMounted variable to outside the SomeComponent function for it to work. Also, you can just use a regular variable instead of createRef to create a reference, etc.
Basically, the following worked for me:
let isMounted;
const SomeComponent = (props) => {
const setImgDimens = (arr) => {
arr.forEach((arrItem, i) => {
if (arrItem.imgPath) {
const uri = `/path/to/${arrItem.imgPath}`;
Image.getSize(uri, (width, height) => {
if (isMounted) { // Added this check.
setArr((currArr) => {
const newWidth = imgWidth;
const ratio = newWidth / width;
const newHeight = ratio * height;
currArr = currArr.map((arrItem, idx) => {
if (idx === i) {
arrItem.width = newWidth;
arrItem.height = newHeight;
}
return arrItem;
});
return currArr;
});
}
});
}
});
};
useEffect(() => {
isMounted = true;
setImgDimens(arr);
return () => {
isMounted = false;
}
}, []);
};