reactjs - storybook + jest play -metodi epäonnistuu, kun kutsutaan shouldHaveBeenCalled funktiolle


Minulla on tämä komponentti:

import {
  FC,
  ReactElement,
  useState,
  MouseEvent as ReactMouseEvent,
  ChangeEvent as ReactChangeEvent,
} from 'react';

import { Stack, TablePagination } from '@mui/material';

export const Pagination: FC<IPaginationProps> = ({
  total,
  page = 1,
  rowsPerPage = 25,
  position = 'start',
  rowsPerPageOptions = [10, 25, 50, 100],
  onPageChange,
  onRowsPerPageChange,
}): ReactElement => {
  const [localPage, setPage] = useState(page);
  const [localRowsPerPage, setRowsPerPage] = useState(rowsPerPage);

  const handleChangePage = (event: ReactMouseEvent<HTMLButtonElement> | null, newPage: number) => {
    setPage(newPage);
    onPageChange({ page: newPage, total, rowsPerPage: localRowsPerPage });
  };

  const handleChangeRowsPerPage = (
    event: ReactChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
  ) => {
    const perPage = parseInt(event.target.value, 10);
    setRowsPerPage(perPage);
    setPage(0);
    onRowsPerPageChange({ page: 0, total, rowsPerPage: perPage });
  };

  return (
    <Stack direction="row" justifyContent={`flex-${position}`}>
      <TablePagination
        component="div"
        showFirstButton
        showLastButton
        count={total}
        page={localPage}
        onPageChange={handleChangePage}
        rowsPerPage={localRowsPerPage}
        onRowsPerPageChange={handleChangeRowsPerPage}
        rowsPerPageOptions={rowsPerPageOptions}
      />
    </Stack>
  );
};

Kirjoitin sille tarinan:

export default {
  title: 'Components/Pagination',
  component: Pagination,
  parameters: {
    actions: {
      handles: ['click'],
    },
  },
  args: {
    total: 100,
    page: 1,
    onPageChange: ({ page }) => {
      // eslint-disable-next-line no-console
      console.log({ page });
    },
    onRowsPerPageChange: ({ rowsPerPage }) => {
      // eslint-disable-next-line no-console
      console.log({ rowsPerPage });
    },
  },
  decorators: [
    (Story) => (
      <div style={{ margin: '3em', boxShadow: '7px 5px 14px -8px rgba(0,0,0,0.55)' }}>
        {Story()}
      </div>
    ),
  ],
} as ComponentMeta<typeof Pagination>;


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

export const Default = Template.bind({});
Default.play = async ({ canvasElement, args }) => {
  const onPageChangeSpy = jest.spyOn(args, 'onPageChange');
  const canvas = await within(canvasElement);

  const prevBtn = canvas.getByLabelText('Go to previous page');
  await userEvent.click(prevBtn);
  await expect(onPageChangeSpy).toHaveBeenCalled();
};

Näen, että painiketta klikattiin ja onPageChange kutsuttiin, koska sivutus muuttuu sen mukaisesti, mutta testi epäonnistuu:

Odotettavissa oleva puhelujen määrä: >= 1

Vastaanotettujen puheluiden määrä: 0

Ja outoin asia siinä on, että jos klikkaan uudelleen suorita Storybookin vuorovaikutukset-välilehdellä, kaikki toimii.

Vastaus #1

Sen sijaan, että käytät jest.spyOn, etkö voisi käyttää tavallisia storybook-argumentteja? Jotain tällaista:

await waitFor(() => expect(args.onPageChange).toHaveBeenCalledTimes(1));

Jotta yllä oleva toimisi, sinun täytyy varmistaa, että toimintasi rekisteröidään oikein. Liittämässäsi kuvakaappauksessa en näe numeroa Actions-välilehden otsikon vieressä (sen pitäisi näyttää tältä: Actions (1)), mikä saa minut uskomaan, että toimintasi eivät ole rekisteröityneet oikein, eikä niitä siksi näy Actions-välilehdellä.

Toiminnan rekisteröimisestä voit katsoa kuvaukseni toisesta viestistä täällä.

Periaatteessa jotain tällaista:

import { action } from '@storybook/addon-actions';

// ...
args: {
  onPageChange: action('onPageChange'),
  //...
},
argTypes: {
  onPageChange: { action: 'onPageChange' },
  //...
},

Vastaus #2

Ongelma on se, että jest.spyOn kutsuttiin liian myöhään, sen jälkeen kun askeleesi olivat suoritetut. Siksi toistokerralla se menee läpi - koska edellinen toiminto on olemassa ja siihen voi päästä käsiksi.
Minulle auttoi, kun siirsin jest.spyOnin Default.playin yläpuolelle:

const onPageChangeSpy = jest.spyOn(args, 'onPageChange'); // replase 'args' with your arguments

Default.play = async ({ canvasElement }) => {
  
  ...
  onPageChangeSpy.mockClear(); //remember to stop spying
};