ESLint doesn't suggest useEffect necessary subscribers (CRA + TS + ESLint + Prettier) - reactjs

When I tried to create custom hook I have faced some issue.
ESLint hasn't suggest me subscribers for useEffect. (fetch hadn't been suggested and i filled it manually) I also tried eslint-plugin-react-hooks, it doesn't work too.
useEffect(() => {
fetch();
}, [fetch]);
Same issue for useCallback
full code of custom hook
import { useState, useEffect, useCallback } from "react";
import { server } from "./server";
interface State<TData> {
data: TData | null;
}
export const useQuery = <TData = any>(query: string) => {
const [state, setState] = useState<State<TData>>({
data: null,
});
const fetch = useCallback(() => {
const fetchApi = async () => {
const { data } = await server.fetch<TData>({ query });
setState({ data });
console.log(`${data} loaded (useQuery hook)`);
};
fetchApi();
}, [query]); // HERE
useEffect(() => {
fetch();
}, [fetch]); // AND HERE
return { ...state, refetch: fetch };
};
ESLint config
{
"extends": [
"eslint:recommended",
"plugin:react/recommended",
"plugin:#typescript-eslint/recommended",
"prettier/#typescript-eslint",
"plugin:prettier/recommended"
],
"plugins": ["react", "#typescript-eslint", "prettier"],
"env": {
"browser": true,
"jasmine": true,
"jest": true
},
"rules": {
"prettier/prettier": [
"error",
{
"endOfLine": "auto"
}
]
},
"settings": {
"react": {
"pragma": "React",
"version": "detect"
}
},
"parser": "#typescript-eslint/parser"
}
devDependencies
"devDependencies": {
"#typescript-eslint/eslint-plugin": "^3.0.2",
"#typescript-eslint/parser": "^3.0.2",
"eslint-config-prettier": "^6.11.0",
"eslint-plugin-prettier": "^3.1.3",
"prettier": "^2.0.5"
}

Related

Storybook play function doesn't show interactions in the bottom panel

I am using storybook interactions addon and the play functionality. This is my code:
export default {
component: MyComponent,
title: "My Component",
args: {
someId: "someId",
},
decorators: [
(Story) => (
<QueryClientProvider client={queryClient}>
<Header />
<Box sx={{ float: "right" }}>
<Layout>
<Story />
</Layout>
</Box>
</QueryClientProvider>
),
],
} as ComponentMeta<typeof MyComponent>;
const Template: ComponentStory<typeof MyComponent> = (args: any) => {
return <MyComponent {...args} />;
};
export const MyComponentWithInteraction = Template.bind({});
MyComponentWithInteraction.play = async ({ canvasElement }) => {
const canvas = within(canvasElement);
const addButton = canvas.getAllByText("+ Add")[0];
await userEvent.click(addButton);
};
My component appears on my storybook, but the interaction tab shows no interactions:
These are the packages related to storybook in my devDependencise
"#storybook/addon-essentials": "6.5.16",
"#storybook/addon-interactions": "6.5.16",
"#storybook/addon-knobs": "^6.4.0",
"#storybook/builder-webpack5": "6.5.16",
"#storybook/jest": "0.0.10",
"#storybook/manager-webpack5": "6.5.16",
"#storybook/react": "^6.5.15",
"#storybook/testing-library": "0.0.13",
"#types/storybook__addon-knobs": "^5.2.1",
"#types/storybook__react": "^5.2.1",
"msw-storybook-addon": "1.7.0",
"tsconfig-paths-webpack-plugin": "4.0.0",
"webpack": "5"
This is my main.ts
module.exports = {
stories: ["../src/**/*.stories.#(mdx|tsx|ts|jsx|js)"],
logLevel: "debug",
addons: [
"#storybook/addon-essentials",
"#storybook/addon-links",
"#storybook/addon-interactions",
"msw-storybook-addon",
],
framework: "#storybook/react",
features: {
interactionsDebugger: true,
},
webpackFinal: async (config, { configType }) => {
config.module.rules.push({
include: path.resolve(__dirname, "../src"),
});
config.resolve.plugins = [
new TsconfigPathsPlugin({
configFile: path.resolve(__dirname, "../tsconfig.json"),
}),
];
// Return the altered config
return config;
},
core: {
builder: "#storybook/builder-webpack5",
},
docs: {
autodocs: true,
},
};
If I add this line of code at the end of my story:
await expect(canvas.getByText("Some text")).toBeInTheDocument();
I see that it fails, because it is comparing it against the initial view before the click. But if I get rid of this line, it shows me the view that is the result of the click. I don't see any running in the control tab of the bottom panel either. This is strange.
I found the issue, although it is not shown in my question, I'm going to answer it here ,maybe I can save someone from spending 3-4 hours on this. my imports looked like this which was wrong:
import { within } from "#testing-library/react";
import userEvent from "#testing-library/user-event";
This is the right import
import { userEvent, within } from "#storybook/testing-library";

Where is this SignIn test with Jest, Typescript, ts-jest and #testing-library/react going wrong?

I am looking to create a test for a login page. The workflow should be as follows:
- Enter email in textbox, click Continue button.
- Button greys out and a "Processing..." loading Spinner appears
- Enter password in textbox, click Sign in button
SignIn.tsx
import { useState} from 'react'
import { Stack, TextField, PrimaryButton, Spinner, DefaultButton, Dropdown, IDropdownOption } from '#fluentui/react';
import { useFormik } from "formik";
import * as yup from 'yup';
import { CredsI, OrganizationI, TokenI } from '../Lib/Interfaces/signin.interface';
import { JwtManager, sourceToken } from '../Classes/tokensManager';
import { RocklandCall } from '../Utils/apiUtils';
import ApiResponseMessage from '../Components/ApiResponseMessage';
import { ResponseTypeEnum } from '../Lib/Enum/responseTypeEnum';
import { signInTokenStyles, signInTextStyles } from '../componentStyling';
import { NotificationManager } from '../Classes/NotificationManager';
import { NotificationDismissEnum } from '../Lib/Enum/NotificationDismissEnum';
import { useMutation, useQuery } from 'react-query';
interface SignInProps {
onSuccess: () => void;
};
/*
* Login panel styling + layout JSX
* API call using axios
*/
function SignIn(props: SignInProps) {
//React boilerplate to keep track of errors
const [loginWaiting, setLoginWaiting] = useState(false);
const [organizationWaiting, setOrganizationWaiting] = useState(false);
const [email, setEmail] = useState("");
const [organizations, setOrganizations] = useState<OrganizationI>({names: []});
const [selectedOrganization, setSelectedOrganization] = useState({key: 0, text: ""});
const [messageBar, setMessageBar] = useState({
show: false,
type: ResponseTypeEnum.NONE,
message: ""
});
const [showFirstAuthentication, setShowFirstAuthentication] = useState(true)
const [showOrganization, setShowOrganization] = useState(false)
const closeErrorMsg = () => setMessageBar({...messageBar, show: false});
// Formik to manage email validation
const emailValidationSchema = yup.object({
email: yup.string().email('Enter a valid email').required('Email is required')
});
const emailFormik = useFormik({
initialValues: { email: "" },
validationSchema: emailValidationSchema,
onSubmit: (values) => origanization(values.email)
});
const origanization = (email: string) => {
setEmail(email);
ApiOrganizations()
}
// Formik to manage password validation
const loginValidationSchema = yup.object({
password: yup.string().required('Password is required')
});
const loginFormik = useFormik({
initialValues: { password: "" },
validationSchema: loginValidationSchema,
onSubmit: (values) => apiLogin(email, values.password)
});
const formikEmailError = (_: string) => emailFormik.errors.email ? emailFormik.errors.email : '';
const formikPasswordError = (_: string) => loginFormik.errors.password ? loginFormik.errors.password: '';
const onBackButton = () => {
setEmail("")
setShowFirstAuthentication(true)
}
const getDropdownOptions = () => organizations.names.map((o, index) => {
return {
key: index,
text: o
} as IDropdownOption});
/*
* Functions for the buttons
* developer credentials:
* username: jp#rocklandscientific.com
* password: access.$1729Rop
*/
const onSuccessLogin = (data: TokenI) => {
let token = data.token;
let expiry = data.tokenExpiry;
JwtManager.setToken(token, expiry);
if (data.message !== "") {
NotificationManager.setMessage(data.message, ResponseTypeEnum.WARNING, NotificationDismissEnum.NONE);
}
//error in unlikely case of token being null (no token so can't log to api unfortunately but we need a UI message)
if (token==null) {
setMessageBar({
show: true,
type: ResponseTypeEnum.ERROR,
message: "Login failed"
})
} else {
//the login was successful, so do whatever parent component wants us to do (such as redirect to dashboard)
props.onSuccess();
}
setLoginWaiting(false)
}
const onError = (error: any, setLoading: any) => {
let message = error.response?.data?.message ?? "";
setMessageBar({
show: true,
type: ResponseTypeEnum.ERROR,
message: message
})
setLoading(false)
}
const getSinginToken = async(creds: CredsI) => {
return await RocklandCall.signInReturnToken(creds)
}
const {mutate: apiLoginQuery} = useMutation(getSinginToken,{
onSuccess: (data) => {
onSuccessLogin(data)
},
onError: (error) => {
onError(error, setLoginWaiting)
}
});
const apiLogin = async (username: string, password: string) => {
setLoginWaiting(true);
let creds: CredsI = { username: username, password: password, organizationName: selectedOrganization.text, sourceToken: sourceToken};
apiLoginQuery(creds) //mutate
}
const onSuccessOrganizationQuery = (data: OrganizationI) => {
setOrganizations(data);
if (data.names.length > 0) setSelectedOrganization({key: 0, text: data.names[0]});
if (data.names.length > 1) setShowOrganization(true)
setOrganizationWaiting(false);
setShowFirstAuthentication(false)
}
const organizationQuery = async(email: string) => {
return await RocklandCall.organizations(email)
}
const {refetch: organizationAPI, status} = useQuery(['organinzation', email], () => organizationQuery(email), {
onSuccess: (data) => {
onSuccessOrganizationQuery (data)
},
onError: (error) => {
onError(error, setOrganizationWaiting)
},
refetchOnWindowFocus: false,
cacheTime: 0,
enabled: false
})
const ApiOrganizations = () => {
setOrganizationWaiting(true);
organizationAPI() //refetch alias name
}
return (
<div className="Sign-in-panel">
<Stack tokens={signInTokenStyles()} style={{marginLeft: 10, marginRight: 10}} >
<Stack.Item align="start" >
<h2>Welcome to AIRDROP</h2>
</Stack.Item>
{messageBar.show &&
<ApiResponseMessage
message={messageBar.message}
type={ResponseTypeEnum.ERROR}
close={closeErrorMsg} />}
{showFirstAuthentication && // The following is the first step of authentication
<form onSubmit={emailFormik.handleSubmit}>
<Stack tokens={signInTokenStyles()}>
<Stack.Item align="start" >
<TextField label="Email" id="email" name="email"
disabled={email !== ""}
required styles={signInTextStyles()} onGetErrorMessage={formikEmailError}
value={emailFormik.values.email} onChange={emailFormik.handleChange} validateOnFocusOut
/>
</Stack.Item>
<Stack.Item align="start" >
<Stack className="Submit-button" horizontal tokens={signInTokenStyles()}>
<PrimaryButton type="submit" disabled={organizationWaiting} >Continue</PrimaryButton>
{ organizationWaiting && <Spinner label="Processing..." ariaLive="assertive" labelPosition="left"/> }
</Stack>
</Stack.Item>
</Stack>
</form>
||
<form onSubmit={loginFormik.handleSubmit}>
{showOrganization && <Dropdown
placeholder="Select organization"
label="Organization"
options={getDropdownOptions()}
selectedKey = {selectedOrganization.key}
id="type"
onChange={(event, option) => setSelectedOrganization({key: option?.key as number ?? 0, text: option?.text ?? ""})}/>
}
<Stack.Item align="start" >
<TextField label="Password" id="password" name="password"
type="password" required styles={signInTextStyles()} onGetErrorMessage={formikPasswordError}
value={loginFormik.values.password} onChange={loginFormik.handleChange} validateOnFocusOut
/>
</Stack.Item>
<Stack horizontal tokens={signInTokenStyles} >
<Stack.Item className="Submit-button" >
<Stack horizontal gap={10}>
<DefaultButton onClick={onBackButton} disabled={loginWaiting} >Back</DefaultButton>
<PrimaryButton type="submit" disabled={loginWaiting} >Sign in</PrimaryButton>
{ loginWaiting && <Spinner label="Signing you in..." ariaLive="assertive" labelPosition="left"/> }
</Stack>
</Stack.Item>
</Stack>
</form>
}
</Stack>
</div>
);
}
export default SignIn;
SignIn.test.tsx
import { QueryClient, QueryClientProvider } from 'react-query';
import SignIn from '../SignIn';
import { setupServer } from 'msw/node'
import { render, screen, fireEvent, waitFor } from '#testing-library/react';
import "#testing-library/jest-dom"
import { rest } from 'msw';
// set up server to mock API login requests
let server = setupServer();
beforeAll(() => server.listen())
afterEach(() => server.resetHandlers())
afterAll(() => server.close())
const queryClient = new QueryClient();
const onSubmit = jest.fn();
beforeEach(() => {
onSubmit.mockClear();
render(
<QueryClientProvider client={queryClient}>
<SignIn onSuccess={onSubmit} />
</QueryClientProvider>
);
})
describe('SignIn Domain', () => {
test('Successful login', async () => {
server.use(
rest.post('https://localhost:5001/api/Auth/Login', (_, res, ctx) => {
return res(ctx.json({ token: 'myToken' }));
}),
);
await waitFor(() => {
fireEvent.change(getEmail(), {
target: {
value: ""
}
})
});
// click the button
await waitFor(() => {
fireEvent.click(screen.getByText('Continue'));
});
const processing = await screen.getByText('Processing...');
// await waitFor(() => {
// expect(onSubmit).toHaveBeenCalledTimes(1);
// });
// await waitFor(() => {
// expect(processing).not.toBeIntheDocument()
// })
// const password = await screen.getByRole('textbox', {
// name: /password/i
// });
await waitFor(() => {
fireEvent.change(getPassword(), {
target: {
value: "dummyPassword"
}
})
})
// click the sign in button
await waitFor(() => {
fireEvent.click(screen.getByText('Sign in'));
});
await waitFor(() => expect(onSubmit).toHaveBeenCalled());
})
})
function getEmail() {
return screen.getByRole('textbox', {
name: /email/i
})
}
function getPassword() {
return screen.getByRole('textbox', {
name: /password/i
});
}
1st attempt:
In a synchronous manner, I tried to:
- get the email and fill it in (successfully)
- click the continue button (successfully)
- get the password and fill it in (unsuccessful)
error:
TestingLibraryElementError: Unable to find an accessible element with the role "textbox" and name `/password/i`
2nd attempt:
Since the re-render isn't instant, I decided to confirm that the "Processing..." Spinner appears, and then wait until it disappears. (unsuccessful)
const processing = await screen.getByText('Processing...');
[..]
await waitFor(() => {
expect(processing).not.toBeIntheDocument()
})
error:
TypeError: The "options.agent" property must be one of Agent-like Object, undefined, or false. Received an instance of Object
TypeError: expect(...).not.toBeIntheDocument is not a function
In case this is needed:
tsconfig.json
{
"compilerOptions": {
"target": "es5",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
"types": ["node", "jest", "#testing-library/jest-dom"]
},
"include": [
"src"
]
}
package.json
{
"name": "airdrop-app",
"version": "0.1.0",
"private": true,
"dependencies": {
"#fluentui/react": "^8.104.5",
"#fluentui/react-icons": "^1.1.145",
"#fluentui/react-icons-mdl2": "^1.3.29",
"#testing-library/jest-dom": "^5.16.5",
"#testing-library/react": "^11.2.7",
"#testing-library/user-event": "^12.8.3",
"#types/d3": "^6.7.5",
"#types/file-saver": "^2.0.5",
"#types/filesaver": "^0.0.30",
"#types/geojson": "^7946.0.10",
"#types/mapbox-gl": "^2.7.10",
"#types/node": "^12.20.55",
"#types/react": "^17.0.52",
"#types/react-dom": "^17.0.18",
"#types/react-motion": "0.0.29",
"#types/react-router-dom": "^5.3.3",
"axios": "^0.21.4",
"d3": "^6.7.0",
"d3-hexbin": "^0.2.2",
"d3-svg-legend": "^2.25.6",
"enzyme": "^3.11.0",
"file-saver": "^2.0.5",
"fluent": "^0.13.0",
"formik": "^2.2.9",
"mapbox-gl": "^2.12.0",
"msw": "^0.28.2",
"react": "^17.0.2",
"react-cookie": "^4.1.1",
"react-dom": "^17.0.2",
"react-map-gl": "^6.1.21",
"react-motion": "^0.5.2",
"react-query": "^3.39.2",
"react-rnd": "^10.4.1",
"react-router-dom": "^5.3.4",
"react-scripts": "5.0.1",
"sprintf-js": "^1.1.2",
"typescript": "^4.9.4",
"web-vitals": "^1.1.2",
"yup": "^0.32.11"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "jest",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"#types/d3-hexbin": "^0.2.3",
"#types/enzyme": "^3.10.12",
"#types/enzyme-adapter-react-16": "^1.0.6",
"#types/sprintf-js": "^1.1.2",
"enzyme-adapter-react-15": "^1.4.4",
"enzyme-adapter-react-16": "^1.15.7",
"jest": "^29.3.1",
"jest-environment-jsdom": "^29.3.1",
"ts-jest": "^29.0.3"
}
}
jest.config.js
/** #type {import('ts-jest').JestConfigWithTsJest} */
module.exports = {
verbose: false,
preset: 'ts-jest',
testEnvironment: 'jsdom',
testEnvironmentOptions: {
url: 'http://localhost/3000'
}
};
Looking forward to discussing any possible solutions.
Thank you.

expect(...).toBeInTheDocument is not a function after setting up

I have '#testing-library/jest-dom' installed so it should be there. Also have the below imports, anything wrong with my config?
TypeError: expect(...).toBeInTheDocument is not a function
52 | renderIt(onSubmit);
53 |
> 54 | expect(screen.getByText(/alberta/i)).toBeInTheDocument();
My test:
import { render, screen } from '#testing-library/react';
import { Formik, Form } from 'formik';
import { Select } from '.';
const options = [
'Alberta',
'British Columbia',
'Manitoba',
'New Brunswick',
'Newfoundland & Labrador',
'Northwest Territories',
'Nova Scotia',
'Nunavut',
'Ontario',
'Prince Edward Island',
'Quebec',
'Saskatchewan',
'Yukon',
];
const renderIt = (onSubmit: () => void) => render(
<Formik
initialValues={{ province: '' }}
onSubmit={onSubmit}
>
<Form>
<Select name="province" options={options} />
</Form>
</Formik>,
);
describe('Select component', () => {
test('It renders', () => {
const onSubmit = jest.fn();
renderIt(onSubmit);
expect(screen.getByText(/alberta/i)).toBeInTheDocument();
});
});
My src/setupTests.ts file:
// jest-dom adds custom jest matchers for asserting on DOM nodes.
// allows you to do things like:
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import '#testing-library/jest-dom';
import '#testing-library/jest-dom/extend-expect';
package.json:
"devDependencies": {
"#testing-library/dom": "8.11.3",
"#testing-library/jest-dom": "5.16.2",
"#testing-library/react": "^13.0.0-alpha.6",
"#testing-library/react-hooks": "7.0.2",
"#testing-library/user-event": "13.5.0",
"#types/jest": "27.4.1",
"jest": "27.5.1",
"ts-jest": "27.1.3",
"typescript": "4.6.2"
},
jest.config.js:
module.exports = {
testEnvironment: 'jsdom',
preset: 'ts-jest',
moduleNameMapper: {
'\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$':
'<rootDir>/__mocks__/fileMock.js',
'\\.(css|less)$': 'identity-obj-proxy',
},
};
You should declare the setupTests.ts in you jest configuration using the setupFilesAfterEnv property.
jest.config.js
module.exports = {
testEnvironment: 'jsdom',
setupFilesAfterEnv: ['<rootDir>/src/setupTests.ts'],
preset: 'ts-jest',
moduleNameMapper: {
'\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$':
'<rootDir>/__mocks__/fileMock.js',
'\\.(css|less)$': 'identity-obj-proxy',
},
};

Web Worker - Jest - Cannot use 'import.meta' outside a module

I'm working on a nextjs 10.1.3 built-in web application. We implemented a web worker to boost up the performance in one of the pages and the plan is to continue adding more workers; also, all the code is properly unit tested, and using the worker-loader in previous webpack versions (4th and below) we were able to test it.
With the new webpack 5 version, the worker-loader plugin is not needed anymore; instead, the way to load a web worker using the new version is new Worker(new URL("#/workers/task.worker.js", import.meta.url));.
Doing it this way, my code is working as expected with npm build && npm start; however, when I try to add the respective unit tests I got the following error: Cannot use 'import.meta' outside a module and everything happens because of the import.meta.url used to add the location of the worker in the browser.
I read many posts on the web regarding babel but I want to get away from that option. Is there any other option to mock the import.meta.url with jest?
Any help will be very welcome. This is the current configuration.
package.json
{
...
"#babel/core": "^7.8.6",
"next": "^10.1.3",
"react": "^16.13.0",
"webpack": "^5.37.1"
"devDependencies": {
...
"enzyme": "^3.11.0",
"enzyme-adapter-react-16": "^1.15.2",
"jest": "^24.9.0",
"jest-cli": "^25.1.0",
...
}
...
}
next.config.js
const {
...
} = process.env;
const basePath = "";
const COMMIT_SHA = [];
const { parsed: localEnv } = require("dotenv").config();
const webpack = require("webpack");
const withBundleAnalyzer = require("#next/bundle-analyzer")({
enabled: process.env.ANALYZE === "true",
});
const nextConfig = {
env: {
NEXT_PUBLIC_COMMIT_SHA: COMMIT_SHA,
},
images: {
domains: [
"...",
],
},
future: {
webpack5: true,
},
productionBrowserSourceMaps: true,
trailingSlash: true,
reactStrictMode: true,
webpack: (config, options) => {
if (localEnv) {
config.plugins.push(new webpack.EnvironmentPlugin(localEnv));
} else {
config.plugins.push(new webpack.EnvironmentPlugin(process.env));
}
config.module.rules.push({
test: /\.(eot|woff|woff2|ttf|svg|png|jpg|gif)$/,
use: {
loader: "url-loader",
options: {
limit: 100000,
name: "[name].[ext]",
},
},
});
config.output = {
...config.output,
chunkFilename: options.isServer
? `${options.dev ? "[name]" : "[name].[fullhash]"}.js`
: `static/chunks/${options.dev ? "[name]" : "[name].[fullhash]"}.js`,
publicPath: `/_next/`,
globalObject: `(typeof self !== 'undefined' ? self : this)`,
};
config.plugins.push(new webpack.IgnorePlugin(/pages.*\/__tests__.*/));
config.plugins.push(
new options.webpack.DefinePlugin({
"process.env.NEXT_IS_SERVER": JSON.stringify(
options.isServer.toString()
),
})
);
return config;
},
};
module.exports = withBundleAnalyzer(nextConfig);
The useEffect worker
useEffect(() => {
if (pageData.data?.length) {
workerRef.current = new Worker(new URL("#/workers/task.worker.js", import.meta.url));
workerRef.current.addEventListener("message", result => {
if (result.error) {
setWorkerError();
} else {
updateData(result.data);
}
});
const ids = pageData.data.map(store => store.id);
workerRef.current.postMessage(ids);
} else {
setNoDataFound();
}
return () => {
workerRef.current && workerRef.current.terminate();
};
}, []);
jest.config.js
module.exports = {
moduleDirectories: ["node_modules", "src", "static", "store"],
modulePathIgnorePatterns: [
"<rootDir>/node_modules/prismjs/plugins/line-numbers",
],
testPathIgnorePatterns: [
"<rootDir>/src/components/component-library",
"<rootDir>/.next",
"jest.config.js",
"next.config.js",
],
collectCoverageFrom: [
"**/src/**",
"**/store/**",
"**/pages/**",
"!**/__tests__/**",
"!**/node_modules/**",
"!**/component-library/**",
],
testEnvironment: "node",
collectCoverage: true,
verbose: false,
automock: false,
setupFiles: ["./setupTests.js"],
moduleNameMapper: {
"#/components/(.*)$": "<rootDir>/src/components/$1",
"#/functions/(.*)$": "<rootDir>/src/components/functions/$1",
"#/services/(.*)$": "<rootDir>/src/components/services/$1",
"#/workers/(.*)$": "<rootDir>/src/components/workers/$1",
"#/scripts(.*)$": "<rootDir>/src/scripts/$1",
"#/src(.*)$": "<rootDir>/src/$1",
"#/__mocks__(.*)$": "<rootDir>/__mocks__/$1",
"#/pages(.*)$": "<rootDir>/pages/$1",
"#/store(.*)$": "<rootDir>/store/$1",
"\\.(css|less)$": "<rootDir>/__mocks__/styleMock.js",
},
coveragePathIgnorePatterns: ["/node_modules/"],
coverageThreshold: {
global: {
branches: 67,
functions: 66,
lines: 73,
statements: 72,
},
},
runner: "groups",
extraGlobals: [],
testTimeout: 10000,
};
In my setup (typescript + ts-jest) I prepended the following node option to make it work:
NODE_OPTIONS=--experimental-vm-modules
Reference can be found here: https://jestjs.io/docs/ecmascript-modules

Jest: get undefined on import some module

I'm making some test to redux with jest but I got sucked because Auth module appears as undefined.
import { auth as Auth } from 'Services/firebase'
export const signIn = (email, password) => {
console.log(Auth) // It get's undefined
return dispatch => {
dispatch(signInInit())
Auth.signIn(email, password, (user) => {
dispatch(push('/'))
return dispatch(signInSuccess(user))
}, (error) => {
return dispatch(signInError(error))
})
}
}
This is the jest configuration:
"jest": {
"collectCoverage": true,
"coverageDirectory": "<rootDir>/coverage",
"verbose": true,
"coveragePathIgnorePatterns": [
"<rootDir>/node_modules"
],
"modulePaths": [
"<rootDir>/src"
],
"moduleFileExtensions": [
"js"
],
"moduleDirectories": [
"node_modules",
"src"
],
"modulePathIgnorePatterns": [
"<rootDir>/public"
],
"moduleNameMapper": {
"\\.(jpg|jpeg|png|gif|eot|otf|svg|ttf|woff|woff2)$": "<rootDir>/src/components/__mocks__/fileTransformer.js",
"^Components(.*)$": "<rootDir>/src/components",
"^Data(.*)$": "<rootDir>/src/data",
"^Hocs(.*)$": "<rootDir>/src/hocs",
"^Scenes(.*)$": "<rootDir>/src/scenes",
"^Services(.*)$": "<rootDir>/src/services",
"^Styles(.*)$": "<rootDir>/src/styles"
}
Really can't understand why this is happening, well any clue will be very welcome.

Resources