@dr.pogodin/react-utils
Version:
Collection of generic ReactJS components and utils
47 lines • 4.61 kB
JavaScript
/* 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