Я пишу Jest/тестирую библиотечные тесты.
Допустим, у нас есть компонент с именем BenchmarksPage
.
Пожалуйста, посмотрите на первую строку его оператора return.
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;
Обратите внимание, что возвращаемое значение заключено в RequireScope
RequireScope
будет отображать свои дочерние элементы только после аутентификации пользователя.
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;
Нам просто нужно, чтобы isPermitted
было установлено на true
(Кстати, useAuth использует JWT для входа пользователя)
Теперь, когда я визуализирую BenchmarksPage
с помощью метода render
библиотеки тестирования,
он не отображается на jsdom, потому что
'isPermitted'
по-прежнему ложно в RequireScope
.
Итак, для того, чтобы сделать isPermitted = true
я установил
NEXT_PUBLIC_IS_LOCAL=true
в .env.development
Согласно первому useEffect
в RequireScope, isPermitted
должно быть true
сейчас.
Тем не менее, компонент все еще не отображается, возвращая
<body></div></body>
Это по-прежнему означает, что isPermitted является ложным.
Что я пробовал:
Я также пробовал использовать .env.local
Я могу гарантировать вам, что все мои настройки верны (jest.config, использование MockProvider для переноса BenchmarksPage и т. д.)
Почему .env.development не работает и не делает isPermitted = true
?
Все логически правильно в моем понимании.
Обновлено: я пытался писать
NEXT_PUBLIC_IS_LOCAL=истина
в файле .env.test тоже
РЕДАКТИРОВАТЬ - ОТВЕТ:
должен был установить
import { loadEnvConfig } from '@next/env';
export default async (): Promise<void> => {
loadEnvConfig(process.env.PWD);
};
в файле setupEnv.ts.
А затем в package.json добавьте globalSetup (поскольку я не использую jest.config.js -> eslint возился с ним вместе с tsconfig)
"jest": {
"moduleNameMapper": {
"^src/(.*)$": "<rootDir>/src/$1"
},
"testEnvironment": "jsdom",
"globalSetup": "<rootDir>/test/setupEnv.ts"
},
Если ваши переменные среды работают во время работы сервера разработки, но не работают во время тестирования, это связано с тем, что Next не устанавливает переменные для Jest во время модульного тестирования.
Во-первых, создайте .env.test
, настроив переменные среды, которые будут использоваться только для тестов.
Затем, чтобы настроить envs в тестовой среде, вы должны добавить это в точку входа в ваши тесты:
// 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)
}
Обратите внимание, что .env.local
не загружается во время тестов
Вы пробовали console.log(process.env), чтобы увидеть, появляется ли вообще какой-либо env?