UNPKG

@dr.pogodin/react-utils

Version:

Collection of generic ReactJS components and utils

47 lines 4.61 kB
/* global jest, document *//* eslint-disable import/no-extraneous-dependencies */import mockdate from"mockdate";import{act}from"react";import{createRoot}from"react-dom/client";import{render}from"@testing-library/react";/** * An alias for [act(..)](https://reactjs.org/docs/test-utils.html#act) * from `react`. */export{act};export{default as getGlobal}from"./global.js";global.IS_REACT_ACT_ENVIRONMENT=true;const originalProcessVersions=process.versions;/** * Tricks **react-utils** into thinking the test is running within client-side * (browser) environment. */export function mockClientSide(){Object.defineProperty(process,"versions",{value:undefined})}/** * Reverts the effect of {@link module:JU.mockClientSide mockClientSide(..)}. */export function unmockClientSide(){Object.defineProperty(process,"versions",{value:originalProcessVersions,writable:false})}/** * Generates a mock UUID, or better said it determenistically transforms given * `seed` number into a UUID-formatted string. * @param {number} seed * @return {string} */export function getMockUuid(seed=0){const x=seed.toString(16).padStart(32,"0");return`${x.slice(0,8)}-${x.slice(8,12)}-${x.slice(12,16)}-${x.slice(16,20)}-${x.slice(20)}`}export function mockAxios(handlers){const axios=jest.requireActual("axios");axios.defaults.adapter=async config=>{for(const handler of handlers){const res=handler(config);if(res){return{config:config,data:null,headers:{},status:200,statusText:"OK",...res}}}// Fallback to the regular network request. let res;try{res=await axios({...config,adapter:["xhr","http","fetch"]});// eslint-disable-next-line no-console console.warn("Network request has not been mocked for a test.\n\nConfig:\n",config,"\n\nResult:\n",JSON.stringify(res,null,2))}catch(e){// eslint-disable-next-line no-console console.warn("Network request has not been mocked for a test, and failed.\n\nConfig:\n",config,"\n\nError\n",JSON.stringify(e,null,2));throw e}return res};return axios}/** * Advances mock timers, and mock date by the specified time. * @param {number} time Time step [ms]. * @returns {Promise} Wait for this to "jump after" any async code which should * be executed because of the mock time movement. */export async function mockTimer(time){mockdate.set(time+Date.now());await jest.advanceTimersByTimeAsync(time)}/** * Mounts `scene` to the DOM, and returns the root scene element. * @param scene * @return Created container DOM element with destroy() function * attached. */export function mount(scene){let root;const element=document.createElement("div");document.body.appendChild(element);const res=element;res.destroy=()=>{// NOTE: As it seems @testing-library may reset this flag to false // when it is simulating user events. global.IS_REACT_ACT_ENVIRONMENT=true;act(()=>{root.unmount()});res.remove()};res.snapshot=()=>{expect(res).toMatchSnapshot()};// NOTE: As it seems @testing-library may reset this flag to false // when it is simulating user events. global.IS_REACT_ACT_ENVIRONMENT=true;act(()=>{root=createRoot(res);root.render(scene)});return res}// NOTE: If in future we have additional options here, they should be distributed // across two objects, depending whether they are applicable to the sync, or async // versions of snapshot(), or both. export function snapshot(element,options){let res;// TODO: Just adding async to the actor function breaks stuff, as it makes // act() asynchronous no matter the `options.await` value, thus breaking all // calls that do not await for snapshot() result... thus... perhaps we need // to have a more complex typing to ensure it all works as intended in all // cases, and while being correctly enforced by TypeScript. // eslint-disable-next-line @typescript-eslint/promise-function-async const promise=act(()=>{res=render(element);return options?.await});if(res===undefined)throw Error("Render failed");if(options?.await){// BEWARE: Although `promise` is thenable (i.e. it has .then() method), // it is not an instance of proper Promise class, and returning it directly // breaks some async logic in Jest test or React test functions... thus, we // wrap it into Promise instance here. return new Promise(resolve=>{void promise.then(()=>{// TODO: These lines are the same as the lines below for sync variant of // the function. We should split and reuse them in both places. const nodes=res.asFragment().childNodes;expect(nodes.length>1?[...nodes]:nodes[0]).toMatchSnapshot();resolve(res)})})}const nodes=res.asFragment().childNodes;expect(nodes.length>1?[...nodes]:nodes[0]).toMatchSnapshot();return res} //# sourceMappingURL=index.js.map