为什么我的 React-Testing 库查询都不起作用?

2024-04-03

我正在使用 Jest/Testing-Library 编写 UI 单元测试。

组件没有在 DOM 上渲染,罪魁祸首是组件'RequireScope'它单独包装了所有组件。

换句话说,每个组件都会返回:

return ( <RequireScope>  // some MUI stuff</RequireScope>
)

这会阻止我的组件在测试时在 DOM 树中呈现。

这是因为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 (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'是真实的。为此'isInitialized, isAuthenticated, permissions'必须是真的。我们从 useAuth() 中获取这 3 个值。

useAuth:

    import { useContext } from 'react';
    import AuthContext from '../contexts/JWTContext';
    
    const useAuth = () => useContext(AuthContext);
    
    export default useAuth;

JWT上下文:

const handlers: Record<string, (state: State, action: Action) => State> = {
  INITIALIZE: (state: State, action: InitializeAction): State => {
    const { isAuthenticated, permissions, user } = action.payload;

    return {
      ...state,
      isAuthenticated,
      isInitialized: true,
      permissions,
      user,
    };
  },
  LOGIN: (state: State): State => {
    return {
      ...state,
      isAuthenticated: true,
    };
  },
  LOGOUT: (state: State): State => ({
    ...state,
    isAuthenticated: false,
    permissions: [],
  }),
};

const reducer = (state: State, action: Action): State =>
  handlers[action.type] ? handlers[action.type](state, action) : state;

const AuthContext = createContext<AuthContextValue>({
  ...initialState,
  platform: 'JWT',
  login: () => Promise.resolve(),
  logout: () => Promise.resolve(),
});

export const AuthProvider: FC<AuthProviderProps> = (props) => {
  const { children } = props;
  const [state, dispatch] = useReducer(reducer, initialState);
  const router = useRouter();
  const reduxDispatch = useDispatch();

  useEffect(() => {
    const initialize = async (): Promise<void> => {
      try {
        if (router.isReady) {
          const { token, permissions, user, companyId } = router.query;

          const accessToken =
            (token as string) || window.localStorage.getItem('accessToken');
          const permsStorage = window.localStorage.getItem('perms');
          const perms = (permissions as string) || permsStorage;
          const userStorage = window.localStorage.getItem('user');
          const selectedCompanyId =
            (companyId as string) || window.localStorage.getItem('companyId');
          const authUser = (user as string) || userStorage;

          if (accessToken && perms) {
            setSession(accessToken, perms, authUser);

            try {
              // check if user is admin by this perm, probably want to add a flag later
              if (perms.includes('create:calcs')) {
                if (!selectedCompanyId) {
                  const response = await reduxDispatch(getAllCompanies());

                  const companyId = response.payload[0].id;
                  reduxDispatch(companyActions.selectCompany(companyId));
                  reduxDispatch(getCurrentCompany({ companyId }));
                } else {
                  reduxDispatch(
                    companyActions.selectCompany(selectedCompanyId),
                  );
                  await reduxDispatch(
                    getCurrentCompany({ companyId: selectedCompanyId }),
                  );
                }
              } else {
                reduxDispatch(companyActions.selectCompany(selectedCompanyId));
                await reduxDispatch(
                  getCurrentCompany({ companyId: selectedCompanyId }),
                );
              }
            } catch (e) {
              console.warn(e);
            } finally {
              dispatch({
                type: 'INITIALIZE',
                payload: {
                  isAuthenticated: true,
                  permissions: JSON.parse(perms),
                  user: JSON.parse(authUser),
                },
              });
            }

            if (token || permissions) {
              router.replace(router.pathname, undefined, { shallow: true });
            }
          } else {
            dispatch({
              type: 'INITIALIZE',
              payload: {
                isAuthenticated: false,
                permissions: [],
                user: undefined,
              },
            });
            setSession(undefined);

            if (router.pathname !== '/client-landing') {
              router.push('/login');
            }
          }
        }
      } catch (err) {
        console.error(err);
        dispatch({
          type: 'INITIALIZE',
          payload: {
            isAuthenticated: false,
            permissions: [],
            user: undefined,
          },
        });
        //router.push('/login');
      }
    };

    initialize();
  }, [router.isReady]);

  const login = useCallback(async (): Promise<void> => {
    const response = await axios.get('/auth/sign-in-with-intuit');
    window.location = response.data;
  }, []);

  const logout = useCallback(async (): Promise<void> => {
    const token = localStorage.getItem('accessToken');

    // only logout if already logged in
    if (token) {
      dispatch({ type: 'LOGOUT' });
    }

    setSession(null);

    router.push('/login');
  }, [dispatch, router]);

  return (
    <AuthContext.Provider
      value={{
        ...state,
        platform: 'JWT',
        login,
        logout,
      }}
    >
      {state.isInitialized && children}
    </AuthContext.Provider>
  );
};

AuthProvider.propTypes = {
  children: PropTypes.node.isRequired,
};

export default AuthContext;

为了实现上述目的,我们只需确保“finally”语句运行(如果我是正确的)。因此条件语句:

if (router.isReady)

and

if (accessToken && perms)

必须满足。

当我渲染这个时如何使路由器存在AuthProviderJest 中的组件?

或者还有其他替代方法来模拟登录用户吗?

我的测试如下所示:

// test BenchmarksPage
test('renders benchmark', () => {
render(
    <HelmetProvider>
      <Provider store={mockStore(initState)}>
        <AuthProvider>
          <BenchmarksPage />
        </AuthProvider>
      </Provider>
    </HelmetProvider>,
  );

  localStorage.setItem('accessToken', 'sampletokenIsInR5cCI6');
  localStorage.setItem(
    'perms',
    JSON.stringify([
      'create:calcs',
    // and so on
}}

由于您的组件有副作用(即gtm.push、 redux-thunk)您可能需要等待组件状态稳定后再进行测试(因为我不知道 CalculationTable 组件中发生了什么)。因此尝试将您的测试更改为:

// Make the test asynchronous by adding `async`
test('renders header and export dropdown', async () => {
  const initState = {};
  const middlewares = [thunk];
  const mockStore = configureStore(middlewares);

  const { findByRole, getByText, getByTestId } = render(
    <Provider store={mockStore(initState)}>
      <CalculationsPage />
    </Provider>,
  );

   // findByRole will wait for the element to be present.
   // Note the `await` keyword
  const header = await findByRole('heading', { name: /calculations/i });
  
  await waitFor(() => expect(getByTestId('analysis-categories-header')).toBeVisible());
}

“findBy 方法是 getBy 查询和 waitFor 的组合。” - 看here https://testing-library.com/docs/dom-testing-library/api-async/#findby-queries了解更多信息。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么我的 React-Testing 库查询都不起作用? 的相关文章

随机推荐

  • 如何使用 DataContractJsonSerializer 处理 Json?

    我有这样的 json 结构 id 12345 first name dino last name he emails preferred 1 personal 2 business 3 other 4 我想获取电子邮件中的值 所以我写了两个
  • 为什么我不应该将序列化数组插入到我的数据库字段中?

    我刚刚看到这个问题的第一条评论在 PHP 中插入序列化数组 https stackoverflow com questions 3588957 inserting into a serialized array in php这让我想知道为什
  • 从类变量引用静态方法

    我知道有这样的情况是有原因的 但不知何故我有它 class foo static method staticmethod def test pass class variable c name i 有什么办法呢 仅供记录 我相信这应该被视为
  • C++ 抛出取消引用的指针

    下面抛出的异常对象的类型是什么 问题1 gt range error r error throw r 答案1 gt range error 的对象 问题2 gt exception p r throw p 答案2 gt 异常的切片对象 问题
  • 如何根据 RFC 2231 对文件名进行编码?

    我需要在 Content Disposition HTTP 标头属性处对文件附件进行编码 我注意到 RFC 2231 是最新的广泛接受的标准 并且想使用它 然而 我很难在核心 Java SE 和 Java EE 库中找到它的实现 你们有什么
  • 汇编,多个参数 -m32 / linux (与 C 中的 stdarg 相同)

    为了解决这个问题 我了解C 而且我仍然是汇编的初学者 所以我在这里遇到了一个小问题 我在获取多个参数时遇到了一些麻烦 如果我应该这样做的话 也许可以对它们进行计数 并在汇编代码中使用格式参数 尝试向具有许多参数的字符串添加一些字节 我知道如
  • bash.exe:警告:找不到/tmp,请创建

    为什么我的崇高文本给我警告 bash exe 警告 找不到 tmp 请创建 尽管它正在正确构建和运行代码 请帮助我看到这个警告后感到很恼火 我搜索了整个网络 但找不到任何解决方案 此错误消息具有误导性 至少对我来说 创建 tmp 目录后并没
  • Flash / Java Applet 的 NPAPI 插件替代方案

    我的问题是关于 2014 年 12 月后 chrome 宣布不支持 NPAPI 插件 Firefox 也遵循同样的规定 我的一个旧应用程序中有一个基于 NPAPI 的插件 到目前为止该插件运行良好 但在 Chrome 和 Firefox 发
  • 将常规文件复用为 Mpeg TS

    我正在使用 mpeg2 ts 和 ISDB T 综合服务数字广播 地面 的文件广播方法工作 我的问题是我无法将文件放入数据流中 或者作为原始视频 或者在mpeg ts 我使用 ffmpeg 和 avconv 这是一个测试示例 ffmpeg
  • java中的变量定义使用接口还是类型?

    ArrayList aList new ArrayList List aList new ArrayList 这两者有什么区别 哪个更好用 为什么 List 是一个接口 而 ArrayList 是该接口的实现 第二种更好 因为这意味着您可以
  • Firebird ADO.NET 4.10.0.0 数据提供程序是否可以与 Firebird 3.0 一起使用?

    我目前正在尝试将我的 ASP net 4 5 项目连接到最近发布的 Firebird 3 0 我使用的是 Visual Studio 2015 社区版 Firebird 3 64 位 并使用 NuGet 获取 ADO NET 4 10 0
  • 尝试将设备注册到 Bluemix 上的 IBM Mobile First Push 服务时,iOS 应用程序崩溃

    我有一个在 iPad mini 4 iOS 9 0 2 上运行的 iOS 9 应用程序 尝试将设备注册到 Bluemix 上的 IMFPush 服务实例时 它崩溃了 以下是在我的应用程序委托中执行注册的代码 func application
  • 如何在 BigQuery 中选择包含所有 NULL 值的数据列

    如何在 BigQuery 中选择包含所有 NULL 值的数据列 A B C NULL 1 NULL NULL NULL NULL NULL 2 NULL NULL 3 NULL 我想检索 A 列和 C 列 请您帮忙 扩展我对米哈伊尔答案的评
  • RTSP服务器java实现问题:(

    我正在编写 RTSP 服务器并遇到一些问题 我使用 VLC 作为客户端 服务器从客户端 VLC 播放器 接收 OPTIONS DESCRIBE SETUP 和 PLAY 命令并回答该命令 通过 SETUP 命令客户端发送端口号 我正在使用该
  • HTML/CSS - 神秘的顶部填充不会消失

    我正在使用 CSS 制作列布局 并且进展顺利 令人惊讶的是 我的问题不是如何让专栏发挥作用 而是如何让专栏发挥作用 这就是功能 但我的专栏顶部却出现了一个令人毛骨悚然的神秘 填充 它正在压低内容 我在这里准备了一个例子 jsFiddle h
  • Callable 应该优先于 Runnable 吗?

    我已经明白了之间的区别Runnable and CallableJava 中的接口 从 Java 1 5 开始 添加了额外的功能Runnable接口并被调用Callable以保持向后兼容性 我的问题是现在我们有Callable接口 我们应该
  • GraphQL 请求错误 - 未知参数“slug”

    我正在尝试将我的 index js 页面链接到文章模板 以使用 slug 和显示来自中间件 Drupal 站点的数据createPages 我的 index js 页面和我的页面上的数据正确显示createPages将文件路径更改为后似乎没
  • OpenCL 在调用 clGetPlatformIDs 时崩溃

    我是 OpenCL 新手 在配备 Intel R HD Graphics 4000 运行 Windows 7 的 Core i5 计算机上工作 我安装了支持 OpenCL 的最新 Intel 驱动程序 GpuCapsViewer 确认我有
  • 何时在 Jersey 资源中使用 @Singleton

    我有一个访问数据库的泽西岛资源 基本上它在资源初始化时打开一个数据库连接 对资源的方法执行查询 我观察到 当我不使用 Singleton 时 数据库会根据每个请求打开 我们知道打开连接真的很昂贵 对吧 所以我的问题是 我应该指定资源是单例的
  • 为什么我的 React-Testing 库查询都不起作用?

    我正在使用 Jest Testing Library 编写 UI 单元测试 组件没有在 DOM 上渲染 罪魁祸首是组件 RequireScope 它单独包装了所有组件 换句话说 每个组件都会返回 return