diff --git a/src/Widget/AttachedFileWidget.test.jsx b/src/Widget/AttachedFileWidget.test.jsx new file mode 100644 index 0000000..4e735b4 --- /dev/null +++ b/src/Widget/AttachedFileWidget.test.jsx @@ -0,0 +1,129 @@ +import React from 'react'; +import { render, fireEvent, screen, waitFor } from '@testing-library/react'; +import '@testing-library/jest-dom/extend-expect'; +import AttachedFileWidget from './AttachedFileWidget'; +import { Provider } from 'react-intl-redux'; +import configureStore from 'redux-mock-store'; + +jest.mock('promise-file-reader', () => ({ + readAsDataURL: jest.fn(() => + Promise.resolve('data:text/plain;base64,SGVsbG8sIFdvcmxkIQ=='), + ), +})); + +const mockStore = configureStore([]); +const store = mockStore({ + intl: { + locale: 'en', + messages: {}, + formatMessage: jest.fn(), + subrequests: {}, + }, + router: { + location: { pathname: '/test' }, + subrequests: {}, + }, +}); + +describe('AttachedFileWidget', () => { + const mockOnChange = jest.fn(); + const mockOpenObjectBrowser = jest.fn(); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('renders without crashing', () => { + const { container } = render( + + + , + ); + expect(container).toBeInTheDocument(); + }); + + it('calls onChange when a file is dropped', async () => { + const { container } = render( + + + , + ); + + let dropzone; + await waitFor(() => { + dropzone = container.querySelector('.file-widget-dropzone input'); + expect(dropzone).toBeInTheDocument(); + }); + expect(dropzone).toBeInTheDocument(); + + const file = new File(['hello'], 'hello.png', { type: 'image/png' }); + Object.defineProperty(dropzone, 'files', { + value: [file], + }); + fireEvent.drop(dropzone); + + await waitFor(() => expect(mockOnChange).toHaveBeenCalled()); + }); + + it('calls onChange when delete button is clicked', () => { + render( + + + , + ); + + const deleteButton = screen.getByLabelText('delete file'); + fireEvent.click(deleteButton); + + expect(mockOnChange).toHaveBeenCalledWith('testId', null); + }); + + it('renders without crashing', async () => { + const { container } = render( + + + , + ); + + let dropzone; + await waitFor(() => { + dropzone = container.querySelector('.file-widget-dropzone input'); + expect(dropzone).toBeInTheDocument(); + }); + expect(dropzone).toBeInTheDocument(); + expect(container.querySelector('.file-picker-toolbar')).toBeInTheDocument(); + expect( + container.querySelector('.file-picker-toolbar button'), + ).toBeInTheDocument(); + fireEvent.change(container.querySelector('.input-toolbar input'), { + target: { value: 'test' }, + }); + + fireEvent.click( + container.querySelector( + '.file-picker-toolbar button.ui.basic.icon.primary.button', + ), + ); + }); +}); diff --git a/src/Widget/AttachedImageWidget.test.jsx b/src/Widget/AttachedImageWidget.test.jsx index c5f3e67..d826022 100644 --- a/src/Widget/AttachedImageWidget.test.jsx +++ b/src/Widget/AttachedImageWidget.test.jsx @@ -1,5 +1,5 @@ import React from 'react'; -import { render, fireEvent } from '@testing-library/react'; +import { render, fireEvent, waitFor } from '@testing-library/react'; import { AttachedImageWidget } from './AttachedImageWidget'; import { Provider } from 'react-intl-redux'; import configureStore from 'redux-mock-store'; @@ -17,7 +17,20 @@ const store = mockStore({ messages: {}, formatMessage: jest.fn(), }, + content: { + subrequests: {}, + }, + router: { + location: { pathname: '/test' }, + }, }); + +jest.mock('promise-file-reader', () => ({ + readAsDataURL: jest.fn(() => + Promise.resolve('data:text/plain;base64,SGVsbG8sIFdvcmxkIQ=='), + ), +})); + describe('AttachedImageWidget', () => { const mockCreateContent = jest.fn(); const mockOpenObjectBrowser = jest.fn(); @@ -62,4 +75,176 @@ describe('AttachedImageWidget', () => { expect(mockOnChange).toHaveBeenCalledWith('imageId', ''); }); + + it('should render the component and handle URL input and cancel button', async () => { + const { container } = render( + + + , + ); + + let dropzone; + await waitFor(() => { + dropzone = container.querySelector('div[tabindex="0"]'); + expect(dropzone).toBeInTheDocument(); + }); + expect(dropzone).toBeInTheDocument(); + + fireEvent.change( + container.querySelector('.toolbar-inner .ui.input input[type="text"]'), + { target: { value: 'test' } }, + ); + fireEvent.click( + container.querySelector( + '.toolbar-inner .ui.buttons button.ui.basic.icon.primary.button', + ), + ); + + fireEvent.change( + container.querySelector('.toolbar-inner .ui.input input[type="text"]'), + { target: { value: 'test' } }, + ); + fireEvent.click( + container.querySelector( + '.toolbar-inner .ui.buttons button.ui.basic.icon.secondary.button.cancel', + ), + ); + }); + + it('should render the component handle object browser button click', async () => { + const { container } = render( + + + , + ); + + let dropzone; + await waitFor(() => { + dropzone = container.querySelector('div[tabindex="0"]'); + expect(dropzone).toBeInTheDocument(); + }); + expect(dropzone).toBeInTheDocument(); + + fireEvent.click( + container.querySelector('.toolbar-inner .ui.buttons button'), + ); + }); + + it('should handle file input and rerender based on request loading and loaded states', async () => { + const { rerender, container } = render( + + + , + ); + + let dropzone; + await waitFor(() => { + dropzone = container.querySelector('div[tabindex="0"]'); + expect(dropzone).toBeInTheDocument(); + }); + expect(dropzone).toBeInTheDocument(); + + const file = new File(['hello'], 'hello.png', { type: 'image/png' }); + Object.defineProperty( + container.querySelector('label[role="button"] input[type="file"]'), + 'files', + { + value: [file], + }, + ); + fireEvent.change( + container.querySelector('label[role="button"] input[type="file"]'), + { target: { value: '' } }, + ); + + rerender( + + + , + ); + + rerender( + + + , + ); + }); + + it('should handle file input via label button ', async () => { + const { container } = render( + + + , + ); + + let dropzone; + await waitFor(() => { + dropzone = container.querySelector('div[tabindex="0"]'); + expect(dropzone).toBeInTheDocument(); + }); + expect(dropzone).toBeInTheDocument(); + + const file = new File(['hello'], 'hello.png', { type: 'image/png' }); + Object.defineProperty( + container.querySelector('label[role="button"] input[type="file"]'), + 'files', + { + value: [file], + }, + ); + fireEvent.change( + container.querySelector('label[role="button"] input[type="file"]'), + { target: { value: '' } }, + ); + }); + + it('should handle drag and drop events ', async () => { + const { container } = render( + + + , + ); + + let dropzone; + await waitFor(() => { + dropzone = container.querySelector('div[tabindex="0"]'); + expect(dropzone).toBeInTheDocument(); + }); + expect(dropzone).toBeInTheDocument(); + + const file = new File(['hello'], 'hello.png', { type: 'image/png' }); + Object.defineProperty(dropzone, 'files', { + value: [file], + }); + fireEvent.dragEnter(dropzone); + fireEvent.dragLeave(dropzone); + fireEvent.drop(dropzone); + }); }); diff --git a/src/Widget/MappingWidget.test.jsx b/src/Widget/MappingWidget.test.jsx new file mode 100644 index 0000000..87fcb7a --- /dev/null +++ b/src/Widget/MappingWidget.test.jsx @@ -0,0 +1,56 @@ +import React from 'react'; +import { render, fireEvent } from '@testing-library/react'; +import MappingWidget from './MappingWidget'; +import '@testing-library/jest-dom/extend-expect'; + +jest.mock('@plone/volto/components', () => ({ + Field: ({ id, value, onChange }) => ( + onChange(id, e.target.value)} + /> + ), +})); + +describe('MappingWidget', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + const options = [ + { id: 'option1', title: 'Option 1' }, + { id: 'option2', title: 'Option 2' }, + ]; + + it('renders without crashing', () => { + const { container } = render( + , + ); + expect(container).toBeTruthy(); + }); + + it('renders correct number of fields', () => { + const { getAllByTestId } = render( + , + ); + expect(getAllByTestId(/field-/).length).toBe(2); + }); + + it('updates value on field change', () => { + const onChange = jest.fn(); + const { getByTestId } = render( + , + ); + + fireEvent.change(getByTestId('field-test-option1'), { + target: { value: 'new value' }, + }); + + expect(onChange).toHaveBeenCalledWith('test', { option1: 'new value' }); + }); +}); diff --git a/src/Widget/ObjectByTypeWidget.test.jsx b/src/Widget/ObjectByTypeWidget.test.jsx new file mode 100644 index 0000000..a57f765 --- /dev/null +++ b/src/Widget/ObjectByTypeWidget.test.jsx @@ -0,0 +1,73 @@ +import React from 'react'; +import { render, fireEvent } from '@testing-library/react'; +import ObjectByTypeWidget from './ObjectByTypeWidget'; +import '@testing-library/jest-dom/extend-expect'; + +jest.mock('@plone/volto/components', () => ({ + Icon: ({ name }) =>
Icon
, + ObjectWidget: ({ id, onChange }) => ( +
+
ObjectWidget
+ +
+ ), +})); + +describe('ObjectByTypeWidget', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + const schemas = [ + { id: 'type1', schema: { title: 'Type 1' }, icon: 'icon1' }, + { id: 'type2', schema: { title: 'Type 2' }, icon: 'icon2' }, + ]; + + it('renders without crashing', () => { + const { container } = render( + , + ); + expect(container).toBeTruthy(); + }); + + it('calls onChange', () => { + const { container } = render( + {}} + />, + ); + expect(container).toBeTruthy(); + fireEvent.change(container.querySelector('#objectwidget-type1'), { + target: { value: 'test' }, + }); + }); + + it('renders correct number of tabs', () => { + const { getAllByTestId } = render( + , + ); + expect(getAllByTestId(/icon-/).length).toBe(2); + }); + + it('renders ObjectWidget for active tab', () => { + const { getByTestId } = render( + , + ); + expect(getByTestId('objectwidget-type1')).toBeInTheDocument(); + }); + + it('switches tab on click', () => { + const { getByTestId } = render( + , + ); + + fireEvent.click(getByTestId('icon-icon2')); + expect(getByTestId('objectwidget-type2')).toBeInTheDocument(); + }); +}); diff --git a/src/Widget/ObjectListInlineWidget.test.jsx b/src/Widget/ObjectListInlineWidget.test.jsx new file mode 100644 index 0000000..88a108a --- /dev/null +++ b/src/Widget/ObjectListInlineWidget.test.jsx @@ -0,0 +1,152 @@ +import React from 'react'; +import { render, fireEvent } from '@testing-library/react'; +import { Provider } from 'react-intl-redux'; +import ObjectListInlineWidget from './ObjectListInlineWidget'; +import configureStore from 'redux-mock-store'; +import '@testing-library/jest-dom/extend-expect'; + +jest.mock('@plone/volto/components', () => ({ + FormFieldWrapper: ({ children }) =>
{children}
, + DragDropList: ({ childList, onMoveItem, children }) => { + const draginfo = { + innerRef: jest.fn(), + draggableProps: jest.fn(), + dragHandleProps: jest.fn(), + }; + return ( +
+
DragDropList
+ {childList.map((child, index) => ( +
{}} + onClick={() => { + onMoveItem({ + source: { index: 0 }, + destination: { index: 1 }, + }); + }} + > + {children({ child: child[1], childId: child[0], index, draginfo })} +
+ ))} +
+ ); + }, + ObjectWidget: ({ onChange }) => ( +
+
ObjectWidget
+ +
+ ), + Icon: () =>
VoltoIcon
, +})); + +jest.mock('uuid', () => ({ + v4: jest.fn(() => 'uuid'), +})); + +const mockStore = configureStore([]); +const store = mockStore({ + intl: { + locale: 'en', + messages: {}, + formatMessage: jest.fn(), + }, +}); + +describe('ObjectListInlineWidget', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + it('renders without crashing', () => { + const { container } = render( + + {}} + /> + , + ); + expect(container).toBeTruthy(); + }); + + it('renders Add button and adds an item on click', () => { + const onChange = jest.fn(); + const { getByText } = render( + + + , + ); + fireEvent.click(getByText(`Add Test`)); + expect(onChange).toHaveBeenCalledWith('test', [ + { + '@id': 'uuid', + }, + ]); + }); + + it('moves an item by calling onMoveItem', () => { + const onChange = jest.fn(); + const { container } = render( + + + , + ); + + fireEvent.click(container.querySelector('.dragdrop-list-item-mock')); + expect(onChange).toHaveBeenCalledWith('test', [ + { '@id': '2' }, + { '@id': '1' }, + ]); + }); + + it('renders DragDropList', () => { + const { getByText } = render( + + + , + ); + expect(getByText('DragDropList')).toBeInTheDocument(); + }); + + it('renders ObjectWidget', () => { + const onChange = jest.fn(); + const { container, getByText } = render( + + ({ title: 'ObjectWidget' })} + value={[{ '@id': '1' }]} + title="Tessst" + onChange={onChange} + /> + , + ); + expect(getByText('ObjectWidget')).toBeInTheDocument(); + expect(container.querySelector('.objectwidget-mock')).toBeInTheDocument(); + fireEvent.change(container.querySelector('.objectwidget-mock input'), { + target: { value: 'test' }, + }); + }); +}); diff --git a/src/demo/BlockEdit.test.jsx b/src/demo/BlockEdit.test.jsx new file mode 100644 index 0000000..a750679 --- /dev/null +++ b/src/demo/BlockEdit.test.jsx @@ -0,0 +1,47 @@ +import React from 'react'; +import { render, fireEvent } from '@testing-library/react'; +import BlockEdit from './BlockEdit'; +import '@testing-library/jest-dom/extend-expect'; + +jest.mock('@plone/volto/components', () => ({ + SidebarPortal: ({ children }) =>
{children}
, +})); +jest.mock('@plone/volto/components/manage/Form/InlineForm', () => (props) => ( +
+
InlineForm
+ +
+)); + +describe('BlockEdit', () => { + it('renders without crashing', () => { + const { container } = render(); + expect(container).toBeTruthy(); + }); + + it('renders SidebarPortal when selected is false', () => { + const { queryByText } = render(); + expect(queryByText('InlineForm')).toBeInTheDocument(); + }); + + it('renders SidebarPortal when selected is true', () => { + const { getByText } = render(); + expect(getByText('InlineForm')).toBeInTheDocument(); + }); + + it('calls onSelectBlock when clicked', () => { + const onSelectBlock = jest.fn(); + const { getByRole } = render(); + fireEvent.click(getByRole('presentation')); + expect(onSelectBlock).toHaveBeenCalled(); + }); + + it('calls onChangeBlock when onChangeField is called', () => { + const onChangeBlock = jest.fn(); + const { container } = render(); + fireEvent.change(container.querySelector('#inlineform'), { + target: { value: 'foo' }, + }); + expect(onChangeBlock).toHaveBeenCalled(); + }); +});