mirror of
https://github.com/strapi/strapi.git
synced 2025-12-11 06:53:12 +00:00
test: add tests for DynamicZone sub components
This commit is contained in:
parent
4bf9291ccd
commit
c58d5a8692
@ -58,7 +58,7 @@ export default function ComponentCard({ children, icon, onClick }) {
|
||||
<button type="button" onClick={onClick}>
|
||||
<ComponentBox borderRadius="borderRadius">
|
||||
<Stack spacing={1} style={{ justifyContent: 'center', alignItems: 'center' }}>
|
||||
<StyledFontAwesomeIcon icon={icon} />
|
||||
<StyledFontAwesomeIcon data-testid="component-card-icon" icon={icon} />
|
||||
<Typography variant="pi" fontWeight="bold" textColor="neutral600">
|
||||
{children}
|
||||
</Typography>
|
||||
|
||||
@ -42,13 +42,19 @@ const ComponentCategory = ({ category, components, variant, isOpen, onAddCompone
|
||||
);
|
||||
};
|
||||
|
||||
ComponentCategory.defaultProps = {
|
||||
components: [],
|
||||
isOpen: false,
|
||||
variant: 'primary',
|
||||
};
|
||||
|
||||
ComponentCategory.propTypes = {
|
||||
category: PropTypes.string.isRequired,
|
||||
components: PropTypes.array.isRequired,
|
||||
isOpen: PropTypes.bool.isRequired,
|
||||
components: PropTypes.array,
|
||||
isOpen: PropTypes.bool,
|
||||
onAddComponent: PropTypes.func.isRequired,
|
||||
onToggle: PropTypes.func.isRequired,
|
||||
variant: PropTypes.oneOf(['primary', 'secondary']).isRequired,
|
||||
variant: PropTypes.oneOf(['primary', 'secondary']),
|
||||
};
|
||||
|
||||
export default ComponentCategory;
|
||||
|
||||
@ -34,7 +34,7 @@ const ComponentPicker = ({ components, isOpen, onClickAddComponent }) => {
|
||||
}, [components, getComponentLayout]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isOpen && dynamicComponentCategories.length) {
|
||||
if (isOpen && dynamicComponentCategories.length > 0) {
|
||||
setCategoryToOpen(dynamicComponentCategories[0].category);
|
||||
}
|
||||
}, [isOpen, dynamicComponentCategories]);
|
||||
@ -95,9 +95,14 @@ const ComponentPicker = ({ components, isOpen, onClickAddComponent }) => {
|
||||
);
|
||||
};
|
||||
|
||||
ComponentPicker.defaultProps = {
|
||||
components: [],
|
||||
isOpen: false,
|
||||
};
|
||||
|
||||
ComponentPicker.propTypes = {
|
||||
components: PropTypes.array.isRequired,
|
||||
isOpen: PropTypes.bool.isRequired,
|
||||
components: PropTypes.array,
|
||||
isOpen: PropTypes.bool,
|
||||
onClickAddComponent: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
|
||||
@ -6,62 +6,41 @@ import { IntlProvider } from 'react-intl';
|
||||
import AddComponentButton from '../AddComponentButton';
|
||||
|
||||
describe('<AddComponentButton />', () => {
|
||||
it('should render the label by default', () => {
|
||||
const setup = (props) =>
|
||||
render(
|
||||
<ThemeProvider theme={lightTheme}>
|
||||
<IntlProvider locale="en" messages={{}} defaultLocale="en">
|
||||
<AddComponentButton label="test" name="name" onClick={jest.fn()} />
|
||||
<AddComponentButton label="test" name="name" onClick={jest.fn()} {...props} />
|
||||
</IntlProvider>
|
||||
</ThemeProvider>
|
||||
);
|
||||
|
||||
it('should render the label by default', () => {
|
||||
setup();
|
||||
|
||||
expect(screen.getByText(/test/)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render the close label if the isOpen prop is true', () => {
|
||||
render(
|
||||
<ThemeProvider theme={lightTheme}>
|
||||
<IntlProvider locale="en" messages={{}} defaultLocale="en">
|
||||
<AddComponentButton label="test" isOpen name="name" onClick={jest.fn()} />
|
||||
</IntlProvider>
|
||||
</ThemeProvider>
|
||||
);
|
||||
setup({ isOpen: true });
|
||||
|
||||
expect(screen.getByText(/Close/)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render the name of the field when the label is an empty string', () => {
|
||||
render(
|
||||
<ThemeProvider theme={lightTheme}>
|
||||
<IntlProvider locale="en" messages={{}} defaultLocale="en">
|
||||
<AddComponentButton label="" name="name" onClick={jest.fn()} />
|
||||
</IntlProvider>
|
||||
</ThemeProvider>
|
||||
);
|
||||
setup({ label: '' });
|
||||
|
||||
expect(screen.getByText(/name/)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render a too high error if there is hasMaxError is true and the component is not open', () => {
|
||||
render(
|
||||
<ThemeProvider theme={lightTheme}>
|
||||
<IntlProvider locale="en" messages={{}} defaultLocale="en">
|
||||
<AddComponentButton hasMaxError label="test" name="name" onClick={jest.fn()} />
|
||||
</IntlProvider>
|
||||
</ThemeProvider>
|
||||
);
|
||||
setup({ hasMaxError: true });
|
||||
|
||||
expect(screen.getByText(/The value is too high./)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render a label telling the user there are X missing components if hasMinError is true and the component is not open', () => {
|
||||
render(
|
||||
<ThemeProvider theme={lightTheme}>
|
||||
<IntlProvider locale="en" messages={{}} defaultLocale="en">
|
||||
<AddComponentButton hasMinError label="test" name="name" onClick={jest.fn()} />
|
||||
</IntlProvider>
|
||||
</ThemeProvider>
|
||||
);
|
||||
setup({ hasMinError: true });
|
||||
|
||||
expect(screen.getByText(/missing components/)).toBeInTheDocument();
|
||||
});
|
||||
@ -69,13 +48,7 @@ describe('<AddComponentButton />', () => {
|
||||
it('should call the onClick handler when the button is clicked', () => {
|
||||
const onClick = jest.fn();
|
||||
|
||||
render(
|
||||
<ThemeProvider theme={lightTheme}>
|
||||
<IntlProvider locale="en" messages={{}} defaultLocale="en">
|
||||
<AddComponentButton label="test" name="name" onClick={onClick} />
|
||||
</IntlProvider>
|
||||
</ThemeProvider>
|
||||
);
|
||||
setup({ onClick });
|
||||
|
||||
screen.getByText(/test/).click();
|
||||
|
||||
@ -85,13 +58,7 @@ describe('<AddComponentButton />', () => {
|
||||
it('should not call the onClick handler when the button is disabled', () => {
|
||||
const onClick = jest.fn();
|
||||
|
||||
render(
|
||||
<ThemeProvider theme={lightTheme}>
|
||||
<IntlProvider locale="en" messages={{}} defaultLocale="en">
|
||||
<AddComponentButton isDisabled label="test" name="name" onClick={onClick} />
|
||||
</IntlProvider>
|
||||
</ThemeProvider>
|
||||
);
|
||||
setup({ onClick, disabled: true });
|
||||
|
||||
screen.getByText(/test/).click();
|
||||
|
||||
|
||||
@ -0,0 +1,114 @@
|
||||
import React from 'react';
|
||||
import { fireEvent, render } from '@testing-library/react';
|
||||
|
||||
import { ThemeProvider, lightTheme } from '@strapi/design-system';
|
||||
|
||||
import GlobalStyle from '../../../../../components/GlobalStyle';
|
||||
|
||||
import ComponentCard from '../ComponentCard';
|
||||
|
||||
describe('ComponentCard', () => {
|
||||
const setup = (props) =>
|
||||
render(
|
||||
<ThemeProvider theme={lightTheme}>
|
||||
<ComponentCard {...props}>test</ComponentCard>
|
||||
<GlobalStyle />
|
||||
</ThemeProvider>
|
||||
);
|
||||
|
||||
it('should render children by default', () => {
|
||||
const { getByTestId, getByText } = setup();
|
||||
expect(getByText('test')).toBeInTheDocument();
|
||||
expect(getByTestId('component-card-icon')).toMatchInlineSnapshot(`
|
||||
.c1 {
|
||||
width: 2rem !important;
|
||||
height: 2rem !important;
|
||||
padding: 0.5625rem;
|
||||
border-radius: 4rem;
|
||||
background: #eaeaef;
|
||||
}
|
||||
|
||||
.c1 path {
|
||||
fill: #8e8ea9;
|
||||
}
|
||||
|
||||
.c2.active .c0,
|
||||
.c2:hover .c0 {
|
||||
background: #d9d8ff;
|
||||
}
|
||||
|
||||
.c2.active .c0 path,
|
||||
.c2:hover .c0 path {
|
||||
fill: #4945ff;
|
||||
}
|
||||
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="svg-inline--fa fa-dice-d6 c0 c1"
|
||||
data-icon="dice-d6"
|
||||
data-prefix="fas"
|
||||
data-testid="component-card-icon"
|
||||
focusable="false"
|
||||
role="img"
|
||||
viewBox="0 0 448 512"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M422.19 109.95L256.21 9.07c-19.91-12.1-44.52-12.1-64.43 0L25.81 109.95c-5.32 3.23-5.29 11.27.06 14.46L224 242.55l198.14-118.14c5.35-3.19 5.38-11.22.05-14.46zm13.84 44.63L240 271.46v223.82c0 12.88 13.39 20.91 24.05 14.43l152.16-92.48c19.68-11.96 31.79-33.94 31.79-57.7v-197.7c0-6.41-6.64-10.43-11.97-7.25zM0 161.83v197.7c0 23.77 12.11 45.74 31.79 57.7l152.16 92.47c10.67 6.48 24.05-1.54 24.05-14.43V271.46L11.97 154.58C6.64 151.4 0 155.42 0 161.83z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
`);
|
||||
});
|
||||
|
||||
it('should render a valid icon when passed its name', () => {
|
||||
const { getByTestId } = setup({ icon: 'fa-camera' });
|
||||
expect(getByTestId('component-card-icon')).toMatchInlineSnapshot(`
|
||||
.c1 {
|
||||
width: 2rem !important;
|
||||
height: 2rem !important;
|
||||
padding: 0.5625rem;
|
||||
border-radius: 4rem;
|
||||
background: #eaeaef;
|
||||
}
|
||||
|
||||
.c1 path {
|
||||
fill: #8e8ea9;
|
||||
}
|
||||
|
||||
.c2.active .c0,
|
||||
.c2:hover .c0 {
|
||||
background: #d9d8ff;
|
||||
}
|
||||
|
||||
.c2.active .c0 path,
|
||||
.c2:hover .c0 path {
|
||||
fill: #4945ff;
|
||||
}
|
||||
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="svg-inline--fa fa-camera c0 c1"
|
||||
data-icon="camera"
|
||||
data-prefix="fas"
|
||||
data-testid="component-card-icon"
|
||||
focusable="false"
|
||||
role="img"
|
||||
viewBox="0 0 512 512"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M512 144v288c0 26.5-21.5 48-48 48H48c-26.5 0-48-21.5-48-48V144c0-26.5 21.5-48 48-48h88l12.3-32.9c7-18.7 24.9-31.1 44.9-31.1h125.5c20 0 37.9 12.4 44.9 31.1L376 96h88c26.5 0 48 21.5 48 48zM376 288c0-66.2-53.8-120-120-120s-120 53.8-120 120 53.8 120 120 120 120-53.8 120-120zm-32 0c0 48.5-39.5 88-88 88s-88-39.5-88-88 39.5-88 88-88 88 39.5 88 88z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
`);
|
||||
});
|
||||
|
||||
it('should call the onClick handler when passed', () => {
|
||||
const onClick = jest.fn();
|
||||
const { getByText } = setup({ onClick });
|
||||
fireEvent.click(getByText('test'));
|
||||
expect(onClick).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,83 @@
|
||||
import React from 'react';
|
||||
import { fireEvent, render, screen } from '@testing-library/react';
|
||||
import { ThemeProvider, lightTheme } from '@strapi/design-system';
|
||||
import { IntlProvider } from 'react-intl';
|
||||
|
||||
import ComponentCategory from '../ComponentCategory';
|
||||
|
||||
jest.mock('@fortawesome/react-fontawesome', () => ({
|
||||
FontAwesomeIcon: () => null,
|
||||
}));
|
||||
|
||||
describe('ComponentCategory', () => {
|
||||
const setup = (props) =>
|
||||
render(
|
||||
<ThemeProvider theme={lightTheme}>
|
||||
<IntlProvider locale="en" messages={{}} defaultLocale="en">
|
||||
<ComponentCategory
|
||||
onAddComponent={jest.fn()}
|
||||
onToggle={jest.fn()}
|
||||
category="testing"
|
||||
{...props}
|
||||
/>
|
||||
</IntlProvider>
|
||||
</ThemeProvider>
|
||||
);
|
||||
|
||||
it('should render my array of components when passed and the accordion is open', () => {
|
||||
setup({
|
||||
isOpen: true,
|
||||
components: [
|
||||
{
|
||||
componentUid: 'test',
|
||||
info: {
|
||||
displayName: 'myComponent',
|
||||
icon: 'test',
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
expect(screen.getByText(/myComponent/)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render the category as the accordion buttons label', () => {
|
||||
setup({
|
||||
category: 'myCategory',
|
||||
});
|
||||
|
||||
expect(screen.getByText(/myCategory/)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should call the onToggle callback when the accordion trigger is pressed', () => {
|
||||
const onToggle = jest.fn();
|
||||
setup({
|
||||
onToggle,
|
||||
});
|
||||
|
||||
fireEvent.click(screen.getByText(/testing/));
|
||||
|
||||
expect(onToggle).toHaveBeenCalledWith('testing');
|
||||
});
|
||||
|
||||
it('should call onAddComponent with the componentUid when a ComponentCard is clicked', () => {
|
||||
const onAddComponent = jest.fn();
|
||||
setup({
|
||||
isOpen: true,
|
||||
onAddComponent,
|
||||
components: [
|
||||
{
|
||||
componentUid: 'test',
|
||||
info: {
|
||||
displayName: 'myComponent',
|
||||
icon: 'test',
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
fireEvent.click(screen.getByText(/myComponent/));
|
||||
|
||||
expect(onAddComponent).toHaveBeenCalledWith('test');
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,75 @@
|
||||
import React from 'react';
|
||||
import { fireEvent, render, screen } from '@testing-library/react';
|
||||
import { ThemeProvider, lightTheme } from '@strapi/design-system';
|
||||
import { IntlProvider } from 'react-intl';
|
||||
|
||||
import ComponentPicker from '../ComponentPicker';
|
||||
|
||||
import { layoutData } from './fixtures';
|
||||
|
||||
jest.mock('@fortawesome/react-fontawesome', () => ({
|
||||
FontAwesomeIcon: () => null,
|
||||
}));
|
||||
|
||||
jest.mock('../../../../hooks', () => ({
|
||||
useContentTypeLayout: jest.fn().mockReturnValue({
|
||||
getComponentLayout: jest.fn().mockImplementation((componentUid) => layoutData[componentUid]),
|
||||
}),
|
||||
}));
|
||||
|
||||
describe('ComponentPicker', () => {
|
||||
afterEach(() => {
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
|
||||
const Component = (props) => (
|
||||
<ThemeProvider theme={lightTheme}>
|
||||
<IntlProvider locale="en" messages={{}} defaultLocale="en">
|
||||
<ComponentPicker isOpen onClickAddComponent={jest.fn()} {...props} />
|
||||
</IntlProvider>
|
||||
</ThemeProvider>
|
||||
);
|
||||
|
||||
const setup = (props) => render(<Component {...props} />);
|
||||
|
||||
it('should by default give me the instruction to Pick one Component', () => {
|
||||
setup();
|
||||
|
||||
expect(screen.getByText(/Pick one component/)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render null if isOpen is false', () => {
|
||||
setup({ isOpen: false });
|
||||
|
||||
expect(screen.queryByText(/Pick one component/)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render the category names by default', () => {
|
||||
setup({ components: ['component1', 'component2'] });
|
||||
|
||||
expect(screen.getByText(/myComponents/)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should open the first category of components when isOpen changes to true from false', () => {
|
||||
const { rerender } = setup({
|
||||
isOpen: false,
|
||||
});
|
||||
|
||||
rerender(<Component isOpen components={['component1', 'component2', 'component3']} />);
|
||||
|
||||
expect(screen.getByText(/component1/)).toBeInTheDocument();
|
||||
expect(screen.queryByText(/component3/)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should call onClickAddComponent with the componentUid when a Component is clicked', () => {
|
||||
const onClickAddComponent = jest.fn();
|
||||
setup({
|
||||
components: ['component1', 'component2'],
|
||||
onClickAddComponent,
|
||||
});
|
||||
|
||||
fireEvent.click(screen.getByText(/component1/));
|
||||
|
||||
expect(onClickAddComponent).toHaveBeenCalledWith('component1');
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,69 @@
|
||||
import React from 'react';
|
||||
import { fireEvent, render, screen } from '@testing-library/react';
|
||||
import { ThemeProvider, lightTheme } from '@strapi/design-system';
|
||||
import { IntlProvider } from 'react-intl';
|
||||
|
||||
import DynamicComponent from '../DynamicComponent';
|
||||
|
||||
import { layoutData } from './fixtures';
|
||||
|
||||
jest.mock('@fortawesome/react-fontawesome', () => ({
|
||||
FontAwesomeIcon: () => null,
|
||||
}));
|
||||
|
||||
jest.mock('../../../../hooks', () => ({
|
||||
useContentTypeLayout: jest.fn().mockReturnValue({
|
||||
getComponentLayout: jest.fn().mockImplementation((componentUid) => layoutData[componentUid]),
|
||||
}),
|
||||
}));
|
||||
|
||||
/**
|
||||
* We _could_ unmock this and use it, but it requires more
|
||||
* harnessing then is necessary and it's not worth it for these
|
||||
* tests when really we're focussing on dynamic zone behaviour.
|
||||
*/
|
||||
jest.mock('../../../FieldComponent', () => () => null);
|
||||
|
||||
describe('DynamicComponent', () => {
|
||||
afterEach(() => {
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
|
||||
const TestComponent = (props) => (
|
||||
<ThemeProvider theme={lightTheme}>
|
||||
<IntlProvider locale="en" messages={{}} defaultLocale="en">
|
||||
<DynamicComponent
|
||||
componentUid="component1"
|
||||
name="dynamiczone"
|
||||
onMoveComponentDownClick={jest.fn()}
|
||||
onMoveComponentUpClick={jest.fn()}
|
||||
onRemoveComponentClick={jest.fn()}
|
||||
{...props}
|
||||
/>
|
||||
</IntlProvider>
|
||||
</ThemeProvider>
|
||||
);
|
||||
|
||||
const setup = (props) => render(<TestComponent {...props} />);
|
||||
|
||||
it('should by default render the name of the component in the accordion trigger', () => {
|
||||
setup();
|
||||
|
||||
expect(screen.getByRole('button', { name: 'component1' })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should allow removal of the component & call the onRemoveComponentClick callback when the field isAllowed', () => {
|
||||
const onRemoveComponentClick = jest.fn();
|
||||
setup({ isFieldAllowed: true, onRemoveComponentClick });
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: 'Delete component1' }));
|
||||
|
||||
expect(onRemoveComponentClick).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not show you the delete component button if isFieldAllowed is false', () => {
|
||||
setup({ isFieldAllowed: false });
|
||||
|
||||
expect(screen.queryByRole('button', { name: 'Delete component1' })).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,23 @@
|
||||
export const layoutData = {
|
||||
component1: {
|
||||
category: 'myComponents',
|
||||
info: {
|
||||
displayName: 'component1',
|
||||
icon: undefined,
|
||||
},
|
||||
},
|
||||
component2: {
|
||||
category: 'myComponents',
|
||||
info: {
|
||||
displayName: 'component2',
|
||||
icon: undefined,
|
||||
},
|
||||
},
|
||||
component3: {
|
||||
category: 'otherComponents',
|
||||
info: {
|
||||
displayName: 'component3',
|
||||
icon: undefined,
|
||||
},
|
||||
},
|
||||
};
|
||||
Loading…
x
Reference in New Issue
Block a user