jeanverster / chakra-ui-steps

Steps component designed to work seamlessly with Chakra UI
https://chakra-ui-steps.vercel.app
390 stars 44 forks source link

Vertical version is not working in unit tests. #108

Open huseyindeniz opened 1 year ago

huseyindeniz commented 1 year ago

Hi and thanks for this beautiful package. I'm having trouble with unit testing of my component which uses vertical steps. Here is a simple demonstration:

import { Box, ChakraProvider } from "@chakra-ui/react";
import { render } from "@testing-library/react";
import { Steps, Step } from "chakra-ui-steps";

import { theme } from "../../../../ui/components/Layout/Theme/theme";

import { Modal } from "./Modal";

describe("Feature: Wallet", () => {
  describe("Component: ConnectionModal/Modal", () => {
    const onClickSpy = jest.fn();

    describe("Scenario: CheckWallet", () => {
      it("should show correct step", () => {
        // Arrange
        // Act
        const { debug } = render(
          <Box>
            <Steps colorScheme="green" activeStep={0} orientation="vertical">
              <Step label="Step 1" />
              <Step label="Step 2" />
              <Step label="Step 3" />
            </Steps>
          </Box>
        );
        // Assert
      });
    });
  });
});

This test fails with the famous error: TypeError: Cannot read properties of undefined (reading 'width')

But if I only remove orientation="vertical" or make it orientation="horizontal", it works fine. Steps is correctly defined in my theme configuration on both application and test-utils file.

jeanverster commented 1 year ago

Hey @huseyindeniz - that error usually means that the Steps component doesn't have access to the StepsTheme object. I may be missing some code, but in the snippet above it seems like you're importing render directly from rtl, so I suspect the test itself does not have access to the chakra ui theme provider? the theme being imported is also unused.. What happens if you wrap the test with ChakraProvider ?

describe("Feature: Wallet", () => {
  describe("Component: ConnectionModal/Modal", () => {
    const onClickSpy = jest.fn();

    describe("Scenario: CheckWallet", () => {
      it("should show correct step", () => {
        // Arrange
        // Act
        const { debug } = render(
          <ChakraProvider theme={theme}>
            <Steps colorScheme="green" activeStep={0} orientation="vertical">
              <Step label="Step 1" />
              <Step label="Step 2" />
              <Step label="Step 3" />
            </Steps>
          </ChakraProvider>
        );
        // Assert
      });
    });
  });
});
huseyindeniz commented 1 year ago

hi @jeanverster thanks for the quick feedback. My example is a modified version of my original test. Original test is more complicated and depends on lots of things. But as you can see in the modified version, I'm able to make it work by only removing orientation. My chakraprovider and it's theme correctly applied in this test. I can also see it in the debug logs. And additionally, I modified my original component and it accepts orientation prop now. I'm able to test it by setting orientation as horizontal. I'll continue with this hacky solution. But I can also show you my configuration:

test-utils.ts => basically it injects chakra provider to all my tests

import { ChakraProvider } from '@chakra-ui/react';
import { render, RenderOptions } from '@testing-library/react';
import i18n from 'i18next';
import LanguageDetector from 'i18next-browser-languagedetector';
import * as React from 'react';
import { initReactI18next, I18nextProvider } from 'react-i18next';

import { theme } from './features/ui/components/Layout/Theme/theme';

// eslint-disable-next-line import/no-named-as-default-member
i18n
  .use(LanguageDetector)
  .use(initReactI18next)
  .init({
    debug: false,
    fallbackLng: 'en',
    keySeparator: false,
    nonExplicitSupportedLngs: true,
    returnEmptyString: false,
    resources: { en: {} },
  });

const AllProviders = ({ children }: { children?: React.ReactNode }) => (
  <ChakraProvider theme={theme}>
    <I18nextProvider i18n={i18n}>{children}</I18nextProvider>
  </ChakraProvider>
);

const customRender = (ui: React.ReactElement, options?: RenderOptions) =>
  render(ui, { wrapper: AllProviders, ...options });

export { customRender as render };

export { i18n };

and this my theme.ts file:

import { extendTheme } from '@chakra-ui/react';
import { StepsTheme as Steps } from 'chakra-ui-steps';

export const theme = extendTheme({
  config: {
    initialColorMode: 'system',
  },
  components: {
    Steps,
  },
  fonts: {
    heading: `'Open Sans', sans-serif`,
    body: `'Raleway', sans-serif`,
  },
});
jeanverster commented 1 year ago

Ah, that makes more sense! Ok I will have a look at this, thanks for raising :)

huseyindeniz commented 1 year ago

thank you @jeanverster

ChukwurahVictor commented 1 year ago

Hi @jeanverster and @huseyindeniz , I keep getting ● Test suite failed to run Cannot find module 'chakra-ui-steps' from 'src/utils/themes.ts' whenever I try to run tests with jest.

Can you please point me to what I'm doing wrong or any resource for using chakra-ui-steps with jest?

my themes.ts file

import { extendTheme, theme } from '@chakra-ui/react';
import { StepsTheme as Steps } from 'chakra-ui-steps';

const colors = Object.freeze({
  bg: {
    white: '#fff'
  },
  brand: {
    main: '#CFE5FF',
    green: '#34D1BF',
    white: '#fff',
    gray: '#9FA2B4'
  },
  typography: {
    black: '#000',
    lightGray: '#6B7280',
    gray: '#9FA2B4',
    neutral: '#414141',
    red: '#FF0000',
    darkGray: '#757575',
    green: '#34D1BF',
    blue: '#178CD0',
    lightBlue: '#0F63FF14',
    darkBlue: '#0F63FF'
  }
});

const fontSizes = Object.freeze({
  ...theme.fontSizes,
  small: '1.2rem',
  label: '1.4rem',
  paragraph: '1.6rem',
  heading6: '1.6rem',
  heading5: '1.9rem',
  heading4: '2.4rem',
  heading3: '2.9rem',
  heading2: '3.5rem',
  heading1: '4.3rem'
});

const fontWeights = Object.freeze({
  ...theme.fontWeights,
  regular: 500,
  medium: 550,
  semiBold: 600,
  bold: 700,
  bolder: 800,
  extraBold: 900
});

const breakpoints = Object.freeze({
  '2xs': '320px', //@media(min-width:320px)
  xs: '375px', //@media(min-width:375px)
  sm: '530px', //@media(min-width:480px)
  md: '840px', //@media(min-width:770px)
  lg: '1024px', //@media(min-width:1024px)
  xl: '1200px', //@media(min-width:1200px)
  '2xl': '1600px', //@media(min-width:1600px)
  '3xl': '1900px' //@media(min-width:1900px)
});

const styles = Object.freeze({
  global: {
    html: {
      fontSize: '62.5%'
    },
    body: {
      fontFamily: "'Montserrat', sans-serif",
      color: '#000',
      backgroundColor: '#fff',
      fontSize: '1.6rem',
      lineHeight: '1.6',
      fontWeight: '500'
    },
    '*, *::before, *::after': {
      margin: '0',
      padding: '0',
      boxSizing: 'border-box'
    }
  }
});

const components = {
  Switch: {
    baseStyle: {
      track: {
        bg: 'gray',
        _checked: {
          bg: '#34D1BF'
        }
      }
    }
  }
};

export default extendTheme({
  breakpoints,
  colors,
  styles,
  fontWeights,
  fontSizes,
  components: {
    ...components,
    Steps
  }
});

my login.test.tsx file

import { QueryClientProvider, QueryClient } from '@tanstack/react-query';
import { ChakraProvider } from '@chakra-ui/react';
import { render, screen } from '@testing-library/react';
import Login from 'src/pages/auth/login';
import themes from 'src/utils/themes';
import { RouterContext } from 'next/dist/shared/lib/router-context';
import { createMockRouter } from 'src/utils/test/createMockRouter';

Object.defineProperty(window, 'matchMedia', {
  writable: true,
  value: jest.fn().mockImplementation(query => ({
    matches: false,
    media: query,
    onchange: null,
    addListener: jest.fn(), // deprecated
    removeListener: jest.fn(), // deprecated
    addEventListener: jest.fn(),
    removeEventListener: jest.fn(),
    dispatchEvent: jest.fn()
  }))
});

describe('Login Page - Rendering', () => {
  it('should have Login to Your Account text', () => {
    const queryClient = new QueryClient();

    render(
      <QueryClientProvider client={queryClient}>
        <ChakraProvider theme={themes}>
          <RouterContext.Provider value={createMockRouter()}>
            <Login />
          </RouterContext.Provider>
        </ChakraProvider>
      </QueryClientProvider>
    );

    const loginText = screen.getByText(/Login to Your Account/i);
    expect(loginText).toBeInTheDocument();
  });
});
huseyindeniz commented 1 year ago

I believe you need to add some code into test-utils.ts file. You can see it in my previous comment.

https://github.com/jeanverster/chakra-ui-steps/issues/108#issuecomment-1381725686