Create React App and JEST property matchers - jestjs

I've got this component that I've simplified for this example as part of a bigger create react app
import React from 'react';
function myVersion() {
return (
<p className={'ActivateSound--para'} data-testid={`version`}>
Version {process.env.REACT_APP_VERSION} has loaded, congrats!
</p>
);
}
export { myVersion };
and in the env I get the version from package.json
REACT_APP_VERSION=$npm_package_version
but in the test I've got
import React from 'react';
import { myVersion } from '../components/my-version';
import { render, screen, cleanup, fireEvent } from '#testing-library/react';
afterEach(cleanup);
describe('my version component', () => {
test('snapshot hasnt changed', () => {
expect(container.firstChild).toMatchSnapshot();
});
});
I am now looking at https://jestjs.io/docs/snapshot-testing#property-matchers. It seems I can exclude the version number somehow, I've tried a bunch of attempts but it's not ignoring the version number
expect(container.firstChild).toMatchSnapshot({
screen.getByTestId('version'): expect.any(String)
});

Related

use Effect not working in react. the error is saying Uexpected token in the web console

I am using use effect in react js and it seems that it has a problem if I use 'effect'
in my react app then it gives me an error saying "Unexpected token"
import React, { Component , useEffect} from 'react'
class Effect extends Component {
useEffect(() => {
console.log('Effect');
})
constructor() {
super()
this.state =
{
count :0
}
this.CH = () =>
{
this.setState(PS =>{
console.log(PS);
return {count:PS.count +1}
})
}
}
render()
{
return(
<>
<button onClick={this.CH}>
</button>
<p>
{`You have clicked me ${this.state.count} times.`}
</p>
</>
)
}
}
export default Effect
and this is the useEffect code:
useEffect(() => {
console.log('Effect');
})
so i followed some vids and it seems to not work for me
they were using this in function based component . but i am not,
so the the problem us this only?
You can only use useEffect on Functional-based components!
On class-based you have some alternatives: componentDidMount()
Docs: https://reactjs.org/docs/react-component.html#componentdidmount
Suggestion: if you are creating a new react app most of the time the best thing to do is to use function components.
Source: https://djoech.medium.com/functional-vs-class-components-in-react-231e3fbd7108#:~:text=The%20most%20obvious%20one%20difference,which%20returns%20a%20React%20element.

React "The top-level-await experiment is not enabled" and "await"

I need to await some data from another file but whenever I try to run following code, an error comes up.
My Code:
import React from 'react';
import './App.css';
import { getApiData } from './functions/player-api-data'
export async function App() {
const data = await (getApiData('a3d7dc03c0e84c3eaa726110df90cbf8', 'player') as any)
console.log(await data)
return (
<div className="App">
<pre>{JSON.stringify(await data)}</pre>
</div>
);
}
export default await App as any
The Error:
Module parse failed: The top-level-await experiment is not enabled (set experiments.topLevelAwait: true to enabled it)
File was processed with these loaders:
* ./node_modules/#pmmmwh/react-refresh-webpack-plugin/loader/index.js
* ./node_modules/babel-loader/lib/index.js
* ./node_modules/source-map-loader/dist/cjs.js
You may need an additional loader to handle the result of these loaders.
Error: The top-level-await experiment is not enabled (set experiments.topLevelAwait: true to enabled it)
I have tried to find the webpack.config.js file but in the existing file, there's no "experiments" object to be found in it. If there is a better way than fixing this specific error that would be great too.
React Components Cannot be Asynchronous. If you want asyncronous data, you can use UseEffect. Modify your code as..
import React,{useEffect} from 'react';
import './App.css';
import { getApiData } from './functions/player-api-data'
function App() {
const [data,setData] = useState();
const getData = async () => {
let result = await getApiData('a3d7dc03c0e84c3eaa726110df90cbf8', 'player')
setData(result)
}
useEffect(() => {
// Fetching Data on Initial Load
getData()
},[])
return (
<div className="App">
// will render if data is primitive or valid jsx
<pre>{data}</pre>
</div>
);
}
export default App

NextJS useRouter() returns null in unit test with Storybook add-on

I'm running into an issue when integrating my stories into my unit tests using Jest & React Testing Library in a NextJS app. In Storybook, my NextJS components render without issue with the Storybook Addon Next Router working as expected. However, in my Jest test file, my test throws an error because useRouter() returns null. I believe I've setup all the addons correctly.
My setup:
NextJS 12
Jest & React Testing Library
Storybook
Storybook Addon Next Router (for using Next router inside Storybook)
#storybook/testing-react (for integrating my stories, args & params with testing library)
The problem:
I've setup all the files according to the docs. The story renders fine inside Storybook and the useRouter() works thanks to the Storybook Next addon. However, the composeStories() function from #storybook/testing-react seems to fail to properly initialize the Next router Provider from the first addon. My unit test fails with the following error:
Test suite failed to run
TypeError: Cannot read property 'locale' of null
And points to the following line inside my component:
// Events.tsx
const { locale = 'en' } = useRouter();
This is my test file:
// Events.test.tsx
import React from 'react';
import { render, screen } from '#testing-library/react';
import { composeStories } from '#storybook/testing-react';
import * as stories from './Events.stories'
const { WithSeasonsAndEvents } = composeStories(stories);
describe('Events Screen', ()=> {
render(<WithSeasonsAndEvents />);
it('renders input data', ()=> {
// set up test
});
});
And my stories file:
// Events.stories.tsx
import React from 'react';
import { ComponentStory, ComponentMeta } from '#storybook/react';
import Events from './Events';
export default {
title: 'Events/Events Screen',
component: Events
} as ComponentMeta<typeof Events>;
const Template: ComponentStory<typeof Events> = (args) => <Events {...args} />;
export const WithSeasonsAndEvents = Template.bind({});
WithSeasonsAndEvents.args = {
// many args here
};
This story renders fine in Storybook. useRouter() works as expected inside my story and I am able to use all of its properties including pathname and locale. However, for some reason the useRouter() function returns null when the composed story is being rendered by React Testing Library.
What I have tried:
Verified installing #storybook/testing-react and set up my global configuration in jest setup file:
// jest-setup.ts
import '#testing-library/jest-dom/extend-expect';
import { setGlobalConfig } from '#storybook/testing-react';
// Storybook's preview file location
import * as globalStorybookConfig from './.storybook/preview';
setGlobalConfig(globalStorybookConfig);
Verified that jest reads my setup file and finds my Storybook preview file
Verified both .storybook/preview.js and .storybook/main.js match the usage guide:
preview.js
// .storybook/preview.js
import '../styles/tailwind.css';
import { RouterContext } from "next/dist/shared/lib/router-context"; // next 12 https://storybook.js.org/docs/react/writing-stories/parameters
export const parameters = {
actions: { argTypesRegex: "^on[A-Z].*" },
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
nextRouter: {
Provider: RouterContext.Provider,
locale: 'en',
},
}
main.js
// .storybook/main.js
module.exports = {
"stories": [
"../stories/**/*.stories.mdx",
"../stories/**/*.stories.#(js|jsx|ts|tsx)",
"../components/**/*.stories.#(js|jsx|ts|tsx|mdx)"
],
"addons": [
"#storybook/addon-links",
"#storybook/addon-essentials",
"storybook-addon-next-router",
],
"framework": "#storybook/react"
}
Stories render fine in Storybook, all useRouter() features work, and when I console.log the useRouter() return value, I get the full suite of NextJS useRouter object properties:
// >> console.log(useRouter()); inside Storybook
{
route: "/",
pathname: "/",
query: {},
asPath: "/",
events: {},
isFallback: false,
locale: 'en'
}
However, on the unit test, when logging the return value of useRouter(); inside my component, it returns null. Understandably, because it is null, the { locale } variable assignment is throwing an error in my unit test.
When logging the value of useRouter variable, both inside my Storybook preview and inside my unit test I get the following function:
// >> console.log(useRouter.toString())
function useRouter() {
return _react.default.useContext(_routerContext.RouterContext);
}
Does anyone have any idea on what can be going wrong? I'm fairly new to Storybook but I've tried looking through GitHuB issues and online and haven't been able to solve. No idea why useRouter() returns null inside Jest if the composeStories() fn should take care of resolving my Storybook params & args. Any insight would be appreciated.
I was running with the same issue. I solved it by exporting WithNextRouter as a decorator on preview.js
// preview.js
import { WithNextRouter } from 'storybook-addon-next-router/dist/decorators';
export const decorators = [WithNextRouter];

Invariant Violation: "main" has not been registered. This can happen if: * Metro (the local dev server) is run from the wrong folder

I have changed my index.js to this as im trying to follow this turorial:
https://www.youtube.com/watch?v=6soHPnHxHxI
import { registerRootComponent } from 'expo';
import React from "react";
import { AppRegistry } from "react-native";
import App from "./App";
import { name as appName } from "./app.json";
import { Provider } from "react-redux";
import { Provider as PaperProvider } from "react-native-paper"
import store from "./store";
import 'react-native-gesture-handler';
const AppRedux = () => {
<Provider {...{store}}>
<PaperProvider>
<App></App>
</PaperProvider>
</Provider>
}
// registerRootComponent calls AppRegistry.registerComponent('main', () => App);
// It also ensures that whether you load the app in the Expo client or in a native build,
// the environment is set up appropriately
//AppRegistry.registerComponent(appName, () => AppRedux);
// registerRootComponent calls AppRegistry.registerComponent('main', () => App);
// It also ensures that whether you load the app in the Expo client or in a native build,
// the environment is set up appropriately
registerRootComponent(AppRedux);
But i keep getting this error:
Invariant Violation: "main" has not been registered. This can happen if: * Metro (the local dev server) is run from the wrong folder.
Open the index.js, the content of the file should be like this:
import { AppRegistry, Platform } from 'react-native';
import App from './App';
AppRegistry.registerComponent('X', () => App);
if (Platform.OS === 'web') {
const rootTag = document.getElementById('root') || document.getElementById('X');
AppRegistry.runApplication('X', { rootTag });
}
If you have this error Invariant Violation: “main” has not been registered you have to replace the 'X' by 'main'.
Another example :
If you have this error Invariant Violation: “app” has not been registered you have to replace the 'X' by 'app'.

Is there a way to mock `module` variable?

I want to test my app init function, that depends on node.js module variable (webpack's module variable to be precise). I can't pass module variable as a function parameter because of this webpack GitHub issue.
I found this rewire library, that seems to be good for my problem. But it looks like it is abandoned and has some errors because of my ES6 imports.
Code sample I want to test:
import ReactDOM from 'react-dom'
import React from 'react'
import Root from './components/Root'
export function init() {
const {AppContainer} = require('react-hot-loader')
const render = Component => {
ReactDOM.render(
<AppContainer>
<Component />
</AppContainer>,
document.getElementById('root')
)
}
render(Root)
if (module.hot) { // the part I would like to test by mocking `module` variable
module.hot.accept('./components/Root', () => { render(Root) })
}
}
init()

Resources