How to load environment variable in tests with Next.js? - reactjs

I'm trying to run a simple test with JavaScript as below.
import React from 'react';
import Customization from 'components/onboarding/customization';
import '#testing-library/jest-dom';
import { render, screen, fireEvent } from '#testing-library/react';
describe('customization render', () => {
it('should render the Hero page with no issue', () => {
render(<Customization />);
const heading = screen.getByText(
/All the Moodmap at one place!/i
);
expect(heading).toBeInTheDocument();
});
it("should call onCLick method on click", () => {
const mockOnClick = jest.fn()
const {container} = render(<Customization />);
const button = getByTestId(container, 'alreadyDownloaded');
fireEvent.click(button);
expect(mockOnClick).toHaveBeenCalledTimes(1)
// const mockOnClick = jest.fn()
// const utils = render(<Customization onClick={mockOnClick} />)
// fireEvent.click(screen.getByText(/already downloaded ⟶/i))
// expect(mockOnClick).toHaveBeenCalledTimes(1)
})
});
When running the tests I'm getting this error
No google analytics trackingId defined
8 | debug: process.env.NODE_ENV !== 'production',
9 | plugins: [
> 10 | googleAnalyticsPlugin({
| ^
11 | trackingId: process.env.NEXT_PUBLIC_GA_TRACKING_ID,
12 | }),
How do I make this error go away - surely it shouldn't require Google Analytics code given the above, it's not in production when running the test?
Update
So I need to make sure the .env file is being loaded!
In my package.json I've got this Jest setup:
"jest": {
"testMatch": [
"**/?(*.)(spec|test).?(m)js?(x)"
],
"moduleNameMapper": {
"\\.(css|less|scss)$": "identity-obj-proxy"
},
"moduleDirectories": [
"node_modules",
"src"
],
"rootDir": "src",
"moduleFileExtensions": [
"js",
"jsx",
"mjs"
],
"transform": {
"^.+\\.m?jsx?$": "babel-jest"
},
"coverageThreshold": {
"global": {
"branches": 80,
"functions": 80,
"lines": 80,
"statements": -10
}
}
},
updated code to use jest.setup - can't get env to load
So
import { configure } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
import "#testing-library/jest-dom";
configure({
adapter: new Adapter()
});
module.exports = {
testMatch: [
"**/?(*.)(spec|test).?(m)js?(x)"
],
moduleNameMapper: {
"\\.(css|less|scss)$": "identity-obj-proxy"
},
moduleDirectories: [
"node_modules",
"src"
],
rootDir: "src",
moduleFileExtensions: [
"js",
"jsx",
"mjs"
],
transform: {
"^.+\\.m?jsx?$": "babel-jest"
},
coverageThreshold: {
"global": {
"branches": 80,
"functions": 80,
"lines": 80,
"statements": -10
}
},
setupFiles: ["../<rootDir>/.config.env.test"]
};
The environment variable files is here:
process.env.NEXT_PUBLIC_GA_TRACKING_ID=xxx
And this is the code that is not loading the environment variables properly.
import Analytics from 'analytics';
import googleAnalyticsPlugin from '#analytics/google-analytics';
import Router from 'next/router';
// Initialize analytics and plugins
// Documentation: https://getanalytics.io
const analytics = Analytics({
debug: process.env.NODE_ENV !== 'production',
plugins: [
googleAnalyticsPlugin({
trackingId: process.env.NEXT_PUBLIC_GA_TRACKING_ID,
}),
],
});

During your tests you can leverage loadEnvConfig from #next/env to make sure your environment variables are loaded the same way Next.js does.
First setup a .env.test to be used during the tests.
NEXT_PUBLIC_GA_TRACKING_ID=ga-test-id
Next, create a Jest global setup file if you don't have one yet, and reference it in your jest.config.js.
// jest.config.js
module.exports = {
//...
setupFilesAfterEnv: ['./jest.setup.js'],
};
Then add the following code into your Jest global setup file.
// jest.setup.js
import { loadEnvConfig } from '#next/env'
loadEnvConfig(process.cwd())

This message means that the trackingId is not defined. As you can see it read from the process.env. You need to create this file in the root of your project and call it .env. Note that the dot is at the beginning of the filename. The content of the file should be as follow:
NEXT_PUBLIC_GA_TRACKING_ID=insert-key-here
If your env file is not being read by jest you can do the following:
// In jest.config.js :
module.exports = {
....
setupFiles: ["<rootDir>/test/setup-tests.ts"]
}
// The file test/test-setup.ts:
import dotenv from 'dotenv';
dotenv.config({path: './config.env.test'});
You can also check this article for more details.

Related

MFE module federation cannot find module for remote child import

Im trying to build out a micro frontend for the first time so my main application can support a sub application. I have it all working when everything is just rendering App.tsx (remote) but as soon as I try and render a child within the remote application I get the Cannot find module error from webpack
Container craco.congig.js
/* eslint-disable #typescript-eslint/no-var-requires */
const CracoEsbuildPlugin = require('craco-esbuild');
const { ModuleFederationPlugin } = require("webpack").container;
const deps = require("./package.json").dependencies;
module.exports = {
webpack: {
plugins: {
add: [
new ModuleFederationPlugin({
name: "Dashboard",
remotes: {
DigitalCanopy: "DigitalCanopy#//localhost:3001/remoteEntry.js",
},
shared: {
react: {
singleton: true,
strictVersion: true,
requiredVersion: deps['react']
},
"react-dom": {
singleton: true,
strictVersion: true,
requiredVersion: deps['react-dom']
},
},
}),
],
},
...
My remote craco.config.js
/* eslint-disable #typescript-eslint/no-var-requires */
const CracoEsbuildPlugin = require('craco-esbuild');
const { ModuleFederationPlugin } = require("webpack").container;
const deps = require("./package.json").dependencies;
module.exports = {
devServer: {
port: 3001
},
webpack: {
plugins: {
add: [
new ModuleFederationPlugin({
name: "DigitalCanopy",
exposes: {
"./DigitalCanopy": "./src/App.tsx",
},
filename: "remoteEntry.js",
shared: {
react: { singleton: true },
"react-dom": { singleton: true },
},
}),
],
},
configure: (webpackConfig) => ({
...webpackConfig,
output: {
...webpackConfig.output,
publicPath: "auto",
},
}),
},
plugins: [
{
plugin: CracoEsbuildPlugin,
options: {
esbuildLoaderOptions: {
// Optional. Defaults to auto-detect loader.
loader: 'tsx', // Set the value to 'tsx' if you use typescript
target: 'es2018',
},
esbuildMinimizerOptions: {
target: 'es2018',
css: true, // if true, OptimizeCssAssetsWebpackPlugin will also be replaced by esbuild.
},
skipEsbuildJest: false, // Optional. Set to true if you want to use babel for jest tests,
esbuildJestOptions: {
loaders: {
'.ts': 'ts',
'.tsx': 'tsx',
},
},
},
},
],
};
My remote App.tsx
import { createTheme, ThemeProvider } from '#mui/material';
import React from 'react';
import './App.css';
import App1 from './App1';
const baseTheme = createTheme({
...MUI THEME STUFF
});
function App() {
return (
<ThemeProvider theme={baseTheme}>
<div className="App">Digital Canopy App</div>;
<App1 />
</ThemeProvider>
);
}
export default App;
This works and renders fine until I try and render <App1 />
Then I get this error Module not found: Error: Can't resolve './App1' in ...
Any ideas? I feel like this should just work. Importing children components from within the remote is pretty standard doesnt seem unique but I cannot find anything similar online which makes me think Im missing something obvious.
Thanks for the help
My issue was I was missing the .tsx extension on the import. I have no idea why that is required by the sub application. My build configs are identical for typescript and my main app does not require that. I'll dig in and try and figure out why

Jest import issue for react js app with custom imports prefix for workerize-loader

I am facing an issue with setting up tests for react app with Jest.
I am using workerize-loader! library to import modules into webworkers.
On tests that use the import worker from workerize-loader!./path/to/file are barking on import of the test. I tried with a custom loader, in the jest.condig.js file but that did not work. When I run the default testing script provided by the react app setup it cannot find the imports in the files. But the code when it runs works alright.
I need help with importing the modules inside of the test suite of jest
Basic test
import React from "react";
import {cleanup, fireEvent, render } from "#testing-library/react";
import App from "./App";);
afterEach(cleanup);
test("basic test", () => {
const { container } = render(<App />);
const linkElement = container.firstChild;
expect(linkElement).toBeInTheDocument();
});
Configuration
jest.config.js
module.exports = {
preset: 'ts-jest',
verbose: true,
testEnvironmentOptions: {
url: 'http://localhost/'
},
testEnvironment: 'jsdom',
transform: {
'workerize-loader(\\?.*)?!(.*)': '<rootDir>/workerize-jest.js',
'^.+\\.(js|jsx)$': 'babel-jest',
'^.+\\.(ts|tsx)?$': 'ts-jest',
},
unmockedModulePathPatterns: [
'node_modules',
],
moduleNameMapper: {
'workerize-loader(\\?.*)?!(.*)': 'identity-obj-proxy'
},
};
.babelrc
{
"plugins": [
"syntax-dynamic-import",
"#babel/transform-runtime"
],
"presets": [
[
"es2015",
{
"modules": true
}
],
"react",
"stage-0"
],
"env": {
"start": {
"presets": [
"react-hmre"
]
},
"test": {
"presets": [
[
"es2015",
{
"modules": true
}
],
"react",
"stage-0"
]
}
}
}
Error message on react-scripts test
Cannot find module 'workerize-loader!./workers/css.worker'
In file
> 2 | import worker from 'workerize-loader!./workers/css.worker';
workerize-jest.js
module.exports = {
process(src, filename) {
return `
async function asyncify() { return this.apply(null, arguments); }
module.exports = function() {
const w = require(${JSON.stringify(filename.replace(/^.+!/, ''))});
const m = {};
for (let i in w) m[i] = asyncify.bind(w[i]);
return m;
};
`;
}
};

How do I unit test amcharts using jest with react and typescript?

I have reproduced the compilation error that I came across while working on a project using Jest with amCharts. Apparently, Jest cannot compile anything to do with amchart for unit testing.
Error
/core.js:8
export { System, system } from "./.internal/core/System";
^^^^^^
SyntaxError: Unexpected token 'export'
1 | import { useEffect, useRef } from "react";
> 2 | import * as am4core from "#amcharts/amcharts4/core";
| ^
3 | import * as am4charts from "#amcharts/amcharts4/charts";
4 |
5 | const data = [
at Runtime.createScriptFromCode (node_modules/jest-runtime/build/index.js:1350:14)
at Object.<anonymous> (src/App.tsx:2:1)
Test Suites: 1 failed, 1 total
Tests: 0 total
Snapshots: 0 total
Time: 1.067 s
Ran all test suites.
Jest.config.ts
module.exports = {
roots: ["<rootDir>/src"],
collectCoverageFrom: ["src/**/*.{js,jsx,ts,tsx}", "!src/**/*.d.ts"],
setupFiles: ["react-app-polyfill/jsdom"],
testMatch: [
"<rootDir>/src/**/__tests__/**/*.{js,jsx,ts,tsx}",
"<rootDir>/src/**/*.{spec,test}.{js,jsx,ts,tsx}",
],
testEnvironment: "jsdom",
testRunner:
"/home/xeonrazr/Playground/amcharts-jest-err/node_modules/jest-circus/runner.js",
transform: {
"^.+\\.(js|jsx|mjs|cjs|ts|tsx)$": "<rootDir>/config/jest/babelTransform.js",
"^.+\\.css$": "<rootDir>/config/jest/cssTransform.js",
"^(?!.*\\.(js|jsx|mjs|cjs|ts|tsx|css|json)$)":
"<rootDir>/config/jest/fileTransform.js",
},
transformIgnorePatterns: [
"[/\\\\]node_modules[/\\\\].+\\.(js|jsx|mjs|cjs|ts|tsx)$",
"^.+\\.module\\.(css|sass|scss)$",
],
modulePaths: [],
moduleNameMapper: {
"^react-native$": "react-native-web",
"^.+\\.module\\.(css|sass|scss)$": "identity-obj-proxy",
},
moduleFileExtensions: [
"web.js",
"js",
"web.ts",
"ts",
"web.tsx",
"tsx",
"json",
"web.jsx",
"jsx",
"node",
],
watchPlugins: [
"jest-watch-typeahead/filename",
"jest-watch-typeahead/testname",
],
resetMocks: true,
};
App.tsx
import { useEffect, useRef } from "react";
import * as am4core from "#amcharts/amcharts4/core";
import * as am4charts from "#amcharts/amcharts4/charts";
const data = [
{
country: "Lithuania",
litres: 501.9,
},
{
country: "Czech Republic",
litres: 301.9,
},
{
country: "Ireland",
litres: 201.1,
},
{
country: "Germany",
litres: 165.8,
},
{
country: "Australia",
litres: 139.9,
},
{
country: "Austria",
litres: 128.3,
},
{
country: "UK",
litres: 99,
},
{
country: "Belgium",
litres: 60,
},
{
country: "The Netherlands",
litres: 50,
},
];
function App() {
const chartRef = useRef(am4core.create("chartdiv", am4charts.PieChart));
useEffect(() => {
let series = chartRef.current.series.push(new am4charts.PieSeries());
series.dataFields.value = "litres";
series.dataFields.category = "country";
chartRef.current.data = data;
chartRef.current.legend = new am4charts.Legend();
}, []);
return (
<div className="App">
<div
data-testid="chartdiv"
style={{ height: "600px", width: "100%" }}
id="chartdiv"
></div>
</div>
);
}
export default App;
App.test.tsx
import { render } from "#testing-library/react";
import App from "./App";
test("renders learn react link", () => {
const { getByTestId } = render(<App />);
const chartEl = getByTestId("chartdiv");
expect(chartEl).toBeInTheDocument();
});
Repo Link
Jest currently doesn't understand either import or export. It looks like you followed common setup docs for your jest config so you're probably using babel and transpiling your code from using modules to using commonjs, but you're not transpiling your dependencies:
transformIgnorePatterns: [
"[/\\\\]node_modules[/\\\\].+\\.(js|jsx|mjs|cjs|ts|tsx)$", // <-- do not transform node_modules
"^.+\\.module\\.(css|sass|scss)$",
],
But, what's causing your problem is that one of your exports (amCharts) needs to be transformed in order to make those import/export statements compliant with the commonjs format that jest is expecting.
Your easiest fix is probably to edit the transformIgnorePatterns:
transformIgnorePatterns: [
//Add other modules here that ship ESM that Jest doesn't like
'node_modules/(?!#amcharts)',
"^.+\\.module\\.(css|sass|scss)$",
],
So we're saying "go ahead and don't transform anything in node modules unless it matches #amcharts. If you get more dependencies that need to be transformed, then you can add it to that regex.
For a different solution, if you like experimental features, then you can try turning on Jest's experimental support for esm: https://jestjs.io/docs/ecmascript-modules.

Cannot use import statement outside a module react native elements

I am running into problems when trying to test a component with react-native-elements.
I am getting "Cannot use import statement outside a module" on import from react-native-elements.
My babel config is:
module.exports = {
presets: ['module:metro-react-native-babel-preset'],
plugins: ['#babel/plugin-proposal-numeric-separator'],
};
jest config:
"jest": {
"preset": "react-native",
"moduleFileExtensions": [
"ts",
"tsx",
"js",
"jsx",
"json",
"node"
],
"setupFiles": [
"<rootDir>/__tests__/testSetup.ts"
]
}
The test is:
import { Questionnaire } from './Questionnaire';
import { render } from '#testing-library/react-native';
describe('Questionnaire', () => {
it('Should render', () => {
const { container } = render(<Questionnaire setSessionState={jest.fn()} />);
expect(container).toMatchSnapshot();
});
});
Any help would be appreciated!

Jest throwing TypeError: Cannot read property 'fetch' of undefined

I'm trying to run some tests with Jest on my react/react-native library (only some business logic inside).
We are testing actions that uses fetch function (polyfill with whatwg-fetch).
I've added whatwg-fetch (thanks to Safari) for react.
Whenever i try to run a test, i'm getting this error:
TypeError: Cannot read property 'fetch' of undefined
at node_modules/whatwg-fetch/fetch.js:4:11
at Object.<anonymous> (node_modules/whatwg-fetch/fetch.js:461:3)
at Object.<anonymous> (node_modules/jest-expo/src/setup.js:138:416)
What can cause this issue? Is there a way in the jest config to avoid this?
Here are some files for debug:
Jest config in package.json
"jest": {
"preset": "jest-expo",
"moduleFileExtensions": [
"js",
"jsx",
"ts",
"tsx"
],
"verbose": true,
"transform": {
"^.+\\.(js|ts|tsx)$": "<rootDir>/node_modules/babel-jest"
},
"testRegex": "(/__tests__/.*|\\.(test|spec))\\.(ts|tsx|js)$",
"testPathIgnorePatterns": [
"\\.snap$",
"<rootDir>/node_modules/",
"<rootDir>/dist/"
],
"transformIgnorePatterns": [
"node_modules/?!react-native"
]
},
Webpack config:
const config = {
entry: [
'whatwg-fetch',
__dirname + '/src/index.ts',
],
devtool: 'source-map',
output: {
path: path.join(__dirname, '/dist'),
filename: 'index.js',
library: 'checkinatwork-module',
libraryTarget: 'umd',
umdNamedDefine: true,
},
module: {
loaders: [
{ test: /\.(tsx|ts)?$/, loader: 'ts-loader', exclude: /node_modules/ },
],
},
resolve: {
modules: [
'./src',
'node_modules',
],
extensions: ['.js', '.ts', '.jsx', '.tsx', 'json'],
},
plugins: [
],
};
Test file:
import expect from 'expect';
import * as actions from '../../src/components/Checkin/checkin.action';
import * as reducers from '../../src/components/Checkin/checkin.reducer';
import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import nock from 'nock';
const middlewares = [ thunk ];
const mockStore = configureMockStore(middlewares);
describe('=> ADD CHECKIN ACTIONS', () => {
describe('- REQUEST', () => {
it('Action: ADD_CHECKIN_REQUEST should request addCawCheckin', () => {
const expectedAction = {
type: actions.ADD_CHECKIN_REQUEST,
isFetching: true,
};
expect(actions.addCheckinRequest())
.toEqual(expectedAction);
});
it('Reducer: newCheckin should trigger ADD_CHECKIN_REQUEST and initiate loading', () => {
const expectedState = {
isFetching: true,
status: null,
};
expect(reducers.newCheckin(reducers.newCheckinDefaultState, actions.addCheckinRequest()))
.toEqual(expectedState);
});
});
Action file:
export const getCheckins = (sessionId, date, url, isRefresh) => {
const config = {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
sessionId: {sessionId},
date: {date},
}),
};
return dispatch => {
if (!isRefresh) {
dispatch(getCheckinsRequest());
}
return fetch(url + 'getCAWCheckIns', config)
.then(response => response.json())
.then(({ checkins }) => {
dispatch(getCheckinsSuccess(checkins));
}).catch(err => {
dispatch(getCheckinsError('Get checkins failed'));
console.error('Get checkins failed: ', err);
});
};
};
Thanks!
I've done it in the spec with:
import { fetch } from 'whatwg-fetch';
global.fetch = fetch;
And it has worked as expected with Jest.
might be late to the game, but this worked for me.
Possible solution #1
Note: React.PropTypes is deprecated as of React v15.5. Please use the prop-types library instead.
If you install the npm package prop-types, it has isomorphic-fetch as a dependency. This will give you fetch as a global. You will still need to import it into your test file. You might need to exclude it from your linter too.
add this to the top of the test file.
import fetch from 'isomorphic-fetch'
I didn't need to call fetch in the test suite, but I needed to make it available.
If you use this approach, I think you would remove the 'whatwg-fetch', from your webpack entry
Hope this helps
Updated: Possible solution #2
Using the example of #zvona from above, but create a MOCKS folder in your app. then a file /globalMock.js. You might not have set it up properly.
__MOCKS__/globalMock.js
// use one of these imports
import { fetch } from 'whatwg-fetch' // if you want to keep using the polyfill
import { fetch } from 'isomorphic-fetch' // from a dependency node module that I spoke of in the previous solution.
global.fetch = fetch
Now in package.json
add this to your Jest configuration:
"jest": {
"verbose": true,
"rootDir": "app",
"setupFiles": ["<rootDir>/__MOCKS__/globalMock.js"]
}
this will allow the use of fetch in your tests.
I also had to use this same concept for localStorage. Where I keep all of my globals that Jest doesn't have access.
Upgrading react-native, jest, and babel-jest to the latest versions fixed this issue for us.
This worked for me. In your expo set up file(node_modules/jest-expo/src/setup.js) where it requires whatwg-fetch, I changed that require to require('fetch-everywhere')
const { Response, Request, Headers, fetch } =
require('fetch-everywhere');
global.Response = Response;
global.Request = Request;
global.Headers = Headers;
global.fetch = fetch;
For some reasons, only fetch everywhere was working with expo and jest.

Resources