PHPFixing
  • Privacy Policy
  • TOS
  • Ask Question
  • Contact Us
  • Home
  • PHP
  • Programming
  • SQL Injection
  • Web3.0

Saturday, August 20, 2022

[FIXED] Why isn't .env.development working in my Next.js app?

 August 20, 2022     environment-variables, jwt, next.js, reactjs, typescript     No comments   

Issue

I am writing Jest/testing library tests.

Let's say we have a component called BenchmarksPage.

Please look at the first line of its return statement.

import {
  Box,
  capitalize,
  Container,
  FormControl,
  InputLabel,
  MenuItem,
  Select,
} from '@material-ui/core';
import { NextPage } from 'next';
import React, { useCallback, useEffect, useState } from 'react';
import { Helmet } from 'react-helmet-async';
import RequireScope from 'src/components/authentication/RequireScope';
import BenchmarkTable from 'src/components/benchmark/BenchmarkTable';
import DashboardLayout from 'src/components/dashboard/DashboardLayout';
import Heading from 'src/components/Heading';
import useSettings from 'src/hooks/useSettings';
import gtm from 'src/lib/gtm';
import { useDispatch, useSelector } from 'src/store';
import { getAllRollingTwelveCalcs } from 'src/store/rolling-twelve/rolling-twelve.thunk';
import { Timeframe, timeframeMap } from 'src/types/benchmark';

const BenchmarksPage: NextPage = () => {
  const { settings } = useSettings();
  const dispatch = useDispatch();

  const [selectedTimeframe, setSelectedTimeframe] = useState<Timeframe>(
    Timeframe.Monthly,
  );

  const company = useSelector((state) => state.company.current);

  useEffect(() => {
    gtm.push({ event: 'page_view' });
  }, []);

  useEffect(() => {
    dispatch(getAllRollingTwelveCalcs());
  }, [company]);

  const handleTimeframeChange = useCallback(
    (
      event: React.ChangeEvent<{
        name?: string;
        value: Timeframe;
        event: Event | React.SyntheticEvent<Element, Event>;
      }>,
    ) => {
      setSelectedTimeframe(event.target.value);
    },
    [],
  );

  return (
    <RequireScope scopes={['query:benchmark-calcs']}>
      <DashboardLayout>
        <Helmet>
          <title>Benchmarks</title>
        </Helmet>
        <Container maxWidth={settings.compact ? 'xl' : false}>
          <Box
            sx={{
              display: 'flex',
              alignItems: 'flex-end',
              justifyContent: 'space-between',
              mb: 4,
            }}
          >
            <Heading>Benchmarks</Heading>
            <FormControl sx={{ width: 300 }}>
              <InputLabel>Timeframe</InputLabel>
              <Select
                sx={{ background: '#ffffff', maxWidth: 400 }}
                value={selectedTimeframe}
                label="Timeframe"
                onChange={handleTimeframeChange}
              >
                {[...timeframeMap.keys()].map((timeframe) => (
                  <MenuItem key={timeframe} value={timeframe}>
                    {capitalize(timeframe)}
                  </MenuItem>
                ))}
              </Select>
            </FormControl>
          </Box>
          <BenchmarkTable timeframe={selectedTimeframe} />
        </Container>
      </DashboardLayout>
    </RequireScope>
  );
};

export default BenchmarksPage;

Note that its return value is wrapped with RequireScope

RequireScope will only render its children when user is authenticated

RequireScope:

import React, { useEffect, useState } from 'react';
import useAuth from 'src/hooks/useAuth';

export interface RequireScopeProps {
  scopes: string[];
}

const RequireScope: React.FC<RequireScopeProps> = React.memo((props) => {
  const { children, scopes } = props;
  const { isInitialized, isAuthenticated, permissions } = useAuth();
  const [isPermitted, setIsPermitted] = useState(false);

  useEffect(() => {
    if (process.env.NEXT_PUBLIC_IS_LOCAL) {
      setIsPermitted(true);
    }
  }, []);

  useEffect(() => {
    if (isAuthenticated && isInitialized) {
      (async () => {
        const hasPermissions = scopes
          .map((s) => {
            return permissions.includes(s);
          })
          .filter(Boolean);

        if (hasPermissions.length === scopes.length) {
          setIsPermitted(true);
        }
      })();
    }
  }, [isAuthenticated, isInitialized, scopes, permissions]);

  if (isPermitted) {
    return <>{children}</>;
  }

  return null;
});

export default RequireScope;

We just need isPermitted to be set to true

(useAuth uses JWT to sign in a user btw)

Now when I render the BenchmarksPage using Testing Library's render method,

it does not get rendered on the jsdom because the

'isPermitted' is still false in RequireScope.

So, in order to make isPermitted = true

I have set

NEXT_PUBLIC_IS_LOCAL=true

in .env.development

According to the first useEffect in RequireScope, isPermitted should be true now.

Yet, the component is still not rendered, returning

<body></div></body> 

This still means isPermitted is false.

What have I tried:

I have also tried using .env.local

I can ensure you that my setUps are all correct (jest.config, using MockProvider to wrap BenchmarksPage etc.)

Why isn't .env.development working, and making isPermitted = true ?

Everything is logically correct to my understanding.

EDIT: I have tried writing

NEXT_PUBLIC_IS_LOCAL=true

in .env.test file too

EDIT - ANSWER:

had to set

import { loadEnvConfig } from '@next/env';

export default async (): Promise<void> => {
  loadEnvConfig(process.env.PWD);
};

in setupEnv.ts file.

And then in package.json, add globalSetup ( since I dont use jest.config.js -> eslint was messing with it along with tsconfig)

"jest": {
    "moduleNameMapper": {
      "^src/(.*)$": "<rootDir>/src/$1"
      },
    "testEnvironment": "jsdom",
    "globalSetup": "<rootDir>/test/setupEnv.ts"
  },

Solution

If your environment variables work while running dev server, but won't work while testing, this is because Next doesn't set variables up for Jest while unit testing.

Firstly, create a .env.test, setting up your environment variables that will be used for tests only.

Then, in order to set envs up in the test environment, you have to add this to your tests entry point:

// jest.config.js
module.exports = {
  globalSetup: '<rootDir>/__test__/setupEnv.js'
}
// __test__/setupEnv.js
import { loadEnvConfig } from '@next/env'

export default async () => {
  const projectDir = process.cwd()
  loadEnvConfig(projectDir)
}

You can change __test__/ directory to whichever is your tests directory.

Please note that .env.local is not loaded during tests



Answered By - Overclocked Skid
Answer Checked By - David Goodson (PHPFixing Volunteer)
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg
Newer Post Older Post Home

0 Comments:

Post a Comment

Note: Only a member of this blog may post a comment.

Total Pageviews

Featured Post

Why Learn PHP Programming

Why Learn PHP Programming A widely-used open source scripting language PHP is one of the most popular programming languages in the world. It...

Subscribe To

Posts
Atom
Posts
Comments
Atom
Comments

Copyright © PHPFixing