Hyun Jae Lee
Hyun Jae Lee

Reputation: 17

Jest throws "Types of property 'id' are incompatible" error when it is trying to use stories from storybook using typescript

So..I am trying to use stories for my unit test with jest + RTL to minimize duplication (explained here) and the test throws "Types of property 'id' are incompatible" error when I pass the args that is also used in my story.

I am currently using below packages as my testing suite.

"@storybook/react": "^6.5.9",
"@storybook/testing-react": "^1.2.4",
"@testing-library/react": "^13.3.0",
"jest": "^28.1.1",
// and others...

Below is my test.

import * as React from 'react';
import { screen, render } from '@testing-library/react';

import { Default as Checkbox } from '../../stories/AccountCheckbox.stories';

const renderCheckbox = () => {
  render(<Checkbox {...Checkbox.args} />); // the point where it throws an type error that says 'id is incompatible'.
}

describe('<AccountCheckbox />', () => {
  test('should display account information', () => {
    renderCheckbox();

    expect(screen.getByTestId('example-test-id')).toBeInTheDocument();
  });
});

Below is my story.

import React from 'react';
import { ComponentMeta, ComponentStory } from '@storybook/react';

import AccountCheckbox from '../components/AccountCheckbox';
import { StoryAppWrapper } from '../../.storybook/wrapper/StoryAppWrapper';

export default {
  title: 'AccountCheckbox',
  component: AccountCheckbox,
  decorators: [(Story) => <StoryAppWrapper>{Story()}</StoryAppWrapper>]
} as ComponentMeta<typeof AccountCheckbox>;

const Template: ComponentStory<typeof AccountCheckbox> = (args) => (
  <AccountCheckbox {...args} />
);

export const Default = Template.bind({});

// These are the args I am trying to utilize in my test as well.
Default.args = {
  id: 'example-id',
  testId: 'example-test-id',
  label: 'Account 1',
  selected: false,
  onChange: () => { },
};

Below is the type for the checkbox I declared.

export type CheckboxProps = {
  id: string;
  testId: string;
  label: string;
  selected: boolean;
  onChange: any;
};

Below is the actual code.

import * as React from 'react';
import { Checkbox } from 'custom-lib';

import { CheckboxProps } from '../../types/types';

const AccountCheckbox = ({
  id,
  testId,
  label,
  selected,
  onChange,
}: CheckboxProps): JSX.Element => {
  return (
    <Checkbox
      id={id}
      data-testid={testId}
      label={label}
      state={selected ? 'true' : 'false'}
      onChange={onChange}
    />
  );
};

export default AccountCheckbox;

Can anyone pinpoint what I've missed within these codes?

Upvotes: 0

Views: 677

Answers (1)

shennan
shennan

Reputation: 11656

You're trying to render the Story, not the Checkbox itself. Why not import the checkbox directly, but use the story args too?

import * as React from 'react';
import { screen, render } from '@testing-library/react';
import AccountCheckbox from '../components/AccountCheckbox';
import { Default as AccountCheckboxStory } from '../../stories/AccountCheckbox.stories';

const renderCheckbox = () => {
  render(<AccountCheckbox {...AccountCheckboxStory.args} />);
}

describe('<AccountCheckbox />', () => {
  test('should display account information', () => {
    renderCheckbox();

    expect(screen.getByTestId('example-test-id')).toBeInTheDocument();
  });
});

You may need to adjust paths to the Story/AccountCheckbox as I'm not quite sure what your directory structure looks like.

Update

Going from your comment and the link, it seems that they are using some form of a compositional function to wrap the exported stories and return them as components. So in your case, would you not do this:

import React from 'react';
import { render, screen } from '@testing-library/react';
import { composeStories } from '@storybook/testing-react';

import * as stories from '../../stories/AccountCheckbox.stories';

const { Default: AccountCheckBoxDefault } = composeStories(stories);

test('renders profile page', async () => {
  render(<AccountCheckBoxDefault />);
  // ... some assertions here
});

describe('<AccountCheckbox />', () => {
  test('should display account information', () => {
    renderCheckbox();

    expect(screen.getByTestId('example-test-id')).toBeInTheDocument();
  });
});

I don't think it's actually necessary to pass in the args, as those args should be used by the story itself.

Upvotes: 1

Related Questions