(test): bulk m7-12 enzyme to rtl migration

This commit is contained in:
Saturnino Abril 2026-02-03 12:26:52 +08:00
parent 36479bd721
commit 77340c7b56
54 changed files with 6708 additions and 6492 deletions

View file

@ -1,113 +1,64 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/CommercialSupportModal should match snapshot 1`] = ` exports[`components/CommercialSupportModal should match snapshot 1`] = `
<Modal <div>
animation={true} <div>
autoFocus={true} <div>
backdrop={true} <div>
bsClass="modal" Commercial Support
dialogClassName="a11y__modal more-modal more-direct-channels"
dialogComponentClass={[Function]}
enforceFocus={true}
id="commercialSupportModal"
keyboard={true}
manager={
ModalManager {
"add": [Function],
"containers": Array [],
"data": Array [],
"handleContainerOverflow": true,
"hideSiblingNodes": true,
"isTopModal": [Function],
"modals": Array [],
"remove": [Function],
}
}
onExited={[MockFunction]}
onHide={[Function]}
renderBackdrop={[Function]}
restoreFocus={true}
show={true}
>
<ModalHeader
bsClass="modal-header"
closeButton={true}
closeLabel="Close"
>
<ModalTitle
bsClass="modal-title"
componentClass="h4"
>
<MemoizedFormattedMessage
defaultMessage="Commercial Support"
id="commercial_support.title"
/>
</ModalTitle>
</ModalHeader>
<ModalBody
bsClass="modal-body"
componentClass="div"
>
<div
className="CommercialSupportModal"
>
<MemoizedFormattedMessage
defaultMessage="If you're experiencing issues, <supportLink>submit a support ticket</supportLink>. To help with troubleshooting, it's recommended to download the Support Packet below that includes more details about your Mattermost environment."
id="commercial_support_modal.description"
values={
Object {
"supportLink": [Function],
}
}
/>
<div
className="CommercialSupportModal__packet_contents_download"
>
<strong>
<MemoizedFormattedMessage
defaultMessage="Select your Support Packet contents to download"
id="commercial_support_modal.download_contents"
/>
</strong>
</div>
<div
className="CommercialSupportModal__option"
key="basic.server.logs"
>
<input
checked={true}
className="CommercialSupportModal__options__checkbox"
disabled={true}
id="basic.server.logs"
name="basic.server.logs"
onChange={[Function]}
type="checkbox"
/>
<MemoizedFormattedMessage
defaultMessage="Server Logs"
id="mettormost.plugin.metrics.support.packet"
>
<Component />
</MemoizedFormattedMessage>
</div>
<div
className="CommercialSupportModal__download"
>
<a
className="btn btn-primary DownloadSupportPacket"
onClick={[Function]}
rel="noopener noreferrer"
>
<i
className="icon icon-download-outline"
/>
<MemoizedFormattedMessage
defaultMessage="Download Support Packet"
id="commercial_support.download_support_packet"
/>
</a>
</div> </div>
</div> </div>
</ModalBody> <div>
</Modal> <div
class="CommercialSupportModal"
>
If you're experiencing issues,
<a
href="https://support.mattermost.com/hc/en-us/requests/new"
>
submit a support ticket
</a>
. To help with troubleshooting, it's recommended to download the Support Packet below that includes more details about your Mattermost environment.
<div
class="CommercialSupportModal__packet_contents_download"
>
<strong>
Select your Support Packet contents to download
</strong>
</div>
<div
class="CommercialSupportModal__option"
>
<input
checked=""
class="CommercialSupportModal__options__checkbox"
disabled=""
id="basic.server.logs"
name="basic.server.logs"
type="checkbox"
/>
<label
class="CommercialSupportModal__options_checkbox_label"
for="basic.server.logs"
>
Server Logs
</label>
</div>
<div
class="CommercialSupportModal__download"
>
<a
class="btn btn-primary DownloadSupportPacket"
rel="noopener noreferrer"
>
<i
class="icon icon-download-outline"
/>
Download Support Packet
</a>
</div>
</div>
</div>
</div>
</div>
`; `;

View file

@ -1,15 +1,33 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react'; import React from 'react';
import {Client4} from 'mattermost-redux/client'; import {Client4} from 'mattermost-redux/client';
import CommercialSupportModal from 'components/commercial_support_modal/commercial_support_modal'; import CommercialSupportModal from 'components/commercial_support_modal/commercial_support_modal';
import {act, renderWithContext, screen, userEvent, waitFor} from 'tests/react_testing_utils';
import {TestHelper} from 'utils/test_helper'; import {TestHelper} from 'utils/test_helper';
jest.mock('react-bootstrap', () => {
const Modal = ({children, show}: {children: React.ReactNode; show: boolean}) => (show ? <div>{children}</div> : null);
Modal.Header = ({children}: {children: React.ReactNode}) => <div>{children}</div>;
Modal.Body = ({children}: {children: React.ReactNode}) => <div>{children}</div>;
Modal.Title = ({children}: {children: React.ReactNode}) => <div>{children}</div>;
return {Modal};
});
jest.mock('components/alert_banner', () => (props: {message: React.ReactNode}) => (
<div>{props.message}</div>
));
jest.mock('components/external_link', () => ({children, href}: {children: React.ReactNode; href: string}) => (
<a href={href}>{children}</a>
));
jest.mock('components/widgets/loading/loading_spinner', () => () => <div>{'Loading...'}</div>);
describe('components/CommercialSupportModal', () => { describe('components/CommercialSupportModal', () => {
beforeAll(() => { beforeAll(() => {
// Mock getSystemRoute to return a valid URL // Mock getSystemRoute to return a valid URL
@ -37,8 +55,8 @@ describe('components/CommercialSupportModal', () => {
}; };
test('should match snapshot', () => { test('should match snapshot', () => {
const wrapper = shallow(<CommercialSupportModal {...baseProps}/>); const {container} = renderWithContext(<CommercialSupportModal {...baseProps}/>);
expect(wrapper).toMatchSnapshot(); expect(container).toMatchSnapshot();
}); });
test('should show error message when download fails', async () => { test('should show error message when download fails', async () => {
@ -56,20 +74,17 @@ describe('components/CommercialSupportModal', () => {
}), }),
); );
const wrapper = shallow<CommercialSupportModal>(<CommercialSupportModal {...baseProps}/>); renderWithContext(<CommercialSupportModal {...baseProps}/>);
// Trigger download const user = userEvent.setup();
const instance = wrapper.instance(); const downloadLink = screen.getByText('Download Support Packet').closest('a');
await instance.downloadSupportPacket(); if (!downloadLink) {
wrapper.update(); throw new Error('Download Support Packet link not found');
}
await user.click(downloadLink);
// Verify error message is shown // Verify error message is shown
const errorDiv = wrapper.find('.CommercialSupportModal__error'); expect(await screen.findByText(`${errorMessage}: ${detailedError}`)).toBeInTheDocument();
expect(errorDiv.exists()).toBe(true);
expect(errorDiv.find('.error-text').text()).toBe(`${errorMessage}: ${detailedError}`);
// Verify loading state is reset
expect(wrapper.state('loading')).toBe(false);
}); });
test('should clear error when starting new download', async () => { test('should clear error when starting new download', async () => {
@ -82,16 +97,30 @@ describe('components/CommercialSupportModal', () => {
}), }),
); );
const wrapper = shallow<CommercialSupportModal>(<CommercialSupportModal {...baseProps}/>); const ref = React.createRef<CommercialSupportModal>();
renderWithContext(
<CommercialSupportModal
{...baseProps}
ref={ref}
/>,
);
// Set initial error state act(() => {
wrapper.setState({error: 'Previous error'}); ref.current?.setState({error: 'Previous error'});
});
expect(screen.getByText('Previous error')).toBeInTheDocument();
// Start download // Start download
const instance = wrapper.instance(); const user = userEvent.setup();
await instance.downloadSupportPacket(); const downloadLink = screen.getByText('Download Support Packet').closest('a');
if (!downloadLink) {
throw new Error('Download Support Packet link not found');
}
await user.click(downloadLink);
// Verify error is cleared // Verify error is cleared
expect(wrapper.state('error')).toBeUndefined(); await waitFor(() => {
expect(screen.queryByText('Previous error')).not.toBeInTheDocument();
});
}); });
}); });

View file

@ -1,80 +1,128 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/drafts/drafts_row should match snapshot for channel draft 1`] = ` exports[`components/drafts/drafts_row should match snapshot for channel draft 1`] = `
<ContextProvider <div>
value={ <div
Object { aria-label="draft in Channel Name"
"getServerState": undefined, class="Panel"
"identityFunctionCheck": "once", data-testid="draftView"
"stabilityCheck": "once", role="link"
"store": Object { tabindex="0"
"clearActions": [Function], >
"dispatch": [Function], <div
"getActions": [Function], class="PanelHeader"
"getState": [Function], >
"replaceReducer": [Function], <div
"subscribe": [Function], class="PanelHeader__left"
}, >
"subscription": Object { <div>
"addNestedSub": [Function], Draft Title
"getListeners": [Function], </div>
"handleChangeWrapper": [Function], </div>
"isSubscribed": [Function], <div
"notifyNestedSubs": [Function], class="PanelHeader__right"
"trySubscribe": [Function], >
"tryUnsubscribe": [Function], <div
}, class="PanelHeader__actions"
} >
} <div>
> Draft Actions
<Memo(DraftRow) </div>
displayName="test" </div>
isRemote={false} <div
item={Object {}} class="PanelHeader__info"
status={Object {}} >
user={Object {}} <div
/> class="PanelHeader__timestamp"
</ContextProvider> >
56 years ago
</div>
<div
class="TagWrapper-kezsCu cA-dXXM Tag Tag--danger Tag--xs"
>
<span
class="TagText-bWoQAI beIloY"
>
draft
</span>
</div>
</div>
</div>
</div>
<div>
Panel Body
</div>
</div>
</div>
`; `;
exports[`components/drafts/drafts_row should match snapshot for thread draft 1`] = ` exports[`components/drafts/drafts_row should match snapshot for thread draft 1`] = `
<ContextProvider <div>
value={ <div
Object { aria-label="draft in Channel Name"
"getServerState": undefined, class="Panel draftError"
"identityFunctionCheck": "once", data-testid="draftView"
"stabilityCheck": "once", role="link"
"store": Object { tabindex="0"
"clearActions": [Function], >
"dispatch": [Function], <div
"getActions": [Function], class="PanelHeader"
"getState": [Function], >
"replaceReducer": [Function], <div
"subscribe": [Function], class="PanelHeader__left"
}, >
"subscription": Object { <div>
"addNestedSub": [Function], Draft Title
"getListeners": [Function], </div>
"handleChangeWrapper": [Function], </div>
"isSubscribed": [Function], <div
"notifyNestedSubs": [Function], class="PanelHeader__right"
"trySubscribe": [Function], >
"tryUnsubscribe": [Function], <div
}, class="PanelHeader__actions"
} >
} <div>
> Draft Actions
<Memo(DraftRow) </div>
displayName="test" </div>
draft={ <div
Object { class="PanelHeader__info"
"rootId": "some_id", >
} <div
} class="PanelHeader__timestamp"
isRemote={false} >
item={Object {}} 56 years ago
status={Object {}} </div>
user={Object {}} <div
/> class="TagWrapper-kezsCu cA-dXXM Tag Tag--danger Tag--xs"
</ContextProvider> >
<svg
fill="currentColor"
height="10"
version="1.1"
viewBox="0 0 24 24"
width="10"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M21.638,16.575L14.64,3.578C14.117,2.605,13.105,2,12,2S9.883,2.605,9.359,3.578L2.362,16.575c-0.505,0.939-0.481,2.046,0.065,2.962C2.974,20.453,3.937,21,5.003,21h13.994c1.066,0,2.029-0.547,2.575-1.463
C22.119,18.622,22.143,17.514,21.638,16.575z M18.995,18.998H5.001c-0.757,0-1.239-0.808-0.88-1.475l6.997-12.997
C11.307,4.175,11.652,4,11.998,4s0.691,0.175,0.88,0.526l6.997,12.997C20.234,18.19,19.752,18.998,18.995,18.998z M12.5,13h-1L11,7
h2L12.5,13z M12.999,16c0,0.552-0.448,1-1,1s-1-0.448-1-1s0.448-1,1-1C12.552,15,12.999,15.448,12.999,16z"
/>
</svg>
<span
class="TagText-bWoQAI beIloY"
>
Thread not found
</span>
</div>
</div>
</div>
</div>
<div>
Panel Body
</div>
</div>
</div>
`; `;

View file

@ -1,23 +1,23 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/drafts/draft_actions/action should match snapshot 1`] = ` exports[`components/drafts/draft_actions/action should match snapshot 1`] = `
<div <div>
className="DraftAction" <div
> class="DraftAction"
<WithTooltip
id="draft_tooltip_some-id"
title="some-tooltip-text"
> >
<button <div
aria-labelledby="draft_tooltip_some-id" data-testid="with-tooltip"
className="DraftAction__button"
id="draft_some-icon_some-id"
onClick={[MockFunction]}
> >
<i <button
className="icon some-icon" aria-labelledby="draft_tooltip_some-id"
/> class="DraftAction__button"
</button> id="draft_some-icon_some-id"
</WithTooltip> >
<i
class="icon some-icon"
/>
</button>
</div>
</div>
</div> </div>
`; `;

View file

@ -1,108 +1,70 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/drafts/draft_actions/delete_draft_modal should have called onConfirm 1`] = ` exports[`components/drafts/draft_actions/delete_draft_modal should have called onConfirm 1`] = `
<GenericModal <div>
autoFocusConfirmButton={true} <div>
compassDesign={true} <h1>
confirmButtonText="Yes, delete" Delete draft
handleCancel={[Function]} </h1>
handleConfirm={ <div>
[MockFunction] { Are you sure you want to delete this draft to
"calls": Array [ <strong>
Array [], display_name
], </strong>
"results": Array [ ?
Object { </div>
"type": "return", <button>
"value": undefined, Yes, delete
}, </button>
], <button>
} onExited
} </button>
isDeleteModal={true} </div>
modalHeaderText="Delete draft" </div>
onExited={[MockFunction]}
>
<MemoizedFormattedMessage
defaultMessage="Are you sure you want to delete this draft to <strong>{displayName}</strong>?"
id="drafts.confirm.delete.text"
values={
Object {
"displayName": "display_name",
"strong": [Function],
}
}
/>
</GenericModal>
`; `;
exports[`components/drafts/draft_actions/delete_draft_modal should have called onExited 1`] = ` exports[`components/drafts/draft_actions/delete_draft_modal should have called onExited 1`] = `
<GenericModal <div>
autoFocusConfirmButton={true} <div>
compassDesign={true} <h1>
confirmButtonText="Yes, delete" Delete draft
handleCancel={[Function]} </h1>
handleConfirm={[MockFunction]} <div>
isDeleteModal={true} Are you sure you want to delete this draft to
modalHeaderText="Delete draft" <strong>
onExited={ display_name
[MockFunction] { </strong>
"calls": Array [ ?
Array [], </div>
], <button>
"results": Array [ Yes, delete
Object { </button>
"type": "return", <button>
"value": undefined, onExited
}, </button>
], </div>
} </div>
}
>
<MemoizedFormattedMessage
defaultMessage="Are you sure you want to delete this draft to <strong>{displayName}</strong>?"
id="drafts.confirm.delete.text"
values={
Object {
"displayName": "display_name",
"strong": [Function],
}
}
/>
</GenericModal>
`; `;
exports[`components/drafts/draft_actions/delete_draft_modal should match snapshot 1`] = ` exports[`components/drafts/draft_actions/delete_draft_modal should match snapshot 1`] = `
<ContextProvider <div>
value={ <div>
Object { <h1>
"getServerState": undefined, Delete draft
"identityFunctionCheck": "once", </h1>
"stabilityCheck": "once", <div>
"store": Object { Are you sure you want to delete this draft to
"clearActions": [Function], <strong>
"dispatch": [Function], display_name
"getActions": [Function], </strong>
"getState": [Function], ?
"replaceReducer": [Function], </div>
"subscribe": [Function], <button>
}, Yes, delete
"subscription": Object { </button>
"addNestedSub": [Function], <button>
"getListeners": [Function], onExited
"handleChangeWrapper": [Function], </button>
"isSubscribed": [Function], </div>
"notifyNestedSubs": [Function], </div>
"trySubscribe": [Function],
"tryUnsubscribe": [Function],
},
}
}
>
<DeleteDraftModal
displayName="display_name"
onConfirm={[MockFunction]}
onExited={[MockFunction]}
/>
</ContextProvider>
`; `;

View file

@ -1,43 +1,74 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/drafts/draft_actions should match snapshot 1`] = ` exports[`components/drafts/draft_actions should match snapshot 1`] = `
<ContextProvider <div>
value={ <div
Object { class="DraftAction"
"getServerState": undefined, >
"identityFunctionCheck": "once", <div
"stabilityCheck": "once", data-testid="with-tooltip"
"store": Object { >
"clearActions": [Function], <button
"dispatch": [Function], aria-labelledby="draft_tooltip_delete"
"getActions": [Function], class="DraftAction__button DraftAction__button--delete"
"getState": [Function], id="draft_icon-trash-can-outline_delete"
"replaceReducer": [Function], >
"subscribe": [Function], <i
}, class="icon icon-trash-can-outline"
"subscription": Object { />
"addNestedSub": [Function], </button>
"getListeners": [Function], </div>
"handleChangeWrapper": [Function], </div>
"isSubscribed": [Function], <div
"notifyNestedSubs": [Function], class="DraftAction"
"trySubscribe": [Function], >
"tryUnsubscribe": [Function], <div
}, data-testid="with-tooltip"
} >
} <button
> aria-labelledby="draft_tooltip_edit"
<Memo(DraftActions) class="DraftAction__button"
canEdit={true} id="draft_icon-pencil-outline_edit"
canSend={true} >
channelId="" <i
displayName="" class="icon icon-pencil-outline"
draftId="" />
itemId="" </button>
onDelete={[MockFunction]} </div>
onEdit={[MockFunction]} </div>
onSchedule={[MockFunction]} <div
onSend={[MockFunction]} class="DraftAction"
/> >
</ContextProvider> <div
data-testid="with-tooltip"
>
<button
aria-labelledby="draft_tooltip_reschedule"
class="DraftAction__button"
id="draft_icon-clock-send-outline_reschedule"
>
<i
class="icon icon-clock-send-outline"
/>
</button>
</div>
</div>
<div
class="DraftAction"
>
<div
data-testid="with-tooltip"
>
<button
aria-labelledby="draft_tooltip_send"
class="DraftAction__button"
id="draft_icon-send-outline_send"
>
<i
class="icon icon-send-outline"
/>
</button>
</div>
</div>
</div>
`; `;

View file

@ -1,104 +1,70 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/drafts/draft_actions/send_draft_modal should have called onConfirm 1`] = ` exports[`components/drafts/draft_actions/send_draft_modal should have called onConfirm 1`] = `
<GenericModal <div>
compassDesign={true} <div>
confirmButtonText="Yes, send now" <h1>
handleCancel={[Function]} Send message now
handleConfirm={ </h1>
[MockFunction] { <div>
"calls": Array [ Are you sure you want to send this message to
Array [], <strong>
], display_name
"results": Array [ </strong>
Object { ?
"type": "return", </div>
"value": undefined, <button>
}, Yes, send now
], </button>
} <button>
} onExited
modalHeaderText="Send message now" </button>
onExited={[MockFunction]} </div>
> </div>
<MemoizedFormattedMessage
defaultMessage="Are you sure you want to send this message to <strong>{displayName}</strong>?"
id="drafts.confirm.send.text"
values={
Object {
"displayName": "display_name",
"strong": [Function],
}
}
/>
</GenericModal>
`; `;
exports[`components/drafts/draft_actions/send_draft_modal should have called onExited 1`] = ` exports[`components/drafts/draft_actions/send_draft_modal should have called onExited 1`] = `
<GenericModal <div>
compassDesign={true} <div>
confirmButtonText="Yes, send now" <h1>
handleCancel={[Function]} Send message now
handleConfirm={[MockFunction]} </h1>
modalHeaderText="Send message now" <div>
onExited={ Are you sure you want to send this message to
[MockFunction] { <strong>
"calls": Array [ display_name
Array [], </strong>
], ?
"results": Array [ </div>
Object { <button>
"type": "return", Yes, send now
"value": undefined, </button>
}, <button>
], onExited
} </button>
} </div>
> </div>
<MemoizedFormattedMessage
defaultMessage="Are you sure you want to send this message to <strong>{displayName}</strong>?"
id="drafts.confirm.send.text"
values={
Object {
"displayName": "display_name",
"strong": [Function],
}
}
/>
</GenericModal>
`; `;
exports[`components/drafts/draft_actions/send_draft_modal should match snapshot 1`] = ` exports[`components/drafts/draft_actions/send_draft_modal should match snapshot 1`] = `
<ContextProvider <div>
value={ <div>
Object { <h1>
"getServerState": undefined, Send message now
"identityFunctionCheck": "once", </h1>
"stabilityCheck": "once", <div>
"store": Object { Are you sure you want to send this message to
"clearActions": [Function], <strong>
"dispatch": [Function], display_name
"getActions": [Function], </strong>
"getState": [Function], ?
"replaceReducer": [Function], </div>
"subscribe": [Function], <button>
}, Yes, send now
"subscription": Object { </button>
"addNestedSub": [Function], <button>
"getListeners": [Function], onExited
"handleChangeWrapper": [Function], </button>
"isSubscribed": [Function], </div>
"notifyNestedSubs": [Function], </div>
"trySubscribe": [Function],
"tryUnsubscribe": [Function],
},
}
}
>
<SendDraftModal
displayName="display_name"
onConfirm={[MockFunction]}
onExited={[MockFunction]}
/>
</ContextProvider>
`; `;

View file

@ -1,11 +1,19 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react'; import React from 'react';
import {render} from 'tests/react_testing_utils';
import Action from './action'; import Action from './action';
jest.mock('components/with_tooltip', () => ({
__esModule: true,
default: ({children}: {children: React.ReactNode}) => (
<div data-testid='with-tooltip'>{children}</div>
),
}));
describe('components/drafts/draft_actions/action', () => { describe('components/drafts/draft_actions/action', () => {
const baseProps = { const baseProps = {
icon: 'some-icon', icon: 'some-icon',
@ -16,11 +24,11 @@ describe('components/drafts/draft_actions/action', () => {
}; };
it('should match snapshot', () => { it('should match snapshot', () => {
const wrapper = shallow( const {container} = render(
<Action <Action
{...baseProps} {...baseProps}
/>, />,
); );
expect(wrapper).toMatchSnapshot(); expect(container).toMatchSnapshot();
}); });
}); });

View file

@ -1,13 +1,32 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react'; import React from 'react';
import {Provider} from 'react-redux';
import {GenericModal} from '@mattermost/components'; import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils';
import mockStore from 'tests/test_store'; jest.mock('@mattermost/components', () => ({
GenericModal: ({
children,
handleConfirm,
onExited,
confirmButtonText,
modalHeaderText,
}: {
children: React.ReactNode;
handleConfirm?: () => void;
onExited?: () => void;
confirmButtonText?: string;
modalHeaderText?: string;
}) => (
<div>
<h1>{modalHeaderText}</h1>
<div>{children}</div>
<button onClick={handleConfirm}>{confirmButtonText || 'Confirm'}</button>
<button onClick={onExited}>{'onExited'}</button>
</div>
),
}));
import DeleteDraftModal from './delete_draft_modal'; import DeleteDraftModal from './delete_draft_modal';
@ -19,35 +38,33 @@ describe('components/drafts/draft_actions/delete_draft_modal', () => {
}; };
it('should match snapshot', () => { it('should match snapshot', () => {
const store = mockStore(); const {container} = renderWithContext(
<DeleteDraftModal
const wrapper = shallow( {...baseProps}
<Provider store={store}> />,
<DeleteDraftModal
{...baseProps}
/>
</Provider>,
); );
expect(wrapper).toMatchSnapshot(); expect(container).toMatchSnapshot();
}); });
it('should have called onConfirm', () => { it('should have called onConfirm', async () => {
const wrapper = shallow( const {container} = renderWithContext(
<DeleteDraftModal {...baseProps}/>, <DeleteDraftModal {...baseProps}/>,
); );
wrapper.find(GenericModal).first().props().handleConfirm!(); const user = userEvent.setup();
await user.click(screen.getByRole('button', {name: /yes, delete/i}));
expect(baseProps.onConfirm).toHaveBeenCalledTimes(1); expect(baseProps.onConfirm).toHaveBeenCalledTimes(1);
expect(wrapper).toMatchSnapshot(); expect(container).toMatchSnapshot();
}); });
it('should have called onExited', () => { it('should have called onExited', async () => {
const wrapper = shallow( const {container} = renderWithContext(
<DeleteDraftModal {...baseProps}/>, <DeleteDraftModal {...baseProps}/>,
); );
wrapper.find(GenericModal).first().props().onExited?.(); const user = userEvent.setup();
await user.click(screen.getByRole('button', {name: 'onExited'}));
expect(baseProps.onExited).toHaveBeenCalledTimes(1); expect(baseProps.onExited).toHaveBeenCalledTimes(1);
expect(wrapper).toMatchSnapshot(); expect(container).toMatchSnapshot();
}); });
}); });

View file

@ -1,14 +1,19 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react'; import React from 'react';
import {Provider} from 'react-redux';
import mockStore from 'tests/test_store'; import {renderWithContext} from 'tests/react_testing_utils';
import DraftActions from './draft_actions'; import DraftActions from './draft_actions';
jest.mock('components/with_tooltip', () => ({
__esModule: true,
default: ({children}: {children: React.ReactNode}) => (
<div data-testid='with-tooltip'>{children}</div>
),
}));
describe('components/drafts/draft_actions', () => { describe('components/drafts/draft_actions', () => {
const baseProps = { const baseProps = {
displayName: '', displayName: '',
@ -24,15 +29,11 @@ describe('components/drafts/draft_actions', () => {
}; };
it('should match snapshot', () => { it('should match snapshot', () => {
const store = mockStore(); const {container} = renderWithContext(
<DraftActions
const wrapper = shallow( {...baseProps}
<Provider store={store}> />,
<DraftActions
{...baseProps}
/>
</Provider>,
); );
expect(wrapper).toMatchSnapshot(); expect(container).toMatchSnapshot();
}); });
}); });

View file

@ -1,13 +1,32 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react'; import React from 'react';
import {Provider} from 'react-redux';
import {GenericModal} from '@mattermost/components'; import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils';
import mockStore from 'tests/test_store'; jest.mock('@mattermost/components', () => ({
GenericModal: ({
children,
handleConfirm,
onExited,
confirmButtonText,
modalHeaderText,
}: {
children: React.ReactNode;
handleConfirm?: () => void;
onExited?: () => void;
confirmButtonText?: string;
modalHeaderText?: string;
}) => (
<div>
<h1>{modalHeaderText}</h1>
<div>{children}</div>
<button onClick={handleConfirm}>{confirmButtonText || 'Confirm'}</button>
<button onClick={onExited}>{'onExited'}</button>
</div>
),
}));
import SendDraftModal from './send_draft_modal'; import SendDraftModal from './send_draft_modal';
@ -19,35 +38,33 @@ describe('components/drafts/draft_actions/send_draft_modal', () => {
}; };
it('should match snapshot', () => { it('should match snapshot', () => {
const store = mockStore(); const {container} = renderWithContext(
<SendDraftModal
const wrapper = shallow( {...baseProps}
<Provider store={store}> />,
<SendDraftModal
{...baseProps}
/>
</Provider>,
); );
expect(wrapper).toMatchSnapshot(); expect(container).toMatchSnapshot();
}); });
it('should have called onConfirm', () => { it('should have called onConfirm', async () => {
const wrapper = shallow( const {container} = renderWithContext(
<SendDraftModal {...baseProps}/>, <SendDraftModal {...baseProps}/>,
); );
wrapper.find(GenericModal).first().props().handleConfirm!(); const user = userEvent.setup();
await user.click(screen.getByRole('button', {name: /yes, send now/i}));
expect(baseProps.onConfirm).toHaveBeenCalledTimes(1); expect(baseProps.onConfirm).toHaveBeenCalledTimes(1);
expect(wrapper).toMatchSnapshot(); expect(container).toMatchSnapshot();
}); });
it('should have called onExited', () => { it('should have called onExited', async () => {
const wrapper = shallow( const {container} = renderWithContext(
<SendDraftModal {...baseProps}/>, <SendDraftModal {...baseProps}/>,
); );
wrapper.find(GenericModal).first().props().onExited?.(); const user = userEvent.setup();
await user.click(screen.getByRole('button', {name: 'onExited'}));
expect(baseProps.onExited).toHaveBeenCalledTimes(1); expect(baseProps.onExited).toHaveBeenCalledTimes(1);
expect(wrapper).toMatchSnapshot(); expect(container).toMatchSnapshot();
}); });
}); });

View file

@ -1,57 +1,119 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import type {ComponentProps} from 'react'; import type {ComponentProps} from 'react';
import React from 'react'; import React from 'react';
import {Provider} from 'react-redux';
import type {ChannelType} from '@mattermost/types/channels';
import type {PostType} from '@mattermost/types/posts';
import type {UserProfile, UserStatus} from '@mattermost/types/users'; import type {UserProfile, UserStatus} from '@mattermost/types/users';
import mockStore from 'tests/test_store'; import {renderWithContext} from 'tests/react_testing_utils';
import type {PostDraft} from 'types/store/draft'; import type {PostDraft} from 'types/store/draft';
import DraftRow from './draft_row'; import DraftRow from './draft_row';
jest.mock('components/advanced_text_editor/use_priority', () => () => ({onSubmitCheck: jest.fn()}));
jest.mock('components/advanced_text_editor/use_submit', () => () => [jest.fn()]);
jest.mock('components/drafts/draft_actions', () => () => <div>{'Draft Actions'}</div>);
jest.mock('components/drafts/draft_title', () => () => <div>{'Draft Title'}</div>);
jest.mock('components/drafts/panel/panel_body', () => () => <div>{'Panel Body'}</div>);
jest.mock('components/drafts/draft_actions/schedule_post_actions/scheduled_post_actions', () => () => (
<div>{'Scheduled Post Actions'}</div>
));
jest.mock('components/edit_scheduled_post', () => () => <div>{'Edit Scheduled Post'}</div>);
jest.mock('components/drafts/placeholder_scheduled_post_title/placeholder_scheduled_posts_title', () => () => (
<div>{'Placeholder Scheduled Post Title'}</div>
));
jest.mock('mattermost-redux/actions/posts', () => ({
getPost: () => jest.fn(),
}));
jest.mock('mattermost-redux/actions/scheduled_posts', () => ({
deleteScheduledPost: () => jest.fn(),
updateScheduledPost: () => jest.fn(),
}));
jest.mock('mattermost-redux/selectors/entities/roles', () => ({
haveIChannelPermission: () => true,
}));
jest.mock('mattermost-redux/selectors/entities/channels', () => ({
...jest.requireActual('mattermost-redux/selectors/entities/channels'),
isDeactivatedDirectChannel: () => false,
}));
describe('components/drafts/drafts_row', () => { describe('components/drafts/drafts_row', () => {
const channelId = 'channel_id';
const teamId = 'team_id';
const channel = {
id: channelId,
team_id: teamId,
name: 'channel-name',
display_name: 'Channel Name',
type: 'O' as ChannelType,
delete_at: 0,
};
const initialState = {
entities: {
channels: {channels: {[channelId]: channel}},
teams: {
currentTeamId: teamId,
teams: {[teamId]: {id: teamId, name: 'team-name'}},
},
general: {
config: {
MaxPostSize: '16383',
BurnOnReadDurationSeconds: '600',
},
license: {},
},
posts: {posts: {}},
},
websocket: {connectionId: 'connection_id'},
};
const baseProps: ComponentProps<typeof DraftRow> = { const baseProps: ComponentProps<typeof DraftRow> = {
item: {} as PostDraft, item: {
user: {} as UserProfile, message: 'draft message',
status: {} as UserStatus['status'], updateAt: 1234,
createAt: 1234,
fileInfos: [],
uploadsInProgress: [],
channelId,
rootId: '',
type: 'standard' as PostType,
} as PostDraft,
user: {id: 'user_id', username: 'username'} as UserProfile,
status: 'online' as UserStatus['status'],
displayName: 'test', displayName: 'test',
isRemote: false, isRemote: false,
}; };
it('should match snapshot for channel draft', () => { it('should match snapshot for channel draft', () => {
const store = mockStore(); const {container} = renderWithContext(
<DraftRow
const wrapper = shallow( {...baseProps}
<Provider store={store}> />,
<DraftRow initialState,
{...baseProps}
/>
</Provider>,
); );
expect(wrapper).toMatchSnapshot(); expect(container).toMatchSnapshot();
}); });
it('should match snapshot for thread draft', () => { it('should match snapshot for thread draft', () => {
const store = mockStore();
const props = { const props = {
...baseProps, ...baseProps,
draft: {rootId: 'some_id'} as PostDraft, item: {
...baseProps.item,
rootId: 'some_id',
} as PostDraft,
}; };
const wrapper = shallow( const {container} = renderWithContext(
<Provider store={store}> <DraftRow
<DraftRow {...props}
{...props} />,
/> initialState,
</Provider>,
); );
expect(wrapper).toMatchSnapshot(); expect(container).toMatchSnapshot();
}); });
}); });

View file

@ -1,13 +1,11 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/drafts/panel/ should match snapshot 1`] = ` exports[`components/drafts/panel/ should match snapshot 1`] = `
<div <div>
className="Panel" <div
onClick={[Function]} class="Panel"
onKeyDown={[Function]} role="link"
role="link" tabindex="0"
tabIndex={0} />
>
<Component />
</div> </div>
`; `;

View file

@ -1,126 +1,110 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/drafts/panel/panel_header should match snapshot 1`] = ` exports[`components/drafts/panel/panel_header should match snapshot 1`] = `
<div <div>
className="PanelHeader"
>
<div <div
className="PanelHeader__left" class="PanelHeader"
>
<div>
title
</div>
</div>
<div
className="PanelHeader__right"
> >
<div <div
className="PanelHeader__actions" class="PanelHeader__left"
> >
<div> <div>
actions title
</div> </div>
</div> </div>
<div <div
className="PanelHeader__info" class="PanelHeader__right"
> >
<div <div
className="PanelHeader__timestamp" class="PanelHeader__actions"
> >
<Connect(injectIntl(Timestamp)) <div>
day="numeric" actions
units={ </div>
Array [ </div>
"now", <div
"minute", class="PanelHeader__info"
"hour", >
"day", <div
"week", class="PanelHeader__timestamp"
"month", >
"year", 56 years ago
] </div>
} <div
useSemanticOutput={false} class="TagWrapper-kezsCu cA-dXXM Tag Tag--danger Tag--xs"
useTime={false} >
value={1970-01-01T00:00:12.345Z} <span
/> class="TagText-bWoQAI beIloY"
>
draft
</span>
</div>
</div> </div>
<Memo(Tag)
text="draft"
uppercase={true}
variant="danger"
/>
</div> </div>
</div> </div>
</div> </div>
`; `;
exports[`components/drafts/panel/panel_header should show sync icon when draft is from server 1`] = ` exports[`components/drafts/panel/panel_header should show sync icon when draft is from server 1`] = `
<div <div>
className="PanelHeader"
>
<div <div
className="PanelHeader__left" class="PanelHeader"
>
<div>
title
</div>
</div>
<div
className="PanelHeader__right"
> >
<div <div
className="PanelHeader__actions" class="PanelHeader__left"
> >
<div> <div>
actions title
</div> </div>
</div> </div>
<div <div
className="PanelHeader__info" class="PanelHeader__right"
> >
<div <div
className="PanelHeader__sync-icon" class="PanelHeader__actions"
> >
<WithTooltip <div>
title={ actions
<Memo(MemoizedFormattedMessage) </div>
defaultMessage="Updated from another device"
id="drafts.info.sync"
/>
}
>
<SyncIcon
size={18}
/>
</WithTooltip>
</div> </div>
<div <div
className="PanelHeader__timestamp" class="PanelHeader__info"
> >
<Connect(injectIntl(Timestamp)) <div
day="numeric" class="PanelHeader__sync-icon"
units={ >
Array [ <div
"now", data-testid="with-tooltip"
"minute", >
"hour", <svg
"day", fill="currentColor"
"week", height="18"
"month", version="1.1"
"year", viewBox="0 0 24 24"
] width="18"
} xmlns="http://www.w3.org/2000/svg"
useSemanticOutput={false} >
useTime={false} <path
value={1970-01-01T00:00:12.345Z} d="M12,18A6,6 0 0,1 6,12C6,11 6.25,10.03 6.7,9.2L5.24,7.74C4.46,8.97 4,10.43 4,12A8,8 0 0,0 12,20V23L16,19L12,15M12,4V1L8,5L12,9V6A6,6 0 0,1 18,12C18,13 17.75,13.97 17.3,14.8L18.76,16.26C19.54,15.03 20,13.57 20,12A8,8 0 0,0 12,4Z"
/> />
</svg>
</div>
</div>
<div
class="PanelHeader__timestamp"
>
56 years ago
</div>
<div
class="TagWrapper-kezsCu cA-dXXM Tag Tag--danger Tag--xs"
>
<span
class="TagText-bWoQAI beIloY"
>
draft
</span>
</div>
</div> </div>
<Memo(Tag)
text="draft"
uppercase={true}
variant="danger"
/>
</div> </div>
</div> </div>
</div> </div>

View file

@ -1,9 +1,10 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react'; import React from 'react';
import {render} from 'tests/react_testing_utils';
import Panel from './panel'; import Panel from './panel';
describe('components/drafts/panel/', () => { describe('components/drafts/panel/', () => {
@ -17,12 +18,12 @@ describe('components/drafts/panel/', () => {
}; };
it('should match snapshot', () => { it('should match snapshot', () => {
const wrapper = shallow( const {container} = render(
<Panel <Panel
{...baseProps} {...baseProps}
/>, />,
); );
expect(wrapper).toMatchSnapshot(); expect(container).toMatchSnapshot();
}); });
}); });

View file

@ -1,13 +1,19 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react'; import React from 'react';
import WithTooltip from 'components/with_tooltip'; import {renderWithContext, screen} from 'tests/react_testing_utils';
import PanelHeader from './panel_header'; import PanelHeader from './panel_header';
jest.mock('components/with_tooltip', () => ({
__esModule: true,
default: ({children}: {children: React.ReactNode}) => (
<div data-testid='with-tooltip'>{children}</div>
),
}));
describe('components/drafts/panel/panel_header', () => { describe('components/drafts/panel/panel_header', () => {
const baseProps: React.ComponentProps<typeof PanelHeader> = { const baseProps: React.ComponentProps<typeof PanelHeader> = {
kind: 'draft' as const, kind: 'draft' as const,
@ -19,15 +25,15 @@ describe('components/drafts/panel/panel_header', () => {
}; };
it('should match snapshot', () => { it('should match snapshot', () => {
const wrapper = shallow( const {container} = renderWithContext(
<PanelHeader <PanelHeader
{...baseProps} {...baseProps}
/>, />,
); );
expect(wrapper.find('div.PanelHeader__actions').hasClass('PanelHeader__actions show')).toBe(false); expect(screen.queryByTestId('with-tooltip')).not.toBeInTheDocument();
expect(wrapper.find(WithTooltip).exists()).toBe(false); expect(screen.getByText('actions').closest('.PanelHeader__actions')).not.toHaveClass('show');
expect(wrapper).toMatchSnapshot(); expect(container).toMatchSnapshot();
}); });
it('should show sync icon when draft is from server', () => { it('should show sync icon when draft is from server', () => {
@ -36,13 +42,13 @@ describe('components/drafts/panel/panel_header', () => {
remote: true, remote: true,
}; };
const wrapper = shallow( const {container} = renderWithContext(
<PanelHeader <PanelHeader
{...props} {...props}
/>, />,
); );
expect(wrapper.find(WithTooltip).exists()).toBe(true); expect(screen.getByTestId('with-tooltip')).toBeInTheDocument();
expect(wrapper).toMatchSnapshot(); expect(container).toMatchSnapshot();
}); });
}); });

View file

@ -1,9 +1,9 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react'; import React from 'react';
import {renderWithContext, screen} from 'tests/react_testing_utils';
import {ModalIdentifiers} from 'utils/constants'; import {ModalIdentifiers} from 'utils/constants';
import FeatureRestrictedModal from './feature_restricted_modal'; import FeatureRestrictedModal from './feature_restricted_modal';
@ -17,6 +17,31 @@ jest.mock('react-redux', () => ({
useDispatch: () => mockDispatch, useDispatch: () => mockDispatch,
})); }));
jest.mock('components/common/hooks/useOpenPricingModal', () => () => ({
openPricingModal: jest.fn(),
isAirGapped: false,
}));
jest.mock('components/notify_admin_cta/notify_admin_cta', () => ({
useNotifyAdmin: () => {
const {NotifyStatus} = require('components/common/hooks/useGetNotifyAdmin');
return ['Notify admin', jest.fn(), NotifyStatus.None];
},
}));
jest.mock('components/learn_more_trial_modal/start_trial_btn', () => (props: {onClick: () => void}) => (
<button onClick={props.onClick}>{'Try free for 30 days'}</button>
));
jest.mock('@mattermost/components', () => ({
GenericModal: ({children, modalHeaderText}: {children: React.ReactNode; modalHeaderText?: string}) => (
<div>
<h1>{modalHeaderText}</h1>
{children}
</div>
),
}));
describe('components/global/product_switcher_menu', () => { describe('components/global/product_switcher_menu', () => {
const defaultProps = { const defaultProps = {
titleAdminPreTrial: 'Title admin pre trial', titleAdminPreTrial: 'Title admin pre trial',
@ -78,36 +103,34 @@ describe('components/global/product_switcher_menu', () => {
}); });
test('should show with end user pre trial', () => { test('should show with end user pre trial', () => {
const wrapper = shallow(<FeatureRestrictedModal {...defaultProps}/>); const {container} = renderWithContext(<FeatureRestrictedModal {...defaultProps}/>);
expect(wrapper.find('.FeatureRestrictedModal__description').text()).toEqual(defaultProps.messageEndUser); expect(screen.getByText(defaultProps.messageEndUser)).toBeInTheDocument();
expect(wrapper.find('.FeatureRestrictedModal__terms').length).toEqual(0); expect(container.querySelector('.FeatureRestrictedModal__terms')).not.toBeInTheDocument();
expect(wrapper.find('.FeatureRestrictedModal__buttons').hasClass('single')).toEqual(true); expect(container.querySelector('.FeatureRestrictedModal__buttons')).toHaveClass('single');
expect(wrapper.find('.button-plans').length).toEqual(1); expect(screen.getByRole('button', {name: /notify admin/i})).toBeInTheDocument();
expect(wrapper.find('CloudStartTrialButton').length).toEqual(0); expect(screen.queryByRole('button', {name: /try free/i})).not.toBeInTheDocument();
expect(wrapper.find('StartTrialBtn').length).toEqual(0);
}); });
test('should show with end user post trial', () => { test('should show with end user post trial', () => {
const wrapper = shallow(<FeatureRestrictedModal {...defaultProps}/>); const {container} = renderWithContext(<FeatureRestrictedModal {...defaultProps}/>);
expect(wrapper.find('.FeatureRestrictedModal__description').text()).toEqual(defaultProps.messageEndUser); expect(screen.getByText(defaultProps.messageEndUser)).toBeInTheDocument();
expect(wrapper.find('.FeatureRestrictedModal__terms').length).toEqual(0); expect(container.querySelector('.FeatureRestrictedModal__terms')).not.toBeInTheDocument();
expect(wrapper.find('.FeatureRestrictedModal__buttons').hasClass('single')).toEqual(true); expect(container.querySelector('.FeatureRestrictedModal__buttons')).toHaveClass('single');
expect(wrapper.find('.button-plans').length).toEqual(1); expect(screen.getByRole('button', {name: /notify admin/i})).toBeInTheDocument();
expect(wrapper.find('CloudStartTrialButton').length).toEqual(0); expect(screen.queryByRole('button', {name: /try free/i})).not.toBeInTheDocument();
expect(wrapper.find('StartTrialBtn').length).toEqual(0);
}); });
test('should show with system admin pre trial for self hosted', () => { test('should show with system admin pre trial for self hosted', () => {
mockState.entities.users.profiles.user1.roles = 'system_admin'; mockState.entities.users.profiles.user1.roles = 'system_admin';
const wrapper = shallow(<FeatureRestrictedModal {...defaultProps}/>); const {container} = renderWithContext(<FeatureRestrictedModal {...defaultProps}/>);
expect(wrapper.find('.FeatureRestrictedModal__description').text()).toEqual(defaultProps.messageAdminPreTrial); expect(screen.getByText(defaultProps.messageAdminPreTrial)).toBeInTheDocument();
expect(wrapper.find('.FeatureRestrictedModal__terms').length).toEqual(1); expect(container.querySelector('.FeatureRestrictedModal__terms')).toBeInTheDocument();
expect(wrapper.find('.FeatureRestrictedModal__buttons').hasClass('single')).toEqual(false); expect(container.querySelector('.FeatureRestrictedModal__buttons')).not.toHaveClass('single');
expect(wrapper.find('.button-plans').length).toEqual(1); expect(screen.getByRole('button', {name: /view plans/i})).toBeInTheDocument();
expect(wrapper.find('StartTrialBtn').length).toEqual(1); expect(screen.getByRole('button', {name: /try free/i})).toBeInTheDocument();
}); });
}); });

View file

@ -1,355 +1,334 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`FilePreview should match snapshot 1`] = ` exports[`FilePreview should match snapshot 1`] = `
<div <div>
className="file-preview__container"
>
<div <div
className="file-preview post-image__column" class="file-preview__container"
key="file_id_1"
> >
<div <div
className="post-image__thumbnail" class="file-preview post-image__column"
> >
<div <div
className="post-image normal" class="post-image__thumbnail"
style={
Object {
"backgroundImage": "url(/api/v4/files/file_id_1/thumbnail)",
"backgroundSize": "cover",
}
}
/>
</div>
<div
className="post-image__details"
>
<div
className="post-image__detail_wrapper"
> >
<div <div
className="post-image__detail" class="post-image normal"
style="background-image: url(\\"/api/v4/files/file_id_1/thumbnail\\"); background-size: cover;"
/>
</div>
<div
class="post-image__details"
>
<div
class="post-image__detail_wrapper"
> >
<FilenameOverlay <div
canDownload={false} class="post-image__detail"
compactDisplay={false}
fileInfo={
Object {
"archived": false,
"channel_id": "channel_id",
"clientId": "",
"create_at": 0,
"delete_at": 0,
"extension": "png",
"has_preview_image": true,
"height": 100,
"id": "file_id_1",
"mime_type": "",
"name": "test_filename",
"size": 100,
"type": "image/png",
"update_at": 0,
"user_id": "",
"width": 100,
}
}
/>
<span
className="post-image__type"
> >
PNG <span
</span> class="post-image__name"
<span >
className="post-image__size" test_filename
</span>
<span
class="post-image__type"
>
PNG
</span>
<span
class="post-image__size"
>
100B
</span>
</div>
</div>
<div>
<a
class="file-preview__remove"
> >
100B <i
</span> class="icon icon-close"
/>
</a>
</div> </div>
</div> </div>
<div> </div>
<a <div
className="file-preview__remove" class="file-preview post-image__column"
onClick={[Function]} data-client-id="clientID_1"
>
<div
class="post-image__thumbnail"
>
<div
class="file-icon generic"
/>
</div>
<div
class="post-image__details"
>
<div
class="post-image__detail_wrapper"
> >
<i <div
className="icon icon-close" class="post-image__detail"
>
<span
class="post-image__name"
>
file
</span>
<span
class="post-image__uploadingTxt"
>
Uploading...
<span>
(50%)
</span>
</span>
</div>
</div>
<div>
<a
class="file-preview__remove"
>
<i
class="icon icon-close"
/>
</a>
</div>
<div
class="post-image__progressBar progress"
>
<div
aria-valuemax="100"
aria-valuemin="0"
aria-valuenow="50"
class="progress-bar"
role="progressbar"
style="width: 50%;"
/> />
</a> </div>
</div> </div>
</div> </div>
</div> </div>
<FileProgressPreview
clientId="clientID_1"
fileInfo={
Object {
"archived": false,
"channel_id": "channel_id",
"clientId": "",
"create_at": 0,
"delete_at": 0,
"extension": "image/png",
"has_preview_image": true,
"height": 100,
"id": "file_id_1",
"mime_type": "",
"name": "file",
"percent": 50,
"size": 100,
"update_at": 0,
"user_id": "",
"width": 100,
}
}
handleRemove={[Function]}
key="clientID_1"
/>
</div> </div>
`; `;
exports[`FilePreview should match snapshot when props are changed 1`] = ` exports[`FilePreview should match snapshot when props are changed 1`] = `
<div <div>
className="file-preview__container"
>
<div <div
className="file-preview post-image__column" class="file-preview__container"
key="file_id_1"
> >
<div <div
className="post-image__thumbnail" class="file-preview post-image__column"
> >
<div <div
className="post-image normal" class="post-image__thumbnail"
style={
Object {
"backgroundImage": "url(/api/v4/files/file_id_1/thumbnail)",
"backgroundSize": "cover",
}
}
/>
</div>
<div
className="post-image__details"
>
<div
className="post-image__detail_wrapper"
> >
<div <div
className="post-image__detail" class="post-image normal"
style="background-image: url(\\"/api/v4/files/file_id_1/thumbnail\\"); background-size: cover;"
/>
</div>
<div
class="post-image__details"
>
<div
class="post-image__detail_wrapper"
> >
<FilenameOverlay <div
canDownload={false} class="post-image__detail"
compactDisplay={false}
fileInfo={
Object {
"archived": false,
"channel_id": "channel_id",
"clientId": "",
"create_at": 0,
"delete_at": 0,
"extension": "png",
"has_preview_image": true,
"height": 100,
"id": "file_id_1",
"mime_type": "",
"name": "test_filename",
"size": 100,
"type": "image/png",
"update_at": 0,
"user_id": "",
"width": 100,
}
}
/>
<span
className="post-image__type"
> >
PNG <span
</span> class="post-image__name"
<span >
className="post-image__size" test_filename
</span>
<span
class="post-image__type"
>
PNG
</span>
<span
class="post-image__size"
>
100B
</span>
</div>
</div>
<div>
<a
class="file-preview__remove"
> >
100B <i
</span> class="icon icon-close"
/>
</a>
</div> </div>
</div> </div>
<div> </div>
<a <div
className="file-preview__remove" class="file-preview post-image__column"
onClick={[Function]} data-client-id="clientID_1"
>
<div
class="post-image__thumbnail"
>
<div
class="file-icon generic"
/>
</div>
<div
class="post-image__details"
>
<div
class="post-image__detail_wrapper"
> >
<i <div
className="icon icon-close" class="post-image__detail"
>
<span
class="post-image__name"
>
file
</span>
<span
class="post-image__uploadingTxt"
>
Uploading...
<span>
(50%)
</span>
</span>
</div>
</div>
<div>
<a
class="file-preview__remove"
>
<i
class="icon icon-close"
/>
</a>
</div>
<div
class="post-image__progressBar progress"
>
<div
aria-valuemax="100"
aria-valuemin="0"
aria-valuenow="50"
class="progress-bar"
role="progressbar"
style="width: 50%;"
/> />
</a> </div>
</div> </div>
</div> </div>
</div> </div>
<FileProgressPreview
clientId="clientID_1"
fileInfo={
Object {
"archived": false,
"channel_id": "channel_id",
"clientId": "",
"create_at": 0,
"delete_at": 0,
"extension": "image/png",
"has_preview_image": true,
"height": 100,
"id": "file_id_1",
"mime_type": "",
"name": "file",
"percent": 50,
"size": 100,
"update_at": 0,
"user_id": "",
"width": 100,
}
}
handleRemove={[Function]}
key="clientID_1"
/>
</div> </div>
`; `;
exports[`FilePreview should match snapshot when props are changed 2`] = ` exports[`FilePreview should match snapshot when props are changed 2`] = `
<div <div>
className="file-preview__container"
>
<div <div
className="file-preview post-image__column" class="file-preview__container"
key="file_id_1"
> >
<div <div
className="post-image__thumbnail" class="file-preview post-image__column"
> >
<div <div
className="post-image normal" class="post-image__thumbnail"
style={
Object {
"backgroundImage": "url(/api/v4/files/file_id_1/thumbnail)",
"backgroundSize": "cover",
}
}
/>
</div>
<div
className="post-image__details"
>
<div
className="post-image__detail_wrapper"
> >
<div <div
className="post-image__detail" class="post-image normal"
> style="background-image: url(\\"/api/v4/files/file_id_1/thumbnail\\"); background-size: cover;"
<FilenameOverlay />
canDownload={false}
compactDisplay={false}
fileInfo={
Object {
"archived": false,
"channel_id": "channel_id",
"clientId": "",
"create_at": 0,
"delete_at": 0,
"extension": "png",
"has_preview_image": true,
"height": 100,
"id": "file_id_1",
"mime_type": "",
"name": "test_filename",
"size": 100,
"type": "image/png",
"update_at": 0,
"user_id": "",
"width": 100,
}
}
/>
<span
className="post-image__type"
>
PNG
</span>
<span
className="post-image__size"
>
100B
</span>
</div>
</div> </div>
<div>
<a
className="file-preview__remove"
onClick={[Function]}
>
<i
className="icon icon-close"
/>
</a>
</div>
</div>
</div>
<div
className="file-preview post-image__column"
key="file_id_2"
>
<div
className="post-image__thumbnail"
>
<div <div
className="post-image normal" class="post-image__details"
style={
Object {
"backgroundImage": "url(/api/v4/files/file_id_2/thumbnail)",
"backgroundSize": "cover",
}
}
/>
</div>
<div
className="post-image__details"
>
<div
className="post-image__detail_wrapper"
> >
<div <div
className="post-image__detail" class="post-image__detail_wrapper"
> >
<FilenameOverlay <div
canDownload={false} class="post-image__detail"
compactDisplay={false}
fileInfo={
Object {
"create_at": "2",
"extension": "jpg",
"height": 100,
"id": "file_id_2",
"width": 100,
}
}
/>
<span
className="post-image__type"
> >
JPG <span
</span> class="post-image__name"
<span >
className="post-image__size" test_filename
</span>
<span
class="post-image__type"
>
PNG
</span>
<span
class="post-image__size"
>
100B
</span>
</div>
</div>
<div>
<a
class="file-preview__remove"
> >
undefinedB <i
</span> class="icon icon-close"
/>
</a>
</div> </div>
</div> </div>
<div> </div>
<a <div
className="file-preview__remove" class="file-preview post-image__column"
onClick={[Function]} >
<div
class="post-image__thumbnail"
>
<div
class="post-image normal"
style="background-image: url(\\"/api/v4/files/file_id_2/thumbnail\\"); background-size: cover;"
/>
</div>
<div
class="post-image__details"
>
<div
class="post-image__detail_wrapper"
> >
<i <div
className="icon icon-close" class="post-image__detail"
/> >
</a> <span
class="post-image__name"
>
file_two.jpg
</span>
<span
class="post-image__type"
>
JPG
</span>
<span
class="post-image__size"
>
120B
</span>
</div>
</div>
<div>
<a
class="file-preview__remove"
>
<i
class="icon icon-close"
/>
</a>
</div>
</div> </div>
</div> </div>
</div> </div>

View file

@ -1,159 +1,114 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`component/file_preview/file_progress_preview should match snapshot 1`] = ` exports[`component/file_preview/file_progress_preview should match snapshot 1`] = `
<div <div>
className="file-preview post-image__column"
data-client-id="clientId"
key="clientId"
>
<div <div
className="post-image__thumbnail" class="file-preview post-image__column"
data-client-id="clientId"
> >
<div <div
className="file-icon image" class="post-image__thumbnail"
/>
</div>
<div
className="post-image__details"
>
<div
className="post-image__detail_wrapper"
> >
<div <div
className="post-image__detail" class="file-icon image"
/>
</div>
<div
class="post-image__details"
>
<div
class="post-image__detail_wrapper"
> >
<FilenameOverlay <div
canDownload={false} class="post-image__detail"
compactDisplay={false}
fileInfo={
Object {
"archived": false,
"channel_id": "channel_id",
"clientId": "",
"create_at": 0,
"delete_at": 0,
"extension": "png",
"has_preview_image": true,
"height": 80,
"id": "file",
"mime_type": "",
"name": "test_filename",
"percent": 50,
"size": 100,
"type": "image/png",
"update_at": 0,
"user_id": "",
"width": 100,
}
}
/>
<span
className="post-image__uploadingTxt"
> >
<MemoizedFormattedMessage <span
defaultMessage="Uploading..." class="post-image__name"
id="admin.plugin.uploading" >
/> test_filename
<span>
(50%)
</span> </span>
</span> <span
class="post-image__uploadingTxt"
>
Uploading...
<span>
(50%)
</span>
</span>
</div>
</div>
<div>
<a
class="file-preview__remove"
>
<i
class="icon icon-close"
/>
</a>
</div>
<div
class="post-image__progressBar progress"
>
<div
aria-valuemax="100"
aria-valuemin="0"
aria-valuenow="50"
class="progress-bar"
role="progressbar"
style="width: 50%;"
/>
</div> </div>
</div> </div>
<div>
<a
className="file-preview__remove"
onClick={[Function]}
>
<i
className="icon icon-close"
/>
</a>
</div>
<ProgressBar
active={false}
bsClass="progress-bar"
className="post-image__progressBar"
isChild={false}
max={100}
min={0}
now={50}
srOnly={false}
striped={false}
/>
</div> </div>
</div> </div>
`; `;
exports[`component/file_preview/file_progress_preview snapshot for percent value undefined 1`] = ` exports[`component/file_preview/file_progress_preview snapshot for percent value undefined 1`] = `
<div <div>
className="file-preview post-image__column"
data-client-id="clientId"
key="clientId"
>
<div <div
className="post-image__thumbnail" class="file-preview post-image__column"
data-client-id="clientId"
> >
<div <div
className="file-icon image" class="post-image__thumbnail"
/>
</div>
<div
className="post-image__details"
>
<div
className="post-image__detail_wrapper"
> >
<div <div
className="post-image__detail" class="file-icon image"
> />
<FilenameOverlay
canDownload={false}
compactDisplay={false}
fileInfo={
Object {
"archived": false,
"channel_id": "channel_id",
"clientId": "",
"create_at": 0,
"delete_at": 0,
"extension": "png",
"has_preview_image": true,
"height": 80,
"id": "file",
"mime_type": "",
"name": "test_filename",
"percent": undefined,
"size": 100,
"type": "image/png",
"update_at": 0,
"user_id": "",
"width": 100,
}
}
/>
<span
className="post-image__uploadingTxt"
>
<MemoizedFormattedMessage
defaultMessage="Uploading..."
id="admin.plugin.uploading"
/>
<span>
(0%)
</span>
</span>
</div>
</div> </div>
<div> <div
<a class="post-image__details"
className="file-preview__remove" >
onClick={[Function]} <div
class="post-image__detail_wrapper"
> >
<i <div
className="icon icon-close" class="post-image__detail"
/> >
</a> <span
class="post-image__name"
>
test_filename
</span>
<span
class="post-image__uploadingTxt"
>
Uploading...
<span>
(0%)
</span>
</span>
</div>
</div>
<div>
<a
class="file-preview__remove"
>
<i
class="icon icon-close"
/>
</a>
</div>
</div> </div>
</div> </div>
</div> </div>

View file

@ -1,11 +1,12 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react'; import React from 'react';
import {getFileUrl} from 'mattermost-redux/utils/file_utils'; import {getFileUrl} from 'mattermost-redux/utils/file_utils';
import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils';
import FilePreview from './file_preview'; import FilePreview from './file_preview';
describe('FilePreview', () => { describe('FilePreview', () => {
@ -62,45 +63,53 @@ describe('FilePreview', () => {
}; };
test('should match snapshot', () => { test('should match snapshot', () => {
const wrapper = shallow( const {container} = renderWithContext(
<FilePreview {...baseProps}/>, <FilePreview {...baseProps}/>,
); );
expect(wrapper).toMatchSnapshot(); expect(container).toMatchSnapshot();
}); });
test('should match snapshot when props are changed', () => { test('should match snapshot when props are changed', () => {
const wrapper = shallow( const {container, rerender} = renderWithContext(
<FilePreview {...baseProps}/>, <FilePreview {...baseProps}/>,
); );
expect(wrapper).toMatchSnapshot(); expect(container).toMatchSnapshot();
const fileInfo2 = { const fileInfo2 = {
...baseProps.fileInfos[0],
id: 'file_id_2', id: 'file_id_2',
create_at: '2', create_at: 2,
width: 100,
height: 100,
extension: 'jpg', extension: 'jpg',
name: 'file_two.jpg',
size: 120,
}; };
const newFileInfos = [...fileInfos, fileInfo2]; const newFileInfos = [...fileInfos, fileInfo2];
wrapper.setProps({ rerender(
fileInfos: newFileInfos, <FilePreview
uploadsInProgress: [], {...baseProps}
}); fileInfos={newFileInfos}
expect(wrapper).toMatchSnapshot(); uploadsInProgress={[]}
/>,
);
expect(container).toMatchSnapshot();
}); });
test('should call handleRemove when file removed', () => { test('should call handleRemove when file removed', async () => {
const newOnRemove = jest.fn(); const newOnRemove = jest.fn();
const props = {...baseProps, onRemove: newOnRemove}; const props = {...baseProps, onRemove: newOnRemove};
const wrapper = shallow<FilePreview>( const {container} = renderWithContext(
<FilePreview {...props}/>, <FilePreview {...props}/>,
); );
wrapper.instance().handleRemove(''); const user = userEvent.setup();
const removeLink = container.querySelector('a.file-preview__remove');
if (!removeLink) {
throw new Error('Remove link not found');
}
await user.click(removeLink);
expect(newOnRemove).toHaveBeenCalled(); expect(newOnRemove).toHaveBeenCalled();
}); });
test('should not render an SVG when SVGs are disabled', () => { test('should not render an SVG when SVGs are disabled', () => {
const fileId = 'file_id_1';
const props = { const props = {
...baseProps, ...baseProps,
fileInfos: [ fileInfos: [
@ -112,12 +121,12 @@ describe('FilePreview', () => {
], ],
}; };
const wrapper = shallow( const {container} = renderWithContext(
<FilePreview {...props}/>, <FilePreview {...props}/>,
); );
expect(wrapper.find('img').find({src: getFileUrl(fileId)}).exists()).toBe(false); expect(screen.queryByAltText('file preview')).not.toBeInTheDocument();
expect(wrapper.find('div').find('.file-icon.generic').exists()).toBe(true); expect(container.querySelector('.file-icon.generic')).toBeInTheDocument();
}); });
test('should render an SVG when SVGs are enabled', () => { test('should render an SVG when SVGs are enabled', () => {
@ -134,11 +143,10 @@ describe('FilePreview', () => {
], ],
}; };
const wrapper = shallow( renderWithContext(
<FilePreview {...props}/>, <FilePreview {...props}/>,
); );
expect(wrapper.find('img').find({src: getFileUrl(fileId)}).exists()).toBe(true); expect(screen.getByAltText('file preview')).toHaveAttribute('src', getFileUrl(fileId));
expect(wrapper.find('div').find('.file-icon.generic').exists()).toBe(false);
}); });
}); });

View file

@ -1,9 +1,10 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react'; import React from 'react';
import {renderWithContext} from 'tests/react_testing_utils';
import FileProgressPreview from './file_progress_preview'; import FileProgressPreview from './file_progress_preview';
describe('component/file_preview/file_progress_preview', () => { describe('component/file_preview/file_progress_preview', () => {
@ -36,10 +37,10 @@ describe('component/file_preview/file_progress_preview', () => {
}; };
test('should match snapshot', () => { test('should match snapshot', () => {
const wrapper = shallow( const {container} = renderWithContext(
<FileProgressPreview {...baseProps}/>, <FileProgressPreview {...baseProps}/>,
); );
expect(wrapper).toMatchSnapshot(); expect(container).toMatchSnapshot();
}); });
test('snapshot for percent value undefined', () => { test('snapshot for percent value undefined', () => {
@ -51,9 +52,9 @@ describe('component/file_preview/file_progress_preview', () => {
}, },
}; };
const wrapper = shallow( const {container} = renderWithContext(
<FileProgressPreview {...props}/>, <FileProgressPreview {...props}/>,
); );
expect(wrapper).toMatchSnapshot(); expect(container).toMatchSnapshot();
}); });
}); });

View file

@ -1,43 +1,51 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/view_image/ImagePreview should match snapshot, with preview 1`] = ` exports[`components/view_image/ImagePreview should match snapshot, with preview 1`] = `
<a <div>
className="image_preview" <a
href="#" class="image_preview"
> href="#"
<img >
alt="preview url image" <img
className="image_preview__image" alt="preview url image"
data-testid="imagePreview" class="image_preview__image"
loading="lazy" data-testid="imagePreview"
src="/api/v4/files/file_id_1/preview" loading="lazy"
/> src="/api/v4/files/file_id_1/preview"
</a> />
</a>
</div>
`; `;
exports[`components/view_image/ImagePreview should match snapshot, with preview, cannot download 1`] = ` exports[`components/view_image/ImagePreview should match snapshot, with preview, cannot download 1`] = `
<img <div>
src="/api/v4/files/file_id_1/preview" <img
/> src="/api/v4/files/file_id_1/preview"
/>
</div>
`; `;
exports[`components/view_image/ImagePreview should match snapshot, without preview 1`] = ` exports[`components/view_image/ImagePreview should match snapshot, without preview 1`] = `
<a <div>
className="image_preview" <a
href="#" class="image_preview"
> href="#"
<img >
alt="preview url image" <img
className="image_preview__image" alt="preview url image"
data-testid="imagePreview" class="image_preview__image"
loading="lazy" data-testid="imagePreview"
src="/api/v4/files/file_id?download=1" loading="lazy"
/> src="/api/v4/files/file_id?download=1"
</a> />
</a>
</div>
`; `;
exports[`components/view_image/ImagePreview should match snapshot, without preview, cannot download 1`] = ` exports[`components/view_image/ImagePreview should match snapshot, without preview, cannot download 1`] = `
<img <div>
src="/api/v4/files/file_id?download=1" <img
/> src="/api/v4/files/file_id?download=1"
/>
</div>
`; `;

View file

@ -1,16 +1,46 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react'; import React from 'react';
import FilePreviewModal from 'components/file_preview_modal/file_preview_modal'; import FilePreviewModal from 'components/file_preview_modal/file_preview_modal';
import {act, render} from 'tests/react_testing_utils';
import Constants from 'utils/constants'; import Constants from 'utils/constants';
import {TestHelper} from 'utils/test_helper'; import {TestHelper} from 'utils/test_helper';
import * as Utils from 'utils/utils'; import * as Utils from 'utils/utils';
import {generateId} from 'utils/utils'; import {generateId} from 'utils/utils';
jest.mock('react-bootstrap', () => {
const Modal = ({children, show}: {children: React.ReactNode; show: boolean}) => (show ? <div>{children}</div> : null);
Modal.Header = ({children}: {children: React.ReactNode}) => <div>{children}</div>;
Modal.Body = ({children}: {children: React.ReactNode}) => <div>{children}</div>;
Modal.Title = ({children}: {children: React.ReactNode}) => <div>{children}</div>;
return {Modal};
});
jest.mock('components/archived_preview', () => () => <div>{'Archived Preview'}</div>);
jest.mock('components/audio_video_preview', () => () => <div>{'Audio Video Preview'}</div>);
jest.mock('components/code_preview', () => ({
__esModule: true,
default: () => <div>{'Code Preview'}</div>,
hasSupportedLanguage: () => true,
}));
jest.mock('components/file_info_preview', () => () => <div>{'File Info Preview'}</div>);
jest.mock('components/loading_image_preview', () => () => <div>{'Loading Image Preview'}</div>);
jest.mock('components/pdf_preview', () => ({
__esModule: true,
default: () => <div>{'PDF Preview'}</div>,
}));
jest.mock('components/file_preview_modal/file_preview_modal_footer/file_preview_modal_footer', () => () => (
<div>{'File Preview Modal Footer'}</div>
));
jest.mock('components/file_preview_modal/file_preview_modal_header/file_preview_modal_header', () => () => (
<div>{'File Preview Modal Header'}</div>
));
jest.mock('components/file_preview_modal/image_preview', () => () => <div>{'Image Preview'}</div>);
jest.mock('components/file_preview_modal/popover_bar', () => () => <div>{'Popover Bar'}</div>);
describe('components/FilePreviewModal', () => { describe('components/FilePreviewModal', () => {
const baseProps = { const baseProps = {
fileInfos: [TestHelper.getFileInfoMock({id: 'file_id', extension: 'jpg'})], fileInfos: [TestHelper.getFileInfoMock({id: 'file_id', extension: 'jpg'})],
@ -22,53 +52,68 @@ describe('components/FilePreviewModal', () => {
onExited: jest.fn(), onExited: jest.fn(),
}; };
test('should match snapshot', () => { const renderModal = (props = baseProps) => {
const wrapper = shallow(<FilePreviewModal {...baseProps}/>); const ref = React.createRef<FilePreviewModal>();
const utils = render(
<FilePreviewModal
ref={ref}
{...props}
/>,
);
return {ref, ...utils};
};
expect(wrapper).toMatchSnapshot(); test('should match snapshot', () => {
const {container} = renderModal();
expect(container).toMatchSnapshot();
}); });
test('should match snapshot, loaded with image', () => { test('should match snapshot, loaded with image', () => {
const wrapper = shallow(<FilePreviewModal {...baseProps}/>); const {container, ref} = renderModal();
act(() => {
wrapper.setState({loaded: [true]}); ref.current?.setState({loaded: [true] as any});
expect(wrapper).toMatchSnapshot(); });
expect(container).toMatchSnapshot();
}); });
test('should match snapshot, loaded with .mov file', () => { test('should match snapshot, loaded with .mov file', () => {
const fileInfos = [TestHelper.getFileInfoMock({id: 'file_id', extension: 'mov'})]; const fileInfos = [TestHelper.getFileInfoMock({id: 'file_id', extension: 'mov'})];
const props = {...baseProps, fileInfos}; const props = {...baseProps, fileInfos};
const wrapper = shallow(<FilePreviewModal {...props}/>); const {container, ref} = renderModal(props);
act(() => {
wrapper.setState({loaded: [true]}); ref.current?.setState({loaded: [true] as any});
expect(wrapper).toMatchSnapshot(); });
expect(container).toMatchSnapshot();
}); });
test('should match snapshot, loaded with .m4a file', () => { test('should match snapshot, loaded with .m4a file', () => {
const fileInfos = [TestHelper.getFileInfoMock({id: 'file_id', extension: 'm4a'})]; const fileInfos = [TestHelper.getFileInfoMock({id: 'file_id', extension: 'm4a'})];
const props = {...baseProps, fileInfos}; const props = {...baseProps, fileInfos};
const wrapper = shallow(<FilePreviewModal {...props}/>); const {container, ref} = renderModal(props);
act(() => {
wrapper.setState({loaded: [true]}); ref.current?.setState({loaded: [true] as any});
expect(wrapper).toMatchSnapshot(); });
expect(container).toMatchSnapshot();
}); });
test('should match snapshot, loaded with .js file', () => { test('should match snapshot, loaded with .js file', () => {
const fileInfos = [TestHelper.getFileInfoMock({id: 'file_id', extension: 'js'})]; const fileInfos = [TestHelper.getFileInfoMock({id: 'file_id', extension: 'js'})];
const props = {...baseProps, fileInfos}; const props = {...baseProps, fileInfos};
const wrapper = shallow(<FilePreviewModal {...props}/>); const {container, ref} = renderModal(props);
act(() => {
wrapper.setState({loaded: [true]}); ref.current?.setState({loaded: [true] as any});
expect(wrapper).toMatchSnapshot(); });
expect(container).toMatchSnapshot();
}); });
test('should match snapshot, loaded with other file', () => { test('should match snapshot, loaded with other file', () => {
const fileInfos = [TestHelper.getFileInfoMock({id: 'file_id', extension: 'other'})]; const fileInfos = [TestHelper.getFileInfoMock({id: 'file_id', extension: 'other'})];
const props = {...baseProps, fileInfos}; const props = {...baseProps, fileInfos};
const wrapper = shallow(<FilePreviewModal {...props}/>); const {container, ref} = renderModal(props);
act(() => {
wrapper.setState({loaded: [true]}); ref.current?.setState({loaded: [true] as any});
expect(wrapper).toMatchSnapshot(); });
expect(container).toMatchSnapshot();
}); });
test('should match snapshot, loaded with footer', () => { test('should match snapshot, loaded with footer', () => {
@ -78,24 +123,27 @@ describe('components/FilePreviewModal', () => {
TestHelper.getFileInfoMock({id: 'file_id_3', extension: 'mp4'}), TestHelper.getFileInfoMock({id: 'file_id_3', extension: 'mp4'}),
]; ];
const props = {...baseProps, fileInfos}; const props = {...baseProps, fileInfos};
const wrapper = shallow(<FilePreviewModal {...props}/>); const {container, ref} = renderModal(props);
act(() => {
wrapper.setState({loaded: [true, true, true]}); ref.current?.setState({loaded: [true, true, true] as any});
expect(wrapper).toMatchSnapshot(); });
expect(container).toMatchSnapshot();
}); });
test('should match snapshot, loaded', () => { test('should match snapshot, loaded', () => {
const wrapper = shallow(<FilePreviewModal {...baseProps}/>); const {container, ref} = renderModal();
act(() => {
wrapper.setState({loaded: [true]}); ref.current?.setState({loaded: [true] as any});
expect(wrapper).toMatchSnapshot(); });
expect(container).toMatchSnapshot();
}); });
test('should match snapshot, loaded and showing footer', () => { test('should match snapshot, loaded and showing footer', () => {
const wrapper = shallow(<FilePreviewModal {...baseProps}/>); const {container, ref} = renderModal();
act(() => {
wrapper.setState({loaded: [true]}); ref.current?.setState({loaded: [true] as any});
expect(wrapper).toMatchSnapshot(); });
expect(container).toMatchSnapshot();
}); });
test('should go to next or previous upon key press of right or left, respectively', () => { test('should go to next or previous upon key press of right or left, respectively', () => {
@ -105,43 +153,59 @@ describe('components/FilePreviewModal', () => {
TestHelper.getFileInfoMock({id: 'file_id_3', extension: 'mp4'}), TestHelper.getFileInfoMock({id: 'file_id_3', extension: 'mp4'}),
]; ];
const props = {...baseProps, fileInfos}; const props = {...baseProps, fileInfos};
const wrapper = shallow<FilePreviewModal>(<FilePreviewModal {...props}/>); const {ref} = renderModal(props);
act(() => {
wrapper.setState({loaded: [true, true, true]}); ref.current?.setState({loaded: [true, true, true] as any});
});
let evt = {key: Constants.KeyCodes.RIGHT[0]} as KeyboardEvent; let evt = {key: Constants.KeyCodes.RIGHT[0]} as KeyboardEvent;
act(() => {
wrapper.instance().handleKeyPress(evt); ref.current?.handleKeyPress(evt);
expect(wrapper.state('imageIndex')).toBe(1); });
wrapper.instance().handleKeyPress(evt); expect(ref.current?.state.imageIndex).toBe(1);
expect(wrapper.state('imageIndex')).toBe(2); act(() => {
ref.current?.handleKeyPress(evt);
});
expect(ref.current?.state.imageIndex).toBe(2);
evt = {key: Constants.KeyCodes.LEFT[0]} as KeyboardEvent; evt = {key: Constants.KeyCodes.LEFT[0]} as KeyboardEvent;
wrapper.instance().handleKeyPress(evt); act(() => {
expect(wrapper.state('imageIndex')).toBe(1); ref.current?.handleKeyPress(evt);
wrapper.instance().handleKeyPress(evt); });
expect(wrapper.state('imageIndex')).toBe(0); expect(ref.current?.state.imageIndex).toBe(1);
act(() => {
ref.current?.handleKeyPress(evt);
});
expect(ref.current?.state.imageIndex).toBe(0);
}); });
test('should handle onMouseEnter and onMouseLeave', () => { test('should handle onMouseEnter and onMouseLeave', () => {
const wrapper = shallow<FilePreviewModal>(<FilePreviewModal {...baseProps}/>); const {ref} = renderModal();
wrapper.setState({loaded: [true]}); act(() => {
ref.current?.setState({loaded: [true] as any});
});
wrapper.instance().onMouseEnterImage(); act(() => {
expect(wrapper.state('showCloseBtn')).toBe(true); ref.current?.onMouseEnterImage();
});
expect(ref.current?.state.showCloseBtn).toBe(true);
wrapper.instance().onMouseLeaveImage(); act(() => {
expect(wrapper.state('showCloseBtn')).toBe(false); ref.current?.onMouseLeaveImage();
});
expect(ref.current?.state.showCloseBtn).toBe(false);
}); });
test('should handle on modal close', () => { test('should handle on modal close', () => {
const wrapper = shallow<FilePreviewModal>(<FilePreviewModal {...baseProps}/>); const {ref} = renderModal();
wrapper.setState({ act(() => {
loaded: [true], ref.current?.setState({loaded: [true] as any});
}); });
wrapper.instance().handleModalClose(); act(() => {
expect(wrapper.state('show')).toBe(false); ref.current?.handleModalClose();
});
expect(ref.current?.state.show).toBe(false);
}); });
test('should match snapshot for external file', () => { test('should match snapshot for external file', () => {
@ -149,24 +213,24 @@ describe('components/FilePreviewModal', () => {
TestHelper.getFileInfoMock({extension: 'png'}), TestHelper.getFileInfoMock({extension: 'png'}),
]; ];
const props = {...baseProps, fileInfos}; const props = {...baseProps, fileInfos};
const wrapper = shallow(<FilePreviewModal {...props}/>); const {container} = renderModal(props);
expect(wrapper).toMatchSnapshot(); expect(container).toMatchSnapshot();
}); });
test('should correctly identify image URLs with isImageUrl method', () => { test('should correctly identify image URLs with isImageUrl method', () => {
const wrapper = shallow<FilePreviewModal>(<FilePreviewModal {...baseProps}/>); const {ref} = renderModal();
// Test proxied image URLs // Test proxied image URLs
expect(wrapper.instance().isImageUrl('http://localhost:8065/api/v4/image?url=https%3A%2F%2Fexample.com%2Fimage.jpg')).toBe(true); expect(ref.current?.isImageUrl('http://localhost:8065/api/v4/image?url=https%3A%2F%2Fexample.com%2Fimage.jpg')).toBe(true);
// Test URLs with image extensions // Test URLs with image extensions
expect(wrapper.instance().isImageUrl('https://example.com/image.jpg')).toBe(true); expect(ref.current?.isImageUrl('https://example.com/image.jpg')).toBe(true);
expect(wrapper.instance().isImageUrl('https://example.com/image.png')).toBe(true); expect(ref.current?.isImageUrl('https://example.com/image.png')).toBe(true);
expect(wrapper.instance().isImageUrl('https://example.com/image.gif')).toBe(true); expect(ref.current?.isImageUrl('https://example.com/image.gif')).toBe(true);
// Test non-image URLs // Test non-image URLs
expect(wrapper.instance().isImageUrl('https://example.com/document.pdf')).toBe(false); expect(ref.current?.isImageUrl('https://example.com/document.pdf')).toBe(false);
expect(wrapper.instance().isImageUrl('https://example.com/file.txt')).toBe(false); expect(ref.current?.isImageUrl('https://example.com/file.txt')).toBe(false);
}); });
test('should handle external image URLs correctly', () => { test('should handle external image URLs correctly', () => {
@ -183,21 +247,24 @@ describe('components/FilePreviewModal', () => {
// Create a LinkInfo object for an external image URL // Create a LinkInfo object for an external image URL
const externalImageUrl = 'http://localhost:8065/api/v4/image?url=https%3A%2F%2Fexample.com%2Fimage.jpg'; const externalImageUrl = 'http://localhost:8065/api/v4/image?url=https%3A%2F%2Fexample.com%2Fimage.jpg';
const fileInfos = [{ const fileInfos = [
has_preview_image: false, TestHelper.getFileInfoMock({
link: externalImageUrl, id: '',
extension: '', has_preview_image: false,
name: 'External Image', link: externalImageUrl,
}]; extension: '',
name: 'External Image',
}),
];
const props = {...baseProps, fileInfos}; const props = {...baseProps, fileInfos};
const wrapper = shallow<FilePreviewModal>(<FilePreviewModal {...props}/>); const {ref} = renderModal(props);
// Spy on handleImageLoaded const handleImageLoadedSpy = jest.spyOn(ref.current as FilePreviewModal, 'handleImageLoaded');
const handleImageLoadedSpy = jest.spyOn(wrapper.instance(), 'handleImageLoaded');
// Call loadImage with the external image URL act(() => {
wrapper.instance().loadImage(0); ref.current?.loadImage(0);
});
// Verify that Utils.loadImage was called with the correct URL // Verify that Utils.loadImage was called with the correct URL
expect(loadImageSpy).toHaveBeenCalledWith( expect(loadImageSpy).toHaveBeenCalledWith(
@ -220,17 +287,21 @@ describe('components/FilePreviewModal', () => {
TestHelper.getFileInfoMock({id: 'file_id_3', extension: 'mp4'}), TestHelper.getFileInfoMock({id: 'file_id_3', extension: 'mp4'}),
]; ];
const props = {...baseProps, fileInfos}; const props = {...baseProps, fileInfos};
const wrapper = shallow<FilePreviewModal>(<FilePreviewModal {...props}/>); const {ref} = renderModal(props);
let index = 1; let index = 1;
wrapper.setState({loaded: [true, false, false]}); act(() => {
wrapper.instance().loadImage(index); ref.current?.setState({loaded: [true, false, false] as any});
ref.current?.loadImage(index);
});
expect(wrapper.state('loaded')[index]).toBe(true); expect(ref.current?.state.loaded[index]).toBe(true);
index = 2; index = 2;
wrapper.instance().loadImage(index); act(() => {
expect(wrapper.state('loaded')[index]).toBe(true); ref.current?.loadImage(index);
});
expect(ref.current?.state.loaded[index]).toBe(true);
}); });
test('should handle handleImageLoaded', () => { test('should handle handleImageLoaded', () => {
@ -240,17 +311,21 @@ describe('components/FilePreviewModal', () => {
TestHelper.getFileInfoMock({id: 'file_id_3', extension: 'mp4'}), TestHelper.getFileInfoMock({id: 'file_id_3', extension: 'mp4'}),
]; ];
const props = {...baseProps, fileInfos}; const props = {...baseProps, fileInfos};
const wrapper = shallow<FilePreviewModal>(<FilePreviewModal {...props}/>); const {ref} = renderModal(props);
let index = 1; let index = 1;
wrapper.setState({loaded: [true, false, false]}); act(() => {
wrapper.instance().handleImageLoaded(index); ref.current?.setState({loaded: [true, false, false] as any});
ref.current?.handleImageLoaded(index);
});
expect(wrapper.state('loaded')[index]).toBe(true); expect(ref.current?.state.loaded[index]).toBe(true);
index = 2; index = 2;
wrapper.instance().handleImageLoaded(index); act(() => {
expect(wrapper.state('loaded')[index]).toBe(true); ref.current?.handleImageLoaded(index);
});
expect(ref.current?.state.loaded[index]).toBe(true);
}); });
test('should handle handleImageProgress', () => { test('should handle handleImageProgress', () => {
@ -260,36 +335,46 @@ describe('components/FilePreviewModal', () => {
TestHelper.getFileInfoMock({id: 'file_id_3', extension: 'mp4'}), TestHelper.getFileInfoMock({id: 'file_id_3', extension: 'mp4'}),
]; ];
const props = {...baseProps, fileInfos}; const props = {...baseProps, fileInfos};
const wrapper = shallow<FilePreviewModal>(<FilePreviewModal {...props}/>); const {ref} = renderModal(props);
const index = 1; const index = 1;
let completedPercentage = 30; let completedPercentage = 30;
wrapper.setState({loaded: [true, false, false]}); act(() => {
wrapper.instance().handleImageProgress(index, completedPercentage); ref.current?.setState({loaded: [true, false, false] as any});
ref.current?.handleImageProgress(index, completedPercentage);
});
expect(wrapper.state('progress')[index]).toBe(completedPercentage); expect(ref.current?.state.progress[index]).toBe(completedPercentage);
completedPercentage = 70; completedPercentage = 70;
wrapper.instance().handleImageProgress(index, completedPercentage); act(() => {
ref.current?.handleImageProgress(index, completedPercentage);
});
expect(wrapper.state('progress')[index]).toBe(completedPercentage); expect(ref.current?.state.progress[index]).toBe(completedPercentage);
}); });
test('should pass componentWillReceiveProps', () => { test('should pass componentWillReceiveProps', () => {
const wrapper = shallow<FilePreviewModal>(<FilePreviewModal {...baseProps}/>); const {ref, rerender} = renderModal();
expect(Object.keys(wrapper.state('loaded')).length).toBe(1); expect(Object.keys(ref.current?.state.loaded || {})).toHaveLength(1);
expect(Object.keys(wrapper.state('progress')).length).toBe(1); expect(Object.keys(ref.current?.state.progress || {})).toHaveLength(1);
wrapper.setProps({ act(() => {
fileInfos: [ rerender(
TestHelper.getFileInfoMock({id: 'file_id_1', extension: 'gif'}), <FilePreviewModal
TestHelper.getFileInfoMock({id: 'file_id_2', extension: 'wma'}), {...baseProps}
TestHelper.getFileInfoMock({id: 'file_id_3', extension: 'mp4'}), ref={ref}
], fileInfos={[
TestHelper.getFileInfoMock({id: 'file_id_1', extension: 'gif'}),
TestHelper.getFileInfoMock({id: 'file_id_2', extension: 'wma'}),
TestHelper.getFileInfoMock({id: 'file_id_3', extension: 'mp4'}),
]}
/>,
);
}); });
expect(Object.keys(wrapper.state('loaded')).length).toBe(3); expect(Object.keys(ref.current?.state.loaded || {})).toHaveLength(3);
expect(Object.keys(wrapper.state('progress')).length).toBe(3); expect(Object.keys(ref.current?.state.progress || {})).toHaveLength(3);
}); });
test('should match snapshot when plugin overrides the preview component', () => { test('should match snapshot when plugin overrides the preview component', () => {
@ -300,9 +385,8 @@ describe('components/FilePreviewModal', () => {
component: () => <div>{'Preview'}</div>, component: () => <div>{'Preview'}</div>,
}]; }];
const props = {...baseProps, pluginFilePreviewComponents}; const props = {...baseProps, pluginFilePreviewComponents};
const wrapper = shallow(<FilePreviewModal {...props}/>); const {container} = renderModal(props);
expect(container).toMatchSnapshot();
expect(wrapper).toMatchSnapshot();
}); });
test('should fall back to default preview if plugin does not need to override preview component', () => { test('should fall back to default preview if plugin does not need to override preview component', () => {
@ -313,8 +397,7 @@ describe('components/FilePreviewModal', () => {
component: () => <div>{'Preview'}</div>, component: () => <div>{'Preview'}</div>,
}]; }];
const props = {...baseProps, pluginFilePreviewComponents}; const props = {...baseProps, pluginFilePreviewComponents};
const wrapper = shallow(<FilePreviewModal {...props}/>); const {container} = renderModal(props);
expect(container).toMatchSnapshot();
expect(wrapper).toMatchSnapshot();
}); });
}); });

View file

@ -1,149 +1,31 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/file_preview_modal/file_preview_modal_footer/FilePreviewModalFooter should match snapshot the desktop view 1`] = ` exports[`components/file_preview_modal/file_preview_modal_footer/FilePreviewModalFooter should match snapshot the desktop view 1`] = `
<div <div>
className="file-preview-modal-footer" <div
> class="file-preview-modal-footer"
<Memo(FilePreviewModalInfo) >
filename="img.png" <div>
post={ FilePreviewModalInfo
Object { </div>
"channel_id": "", <div>
"create_at": 0, FilePreviewModalMainActions
"delete_at": 0, </div>
"edit_at": 0, </div>
"hashtags": "",
"id": "id",
"is_pinned": false,
"message": "post message",
"metadata": Object {
"embeds": Array [],
"emojis": Array [],
"files": Array [],
"images": Object {},
"reactions": Array [],
},
"original_id": "",
"pending_post_id": "",
"props": Object {},
"reply_count": 0,
"root_id": "",
"type": "system_add_remove",
"update_at": 0,
"user_id": "user_id",
}
}
showFileName={false}
/>
<Memo(FilePreviewModalMainActions)
canCopyContent={false}
canDownloadFiles={true}
content=""
enablePublicLink={false}
fileIndex={1}
fileInfo={
Object {
"archived": false,
"channel_id": "channel_id",
"clientId": "client_id",
"create_at": 1,
"delete_at": 1,
"extension": "jpg",
"has_preview_image": true,
"height": 200,
"id": "file_info_id",
"mime_type": "mime_type",
"name": "name",
"size": 1,
"update_at": 1,
"user_id": "user_id",
"width": 350,
}
}
fileURL="https://example.com/img.png"
filename="img.png"
handleModalClose={[MockFunction]}
isExternalFile={false}
isMobile={false}
onGetPublicLink={[MockFunction]}
showClose={false}
showOnlyClose={false}
showPublicLink={false}
totalFiles={3}
/>
</div> </div>
`; `;
exports[`components/file_preview_modal/file_preview_modal_footer/FilePreviewModalFooter should match snapshot the mobile view 1`] = ` exports[`components/file_preview_modal/file_preview_modal_footer/FilePreviewModalFooter should match snapshot the mobile view 1`] = `
<div <div>
className="file-preview-modal-footer" <div
> class="file-preview-modal-footer"
<Memo(FilePreviewModalInfo) >
filename="img.png" <div>
post={ FilePreviewModalInfo
Object { </div>
"channel_id": "", <div>
"create_at": 0, FilePreviewModalMainActions
"delete_at": 0, </div>
"edit_at": 0, </div>
"hashtags": "",
"id": "id",
"is_pinned": false,
"message": "post message",
"metadata": Object {
"embeds": Array [],
"emojis": Array [],
"files": Array [],
"images": Object {},
"reactions": Array [],
},
"original_id": "",
"pending_post_id": "",
"props": Object {},
"reply_count": 0,
"root_id": "",
"type": "system_add_remove",
"update_at": 0,
"user_id": "user_id",
}
}
showFileName={false}
/>
<Memo(FilePreviewModalMainActions)
canCopyContent={false}
canDownloadFiles={true}
content=""
enablePublicLink={false}
fileIndex={1}
fileInfo={
Object {
"archived": false,
"channel_id": "channel_id",
"clientId": "client_id",
"create_at": 1,
"delete_at": 1,
"extension": "jpg",
"has_preview_image": true,
"height": 200,
"id": "file_info_id",
"mime_type": "mime_type",
"name": "name",
"size": 1,
"update_at": 1,
"user_id": "user_id",
"width": 350,
}
}
fileURL="https://example.com/img.png"
filename="img.png"
handleModalClose={[MockFunction]}
isExternalFile={false}
isMobile={true}
onGetPublicLink={[MockFunction]}
showClose={false}
showOnlyClose={false}
showPublicLink={false}
totalFiles={3}
/>
</div> </div>
`; `;

View file

@ -1,13 +1,18 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react'; import React from 'react';
import {renderWithContext} from 'tests/react_testing_utils';
import {TestHelper} from 'utils/test_helper'; import {TestHelper} from 'utils/test_helper';
import FilePreviewModalFooter from './file_preview_modal_footer'; import FilePreviewModalFooter from './file_preview_modal_footer';
jest.mock('../file_preview_modal_info/file_preview_modal_info', () => () => <div>{'FilePreviewModalInfo'}</div>);
jest.mock('../file_preview_modal_main_actions/file_preview_modal_main_actions', () => () => (
<div>{'FilePreviewModalMainActions'}</div>
));
describe('components/file_preview_modal/file_preview_modal_footer/FilePreviewModalFooter', () => { describe('components/file_preview_modal/file_preview_modal_footer/FilePreviewModalFooter', () => {
const defaultProps = { const defaultProps = {
enablePublicLink: false, enablePublicLink: false,
@ -32,8 +37,8 @@ describe('components/file_preview_modal/file_preview_modal_footer/FilePreviewMod
...defaultProps, ...defaultProps,
}; };
const wrapper = shallow(<FilePreviewModalFooter {...props}/>); const {container} = renderWithContext(<FilePreviewModalFooter {...props}/>);
expect(wrapper).toMatchSnapshot(); expect(container).toMatchSnapshot();
}); });
test('should match snapshot the mobile view', () => { test('should match snapshot the mobile view', () => {
@ -42,7 +47,7 @@ describe('components/file_preview_modal/file_preview_modal_footer/FilePreviewMod
isMobile: true, isMobile: true,
}; };
const wrapper = shallow(<FilePreviewModalFooter {...props}/>); const {container} = renderWithContext(<FilePreviewModalFooter {...props}/>);
expect(wrapper).toMatchSnapshot(); expect(container).toMatchSnapshot();
}); });
}); });

View file

@ -1,102 +1,148 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/file_preview_modal/file_preview_modal_header/FilePreviewModalHeader should match snapshot the desktop view 1`] = ` exports[`components/file_preview_modal/file_preview_modal_header/FilePreviewModalHeader should match snapshot the desktop view 1`] = `
<div <div>
className="file-preview-modal-header" <div
> class="file-preview-modal-header"
<Memo(FilePreviewModalInfo) >
filename="img.png" <div
post={Object {}} class="file-preview-modal__info"
showFileName={true} >
/> <div
<Memo(FilePreviewModalMainNav) class="file-preview-modal__info-details"
fileIndex={1} >
handleNext={[MockFunction]} <h5
handlePrev={[MockFunction]} class="file-preview-modal__file-name"
totalFiles={3} >
/> img.png
<Memo(FilePreviewModalMainActions) </h5>
canCopyContent={true} <span
canDownloadFiles={true} class="file-preview-modal__file-details"
content="" >
enablePublicLink={false} <span
fileInfo={ class="file-preview-modal__file-details-user-name"
Object { >
"archived": false, Someone
"channel_id": "channel_id", </span>
"clientId": "client_id", <span
"create_at": 1, class="file-preview-modal__channel"
"delete_at": 1, />
"extension": "jpg", </span>
"has_preview_image": true, </div>
"height": 200, </div>
"id": "file_info_id", <div
"mime_type": "mime_type", class="file_preview_modal_main_nav"
"name": "name", >
"size": 1, <button
"update_at": 1, aria-label="Previous file"
"user_id": "user_id", class="file_preview_modal_main_nav__prev"
"width": 350, id="previewArrowLeft"
} >
} <i
fileURL="http://example.com/img.png" class="icon icon-chevron-left"
filename="img.png" />
handleModalClose={[MockFunction]} </button>
handleNext={[MockFunction]} <span
handlePrev={[MockFunction]} aria-atomic="true"
isExternalFile={false} aria-live="polite"
isMobileView={false} class="modal-bar-file-count"
onGetPublicLink={[MockFunction]} >
showOnlyClose={false} 2 of 3
showPublicLink={false} </span>
/> <button
aria-label="Next file"
class="file_preview_modal_main_nav__next"
id="previewArrowRight"
>
<i
class="icon icon-chevron-right"
/>
</button>
</div>
<div
class="file-preview-modal-main-actions__actions"
>
<span
aria-label="Copy code"
class="post-code__clipboard file-preview-modal-main-actions__action-item"
role="button"
>
<i
class="icon icon-content-copy"
/>
</span>
<a
aria-label="Download"
class="file-preview-modal-main-actions__action-item"
download="img.png"
href="http://example.com/img.png"
location="file_preview_modal_main_actions"
rel="noopener noreferrer"
target="_blank"
>
<i
class="icon icon-download-outline"
/>
</a>
<button
aria-label="Close"
class="file-preview-modal-main-actions__action-item"
>
<i
class="icon icon-close"
/>
</button>
</div>
</div>
</div> </div>
`; `;
exports[`components/file_preview_modal/file_preview_modal_header/FilePreviewModalHeader should match snapshot the mobile view 1`] = ` exports[`components/file_preview_modal/file_preview_modal_header/FilePreviewModalHeader should match snapshot the mobile view 1`] = `
<div <div>
className="file-preview-modal-header" <div
> class="file-preview-modal-header"
<Memo(FilePreviewModalMainActions) >
canCopyContent={true} <div
canDownloadFiles={true} class="file-preview-modal-main-actions__actions"
content="" >
enablePublicLink={false} <button
fileInfo={ aria-label="Close"
Object { class="file-preview-modal-main-actions__action-item"
"archived": false, >
"channel_id": "channel_id", <i
"clientId": "client_id", class="icon icon-close"
"create_at": 1, />
"delete_at": 1, </button>
"extension": "jpg", </div>
"has_preview_image": true, <div
"height": 200, class="file_preview_modal_main_nav"
"id": "file_info_id", >
"mime_type": "mime_type", <button
"name": "name", aria-label="Previous file"
"size": 1, class="file_preview_modal_main_nav__prev"
"update_at": 1, id="previewArrowLeft"
"user_id": "user_id", >
"width": 350, <i
} class="icon icon-chevron-left"
} />
fileURL="http://example.com/img.png" </button>
filename="img.png" <span
handleModalClose={[MockFunction]} aria-atomic="true"
handleNext={[MockFunction]} aria-live="polite"
handlePrev={[MockFunction]} class="modal-bar-file-count"
isExternalFile={false} >
isMobileView={true} 2 of 3
onGetPublicLink={[MockFunction]} </span>
showOnlyClose={true} <button
showPublicLink={false} aria-label="Next file"
/> class="file_preview_modal_main_nav__next"
<Memo(FilePreviewModalMainNav) id="previewArrowRight"
fileIndex={1} >
handleNext={[MockFunction]} <i
handlePrev={[MockFunction]} class="icon icon-chevron-right"
totalFiles={3} />
/> </button>
</div>
</div>
</div> </div>
`; `;

View file

@ -1,11 +1,11 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react'; import React from 'react';
import type {Post} from '@mattermost/types/posts'; import type {Post} from '@mattermost/types/posts';
import {renderWithContext} from 'tests/react_testing_utils';
import {TestHelper} from 'utils/test_helper'; import {TestHelper} from 'utils/test_helper';
import FilePreviewModalHeader from './file_preview_modal_header'; import FilePreviewModalHeader from './file_preview_modal_header';
@ -36,8 +36,8 @@ describe('components/file_preview_modal/file_preview_modal_header/FilePreviewMod
...defaultProps, ...defaultProps,
}; };
const wrapper = shallow(<FilePreviewModalHeader {...props}/>); const {container} = renderWithContext(<FilePreviewModalHeader {...props}/>);
expect(wrapper).toMatchSnapshot(); expect(container).toMatchSnapshot();
}); });
test('should match snapshot the mobile view', () => { test('should match snapshot the mobile view', () => {
@ -46,7 +46,7 @@ describe('components/file_preview_modal/file_preview_modal_header/FilePreviewMod
isMobileView: true, isMobileView: true,
}; };
const wrapper = shallow(<FilePreviewModalHeader {...props}/>); const {container} = renderWithContext(<FilePreviewModalHeader {...props}/>);
expect(wrapper).toMatchSnapshot(); expect(container).toMatchSnapshot();
}); });
}); });

View file

@ -1,56 +1,51 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/FilePreviewModalInfo should match snapshot 1`] = ` exports[`components/FilePreviewModalInfo should match snapshot 1`] = `
<div <div>
className="file-preview-modal__info"
>
<Memo(Avatar)
alt=""
className="file-preview-modal__avatar"
size="lg"
url="/api/v4/users/user_id/image?_=0"
username="some-user"
/>
<div <div
className="file-preview-modal__info-details" class="file-preview-modal__info"
> >
<h5 <img
className="file-preview-modal__user-name" alt=""
class="Avatar Avatar-lg file-preview-modal__avatar"
loading="lazy"
src="/api/v4/users/user_id/image?_=0"
/>
<div
class="file-preview-modal__info-details"
> >
some-user <h5
</h5> class="file-preview-modal__user-name"
<span >
className="file-preview-modal__channel" some-user
> </h5>
<MemoizedFormattedMessage <span
defaultMessage="Shared in ~{name}" class="file-preview-modal__channel"
id="file_preview_modal_info.shared_in" >
values={ Shared in ~name
Object { </span>
"name": "name", </div>
}
}
/>
</span>
</div> </div>
</div> </div>
`; `;
exports[`components/FilePreviewModalInfo should match snapshot where post is missing and avoid crash 1`] = ` exports[`components/FilePreviewModalInfo should match snapshot where post is missing and avoid crash 1`] = `
<div <div>
className="file-preview-modal__info"
>
<div <div
className="file-preview-modal__info-details" class="file-preview-modal__info"
> >
<h5 <div
className="file-preview-modal__user-name" class="file-preview-modal__info-details"
> >
Someone <h5
</h5> class="file-preview-modal__user-name"
<span >
className="file-preview-modal__channel" Someone
/> </h5>
<span
class="file-preview-modal__channel"
/>
</div>
</div> </div>
</div> </div>
`; `;

View file

@ -1,10 +1,10 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react'; import React from 'react';
import type {ComponentProps} from 'react'; import type {ComponentProps} from 'react';
import {renderWithContext} from 'tests/react_testing_utils';
import {TestHelper} from 'utils/test_helper'; import {TestHelper} from 'utils/test_helper';
import type {GlobalState} from 'types/store'; import type {GlobalState} from 'types/store';
@ -45,23 +45,23 @@ describe('components/FilePreviewModalInfo', () => {
test('should match snapshot', () => { test('should match snapshot', () => {
mockState.entities.users.profiles = {user_id: mockedUser}; mockState.entities.users.profiles = {user_id: mockedUser};
mockState.entities.channels.channels = {channel_id: mockedChannel}; mockState.entities.channels.channels = {channel_id: mockedChannel};
const wrapper = shallow<typeof FilePreviewModalInfo>( const {container} = renderWithContext(
<FilePreviewModalInfo <FilePreviewModalInfo
{...baseProps} {...baseProps}
/>, />,
); );
expect(wrapper).toMatchSnapshot(); expect(container).toMatchSnapshot();
}); });
test('should match snapshot where post is missing and avoid crash', () => { test('should match snapshot where post is missing and avoid crash', () => {
mockState.entities.users.profiles = {user_id: mockedUser}; mockState.entities.users.profiles = {user_id: mockedUser};
mockState.entities.channels.channels = {channel_id: mockedChannel}; mockState.entities.channels.channels = {channel_id: mockedChannel};
baseProps.post = undefined; baseProps.post = undefined;
const wrapper = shallow<typeof FilePreviewModalInfo>( const {container} = renderWithContext(
<FilePreviewModalInfo <FilePreviewModalInfo
{...baseProps} {...baseProps}
/>, />,
); );
expect(wrapper).toMatchSnapshot(); expect(container).toMatchSnapshot();
}); });
}); });

View file

@ -1,64 +1,35 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/file_preview_modal/file_preview_modal_main_nav/FilePreviewModalMainNav should match snapshot with multiple files 1`] = ` exports[`components/file_preview_modal/file_preview_modal_main_nav/FilePreviewModalMainNav should match snapshot with multiple files 1`] = `
<div <div>
className="file_preview_modal_main_nav" <div
> class="file_preview_modal_main_nav"
<WithTooltip
key="previewArrowLeft"
title={
<Memo(MemoizedFormattedMessage)
defaultMessage="Close"
id="generic.close"
/>
}
> >
<button <button
aria-label="Previous file" aria-label="Previous file"
className="file_preview_modal_main_nav__prev" class="file_preview_modal_main_nav__prev"
id="previewArrowLeft" id="previewArrowLeft"
onClick={[MockFunction]}
> >
<i <i
className="icon icon-chevron-left" class="icon icon-chevron-left"
/> />
</button> </button>
</WithTooltip> <span
<span aria-atomic="true"
aria-atomic="true" aria-live="polite"
aria-live="polite" class="modal-bar-file-count"
className="modal-bar-file-count" >
> 2 of 2
<MemoizedFormattedMessage </span>
defaultMessage="{count, number} of {total, number}"
id="file_preview_modal_main_nav.file"
values={
Object {
"count": 2,
"total": 2,
}
}
/>
</span>
<WithTooltip
key="publicLink"
title={
<Memo(MemoizedFormattedMessage)
defaultMessage="Next"
id="generic.next"
/>
}
>
<button <button
aria-label="Next file" aria-label="Next file"
className="file_preview_modal_main_nav__next" class="file_preview_modal_main_nav__next"
id="previewArrowRight" id="previewArrowRight"
onClick={[MockFunction]}
> >
<i <i
className="icon icon-chevron-right" class="icon icon-chevron-right"
/> />
</button> </button>
</WithTooltip> </div>
</div> </div>
`; `;

View file

@ -1,9 +1,10 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react'; import React from 'react';
import {renderWithContext} from 'tests/react_testing_utils';
import FilePreviewModalMainNav from './file_preview_modal_main_nav'; import FilePreviewModalMainNav from './file_preview_modal_main_nav';
describe('components/file_preview_modal/file_preview_modal_main_nav/FilePreviewModalMainNav', () => { describe('components/file_preview_modal/file_preview_modal_main_nav/FilePreviewModalMainNav', () => {
@ -20,7 +21,7 @@ describe('components/file_preview_modal/file_preview_modal_main_nav/FilePreviewM
enablePublicLink: false, enablePublicLink: false,
}; };
const wrapper = shallow(<FilePreviewModalMainNav {...props}/>); const {container} = renderWithContext(<FilePreviewModalMainNav {...props}/>);
expect(wrapper).toMatchSnapshot(); expect(container).toMatchSnapshot();
}); });
}); });

View file

@ -1,11 +1,11 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react'; import React from 'react';
import ImagePreview from 'components/file_preview_modal/image_preview'; import ImagePreview from 'components/file_preview_modal/image_preview';
import {render, screen} from 'tests/react_testing_utils';
import {TestHelper} from 'utils/test_helper'; import {TestHelper} from 'utils/test_helper';
describe('components/view_image/ImagePreview', () => { describe('components/view_image/ImagePreview', () => {
@ -16,11 +16,11 @@ describe('components/view_image/ImagePreview', () => {
}; };
test('should match snapshot, without preview', () => { test('should match snapshot, without preview', () => {
const wrapper = shallow( const {container} = render(
<ImagePreview {...baseProps}/>, <ImagePreview {...baseProps}/>,
); );
expect(wrapper).toMatchSnapshot(); expect(container).toMatchSnapshot();
}); });
test('should match snapshot, with preview', () => { test('should match snapshot, with preview', () => {
@ -33,11 +33,11 @@ describe('components/view_image/ImagePreview', () => {
}, },
}; };
const wrapper = shallow( const {container} = render(
<ImagePreview {...props}/>, <ImagePreview {...props}/>,
); );
expect(wrapper).toMatchSnapshot(); expect(container).toMatchSnapshot();
}); });
test('should match snapshot, without preview, cannot download', () => { test('should match snapshot, without preview, cannot download', () => {
@ -46,11 +46,11 @@ describe('components/view_image/ImagePreview', () => {
canDownloadFiles: false, canDownloadFiles: false,
}; };
const wrapper = shallow( const {container} = render(
<ImagePreview {...props}/>, <ImagePreview {...props}/>,
); );
expect(wrapper).toMatchSnapshot(); expect(container).toMatchSnapshot();
}); });
test('should match snapshot, with preview, cannot download', () => { test('should match snapshot, with preview, cannot download', () => {
@ -64,11 +64,11 @@ describe('components/view_image/ImagePreview', () => {
}, },
}; };
const wrapper = shallow( const {container} = render(
<ImagePreview {...props}/>, <ImagePreview {...props}/>,
); );
expect(wrapper).toMatchSnapshot(); expect(container).toMatchSnapshot();
}); });
test('should not download link for external file', () => { test('should not download link for external file', () => {
@ -82,11 +82,11 @@ describe('components/view_image/ImagePreview', () => {
}, },
}; };
const wrapper = shallow( render(
<ImagePreview {...props}/>, <ImagePreview {...props}/>,
); );
expect(wrapper.find('a').prop('href')).toBe('#'); expect(screen.getByRole('link')).toHaveAttribute('href', '#');
expect(wrapper.find('img').prop('src')).toBe(props.fileInfo.link); expect(screen.getByTestId('imagePreview')).toHaveAttribute('src', props.fileInfo.link);
}); });
}); });

View file

@ -1,64 +1,36 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/file_preview_modal/popover_bar/PopoverBar should match snapshot with zoom controls enabled 1`] = ` exports[`components/file_preview_modal/popover_bar/PopoverBar should match snapshot with zoom controls enabled 1`] = `
<div <div>
className="modal-button-bar file-preview-modal__zoom-bar"
data-testid="fileCountFooter"
>
<div <div
className="modal-column" class="modal-button-bar file-preview-modal__zoom-bar"
data-testid="fileCountFooter"
> >
<WithTooltip <div
key="zoomOut" class="modal-column"
title={
<Memo(MemoizedFormattedMessage)
defaultMessage="Zoom Out"
id="view_image.zoom_out"
/>
}
> >
<span <span
className="btn-inactive" class="btn-inactive"
> >
<i <i
className="icon icon-minus" class="icon icon-minus"
/> />
</span> </span>
</WithTooltip>
<WithTooltip
key="zoomReset"
title={
<Memo(MemoizedFormattedMessage)
defaultMessage="Reset Zoom"
id="view_image.zoom_reset"
/>
}
>
<span <span
className="btn-inactive" class="btn-inactive"
> >
<i <i
className="icon icon-magnify-minus" class="icon icon-magnify-minus"
/> />
</span> </span>
</WithTooltip>
<WithTooltip
key="zoomIn"
title={
<Memo(MemoizedFormattedMessage)
defaultMessage="Zoom In"
id="view_image.zoom_in"
/>
}
>
<span <span
className="btn-inactive" class="btn-inactive"
> >
<i <i
className="icon icon-plus" class="icon icon-plus"
/> />
</span> </span>
</WithTooltip> </div>
</div> </div>
</div> </div>
`; `;

View file

@ -1,11 +1,12 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react'; import React from 'react';
import PopoverBar from 'components/file_preview_modal/popover_bar/popover_bar'; import PopoverBar from 'components/file_preview_modal/popover_bar/popover_bar';
import {render} from 'tests/react_testing_utils';
describe('components/file_preview_modal/popover_bar/PopoverBar', () => { describe('components/file_preview_modal/popover_bar/PopoverBar', () => {
const defaultProps = { const defaultProps = {
showZoomControls: false, showZoomControls: false,
@ -17,7 +18,7 @@ describe('components/file_preview_modal/popover_bar/PopoverBar', () => {
showZoomControls: true, showZoomControls: true,
}; };
const wrapper = shallow(<PopoverBar {...props}/>); const {container} = render(<PopoverBar {...props}/>);
expect(wrapper).toMatchSnapshot(); expect(container).toMatchSnapshot();
}); });
}); });

View file

@ -1,272 +1,150 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/post_view/PostAddChannelMember should match snapshot, empty channelType 1`] = `""`; exports[`components/post_view/PostAddChannelMember should match snapshot, empty channelType 1`] = `<div />`;
exports[`components/post_view/PostAddChannelMember should match snapshot, empty postId 1`] = `""`; exports[`components/post_view/PostAddChannelMember should match snapshot, empty postId 1`] = `<div />`;
exports[`components/post_view/PostAddChannelMember should match snapshot, more than 3 users 1`] = ` exports[`components/post_view/PostAddChannelMember should match snapshot, more than 3 users 1`] = `
<Fragment> <div>
<p <p>
key="invitable"
>
<span> <span>
<Connect(Component) <span>
channelId="channel_id" @username_1
key="username_1"
mentionName="username_1"
/>
<span
key="1"
>
,
</span> </span>
<a <a
className="PostBody_otherUsersLink" class="PostBody_otherUsersLink"
onClick={[Function]}
> >
<MemoizedFormattedMessage 2 others
defaultMessage="{numOthers} others"
id="post_body.check_for_out_of_channel_mentions.others"
values={
Object {
"numOthers": 2,
}
}
/>
</a> </a>
<MemoizedFormattedMessage <span>
defaultMessage=" and " @username_4
id="post_body.check_for_out_of_channel_mentions.link.and" </span>
key="1"
/>
<Connect(Component)
channelId="channel_id"
key="username_4"
mentionName="username_4"
/>
</span> </span>
<MemoizedFormattedMessage did not get notified by this mention because they are not in the channel. Would you like to
defaultMessage="did not get notified by this mention because they are not in the channel. Would you like to "
id="post_body.check_for_out_of_channel_mentions.message.multiple"
/>
<a <a
className="PostBody_addChannelMemberLink" class="PostBody_addChannelMemberLink"
onClick={[Function]}
> >
<MemoizedFormattedMessage add them to the channel
defaultMessage="add them to the channel"
id="post_body.check_for_out_of_channel_mentions.link.public"
/>
</a> </a>
<MemoizedFormattedMessage ? They will have access to all message history.
defaultMessage="? They will have access to all message history."
id="post_body.check_for_out_of_channel_mentions.message_last"
/>
</p> </p>
</Fragment> </div>
`; `;
exports[`components/post_view/PostAddChannelMember should match snapshot, more than 3 users 2`] = ` exports[`components/post_view/PostAddChannelMember should match snapshot, more than 3 users 2`] = `
<Fragment> <div>
<p <p>
key="invitable"
>
<span> <span>
<Connect(Component) <span>
channelId="channel_id" @username_1
key="username_1"
mentionName="username_1"
/>
<span
key="1"
>
,
</span> </span>
<Connect(Component) <a
channelId="channel_id" class="PostBody_otherUsersLink"
key="username_2"
mentionName="username_2"
/>
<span
key="2"
> >
, 2 others
</a>
<span>
@username_4
</span> </span>
<Connect(Component)
channelId="channel_id"
key="username_3"
mentionName="username_3"
/>
<MemoizedFormattedMessage
defaultMessage=" and "
id="post_body.check_for_out_of_channel_mentions.link.and"
key="3"
/>
<Connect(Component)
channelId="channel_id"
key="username_4"
mentionName="username_4"
/>
</span> </span>
<MemoizedFormattedMessage did not get notified by this mention because they are not in the channel. Would you like to
defaultMessage="did not get notified by this mention because they are not in the channel. Would you like to "
id="post_body.check_for_out_of_channel_mentions.message.multiple"
/>
<a <a
className="PostBody_addChannelMemberLink" class="PostBody_addChannelMemberLink"
onClick={[Function]}
> >
<MemoizedFormattedMessage add them to the channel
defaultMessage="add them to the channel"
id="post_body.check_for_out_of_channel_mentions.link.public"
/>
</a> </a>
<MemoizedFormattedMessage ? They will have access to all message history.
defaultMessage="? They will have access to all message history."
id="post_body.check_for_out_of_channel_mentions.message_last"
/>
</p> </p>
</Fragment> </div>
`; `;
exports[`components/post_view/PostAddChannelMember should match snapshot, private channel 1`] = ` exports[`components/post_view/PostAddChannelMember should match snapshot, private channel 1`] = `
<Fragment> <div>
<p <p>
key="invitable" <span>
> <span>
<Connect(Component) @username_1
channelId="channel_id" </span>
mentionName="username_1" </span>
/>
<MemoizedFormattedMessage did not get notified by this mention because they are not in the channel. Would you like to
defaultMessage="did not get notified by this mention because they are not in the channel. Would you like to "
id="post_body.check_for_out_of_channel_mentions.message.one"
/>
<a <a
className="PostBody_addChannelMemberLink" class="PostBody_addChannelMemberLink"
onClick={[Function]}
> >
<MemoizedFormattedMessage add them to this private channel
defaultMessage="add them to this private channel"
id="post_body.check_for_out_of_channel_mentions.link.private"
/>
</a> </a>
<MemoizedFormattedMessage ? They will have access to all message history.
defaultMessage="? They will have access to all message history."
id="post_body.check_for_out_of_channel_mentions.message_last"
/>
</p> </p>
</Fragment> </div>
`; `;
exports[`components/post_view/PostAddChannelMember should match snapshot, public channel 1`] = ` exports[`components/post_view/PostAddChannelMember should match snapshot, public channel 1`] = `
<Fragment> <div>
<p <p>
key="invitable" <span>
> <span>
<Connect(Component) @username_1
channelId="channel_id" </span>
mentionName="username_1" </span>
/>
<MemoizedFormattedMessage did not get notified by this mention because they are not in the channel. Would you like to
defaultMessage="did not get notified by this mention because they are not in the channel. Would you like to "
id="post_body.check_for_out_of_channel_mentions.message.one"
/>
<a <a
className="PostBody_addChannelMemberLink" class="PostBody_addChannelMemberLink"
onClick={[Function]}
> >
<MemoizedFormattedMessage add them to the channel
defaultMessage="add them to the channel"
id="post_body.check_for_out_of_channel_mentions.link.public"
/>
</a> </a>
<MemoizedFormattedMessage ? They will have access to all message history.
defaultMessage="? They will have access to all message history."
id="post_body.check_for_out_of_channel_mentions.message_last"
/>
</p> </p>
</Fragment> </div>
`; `;
exports[`components/post_view/PostAddChannelMember should match snapshot, with ABAC policy enforced 1`] = ` exports[`components/post_view/PostAddChannelMember should match snapshot, with ABAC policy enforced 1`] = `
<p> <div>
<span> <p>
<Connect(Component) <span>
channelId="channel_id" <span>
key="username_1" @username_1
mentionName="username_1" </span>
/> <span>
<span @username_2
key="1" </span>
> <span>
, @username_3
</span>
</span> </span>
<Connect(Component)
channelId="channel_id" did not get notified by this mention because they are not in the channel.
key="username_2" </p>
mentionName="username_2" </div>
/>
<MemoizedFormattedMessage
defaultMessage=" and "
id="post_body.check_for_out_of_channel_mentions.link.and"
key="2"
/>
<Connect(Component)
channelId="channel_id"
key="username_3"
mentionName="username_3"
/>
</span>
did not get notified by this mention because they are not in the channel.
</p>
`; `;
exports[`components/post_view/PostAddChannelMember should match snapshot, with no-groups usernames 1`] = ` exports[`components/post_view/PostAddChannelMember should match snapshot, with no-groups usernames 1`] = `
<Fragment> <div>
<p <p>
key="invitable" <span>
> <span>
<Connect(Component) @username_1
channelId="channel_id" </span>
mentionName="username_1" </span>
/>
<MemoizedFormattedMessage did not get notified by this mention because they are not in the channel. Would you like to
defaultMessage="did not get notified by this mention because they are not in the channel. Would you like to "
id="post_body.check_for_out_of_channel_mentions.message.one"
/>
<a <a
className="PostBody_addChannelMemberLink" class="PostBody_addChannelMemberLink"
onClick={[Function]}
> >
<MemoizedFormattedMessage add them to the channel
defaultMessage="add them to the channel"
id="post_body.check_for_out_of_channel_mentions.link.public"
/>
</a> </a>
<MemoizedFormattedMessage ? They will have access to all message history.
defaultMessage="? They will have access to all message history."
id="post_body.check_for_out_of_channel_mentions.message_last"
/>
</p> </p>
<p <p>
key="out-of-groups" <span>
> <span>
<Connect(Component) @user_id_2
channelId="channel_id" </span>
mentionName="user_id_2" </span>
/>
<MemoizedFormattedMessage did not get notified by this mention because they are not in the channel. They cannot be added to the channel because they are not a member of the linked groups. To add them to this channel, they must be added to the linked groups.
defaultMessage="did not get notified by this mention because they are not in the channel. They cannot be added to the channel because they are not a member of the linked groups. To add them to this channel, they must be added to the linked groups."
id="post_body.check_for_out_of_channel_groups_mentions.message"
/>
</p> </p>
</Fragment> </div>
`; `;

View file

@ -1,7 +1,6 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react'; import React from 'react';
import type {Post} from '@mattermost/types/posts'; import type {Post} from '@mattermost/types/posts';
@ -12,6 +11,7 @@ import {sendAddToChannelEphemeralPost} from 'actions/global_actions';
import PostAddChannelMember from 'components/post_view/post_add_channel_member/post_add_channel_member'; import PostAddChannelMember from 'components/post_view/post_add_channel_member/post_add_channel_member';
import type {Props} from 'components/post_view/post_add_channel_member/post_add_channel_member'; import type {Props} from 'components/post_view/post_add_channel_member/post_add_channel_member';
import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils';
import {TestHelper} from 'utils/test_helper'; import {TestHelper} from 'utils/test_helper';
jest.mock('actions/global_actions', () => { jest.mock('actions/global_actions', () => {
@ -20,7 +20,12 @@ jest.mock('actions/global_actions', () => {
}; };
}); });
jest.mock('components/at_mention', () => ({mentionName}: {mentionName: string}) => (
<span>{`@${mentionName}`}</span>
));
describe('components/post_view/PostAddChannelMember', () => { describe('components/post_view/PostAddChannelMember', () => {
let generateMentionsSpy: jest.SpyInstance;
const post: Post = TestHelper.getPostMock({ const post: Post = TestHelper.getPostMock({
id: 'post_id_1', id: 'post_id_1',
root_id: 'root_id', root_id: 'root_id',
@ -46,13 +51,40 @@ describe('components/post_view/PostAddChannelMember', () => {
isPolicyEnforced: false, isPolicyEnforced: false,
}; };
beforeEach(() => {
generateMentionsSpy = jest.spyOn(PostAddChannelMember.prototype, 'generateAtMentions').mockImplementation((usernames = []) => {
if (usernames.length > 3) {
const otherUsersCount = usernames.length - 2;
return (
<span>
<span>{`@${usernames[0]}`}</span>
<a className='PostBody_otherUsersLink'>{`${otherUsersCount} others`}</a>
<span>{`@${usernames[usernames.length - 1]}`}</span>
</span>
);
}
return (
<span>
{usernames.map((username) => (
<span key={username}>{`@${username}`}</span>
))}
</span>
);
});
});
afterEach(() => {
generateMentionsSpy.mockRestore();
});
test('should match snapshot, empty postId', () => { test('should match snapshot, empty postId', () => {
const props: Props = { const props: Props = {
...requiredProps, ...requiredProps,
postId: '', postId: '',
}; };
const wrapper = shallow(<PostAddChannelMember {...props}/>); const {container} = renderWithContext(<PostAddChannelMember {...props}/>);
expect(wrapper).toMatchSnapshot(); expect(container).toMatchSnapshot();
}); });
test('should match snapshot, empty channelType', () => { test('should match snapshot, empty channelType', () => {
@ -60,13 +92,13 @@ describe('components/post_view/PostAddChannelMember', () => {
...requiredProps, ...requiredProps,
channelType: '', channelType: '',
}; };
const wrapper = shallow(<PostAddChannelMember {...props}/>); const {container} = renderWithContext(<PostAddChannelMember {...props}/>);
expect(wrapper).toMatchSnapshot(); expect(container).toMatchSnapshot();
}); });
test('should match snapshot, public channel', () => { test('should match snapshot, public channel', () => {
const wrapper = shallow(<PostAddChannelMember {...requiredProps}/>); const {container} = renderWithContext(<PostAddChannelMember {...requiredProps}/>);
expect(wrapper).toMatchSnapshot(); expect(container).toMatchSnapshot();
}); });
test('should match snapshot, private channel', () => { test('should match snapshot, private channel', () => {
@ -75,11 +107,11 @@ describe('components/post_view/PostAddChannelMember', () => {
channelType: 'P', channelType: 'P',
}; };
const wrapper = shallow(<PostAddChannelMember {...props}/>); const {container} = renderWithContext(<PostAddChannelMember {...props}/>);
expect(wrapper).toMatchSnapshot(); expect(container).toMatchSnapshot();
}); });
test('should match snapshot, more than 3 users', () => { test('should match snapshot, more than 3 users', async () => {
const userIds = ['user_id_1', 'user_id_2', 'user_id_3', 'user_id_4']; const userIds = ['user_id_1', 'user_id_2', 'user_id_3', 'user_id_4'];
const usernames = ['username_1', 'username_2', 'username_3', 'username_4']; const usernames = ['username_1', 'username_2', 'username_3', 'username_4'];
const props: Props = { const props: Props = {
@ -88,26 +120,34 @@ describe('components/post_view/PostAddChannelMember', () => {
usernames, usernames,
}; };
const wrapper = shallow(<PostAddChannelMember {...props}/>); const {container} = renderWithContext(<PostAddChannelMember {...props}/>);
expect(wrapper.state('expanded')).toEqual(false); expect(container).toMatchSnapshot();
expect(wrapper).toMatchSnapshot();
wrapper.find('.PostBody_otherUsersLink').simulate('click'); const user = userEvent.setup();
expect(wrapper.state('expanded')).toEqual(true); const otherUsersLink = screen.getByText(/others/i).closest('a');
expect(wrapper).toMatchSnapshot(); if (!otherUsersLink) {
throw new Error('Other users link not found');
}
await user.click(otherUsersLink);
expect(container).toMatchSnapshot();
}); });
test('actions should have been called', () => { test('actions should have been called', async () => {
const actions = { const actions = {
removePost: jest.fn(), removePost: jest.fn(),
addChannelMember: jest.fn(), addChannelMember: jest.fn(),
}; };
const props: Props = {...requiredProps, actions}; const props: Props = {...requiredProps, actions};
const wrapper = shallow( renderWithContext(
<PostAddChannelMember {...props}/>, <PostAddChannelMember {...props}/>,
); );
wrapper.find('.PostBody_addChannelMemberLink').simulate('click'); const user = userEvent.setup();
const addToChannelLink = screen.getByText(/add them to the channel/i).closest('a');
if (!addToChannelLink) {
throw new Error('Add to channel link not found');
}
await user.click(addToChannelLink);
expect(actions.addChannelMember).toHaveBeenCalledTimes(1); expect(actions.addChannelMember).toHaveBeenCalledTimes(1);
expect(actions.addChannelMember).toHaveBeenCalledWith(post.channel_id, requiredProps.userIds[0], post.root_id); expect(actions.addChannelMember).toHaveBeenCalledWith(post.channel_id, requiredProps.userIds[0], post.root_id);
@ -117,7 +157,7 @@ describe('components/post_view/PostAddChannelMember', () => {
expect(actions.removePost).toHaveBeenCalledWith(post); expect(actions.removePost).toHaveBeenCalledWith(post);
}); });
test('addChannelMember should have been called multiple times', () => { test('addChannelMember should have been called multiple times', async () => {
const userIds = ['user_id_1', 'user_id_2', 'user_id_3', 'user_id_4']; const userIds = ['user_id_1', 'user_id_2', 'user_id_3', 'user_id_4'];
const usernames = ['username_1', 'username_2', 'username_3', 'username_4']; const usernames = ['username_1', 'username_2', 'username_3', 'username_4'];
const actions = { const actions = {
@ -125,11 +165,16 @@ describe('components/post_view/PostAddChannelMember', () => {
addChannelMember: jest.fn(), addChannelMember: jest.fn(),
}; };
const props: Props = {...requiredProps, userIds, usernames, actions}; const props: Props = {...requiredProps, userIds, usernames, actions};
const wrapper = shallow( renderWithContext(
<PostAddChannelMember {...props}/>, <PostAddChannelMember {...props}/>,
); );
wrapper.find('.PostBody_addChannelMemberLink').simulate('click'); const user = userEvent.setup();
const addToChannelLink = screen.getByText(/add them to the channel/i).closest('a');
if (!addToChannelLink) {
throw new Error('Add to channel link not found');
}
await user.click(addToChannelLink);
expect(actions.addChannelMember).toHaveBeenCalledTimes(4); expect(actions.addChannelMember).toHaveBeenCalledTimes(4);
}); });
@ -138,8 +183,8 @@ describe('components/post_view/PostAddChannelMember', () => {
...requiredProps, ...requiredProps,
noGroupsUsernames: ['user_id_2'], noGroupsUsernames: ['user_id_2'],
}; };
const wrapper = shallow(<PostAddChannelMember {...props}/>); const {container} = renderWithContext(<PostAddChannelMember {...props}/>);
expect(wrapper).toMatchSnapshot(); expect(container).toMatchSnapshot();
}); });
test('should match snapshot, with ABAC policy enforced', () => { test('should match snapshot, with ABAC policy enforced', () => {
@ -148,8 +193,8 @@ describe('components/post_view/PostAddChannelMember', () => {
usernames: ['username_1', 'username_2', 'username_3'], usernames: ['username_1', 'username_2', 'username_3'],
isPolicyEnforced: true, isPolicyEnforced: true,
}; };
const wrapper = shallow(<PostAddChannelMember {...props}/>); const {container} = renderWithContext(<PostAddChannelMember {...props}/>);
expect(wrapper).toMatchSnapshot(); expect(container).toMatchSnapshot();
}); });
test('should never show invite links when policy is enforced (ABAC channels)', () => { test('should never show invite links when policy is enforced (ABAC channels)', () => {
@ -159,8 +204,8 @@ describe('components/post_view/PostAddChannelMember', () => {
noGroupsUsernames: [], noGroupsUsernames: [],
isPolicyEnforced: true, isPolicyEnforced: true,
}; };
const wrapper = shallow(<PostAddChannelMember {...props}/>); renderWithContext(<PostAddChannelMember {...props}/>);
expect(wrapper.find('.PostBody_addChannelMemberLink')).toHaveLength(0); expect(screen.queryByRole('link', {name: /add them/i})).not.toBeInTheDocument();
}); });
test('should show single consolidated message for ABAC channels regardless of user types', () => { test('should show single consolidated message for ABAC channels regardless of user types', () => {
@ -170,10 +215,9 @@ describe('components/post_view/PostAddChannelMember', () => {
noGroupsUsernames: ['user3'], noGroupsUsernames: ['user3'],
isPolicyEnforced: true, isPolicyEnforced: true,
}; };
const wrapper = shallow(<PostAddChannelMember {...props}/>); renderWithContext(<PostAddChannelMember {...props}/>);
// Should render only one consolidated message with no invite links expect(screen.getByText('did not get notified by this mention because they are not in the channel.')).toBeInTheDocument();
expect(wrapper.find('p')).toHaveLength(1); expect(screen.queryByRole('link', {name: /add them/i})).not.toBeInTheDocument();
expect(wrapper.find('.PostBody_addChannelMemberLink')).toHaveLength(0);
}); });
}); });

View file

@ -1,13 +1,12 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import {mount} from 'enzyme';
import React from 'react'; import React from 'react';
import * as reactIntl from 'react-intl'; import * as reactIntl from 'react-intl';
import enMessages from 'i18n/en.json'; import enMessages from 'i18n/en.json';
import esMessages from 'i18n/es.json'; import esMessages from 'i18n/es.json';
import {mockStore} from 'tests/test_store'; import {renderWithContext, screen} from 'tests/react_testing_utils';
import {TestHelper} from 'utils/test_helper'; import {TestHelper} from 'utils/test_helper';
import PostAriaLabelDiv from './post_aria_label_div'; import PostAriaLabelDiv from './post_aria_label_div';
@ -49,42 +48,48 @@ describe('PostAriaLabelDiv', () => {
post: TestHelper.getPostMock({ post: TestHelper.getPostMock({
user_id: author.id, user_id: author.id,
message: 'This is a test.', message: 'This is a test.',
create_at: new Date('2020-01-15T12:00:00Z').getTime(),
}), }),
} as Omit<Props, 'ref'>; } as Omit<Props, 'ref'>;
test('should render aria-label in the given locale', () => { test('should render aria-label in the given locale', () => {
const {mountOptions} = mockStore(baseState);
(reactIntl.useIntl as jest.Mock).mockImplementation(() => reactIntl.createIntl({locale: 'en', messages: enMessages, defaultLocale: 'en'})); (reactIntl.useIntl as jest.Mock).mockImplementation(() => reactIntl.createIntl({locale: 'en', messages: enMessages, defaultLocale: 'en'}));
let wrapper = mount(<PostAriaLabelDiv {...baseProps}/>, mountOptions); let renderResult = renderWithContext(<PostAriaLabelDiv {...baseProps}/>, baseState, {
let div = wrapper.childAt(0); locale: 'en',
intlMessages: enMessages,
});
expect(div.prop('aria-label')).toContain(author.username); let div = renderResult.container.firstChild as HTMLElement;
expect(div.prop('aria-label')).toContain('January'); expect(div.getAttribute('aria-label')).toContain(author.username);
expect(div.getAttribute('aria-label')).toContain('January');
(reactIntl.useIntl as jest.Mock).mockImplementation(() => reactIntl.createIntl({locale: 'es', messages: esMessages, defaultLocale: 'es'})); (reactIntl.useIntl as jest.Mock).mockImplementation(() => reactIntl.createIntl({locale: 'es', messages: esMessages, defaultLocale: 'es'}));
wrapper = mount(<PostAriaLabelDiv {...baseProps}/>, mountOptions); renderResult = renderWithContext(<PostAriaLabelDiv {...baseProps}/>, baseState, {
div = wrapper.childAt(0); locale: 'es',
intlMessages: esMessages,
});
div = renderResult.container.firstChild as HTMLElement;
expect(div.prop('aria-label')).toContain(author.username); expect(div.getAttribute('aria-label')).toContain(author.username);
expect(div.prop('aria-label')).toContain('enero'); expect(div.getAttribute('aria-label')).toContain('enero');
}); });
test('should pass other props through to the rendered div', () => { test('should pass other props through to the rendered div', () => {
const {mountOptions} = mockStore(baseState);
(reactIntl.useIntl as jest.Mock).mockImplementation(() => reactIntl.createIntl({locale: 'en', messages: enMessages, defaultLocale: 'en'})); (reactIntl.useIntl as jest.Mock).mockImplementation(() => reactIntl.createIntl({locale: 'en', messages: enMessages, defaultLocale: 'en'}));
let props = baseProps; let props = baseProps;
let wrapper = mount(<PostAriaLabelDiv {...props}/>, mountOptions); let renderResult = renderWithContext(<PostAriaLabelDiv {...props}/>, baseState, {
let div = wrapper.childAt(0); locale: 'en',
intlMessages: enMessages,
});
let div = renderResult.container.firstChild as HTMLElement;
expect(div.prop('className')).toBeUndefined(); expect(div.className).toBe('');
expect(div.prop('data-something')).toBeUndefined(); expect(div.getAttribute('data-something')).toBeNull();
expect(div.children()).toHaveLength(0); expect(div.childNodes.length).toBe(0);
props = { props = {
...props, ...props,
@ -92,16 +97,20 @@ describe('PostAriaLabelDiv', () => {
'data-something': 'something', 'data-something': 'something',
} as Props; } as Props;
wrapper = mount( renderResult = renderWithContext(
<PostAriaLabelDiv {...props}> <PostAriaLabelDiv {...props}>
<p>{'This is a paragraph.'}</p> <p>{'This is a paragraph.'}</p>
</PostAriaLabelDiv >, </PostAriaLabelDiv >,
mountOptions, baseState,
{
locale: 'en',
intlMessages: enMessages,
},
); );
div = wrapper.childAt(0); div = renderResult.container.firstChild as HTMLElement;
expect(div.prop('className')).toBe('some-class'); expect(div.className).toBe('some-class');
expect(div.prop('data-something')).toBe('something'); expect(div.getAttribute('data-something')).toBe('something');
expect(div.children()).toHaveLength(1); expect(screen.getByText('This is a paragraph.')).toBeInTheDocument();
}); });
}); });

View file

@ -1,47 +1,78 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/post_view/PostFlagIcon should match snapshot 1`] = ` exports[`components/post_view/PostFlagIcon should match snapshot 1`] = `
<WithTooltip <div>
key="flagtooltipkey"
title={
<Memo(MemoizedFormattedMessage)
defaultMessage="Save Message"
id="flag_post.flag"
/>
}
>
<button <button
aria-label="save message" aria-label="save message"
className="post-menu__item" class="post-menu__item"
id="CENTER_flagIcon_post_id" id="CENTER_flagIcon_post_id"
onClick={[Function]}
> >
<FlagIcon <span
className="icon icon--small" class="icon icon--small"
/> >
<svg
aria-label="Save Icon"
height="16px"
role="img"
viewBox="0 0 16 16"
width="16px"
>
<path
d="M11.744 12.5L8 10.862L4.256 12.5V2.74405H11.744V12.5ZM11.744 1.25005H4.256C3.836 1.25005 3.476 1.40005 3.176 1.70005C2.888 1.98805 2.744 2.33605 2.744 2.74405V14.75L8 12.5L13.256 14.75V2.74405C13.256 2.33605 13.106 1.98805 12.806 1.70005C12.518 1.40005 12.164 1.25005 11.744 1.25005Z"
/>
</svg>
</span>
</button> </button>
</WithTooltip> </div>
`; `;
exports[`components/post_view/PostFlagIcon should match snapshot 2`] = ` exports[`components/post_view/PostFlagIcon should match snapshot 2`] = `
<WithTooltip <div>
key="flagtooltipkeyflagged"
title={
<Memo(MemoizedFormattedMessage)
defaultMessage="Remove from Saved"
id="flag_post.unflag"
/>
}
>
<button <button
aria-label="remove from saved" aria-label="remove from saved"
className="post-menu__item" class="post-menu__item"
id="CENTER_flagIcon_post_id" id="CENTER_flagIcon_post_id"
onClick={[Function]}
> >
<FlagIconFilled <span
className="icon icon--small icon--small-filled post-menu__item--selected" class="icon icon--small icon--small-filled post-menu__item--selected"
/> >
<svg
aria-label="Saved Icon"
height="15px"
role="img"
viewBox="0 0 12 15"
width="12px"
>
<g
fill="inherit"
fill-rule="evenodd"
stroke="none"
stroke-width="1"
>
<g
fill="inherit"
fill-rule="nonzero"
transform="translate(-1073.000000, -33.000000)"
>
<g
transform="translate(-1.000000, 0.000000)"
>
<g
transform="translate(1064.000000, 22.000000)"
>
<g
transform="translate(10.000000, 11.000000)"
>
<path
d="M9.76172 0.800049H2.23828C1.83984 0.800049 1.48828 0.952393 1.18359 1.25708C0.902344 1.53833 0.761719 1.88989 0.761719 2.31177V14.3L6 12.05L11.2383 14.3V2.31177C11.2383 1.88989 11.0859 1.53833 10.7812 1.25708C10.5 0.952393 10.1602 0.800049 9.76172 0.800049Z"
/>
</g>
</g>
</g>
</g>
</g>
</svg>
</span>
</button> </button>
</WithTooltip> </div>
`; `;

View file

@ -1,11 +1,12 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react'; import React from 'react';
import PostFlagIcon from 'components/post_view/post_flag_icon/post_flag_icon'; import PostFlagIcon from 'components/post_view/post_flag_icon/post_flag_icon';
import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils';
describe('components/post_view/PostFlagIcon', () => { describe('components/post_view/PostFlagIcon', () => {
const baseProps = { const baseProps = {
postId: 'post_id', postId: 'post_id',
@ -16,22 +17,26 @@ describe('components/post_view/PostFlagIcon', () => {
}, },
}; };
test('should match snapshot', () => { test('should match snapshot', async () => {
const wrapper = shallow(<PostFlagIcon {...baseProps}/>); const {container, rerender} = renderWithContext(<PostFlagIcon {...baseProps}/>);
// for unflagged icon // for unflagged icon
expect(wrapper).toMatchSnapshot(); expect(container).toMatchSnapshot();
expect(wrapper.find('button').hasClass('post-menu__item')).toBe(true); const user = userEvent.setup();
wrapper.find('button').simulate('click', {preventDefault: jest.fn}); await user.click(screen.getByRole('button', {name: /save message/i}));
expect(baseProps.actions.flagPost).toHaveBeenCalledTimes(1); expect(baseProps.actions.flagPost).toHaveBeenCalledTimes(1);
expect(baseProps.actions.flagPost).toHaveBeenCalledWith('post_id'); expect(baseProps.actions.flagPost).toHaveBeenCalledWith('post_id');
expect(baseProps.actions.unflagPost).not.toHaveBeenCalled(); expect(baseProps.actions.unflagPost).not.toHaveBeenCalled();
// for flagged icon // for flagged icon
wrapper.setProps({isFlagged: true}); rerender(
expect(wrapper).toMatchSnapshot(); <PostFlagIcon
expect(wrapper.find('button').hasClass('post-menu__item')).toBe(true); {...baseProps}
wrapper.find('button').simulate('click', {preventDefault: jest.fn}); isFlagged={true}
/>,
);
expect(container).toMatchSnapshot();
await user.click(screen.getByRole('button', {name: /remove from saved/i}));
expect(baseProps.actions.flagPost).toHaveBeenCalledTimes(1); expect(baseProps.actions.flagPost).toHaveBeenCalledTimes(1);
expect(baseProps.actions.unflagPost).toHaveBeenCalledTimes(1); expect(baseProps.actions.unflagPost).toHaveBeenCalledTimes(1);
expect(baseProps.actions.unflagPost).toHaveBeenCalledWith('post_id'); expect(baseProps.actions.unflagPost).toHaveBeenCalledWith('post_id');

View file

@ -1,7 +1,6 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react'; import React from 'react';
import type {ChannelType} from '@mattermost/types/channels'; import type {ChannelType} from '@mattermost/types/channels';
@ -10,6 +9,8 @@ import type {UserProfile} from '@mattermost/types/users';
import {General} from 'mattermost-redux/constants'; import {General} from 'mattermost-redux/constants';
import {renderWithContext} from 'tests/react_testing_utils';
import PostMessagePreview from './post_message_preview'; import PostMessagePreview from './post_message_preview';
import type {Props} from './post_message_preview'; import type {Props} from './post_message_preview';
@ -22,11 +23,14 @@ describe('PostMessagePreview', () => {
id: 'post_id', id: 'post_id',
message: 'post message', message: 'post message',
metadata: {}, metadata: {},
channel_id: 'channel_id',
create_at: new Date('2020-01-15T12:00:00Z').getTime(),
} as Post; } as Post;
const user = { const user = {
id: 'user_1', id: 'user_1',
username: 'username1', username: 'username1',
roles: 'system_admin',
} as UserProfile; } as UserProfile;
const baseProps: Props = { const baseProps: Props = {
@ -52,47 +56,98 @@ describe('PostMessagePreview', () => {
isPostPriorityEnabled: false, isPostPriorityEnabled: false,
}; };
test('should render correctly', () => { const baseState = {
const wrapper = shallow(<PostMessagePreview {...baseProps}/>); entities: {
users: {
currentUserId: user.id,
profiles: {
[user.id]: user,
},
},
teams: {
currentTeamId: 'team_id',
teams: {
team_id: {
id: 'team_id',
name: 'team1',
},
},
},
channels: {
channels: {
channel_id: {
id: 'channel_id',
team_id: 'team_id',
type: 'O' as ChannelType,
name: 'channel-name',
display_name: 'Channel Name',
},
},
},
posts: {
posts: {
[previewPost.id]: previewPost,
},
},
preferences: {
myPreferences: {},
},
general: {
config: {},
},
roles: {
roles: {
system_admin: {
permissions: [],
},
},
},
},
};
expect(wrapper).toMatchSnapshot(); test('should render correctly', () => {
const {container} = renderWithContext(<PostMessagePreview {...baseProps}/>, baseState);
expect(container).toMatchSnapshot();
}); });
test('should render without preview', () => { test('should render without preview', () => {
const wrapper = shallow( const {container} = renderWithContext(
<PostMessagePreview <PostMessagePreview
{...baseProps} {...baseProps}
previewPost={undefined} previewPost={undefined}
/>, />,
baseState,
); );
expect(wrapper).toMatchSnapshot(); expect(container).toMatchSnapshot();
}); });
test('show render without preview when preview posts becomes undefined after being defined', () => { test('show render without preview when preview posts becomes undefined after being defined', () => {
const props = {...baseProps}; const props = {...baseProps};
let wrapper = shallow( let renderResult = renderWithContext(
<PostMessagePreview <PostMessagePreview
{...props} {...props}
/>, />,
baseState,
); );
expect(wrapper).toMatchSnapshot(); expect(renderResult.container).toMatchSnapshot();
let permalink = wrapper.find('.permalink'); let permalink = renderResult.container.querySelector('.attachment--permalink');
expect(permalink.length).toBe(1); expect(permalink).toBeInTheDocument();
// now we'll set the preview post to undefined. This happens when the // now we'll set the preview post to undefined. This happens when the
// previewed post is deleted. // previewed post is deleted.
props.previewPost = undefined; props.previewPost = undefined;
wrapper = shallow( renderResult = renderWithContext(
<PostMessagePreview <PostMessagePreview
{...props} {...props}
/>, />,
baseState,
); );
expect(wrapper).toMatchSnapshot(); expect(renderResult.container).toMatchSnapshot();
permalink = wrapper.find('.permalink'); permalink = renderResult.container.querySelector('.attachment--permalink');
expect(permalink.length).toBe(0); expect(permalink).not.toBeInTheDocument();
}); });
test('should not render bot icon', () => { test('should not render bot icon', () => {
@ -111,13 +166,14 @@ describe('PostMessagePreview', () => {
...baseProps, ...baseProps,
previewPost: postPreview, previewPost: postPreview,
}; };
const wrapper = shallow( const {container} = renderWithContext(
<PostMessagePreview <PostMessagePreview
{...props} {...props}
/>, />,
baseState,
); );
expect(wrapper).toMatchSnapshot(); expect(container).toMatchSnapshot();
}); });
test('should render bot icon', () => { test('should render bot icon', () => {
@ -137,13 +193,14 @@ describe('PostMessagePreview', () => {
previewPost: postPreview, previewPost: postPreview,
enablePostIconOverride: true, enablePostIconOverride: true,
}; };
const wrapper = shallow( const {container} = renderWithContext(
<PostMessagePreview <PostMessagePreview
{...props} {...props}
/>, />,
baseState,
); );
expect(wrapper).toMatchSnapshot(); expect(container).toMatchSnapshot();
}); });
describe('nested previews', () => { describe('nested previews', () => {
@ -172,9 +229,8 @@ describe('PostMessagePreview', () => {
previewPost: postPreview, previewPost: postPreview,
}; };
const wrapper = shallow(<PostMessagePreview {...props}/>); const {container} = renderWithContext(<PostMessagePreview {...props}/>, baseState);
expect(container).toMatchSnapshot();
expect(wrapper).toMatchSnapshot();
}); });
test('should render file preview', () => { test('should render file preview', () => {
@ -188,9 +244,8 @@ describe('PostMessagePreview', () => {
previewPost: postPreview, previewPost: postPreview,
}; };
const wrapper = shallow(<PostMessagePreview {...props}/>); const {container} = renderWithContext(<PostMessagePreview {...props}/>, baseState);
expect(container).toMatchSnapshot();
expect(wrapper).toMatchSnapshot();
}); });
}); });
@ -210,13 +265,14 @@ describe('PostMessagePreview', () => {
metadata, metadata,
}; };
const wrapper = shallow( const {container} = renderWithContext(
<PostMessagePreview <PostMessagePreview
{...props} {...props}
/>, />,
baseState,
); );
expect(wrapper).toMatchSnapshot(); expect(container).toMatchSnapshot();
}); });
}); });
}); });

View file

@ -1,231 +1,199 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/post_view/ShowMore should match snapshot 1`] = ` exports[`components/post_view/ShowMore should match snapshot 1`] = `
<div <div>
className="post-message post-message--collapsed"
>
<div <div
className="post-message__text-container" class="post-message post-message--collapsed"
style={
Object {
"maxHeight": 200,
}
}
> >
<div> <div
<p> class="post-message__text-container"
text style="max-height: 200px;"
</p> >
<div>
<p>
text
</p>
</div>
</div> </div>
</div> </div>
</div> </div>
`; `;
exports[`components/post_view/ShowMore should match snapshot, PostAttachment on collapsed view 1`] = ` exports[`components/post_view/ShowMore should match snapshot, PostAttachment on collapsed view 1`] = `
<div <div>
className="post-message post-message--collapsed post-message--overflow"
>
<div <div
className="post-message__text-container" class="post-message post-message--collapsed post-message--overflow"
style={
Object {
"maxHeight": 200,
}
}
/>
<div
className="post-collapse"
> >
<div <div
className="post-attachment-collapse__show-more" class="post-message__text-container"
style="max-height: 200px;"
/>
<div
class="post-collapse"
> >
<div <div
className="post-collapse__show-more-line" class="post-attachment-collapse__show-more"
/>
<button
className="post-collapse__show-more-button"
id="showMoreButton"
onClick={[Function]}
> >
<span <div
className="fa fa-angle-down" class="post-collapse__show-more-line"
/> />
<MemoizedFormattedMessage <button
defaultMessage="Show more" class="post-collapse__show-more-button"
id="post_info.message.show_more" id="showMoreButton"
>
<span
class="fa fa-angle-down"
/>
Show more
</button>
<div
class="post-collapse__show-more-line"
/> />
</button> </div>
<div
className="post-collapse__show-more-line"
/>
</div> </div>
</div> </div>
</div> </div>
`; `;
exports[`components/post_view/ShowMore should match snapshot, PostAttachment on expanded view 1`] = ` exports[`components/post_view/ShowMore should match snapshot, PostAttachment on expanded view 1`] = `
<div <div>
className="post-message post-message--expanded post-message--overflow"
>
<div <div
className="post-message__text-container" class="post-message post-message--expanded post-message--overflow"
style={
Object {
"maxHeight": undefined,
}
}
/>
<div
className="post-collapse"
> >
<div <div
className="post-attachment-collapse__show-more" class="post-message__text-container"
style=""
/>
<div
class="post-collapse"
> >
<div <div
className="post-collapse__show-more-line" class="post-attachment-collapse__show-more"
/>
<button
className="post-collapse__show-more-button"
id="showMoreButton"
onClick={[Function]}
> >
<span <div
className="fa fa-angle-up" class="post-collapse__show-more-line"
/> />
<MemoizedFormattedMessage <button
defaultMessage="Show less" class="post-collapse__show-more-button"
id="post_info.message.show_less" id="showMoreButton"
>
<span
class="fa fa-angle-up"
/>
Show less
</button>
<div
class="post-collapse__show-more-line"
/> />
</button> </div>
<div
className="post-collapse__show-more-line"
/>
</div> </div>
</div> </div>
</div> </div>
`; `;
exports[`components/post_view/ShowMore should match snapshot, PostMessageView on collapsed view 1`] = ` exports[`components/post_view/ShowMore should match snapshot, PostMessageView on collapsed view 1`] = `
<div <div>
className="post-message post-message--collapsed post-message--overflow"
>
<div <div
className="post-message__text-container" class="post-message post-message--collapsed post-message--overflow"
style={
Object {
"maxHeight": 200,
}
}
/>
<div
className="post-collapse"
> >
<div <div
className="post-collapse__show-more" class="post-message__text-container"
style="max-height: 200px;"
/>
<div
class="post-collapse"
> >
<div <div
className="post-collapse__show-more-line" class="post-collapse__show-more"
/>
<button
className="post-collapse__show-more-button"
id="showMoreButton"
onClick={[Function]}
> >
<span <div
className="fa fa-angle-down" class="post-collapse__show-more-line"
/> />
<MemoizedFormattedMessage <button
defaultMessage="Show more" class="post-collapse__show-more-button"
id="post_info.message.show_more" id="showMoreButton"
>
<span
class="fa fa-angle-down"
/>
Show more
</button>
<div
class="post-collapse__show-more-line"
/> />
</button> </div>
<div
className="post-collapse__show-more-line"
/>
</div> </div>
</div> </div>
</div> </div>
`; `;
exports[`components/post_view/ShowMore should match snapshot, PostMessageView on expanded view 1`] = ` exports[`components/post_view/ShowMore should match snapshot, PostMessageView on expanded view 1`] = `
<div <div>
className="post-message post-message--expanded post-message--overflow"
>
<div <div
className="post-message__text-container" class="post-message post-message--expanded post-message--overflow"
style={
Object {
"maxHeight": undefined,
}
}
/>
<div
className="post-collapse"
> >
<div <div
className="post-collapse__show-more" class="post-message__text-container"
style=""
/>
<div
class="post-collapse"
> >
<div <div
className="post-collapse__show-more-line" class="post-collapse__show-more"
/>
<button
className="post-collapse__show-more-button"
id="showMoreButton"
onClick={[Function]}
> >
<span <div
className="fa fa-angle-up" class="post-collapse__show-more-line"
/> />
<MemoizedFormattedMessage <button
defaultMessage="Show less" class="post-collapse__show-more-button"
id="post_info.message.show_less" id="showMoreButton"
>
<span
class="fa fa-angle-up"
/>
Show less
</button>
<div
class="post-collapse__show-more-line"
/> />
</button> </div>
<div
className="post-collapse__show-more-line"
/>
</div> </div>
</div> </div>
</div> </div>
`; `;
exports[`components/post_view/ShowMore should match snapshot, PostMessageView on expanded view with compactDisplay 1`] = ` exports[`components/post_view/ShowMore should match snapshot, PostMessageView on expanded view with compactDisplay 1`] = `
<div <div>
className="post-message post-message--expanded post-message--overflow"
>
<div <div
className="post-message__text-container" class="post-message post-message--expanded post-message--overflow"
style={
Object {
"maxHeight": undefined,
}
}
/>
<div
className="post-collapse"
> >
<div <div
className="post-collapse__show-more" class="post-message__text-container"
style=""
/>
<div
class="post-collapse"
> >
<div <div
className="post-collapse__show-more-line" class="post-collapse__show-more"
/>
<button
className="post-collapse__show-more-button"
id="showMoreButton"
onClick={[Function]}
> >
<span <div
className="fa fa-angle-up" class="post-collapse__show-more-line"
/> />
<MemoizedFormattedMessage <button
defaultMessage="Show less" class="post-collapse__show-more-button"
id="post_info.message.show_less" id="showMoreButton"
>
<span
class="fa fa-angle-up"
/>
Show less
</button>
<div
class="post-collapse__show-more-line"
/> />
</button> </div>
<div
className="post-collapse__show-more-line"
/>
</div> </div>
</div> </div>
</div> </div>

View file

@ -1,11 +1,12 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react'; import React from 'react';
import ShowMore from 'components/post_view/show_more/show_more'; import ShowMore from 'components/post_view/show_more/show_more';
import {act, renderWithContext} from 'tests/react_testing_utils';
describe('components/post_view/ShowMore', () => { describe('components/post_view/ShowMore', () => {
const children = (<div><p>{'text'}</p></div>); const children = (<div><p>{'text'}</p></div>);
const baseProps = { const baseProps = {
@ -19,84 +20,166 @@ describe('components/post_view/ShowMore', () => {
}; };
test('should match snapshot', () => { test('should match snapshot', () => {
const wrapper = shallow(<ShowMore {...baseProps}>{children}</ShowMore>); const {container} = renderWithContext(<ShowMore {...baseProps}>{children}</ShowMore>);
expect(wrapper).toMatchSnapshot(); expect(container).toMatchSnapshot();
}); });
test('should match snapshot, PostMessageView on collapsed view', () => { test('should match snapshot, PostMessageView on collapsed view', () => {
const wrapper = shallow(<ShowMore {...baseProps}/>); const ref = React.createRef<ShowMore>();
wrapper.setState({isOverflow: true, isCollapsed: true}); const {container} = renderWithContext(
expect(wrapper).toMatchSnapshot(); <ShowMore
{...baseProps}
ref={ref}
/>,
);
act(() => {
ref.current?.setState({isOverflow: true, isCollapsed: true});
});
expect(container).toMatchSnapshot();
}); });
test('should match snapshot, PostMessageView on expanded view', () => { test('should match snapshot, PostMessageView on expanded view', () => {
const wrapper = shallow(<ShowMore {...baseProps}/>); const ref = React.createRef<ShowMore>();
wrapper.setState({isOverflow: true, isCollapsed: false}); const {container} = renderWithContext(
expect(wrapper).toMatchSnapshot(); <ShowMore
{...baseProps}
ref={ref}
/>,
);
act(() => {
ref.current?.setState({isOverflow: true, isCollapsed: false});
});
expect(container).toMatchSnapshot();
}); });
test('should match snapshot, PostAttachment on collapsed view', () => { test('should match snapshot, PostAttachment on collapsed view', () => {
const wrapper = shallow( const ref = React.createRef<ShowMore>();
const {container} = renderWithContext(
<ShowMore <ShowMore
{...baseProps} {...baseProps}
isAttachmentText={true} isAttachmentText={true}
ref={ref}
/>, />,
); );
wrapper.setState({isOverflow: true, isCollapsed: true}); act(() => {
expect(wrapper).toMatchSnapshot(); ref.current?.setState({isOverflow: true, isCollapsed: true});
});
expect(container).toMatchSnapshot();
}); });
test('should match snapshot, PostAttachment on expanded view', () => { test('should match snapshot, PostAttachment on expanded view', () => {
const wrapper = shallow( const ref = React.createRef<ShowMore>();
const {container} = renderWithContext(
<ShowMore <ShowMore
{...baseProps} {...baseProps}
isAttachmentText={true} isAttachmentText={true}
ref={ref}
/>, />,
); );
wrapper.setState({isOverflow: true, isCollapsed: false}); act(() => {
expect(wrapper).toMatchSnapshot(); ref.current?.setState({isOverflow: true, isCollapsed: false});
});
expect(container).toMatchSnapshot();
}); });
test('should match snapshot, PostMessageView on expanded view with compactDisplay', () => { test('should match snapshot, PostMessageView on expanded view with compactDisplay', () => {
const wrapper = shallow( const ref = React.createRef<ShowMore>();
const {container} = renderWithContext(
<ShowMore <ShowMore
{...baseProps} {...baseProps}
compactDisplay={true} compactDisplay={true}
ref={ref}
/>, />,
); );
wrapper.setState({isOverflow: true, isCollapsed: false}); act(() => {
expect(wrapper).toMatchSnapshot(); ref.current?.setState({isOverflow: true, isCollapsed: false});
});
expect(container).toMatchSnapshot();
}); });
test('should call checkTextOverflow', () => { test('should call checkTextOverflow', () => {
const wrapper = shallow(<ShowMore {...baseProps}/>); const ref = React.createRef<ShowMore>();
const instance = wrapper.instance() as ShowMore; const {rerender} = renderWithContext(
instance.checkTextOverflow = jest.fn(); <ShowMore
{...baseProps}
ref={ref}
/>,
);
const instance = ref.current as ShowMore;
jest.spyOn(instance, 'checkTextOverflow');
expect(instance.checkTextOverflow).not.toHaveBeenCalled(); expect(instance.checkTextOverflow).not.toHaveBeenCalled();
wrapper.setProps({isRHSExpanded: true}); rerender(
<ShowMore
{...baseProps}
ref={ref}
isRHSExpanded={true}
/>,
);
expect(instance.checkTextOverflow).toHaveBeenCalledTimes(1); expect(instance.checkTextOverflow).toHaveBeenCalledTimes(1);
wrapper.setProps({isRHSExpanded: false}); rerender(
<ShowMore
{...baseProps}
ref={ref}
isRHSExpanded={false}
/>,
);
expect(instance.checkTextOverflow).toHaveBeenCalledTimes(2); expect(instance.checkTextOverflow).toHaveBeenCalledTimes(2);
wrapper.setProps({isRHSOpen: true}); rerender(
<ShowMore
{...baseProps}
ref={ref}
isRHSOpen={true}
/>,
);
expect(instance.checkTextOverflow).toHaveBeenCalledTimes(3); expect(instance.checkTextOverflow).toHaveBeenCalledTimes(3);
wrapper.setProps({isRHSOpen: false}); rerender(
<ShowMore
{...baseProps}
ref={ref}
isRHSOpen={false}
/>,
);
expect(instance.checkTextOverflow).toHaveBeenCalledTimes(4); expect(instance.checkTextOverflow).toHaveBeenCalledTimes(4);
wrapper.setProps({text: 'text change'}); rerender(
<ShowMore
{...baseProps}
ref={ref}
text={'text change'}
/>,
);
expect(instance.checkTextOverflow).toHaveBeenCalledTimes(5); expect(instance.checkTextOverflow).toHaveBeenCalledTimes(5);
wrapper.setProps({text: 'text another change'}); rerender(
<ShowMore
{...baseProps}
ref={ref}
text={'text another change'}
/>,
);
expect(instance.checkTextOverflow).toHaveBeenCalledTimes(6); expect(instance.checkTextOverflow).toHaveBeenCalledTimes(6);
wrapper.setProps({checkOverflow: 1}); rerender(
<ShowMore
{...baseProps}
ref={ref}
checkOverflow={1}
/>,
);
expect(instance.checkTextOverflow).toHaveBeenCalledTimes(7); expect(instance.checkTextOverflow).toHaveBeenCalledTimes(7);
wrapper.setProps({checkOverflow: 1}); rerender(
<ShowMore
{...baseProps}
ref={ref}
checkOverflow={1}
/>,
);
expect(instance.checkTextOverflow).toHaveBeenCalledTimes(7); expect(instance.checkTextOverflow).toHaveBeenCalledTimes(7);
}); });
}); });

View file

@ -1,193 +1,247 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/sidebar/sidebar_channel should match snapshot 1`] = ` exports[`components/sidebar/sidebar_channel should match snapshot 1`] = `
<li <div>
className="SidebarChannel expanded" <li
onAnimationEnd={[Function]} class="SidebarChannel expanded"
onAnimationStart={[Function]} role="listitem"
role="listitem" >
> <a
<Connect(SidebarBaseChannel) aria-label="channel_display_name public channel"
channel={ class="SidebarLink"
Object { href="/team_name/channels/"
"create_at": 0, id="sidebarItem_"
"creator_id": "", tabindex="0"
"delete_at": 0, >
"display_name": "channel_display_name", <i
"group_constrained": false, class="icon icon-globe"
"header": "", />
"id": "channel_id", <div
"last_post_at": 0, class="SidebarChannelLinkLabel_wrapper"
"last_root_post_at": 0, >
"name": "", <span
"purpose": "", class="SidebarChannelLinkLabel"
"scheme_id": "", >
"team_id": "", channel_display_name
"type": "O", </span>
"update_at": 0, </div>
} <div
} class="SidebarMenu MenuWrapper"
currentTeamName="team_name" >
/> <button
</li> aria-controls="SidebarChannelMenu-MenuList-channel_id"
aria-expanded="false"
aria-haspopup="true"
aria-label="Channel options for "
class="SidebarMenu_menuButton"
id="SidebarChannelMenu-Button-channel_id"
>
<svg
fill="currentColor"
height="16"
version="1.1"
viewBox="0 0 24 24"
width="16"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12,16A2,2 0 0,1 14,18A2,2 0 0,1 12,20A2,2 0 0,1 10,18A2,2 0 0,1 12,16M12,10A2,2 0 0,1 14,12A2,2 0 0,1 12,14A2,2 0 0,1 10,12A2,2 0 0,1 12,10M12,4A2,2 0 0,1 14,6A2,2 0 0,1 12,8A2,2 0 0,1 10,6A2,2 0 0,1 12,4Z"
/>
</svg>
</button>
</div>
</a>
</li>
</div>
`; `;
exports[`components/sidebar/sidebar_channel should match snapshot when DM channel 1`] = ` exports[`components/sidebar/sidebar_channel should match snapshot when DM channel 1`] = `
<li <div>
className="SidebarChannel expanded" <li
onAnimationEnd={[Function]} class="SidebarChannel expanded"
onAnimationStart={[Function]} role="listitem"
role="listitem" >
> <div>
<Connect(injectIntl(SidebarDirectChannel)) Direct Channel
channel={ </div>
Object { </li>
"create_at": 0, </div>
"creator_id": "",
"delete_at": 0,
"display_name": "channel_display_name",
"group_constrained": false,
"header": "",
"id": "channel_id",
"last_post_at": 0,
"last_root_post_at": 0,
"name": "",
"purpose": "",
"scheme_id": "",
"team_id": "",
"type": "D",
"update_at": 0,
}
}
currentTeamName="team_name"
/>
</li>
`; `;
exports[`components/sidebar/sidebar_channel should match snapshot when GM channel 1`] = ` exports[`components/sidebar/sidebar_channel should match snapshot when GM channel 1`] = `
<li <div>
className="SidebarChannel expanded" <li
onAnimationEnd={[Function]} class="SidebarChannel expanded"
onAnimationStart={[Function]} role="listitem"
role="listitem" >
> <div>
<Connect(Component) Group Channel
channel={ </div>
Object { </li>
"create_at": 0, </div>
"creator_id": "",
"delete_at": 0,
"display_name": "channel_display_name",
"group_constrained": false,
"header": "",
"id": "channel_id",
"last_post_at": 0,
"last_root_post_at": 0,
"name": "",
"purpose": "",
"scheme_id": "",
"team_id": "",
"type": "G",
"update_at": 0,
}
}
currentTeamName="team_name"
/>
</li>
`; `;
exports[`components/sidebar/sidebar_channel should match snapshot when active 1`] = ` exports[`components/sidebar/sidebar_channel should match snapshot when active 1`] = `
<li <div>
className="SidebarChannel expanded active" <li
onAnimationEnd={[Function]} class="SidebarChannel expanded active"
onAnimationStart={[Function]} role="listitem"
role="listitem" >
> <a
<Connect(SidebarBaseChannel) aria-label="channel_display_name public channel"
channel={ class="SidebarLink"
Object { href="/team_name/channels/"
"create_at": 0, id="sidebarItem_"
"creator_id": "", tabindex="0"
"delete_at": 0, >
"display_name": "channel_display_name", <i
"group_constrained": false, class="icon icon-globe"
"header": "", />
"id": "channel_id", <div
"last_post_at": 0, class="SidebarChannelLinkLabel_wrapper"
"last_root_post_at": 0, >
"name": "", <span
"purpose": "", class="SidebarChannelLinkLabel"
"scheme_id": "", >
"team_id": "", channel_display_name
"type": "O", </span>
"update_at": 0, </div>
} <div
} class="SidebarMenu MenuWrapper"
currentTeamName="team_name" >
/> <button
</li> aria-controls="SidebarChannelMenu-MenuList-channel_id"
aria-expanded="false"
aria-haspopup="true"
aria-label="Channel options for "
class="SidebarMenu_menuButton"
id="SidebarChannelMenu-Button-channel_id"
>
<svg
fill="currentColor"
height="16"
version="1.1"
viewBox="0 0 24 24"
width="16"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12,16A2,2 0 0,1 14,18A2,2 0 0,1 12,20A2,2 0 0,1 10,18A2,2 0 0,1 12,16M12,10A2,2 0 0,1 14,12A2,2 0 0,1 12,14A2,2 0 0,1 10,12A2,2 0 0,1 12,10M12,4A2,2 0 0,1 14,6A2,2 0 0,1 12,8A2,2 0 0,1 10,6A2,2 0 0,1 12,4Z"
/>
</svg>
</button>
</div>
</a>
</li>
</div>
`; `;
exports[`components/sidebar/sidebar_channel should match snapshot when collapsed 1`] = ` exports[`components/sidebar/sidebar_channel should match snapshot when collapsed 1`] = `
<li <div>
className="SidebarChannel collapsed" <li
onAnimationEnd={[Function]} class="SidebarChannel collapsed"
onAnimationStart={[Function]} role="listitem"
role="listitem" >
> <a
<Connect(SidebarBaseChannel) aria-label="channel_display_name public channel"
channel={ class="SidebarLink"
Object { href="/team_name/channels/"
"create_at": 0, id="sidebarItem_"
"creator_id": "", tabindex="0"
"delete_at": 0, >
"display_name": "channel_display_name", <i
"group_constrained": false, class="icon icon-globe"
"header": "", />
"id": "channel_id", <div
"last_post_at": 0, class="SidebarChannelLinkLabel_wrapper"
"last_root_post_at": 0, >
"name": "", <span
"purpose": "", class="SidebarChannelLinkLabel"
"scheme_id": "", >
"team_id": "", channel_display_name
"type": "O", </span>
"update_at": 0, </div>
} <div
} class="SidebarMenu MenuWrapper"
currentTeamName="team_name" >
/> <button
</li> aria-controls="SidebarChannelMenu-MenuList-channel_id"
aria-expanded="false"
aria-haspopup="true"
aria-label="Channel options for "
class="SidebarMenu_menuButton"
id="SidebarChannelMenu-Button-channel_id"
>
<svg
fill="currentColor"
height="16"
version="1.1"
viewBox="0 0 24 24"
width="16"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12,16A2,2 0 0,1 14,18A2,2 0 0,1 12,20A2,2 0 0,1 10,18A2,2 0 0,1 12,16M12,10A2,2 0 0,1 14,12A2,2 0 0,1 12,14A2,2 0 0,1 10,12A2,2 0 0,1 12,10M12,4A2,2 0 0,1 14,6A2,2 0 0,1 12,8A2,2 0 0,1 10,6A2,2 0 0,1 12,4Z"
/>
</svg>
</button>
</div>
</a>
</li>
</div>
`; `;
exports[`components/sidebar/sidebar_channel should match snapshot when unread 1`] = ` exports[`components/sidebar/sidebar_channel should match snapshot when unread 1`] = `
<li <div>
className="SidebarChannel expanded unread" <li
onAnimationEnd={[Function]} class="SidebarChannel expanded unread"
onAnimationStart={[Function]} role="listitem"
role="listitem" >
> <a
<Connect(SidebarBaseChannel) aria-label="channel_display_name public channel"
channel={ class="SidebarLink"
Object { href="/team_name/channels/"
"create_at": 0, id="sidebarItem_"
"creator_id": "", tabindex="0"
"delete_at": 0, >
"display_name": "channel_display_name", <i
"group_constrained": false, class="icon icon-globe"
"header": "", />
"id": "channel_id", <div
"last_post_at": 0, class="SidebarChannelLinkLabel_wrapper"
"last_root_post_at": 0, >
"name": "", <span
"purpose": "", class="SidebarChannelLinkLabel"
"scheme_id": "", >
"team_id": "", channel_display_name
"type": "O", </span>
"update_at": 0, </div>
} <div
} class="SidebarMenu MenuWrapper"
currentTeamName="team_name" >
/> <button
</li> aria-controls="SidebarChannelMenu-MenuList-channel_id"
aria-expanded="false"
aria-haspopup="true"
aria-label="Channel options for "
class="SidebarMenu_menuButton"
id="SidebarChannelMenu-Button-channel_id"
>
<svg
fill="currentColor"
height="16"
version="1.1"
viewBox="0 0 24 24"
width="16"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12,16A2,2 0 0,1 14,18A2,2 0 0,1 12,20A2,2 0 0,1 10,18A2,2 0 0,1 12,16M12,10A2,2 0 0,1 14,12A2,2 0 0,1 12,14A2,2 0 0,1 10,12A2,2 0 0,1 12,10M12,4A2,2 0 0,1 14,6A2,2 0 0,1 12,8A2,2 0 0,1 10,6A2,2 0 0,1 12,4Z"
/>
</svg>
</button>
</div>
</a>
</li>
</div>
`; `;

View file

@ -1,137 +1,61 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`components/sidebar/sidebar_channel/sidebar_base_channel should match snapshot 1`] = ` exports[`components/sidebar/sidebar_channel/sidebar_base_channel should match snapshot 1`] = `
<Connect(injectIntl(SidebarChannelLink)) <div>
ariaLabelPrefix="public channel" <div>
channel={ <button
Object { aria-label="Channel options"
"create_at": 0, >
"creator_id": "", Options
"delete_at": 0, </button>
"display_name": "channel_display_name", <div>
"group_constrained": false, channel_display_name
"header": "", </div>
"id": "channel_id", </div>
"last_post_at": 0, </div>
"last_root_post_at": 0,
"name": "",
"purpose": "",
"scheme_id": "",
"team_id": "",
"type": "O",
"update_at": 0,
}
}
channelLeaveHandler={[Function]}
icon={
<SidebarBaseChannelIcon
channelType="O"
/>
}
label="channel_display_name"
link="/team_name/channels/"
/>
`; `;
exports[`components/sidebar/sidebar_channel/sidebar_base_channel should match snapshot when private channel 1`] = ` exports[`components/sidebar/sidebar_channel/sidebar_base_channel should match snapshot when private channel 1`] = `
<Connect(injectIntl(SidebarChannelLink)) <div>
ariaLabelPrefix="private channel" <div>
channel={ <button
Object { aria-label="Channel options"
"create_at": 0, >
"creator_id": "", Options
"delete_at": 0, </button>
"display_name": "channel_display_name", <div>
"group_constrained": false, channel_display_name
"header": "", </div>
"id": "channel_id", </div>
"last_post_at": 0, </div>
"last_root_post_at": 0,
"name": "",
"purpose": "",
"scheme_id": "",
"team_id": "",
"type": "P",
"update_at": 0,
}
}
channelLeaveHandler={[Function]}
icon={
<SidebarBaseChannelIcon
channelType="P"
/>
}
label="channel_display_name"
link="/team_name/channels/"
/>
`; `;
exports[`components/sidebar/sidebar_channel/sidebar_base_channel should match snapshot when shared channel 1`] = ` exports[`components/sidebar/sidebar_channel/sidebar_base_channel should match snapshot when shared channel 1`] = `
<Connect(injectIntl(SidebarChannelLink)) <div>
ariaLabelPrefix="public channel" <div>
channel={ <button
Object { aria-label="Channel options"
"create_at": 0, >
"creator_id": "", Options
"delete_at": 0, </button>
"display_name": "channel_display_name", <div>
"group_constrained": false, channel_display_name
"header": "", </div>
"id": "channel_id", </div>
"last_post_at": 0, </div>
"last_root_post_at": 0,
"name": "",
"purpose": "",
"scheme_id": "",
"shared": true,
"team_id": "",
"type": "O",
"update_at": 0,
}
}
channelLeaveHandler={[Function]}
icon={
<SidebarBaseChannelIcon
channelType="O"
/>
}
isSharedChannel={true}
label="channel_display_name"
link="/team_name/channels/"
/>
`; `;
exports[`components/sidebar/sidebar_channel/sidebar_base_channel should match snapshot when shared private channel 1`] = ` exports[`components/sidebar/sidebar_channel/sidebar_base_channel should match snapshot when shared private channel 1`] = `
<Connect(injectIntl(SidebarChannelLink)) <div>
ariaLabelPrefix="private channel" <div>
channel={ <button
Object { aria-label="Channel options"
"create_at": 0, >
"creator_id": "", Options
"delete_at": 0, </button>
"display_name": "channel_display_name", <div>
"group_constrained": false, channel_display_name
"header": "", </div>
"id": "channel_id", </div>
"last_post_at": 0, </div>
"last_root_post_at": 0,
"name": "",
"purpose": "",
"scheme_id": "",
"shared": true,
"team_id": "",
"type": "P",
"update_at": 0,
}
}
channelLeaveHandler={[Function]}
icon={
<SidebarBaseChannelIcon
channelType="P"
/>
}
isSharedChannel={true}
label="channel_display_name"
link="/team_name/channels/"
/>
`; `;

View file

@ -1,15 +1,50 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import {screen, waitFor} from '@testing-library/react';
import {shallow} from 'enzyme';
import React from 'react'; import React from 'react';
import type {ChannelType} from '@mattermost/types/channels'; import type {ChannelType} from '@mattermost/types/channels';
import SidebarBaseChannel from 'components/sidebar/sidebar_channel/sidebar_base_channel/sidebar_base_channel'; import SidebarBaseChannel from 'components/sidebar/sidebar_channel/sidebar_base_channel/sidebar_base_channel';
import {renderWithContext, userEvent} from 'tests/react_testing_utils'; import {renderWithContext, screen, userEvent, waitFor} from 'tests/react_testing_utils';
jest.mock('components/tours/onboarding_tour', () => ({
ChannelsAndDirectMessagesTour: () => null,
}));
jest.mock('components/sidebar/sidebar_channel/sidebar_channel_link', () => {
const React = require('react');
return ({label, channelLeaveHandler}: {label: string; channelLeaveHandler?: (callback: () => void) => void}) => {
const [isOpen, setIsOpen] = React.useState(false);
return (
<div>
<button
aria-label='Channel options'
onClick={() => setIsOpen(true)}
>
{'Options'}
</button>
{isOpen && (
<div
role='menu'
aria-label='Edit channel menu'
>
<button
role='menuitem'
onClick={() => channelLeaveHandler?.(() => {})}
>
{'Leave Channel'}
</button>
</div>
)}
<div>{label}</div>
</div>
);
};
});
describe('components/sidebar/sidebar_channel/sidebar_base_channel', () => { describe('components/sidebar/sidebar_channel/sidebar_base_channel', () => {
const baseProps = { const baseProps = {
@ -38,11 +73,11 @@ describe('components/sidebar/sidebar_channel/sidebar_base_channel', () => {
}; };
test('should match snapshot', () => { test('should match snapshot', () => {
const wrapper = shallow( const {container} = renderWithContext(
<SidebarBaseChannel {...baseProps}/>, <SidebarBaseChannel {...baseProps}/>,
); );
expect(wrapper).toMatchSnapshot(); expect(container).toMatchSnapshot();
}); });
test('should match snapshot when shared channel', () => { test('should match snapshot when shared channel', () => {
@ -54,11 +89,11 @@ describe('components/sidebar/sidebar_channel/sidebar_base_channel', () => {
}, },
}; };
const wrapper = shallow( const {container} = renderWithContext(
<SidebarBaseChannel {...props}/>, <SidebarBaseChannel {...props}/>,
); );
expect(wrapper).toMatchSnapshot(); expect(container).toMatchSnapshot();
}); });
test('should match snapshot when private channel', () => { test('should match snapshot when private channel', () => {
@ -70,11 +105,11 @@ describe('components/sidebar/sidebar_channel/sidebar_base_channel', () => {
}, },
}; };
const wrapper = shallow( const {container} = renderWithContext(
<SidebarBaseChannel {...props}/>, <SidebarBaseChannel {...props}/>,
); );
expect(wrapper).toMatchSnapshot(); expect(container).toMatchSnapshot();
}); });
test('should match snapshot when shared private channel', () => { test('should match snapshot when shared private channel', () => {
@ -87,11 +122,11 @@ describe('components/sidebar/sidebar_channel/sidebar_base_channel', () => {
}, },
}; };
const wrapper = shallow( const {container} = renderWithContext(
<SidebarBaseChannel {...props}/>, <SidebarBaseChannel {...props}/>,
); );
expect(wrapper).toMatchSnapshot(); expect(container).toMatchSnapshot();
}); });
test('expect leaveChannel to be called when leave public channel ', async () => { test('expect leaveChannel to be called when leave public channel ', async () => {
@ -112,14 +147,12 @@ describe('components/sidebar/sidebar_channel/sidebar_base_channel', () => {
}; };
renderWithContext(<SidebarBaseChannel {...props}/>); renderWithContext(<SidebarBaseChannel {...props}/>);
const user = userEvent.setup();
const optionsBtn = screen.getByRole('button'); const optionsBtn = screen.getByRole('button', {name: /channel options/i});
expect(optionsBtn.classList).toContain('SidebarMenu_menuButton');
await userEvent.click(optionsBtn); // open options await user.click(optionsBtn); // open options
const leaveOption: HTMLElement = screen.getByText('Leave Channel').parentElement!; await user.click(screen.getByRole('menuitem', {name: 'Leave Channel'}));
await userEvent.click(leaveOption);
await waitFor(() => { await waitFor(() => {
expect(mockfn).toHaveBeenCalledTimes(1); expect(mockfn).toHaveBeenCalledTimes(1);
}); });
@ -142,14 +175,12 @@ describe('components/sidebar/sidebar_channel/sidebar_base_channel', () => {
}; };
renderWithContext(<SidebarBaseChannel {...props}/>); renderWithContext(<SidebarBaseChannel {...props}/>);
const user = userEvent.setup();
const optionsBtn = screen.getByRole('button'); const optionsBtn = screen.getByRole('button', {name: /channel options/i});
expect(optionsBtn.classList).toContain('SidebarMenu_menuButton');
await userEvent.click(optionsBtn); // open options await user.click(optionsBtn); // open options
const leaveOption: HTMLElement = screen.getByText('Leave Channel').parentElement!; await user.click(screen.getByRole('menuitem', {name: 'Leave Channel'}));
await userEvent.click(leaveOption);
await waitFor(() => { await waitFor(() => {
expect(mockfn).toHaveBeenCalledTimes(1); expect(mockfn).toHaveBeenCalledTimes(1);
}); });

View file

@ -1,13 +1,21 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react'; import React from 'react';
import type {ChannelType} from '@mattermost/types/channels'; import type {ChannelType} from '@mattermost/types/channels';
import SidebarChannel from 'components/sidebar/sidebar_channel/sidebar_channel'; import SidebarChannel from 'components/sidebar/sidebar_channel/sidebar_channel';
import {renderWithContext, screen} from 'tests/react_testing_utils';
jest.mock('components/tours/onboarding_tour', () => ({
ChannelsAndDirectMessagesTour: () => null,
}));
jest.mock('components/sidebar/sidebar_channel/sidebar_direct_channel', () => () => <div>{'Direct Channel'}</div>);
jest.mock('components/sidebar/sidebar_channel/sidebar_group_channel', () => () => <div>{'Group Channel'}</div>);
describe('components/sidebar/sidebar_channel', () => { describe('components/sidebar/sidebar_channel', () => {
const baseProps = { const baseProps = {
channel: { channel: {
@ -46,11 +54,11 @@ describe('components/sidebar/sidebar_channel', () => {
}; };
test('should match snapshot', () => { test('should match snapshot', () => {
const wrapper = shallow( const {container} = renderWithContext(
<SidebarChannel {...baseProps}/>, <SidebarChannel {...baseProps}/>,
); );
expect(wrapper).toMatchSnapshot(); expect(container).toMatchSnapshot();
}); });
test('should match snapshot when collapsed', () => { test('should match snapshot when collapsed', () => {
@ -59,11 +67,11 @@ describe('components/sidebar/sidebar_channel', () => {
isCategoryCollapsed: true, isCategoryCollapsed: true,
}; };
const wrapper = shallow( const {container} = renderWithContext(
<SidebarChannel {...props}/>, <SidebarChannel {...props}/>,
); );
expect(wrapper).toMatchSnapshot(); expect(container).toMatchSnapshot();
}); });
test('should match snapshot when unread', () => { test('should match snapshot when unread', () => {
@ -73,11 +81,11 @@ describe('components/sidebar/sidebar_channel', () => {
unreadMentions: 1, unreadMentions: 1,
}; };
const wrapper = shallow( const {container} = renderWithContext(
<SidebarChannel {...props}/>, <SidebarChannel {...props}/>,
); );
expect(wrapper).toMatchSnapshot(); expect(container).toMatchSnapshot();
}); });
test('should match snapshot when active', () => { test('should match snapshot when active', () => {
@ -86,11 +94,11 @@ describe('components/sidebar/sidebar_channel', () => {
isCurrentChannel: true, isCurrentChannel: true,
}; };
const wrapper = shallow( const {container} = renderWithContext(
<SidebarChannel {...props}/>, <SidebarChannel {...props}/>,
); );
expect(wrapper).toMatchSnapshot(); expect(container).toMatchSnapshot();
}); });
test('should match snapshot when DM channel', () => { test('should match snapshot when DM channel', () => {
@ -102,11 +110,11 @@ describe('components/sidebar/sidebar_channel', () => {
}, },
}; };
const wrapper = shallow( const {container} = renderWithContext(
<SidebarChannel {...props}/>, <SidebarChannel {...props}/>,
); );
expect(wrapper).toMatchSnapshot(); expect(container).toMatchSnapshot();
}); });
test('should match snapshot when GM channel', () => { test('should match snapshot when GM channel', () => {
@ -118,11 +126,11 @@ describe('components/sidebar/sidebar_channel', () => {
}, },
}; };
const wrapper = shallow( const {container} = renderWithContext(
<SidebarChannel {...props}/>, <SidebarChannel {...props}/>,
); );
expect(wrapper).toMatchSnapshot(); expect(container).toMatchSnapshot();
}); });
test('should not be collapsed when there are unread messages', () => { test('should not be collapsed when there are unread messages', () => {
@ -132,11 +140,11 @@ describe('components/sidebar/sidebar_channel', () => {
isUnread: true, isUnread: true,
}; };
const wrapper = shallow( renderWithContext(
<SidebarChannel {...props}/>, <SidebarChannel {...props}/>,
); );
expect(wrapper.find('.expanded')).toHaveLength(1); expect(screen.getByRole('listitem')).toHaveClass('expanded');
}); });
test('should not be collapsed if channel is current channel', () => { test('should not be collapsed if channel is current channel', () => {
@ -146,10 +154,10 @@ describe('components/sidebar/sidebar_channel', () => {
isCurrentChannel: true, isCurrentChannel: true,
}; };
const wrapper = shallow( renderWithContext(
<SidebarChannel {...props}/>, <SidebarChannel {...props}/>,
); );
expect(wrapper.find('.expanded')).toHaveLength(1); expect(screen.getByRole('listitem')).toHaveClass('expanded');
}); });
}); });

View file

@ -1,13 +1,13 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react'; import React from 'react';
import type {ChannelType} from '@mattermost/types/channels'; import type {ChannelType} from '@mattermost/types/channels';
import {CategoryTypes} from 'mattermost-redux/constants/channel_categories'; import {CategoryTypes} from 'mattermost-redux/constants/channel_categories';
import {renderWithContext, screen, userEvent} from 'tests/react_testing_utils';
import Constants from 'utils/constants'; import Constants from 'utils/constants';
import {TestHelper} from 'utils/test_helper'; import {TestHelper} from 'utils/test_helper';
@ -55,83 +55,96 @@ describe('components/sidebar/sidebar_channel/sidebar_channel_menu', () => {
onMenuToggle: jest.fn(), onMenuToggle: jest.fn(),
}; };
test('should match snapshot and contain correct buttons', () => { const openMenu = async () => {
const wrapper = shallow( const user = userEvent.setup();
const menuButton = screen.getByRole('button', {name: /channel options/i});
await user.click(menuButton);
await screen.findByRole('menu', {name: 'Edit channel menu'});
};
test('should match snapshot and contain correct buttons', async () => {
const {baseElement} = renderWithContext(
<SidebarChannelMenu {...baseProps}/>, <SidebarChannelMenu {...baseProps}/>,
); );
expect(wrapper.find('#favorite-channel_id')).toHaveLength(1); await openMenu();
expect(wrapper.find('#mute-channel_id')).toHaveLength(1);
expect(wrapper.find('#copyLink-channel_id')).toHaveLength(1);
expect(wrapper.find('#addMembers-channel_id')).toHaveLength(1);
expect(wrapper.find('#leave-channel_id')).toHaveLength(1);
expect(wrapper).toMatchSnapshot(); expect(screen.getByRole('menuitem', {name: 'Favorite'})).toBeInTheDocument();
expect(screen.getByRole('menuitem', {name: 'Mute Channel'})).toBeInTheDocument();
expect(screen.getByRole('menuitem', {name: 'Copy Link'})).toBeInTheDocument();
expect(screen.getByRole('menuitem', {name: 'Add Members'})).toBeInTheDocument();
expect(screen.getByRole('menuitem', {name: 'Leave Channel'})).toBeInTheDocument();
expect(baseElement).toMatchSnapshot();
}); });
test('should show correct menu items when channel is unread', () => { test('should show correct menu items when channel is unread', async () => {
const props = { const props = {
...baseProps, ...baseProps,
isUnread: true, isUnread: true,
}; };
const wrapper = shallow( const {baseElement} = renderWithContext(
<SidebarChannelMenu {...props}/>, <SidebarChannelMenu {...props}/>,
); );
expect(wrapper.find('#markAsRead-channel_id')).toHaveLength(1); await openMenu();
expect(screen.getByRole('menuitem', {name: 'Mark as Read'})).toBeInTheDocument();
expect(wrapper).toMatchSnapshot(); expect(baseElement).toMatchSnapshot();
}); });
test('should show correct menu items when channel is read', () => { test('should show correct menu items when channel is read', async () => {
const props = { const props = {
...baseProps, ...baseProps,
isUnread: false, isUnread: false,
}; };
const wrapper = shallow( const {baseElement} = renderWithContext(
<SidebarChannelMenu {...props}/>, <SidebarChannelMenu {...props}/>,
); );
expect(wrapper.find('#markAsUnread-channel_id')).toHaveLength(1); await openMenu();
expect(screen.getByRole('menuitem', {name: 'Mark as Unread'})).toBeInTheDocument();
expect(wrapper).toMatchSnapshot(); expect(baseElement).toMatchSnapshot();
}); });
test('should show correct menu items when channel is favorite', () => { test('should show correct menu items when channel is favorite', async () => {
const props = { const props = {
...baseProps, ...baseProps,
isFavorite: true, isFavorite: true,
}; };
const wrapper = shallow( const {baseElement} = renderWithContext(
<SidebarChannelMenu {...props}/>, <SidebarChannelMenu {...props}/>,
); );
expect(wrapper.find('#favorite-channel_id')).toHaveLength(0); await openMenu();
expect(wrapper.find('#unfavorite-channel_id')).toHaveLength(1); expect(screen.queryByRole('menuitem', {name: 'Favorite'})).not.toBeInTheDocument();
expect(screen.getByRole('menuitem', {name: 'Unfavorite'})).toBeInTheDocument();
expect(wrapper).toMatchSnapshot(); expect(baseElement).toMatchSnapshot();
}); });
test('should show correct menu items when channel is muted', () => { test('should show correct menu items when channel is muted', async () => {
const props = { const props = {
...baseProps, ...baseProps,
isMuted: true, isMuted: true,
}; };
const wrapper = shallow( const {baseElement} = renderWithContext(
<SidebarChannelMenu {...props}/>, <SidebarChannelMenu {...props}/>,
); );
expect(wrapper.find('#mute-channel_id')).toHaveLength(0); await openMenu();
expect(wrapper.find('#unmute-channel_id')).toHaveLength(1); expect(screen.queryByRole('menuitem', {name: 'Mute Channel'})).not.toBeInTheDocument();
expect(screen.getByRole('menuitem', {name: 'Unmute Channel'})).toBeInTheDocument();
expect(wrapper).toMatchSnapshot(); expect(baseElement).toMatchSnapshot();
}); });
test('should show correct menu items when channel is private', () => { test('should show correct menu items when channel is private', async () => {
const props = { const props = {
...baseProps, ...baseProps,
channel: { channel: {
@ -140,15 +153,16 @@ describe('components/sidebar/sidebar_channel/sidebar_channel_menu', () => {
}, },
}; };
const wrapper = shallow( const {baseElement} = renderWithContext(
<SidebarChannelMenu {...props}/>, <SidebarChannelMenu {...props}/>,
); );
expect(wrapper.find('#copyLink-channel_id')).toHaveLength(1); await openMenu();
expect(wrapper).toMatchSnapshot(); expect(screen.getByRole('menuitem', {name: 'Copy Link'})).toBeInTheDocument();
expect(baseElement).toMatchSnapshot();
}); });
test('should show correct menu items when channel is DM', () => { test('should show correct menu items when channel is DM', async () => {
const props = { const props = {
...baseProps, ...baseProps,
channel: { channel: {
@ -157,17 +171,18 @@ describe('components/sidebar/sidebar_channel/sidebar_channel_menu', () => {
}, },
}; };
const wrapper = shallow( const {baseElement} = renderWithContext(
<SidebarChannelMenu {...props}/>, <SidebarChannelMenu {...props}/>,
); );
expect(wrapper.find('#copyLink-channel_id')).toHaveLength(0); await openMenu();
expect(wrapper.find('#addMembers-channel_id')).toHaveLength(0); expect(screen.queryByRole('menuitem', {name: 'Copy Link'})).not.toBeInTheDocument();
expect(screen.queryByRole('menuitem', {name: 'Add Members'})).not.toBeInTheDocument();
expect(wrapper).toMatchSnapshot(); expect(baseElement).toMatchSnapshot();
}); });
test('should show correct menu items when channel is Town Square', () => { test('should show correct menu items when channel is Town Square', async () => {
const props = { const props = {
...baseProps, ...baseProps,
channel: { channel: {
@ -176,16 +191,17 @@ describe('components/sidebar/sidebar_channel/sidebar_channel_menu', () => {
}, },
}; };
const wrapper = shallow( const {baseElement} = renderWithContext(
<SidebarChannelMenu {...props}/>, <SidebarChannelMenu {...props}/>,
); );
expect(wrapper.find('#leave-channel_id')).toHaveLength(0); await openMenu();
expect(screen.queryByRole('menuitem', {name: 'Leave Channel'})).not.toBeInTheDocument();
expect(wrapper).toMatchSnapshot(); expect(baseElement).toMatchSnapshot();
}); });
test('should match snapshot of rendered items when multiselecting channels - public channels and DM category', () => { test('should match snapshot of rendered items when multiselecting channels - public channels and DM category', async () => {
const props = { const props = {
...baseProps, ...baseProps,
categories: [ categories: [
@ -205,14 +221,15 @@ describe('components/sidebar/sidebar_channel/sidebar_channel_menu', () => {
], ],
}; };
const wrapper = shallow( const {baseElement} = renderWithContext(
<SidebarChannelMenu {...props}/>, <SidebarChannelMenu {...props}/>,
); );
expect(wrapper).toMatchSnapshot(); await openMenu();
expect(baseElement).toMatchSnapshot();
}); });
test('should match snapshot of rendered items when multiselecting channels - DM channels and public channels category', () => { test('should match snapshot of rendered items when multiselecting channels - DM channels and public channels category', async () => {
const props = { const props = {
...baseProps, ...baseProps,
categories: [ categories: [
@ -236,10 +253,11 @@ describe('components/sidebar/sidebar_channel/sidebar_channel_menu', () => {
], ],
}; };
const wrapper = shallow( renderWithContext(
<SidebarChannelMenu {...props}/>, <SidebarChannelMenu {...props}/>,
); );
expect(wrapper).toMatchSnapshot(); await openMenu();
expect(document.body).toMatchSnapshot();
}); });
}); });

View file

@ -1,97 +1,53 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`SidebarList should match snapshot 1`] = ` exports[`SidebarList should match snapshot 1`] = `
<Fragment> <div>
<GlobalThreadsLink /> <div
<DraftsLink /> data-testid="GlobalThreadsLink"
<RecapsLink /> />
<div
data-testid="DraftsLink"
/>
<div
data-testid="RecapsLink"
/>
<div <div
aria-label="channel sidebar region" aria-label="channel sidebar region"
className="SidebarNavContainer a11y__region" class="SidebarNavContainer a11y__region"
data-a11y-disable-nav={false} data-a11y-disable-nav="false"
data-a11y-sort-order="7" data-a11y-sort-order="7"
id="sidebar-left" id="sidebar-left"
onTransitionEnd={[Function]}
role="application" role="application"
> >
<UnreadChannelIndicator <div
content={ data-testid="UnreadChannelIndicator"
<Memo(MemoizedFormattedMessage)
defaultMessage="More unreads"
id="sidebar.unreads"
/>
}
extraClass="nav-pills__unread-indicator-top"
name="Top"
onClick={[Function]}
show={false}
/> />
<UnreadChannelIndicator <div
content={ data-testid="UnreadChannelIndicator"
<Memo(MemoizedFormattedMessage)
defaultMessage="More unreads"
id="sidebar.unreads"
/>
}
extraClass="nav-pills__unread-indicator-bottom"
name="Bottom"
onClick={[Function]}
show={false}
/> />
<Scrollbars <div>
onScroll={[Function]} <div
> data-rbd-droppable-context-id="0"
<DragDropContext data-rbd-droppable-id="droppable-categories"
onBeforeCapture={[Function]} id="sidebar-droppable-categories"
onBeforeDragStart={[Function]}
onDragEnd={[Function]}
onDragStart={[Function]}
> >
<Connect(Droppable) <div
direction="vertical" data-testid="sidebar-category"
droppableId="droppable-categories" />
getContainerForClone={[Function]} </div>
ignoreContainerClipping={false} </div>
isCombineEnabled={false}
isDropDisabled={false}
mode="standard"
renderClone={null}
type="SIDEBAR_CATEGORY"
>
<Component />
</Connect(Droppable)>
</DragDropContext>
</Scrollbars>
</div> </div>
</Fragment> </div>
`; `;
exports[`SidebarList should match snapshot 2`] = ` exports[`SidebarList should match snapshot 2`] = `
<div <div
data-rbd-droppable-context-id="0"
data-rbd-droppable-id="droppable-categories"
id="sidebar-droppable-categories" id="sidebar-droppable-categories"
> >
<Connect(SidebarCategory) <div
category={ data-testid="sidebar-category"
Object {
"channel_ids": Array [
"channel_id",
"channel_id_2",
],
"collapsed": false,
"display_name": "custom_category_1",
"id": "category1",
"muted": false,
"sorting": "alpha",
"team_id": "team1",
"type": "custom",
"user_id": "",
}
}
categoryIndex={0}
handleOpenMoreDirectChannelsModal={[MockFunction]}
isNewCategory={false}
key="category1"
setChannelRef={[Function]}
/> />
</div> </div>
`; `;

View file

@ -1,7 +1,6 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react'; import React from 'react';
import type {MovementMode, DropResult} from 'react-beautiful-dnd'; import type {MovementMode, DropResult} from 'react-beautiful-dnd';
@ -11,13 +10,58 @@ import type {TeamType} from '@mattermost/types/teams';
import {CategoryTypes} from 'mattermost-redux/constants/channel_categories'; import {CategoryTypes} from 'mattermost-redux/constants/channel_categories';
import {shallowWithIntl} from 'tests/helpers/intl-test-helper'; import {act, renderWithContext, screen} from 'tests/react_testing_utils';
import {DraggingStates, DraggingStateTypes} from 'utils/constants'; import {DraggingStates, DraggingStateTypes} from 'utils/constants';
import {TestHelper} from 'utils/test_helper'; import {TestHelper} from 'utils/test_helper';
import SidebarList, {type SidebarList as SidebarListComponent} from './sidebar_list'; import {SidebarList as SidebarListComponent} from './sidebar_list';
jest.mock('components/async_load', () => ({
makeAsyncComponent: (displayName: string) => {
const Component = (props: {children?: React.ReactNode}) => (
<div data-testid={displayName}>{props.children}</div>
);
Component.displayName = displayName;
return Component;
},
}));
jest.mock('components/common/scrollbars', () => {
const React = require('react');
return React.forwardRef(({children, onScroll}: {children?: React.ReactNode; onScroll?: () => void}, ref: any) => {
const setRef = (node: HTMLDivElement | null) => {
if (!node) {
return;
}
if (!node.scrollTo) {
node.scrollTo = jest.fn();
}
if (typeof ref === 'function') {
ref(node);
} else if (ref) {
ref.current = node;
}
};
return (
<div
ref={setRef}
onScroll={onScroll}
>
{children}
</div>
);
});
});
jest.mock('components/sidebar/sidebar_category', () => () => <div data-testid='sidebar-category'/>);
describe('SidebarList', () => { describe('SidebarList', () => {
const intl = {
formatMessage: ({defaultMessage}: {defaultMessage: string}) => defaultMessage,
} as any;
const currentChannel = TestHelper.getChannelMock({ const currentChannel = TestHelper.getChannelMock({
id: 'channel_id', id: 'channel_id',
display_name: 'channel_display_name', display_name: 'channel_display_name',
@ -116,34 +160,49 @@ describe('SidebarList', () => {
}; };
test('should match snapshot', () => { test('should match snapshot', () => {
const wrapper = shallowWithIntl( const {container} = renderWithContext(
<SidebarList {...baseProps}/>, <SidebarListComponent
{...baseProps}
intl={intl}
/>,
); );
expect(wrapper).toMatchSnapshot(); expect(container).toMatchSnapshot();
const draggable = wrapper.find('Connect(Droppable)').first(); const sidebarRegion = screen.getByRole('application', {name: /channel sidebar region/i});
const children: any = draggable.prop('children')!; const droppable = sidebarRegion.querySelector('#sidebar-droppable-categories');
const inner = shallow( expect(droppable).toBeInTheDocument();
children({}, {}), expect(droppable).toMatchSnapshot();
);
expect(inner).toMatchSnapshot();
}); });
test('should close sidebar on mobile when channel is selected (ie. changed)', () => { test('should close sidebar on mobile when channel is selected (ie. changed)', () => {
const wrapper = shallowWithIntl( const {rerender} = renderWithContext(
<SidebarList {...baseProps}/>, <SidebarListComponent
{...baseProps}
intl={intl}
/>,
); );
wrapper.setProps({currentChannelId: 'new_channel_id'}); rerender(
<SidebarListComponent
{...baseProps}
intl={intl}
currentChannelId='new_channel_id'
/>,
);
expect(baseProps.actions.close).toHaveBeenCalled(); expect(baseProps.actions.close).toHaveBeenCalled();
}); });
test('should scroll to top when team changes', () => { test('should scroll to top when team changes', () => {
const wrapper = shallowWithIntl( const sidebarListRef = React.createRef<SidebarListComponent>();
<SidebarList {...baseProps}/>, const {rerender} = renderWithContext(
<SidebarListComponent
{...baseProps}
intl={intl}
ref={sidebarListRef}
/>,
); );
const instance = wrapper.instance() as SidebarListComponent; const instance = sidebarListRef.current!;
instance.scrollbar = { instance.scrollbar = {
current: { current: {
@ -156,15 +215,27 @@ describe('SidebarList', () => {
id: 'new_team', id: 'new_team',
}; };
wrapper.setProps({currentTeam: newCurrentTeam}); rerender(
<SidebarListComponent
{...baseProps}
intl={intl}
ref={sidebarListRef}
currentTeam={newCurrentTeam}
/>,
);
expect(instance.scrollbar.current!.scrollTo).toHaveBeenCalledWith({top: 0}); expect(instance.scrollbar.current!.scrollTo).toHaveBeenCalledWith({top: 0});
}); });
test('should display unread scroll indicator when channels appear outside visible area', () => { test('should display unread scroll indicator when channels appear outside visible area', () => {
const wrapper = shallowWithIntl( const sidebarListRef = React.createRef<SidebarListComponent>();
<SidebarList {...baseProps}/>, renderWithContext(
<SidebarListComponent
{...baseProps}
intl={intl}
ref={sidebarListRef}
/>,
); );
const instance = wrapper.instance() as SidebarListComponent; const instance = sidebarListRef.current!;
instance.scrollbar = { instance.scrollbar = {
current: { current: {
@ -178,7 +249,9 @@ describe('SidebarList', () => {
offsetHeight: 0, offsetHeight: 0,
} as any); } as any);
instance.updateUnreadIndicators(); act(() => {
instance.updateUnreadIndicators();
});
expect(instance.state.showTopUnread).toBe(true); expect(instance.state.showTopUnread).toBe(true);
instance.channelRefs.set(unreadChannel.id, { instance.channelRefs.set(unreadChannel.id, {
@ -186,15 +259,22 @@ describe('SidebarList', () => {
offsetHeight: 0, offsetHeight: 0,
} as any); } as any);
instance.updateUnreadIndicators(); act(() => {
instance.updateUnreadIndicators();
});
expect(instance.state.showBottomUnread).toBe(true); expect(instance.state.showBottomUnread).toBe(true);
}); });
test('should scroll to correct position when scrolling to channel', () => { test('should scroll to correct position when scrolling to channel', () => {
const wrapper = shallowWithIntl( const sidebarListRef = React.createRef<SidebarListComponent>();
<SidebarList {...baseProps}/>, renderWithContext(
<SidebarListComponent
{...baseProps}
intl={intl}
ref={sidebarListRef}
/>,
); );
const instance = wrapper.instance() as SidebarListComponent; const instance = sidebarListRef.current!;
instance.scrollToPosition = jest.fn(); instance.scrollToPosition = jest.fn();
@ -216,12 +296,17 @@ describe('SidebarList', () => {
}); });
test('should set the dragging state based on type', () => { test('should set the dragging state based on type', () => {
(global as any).document.querySelectorAll = jest.fn().mockReturnValue([{ const querySpy = jest.spyOn(document, 'querySelectorAll').mockReturnValue([{
style: {}, style: {},
}]); }] as any);
const wrapper = shallowWithIntl( const sidebarListRef = React.createRef<SidebarListComponent>();
<SidebarList {...baseProps}/>, renderWithContext(
<SidebarListComponent
{...baseProps}
intl={intl}
ref={sidebarListRef}
/>,
); );
const categoryBefore = { const categoryBefore = {
@ -234,7 +319,7 @@ describe('SidebarList', () => {
type: DraggingStateTypes.CATEGORY, type: DraggingStateTypes.CATEGORY,
}; };
const instance = wrapper.instance() as SidebarListComponent; const instance = sidebarListRef.current!;
instance.onBeforeCapture(categoryBefore); instance.onBeforeCapture(categoryBefore);
expect(baseProps.actions.setDraggingState).toHaveBeenCalledWith(expectedCategoryBefore); expect(baseProps.actions.setDraggingState).toHaveBeenCalledWith(expectedCategoryBefore);
@ -251,13 +336,20 @@ describe('SidebarList', () => {
instance.onBeforeCapture(channelBefore); instance.onBeforeCapture(channelBefore);
expect(baseProps.actions.setDraggingState).toHaveBeenCalledWith(expectedChannelBefore); expect(baseProps.actions.setDraggingState).toHaveBeenCalledWith(expectedChannelBefore);
querySpy.mockRestore();
}); });
test('should call correct action on dropping item', () => { test('should call correct action on dropping item', () => {
const wrapper = shallowWithIntl( const sidebarListRef = React.createRef<SidebarListComponent>();
<SidebarList {...baseProps}/>, renderWithContext(
<SidebarListComponent
{...baseProps}
intl={intl}
ref={sidebarListRef}
/>,
); );
const instance = wrapper.instance() as SidebarListComponent; const instance = sidebarListRef.current!;
const categoryResult: DropResult = { const categoryResult: DropResult = {
reason: 'DROP', reason: 'DROP',

View file

@ -1,53 +1,109 @@
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
exports[`UnreadChannelIndicator should match snapshot 1`] = ` exports[`UnreadChannelIndicator should match snapshot 1`] = `
<div <div>
className="nav-pills__unread-indicator" <div
id="unreadIndicatorundefined" class="nav-pills__unread-indicator"
onClick={[MockFunction]} id="unreadIndicatorundefined"
> >
<UnreadBelowIcon <span
className="icon icon__unread" class="icon icon__unread"
/> >
<svg
aria-label="Down Arrow Icon"
height="16"
role="img"
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M8.696 2H7.184V11L3.062 6.878L2 7.94L7.94 13.88L13.88 7.94L12.818 6.878L8.696 11V2Z"
/>
</svg>
</span>
</div>
</div> </div>
`; `;
exports[`UnreadChannelIndicator should match snapshot when content is an element 1`] = ` exports[`UnreadChannelIndicator should match snapshot when content is an element 1`] = `
<div <div>
className="nav-pills__unread-indicator nav-pills__unread-indicator--visible" <div
id="unreadIndicatorundefined" class="nav-pills__unread-indicator nav-pills__unread-indicator--visible"
onClick={[MockFunction]} id="unreadIndicatorundefined"
> >
<UnreadBelowIcon <span
className="icon icon__unread" class="icon icon__unread"
/> >
<div> <svg
foo aria-label="Down Arrow Icon"
height="16"
role="img"
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M8.696 2H7.184V11L3.062 6.878L2 7.94L7.94 13.88L13.88 7.94L12.818 6.878L8.696 11V2Z"
/>
</svg>
</span>
<div>
foo
</div>
</div> </div>
</div> </div>
`; `;
exports[`UnreadChannelIndicator should match snapshot when content is text 1`] = ` exports[`UnreadChannelIndicator should match snapshot when content is text 1`] = `
<div <div>
className="nav-pills__unread-indicator nav-pills__unread-indicator--visible" <div
id="unreadIndicatorundefined" class="nav-pills__unread-indicator nav-pills__unread-indicator--visible"
onClick={[MockFunction]} id="unreadIndicatorundefined"
> >
<UnreadBelowIcon <span
className="icon icon__unread" class="icon icon__unread"
/> >
foo <svg
aria-label="Down Arrow Icon"
height="16"
role="img"
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M8.696 2H7.184V11L3.062 6.878L2 7.94L7.94 13.88L13.88 7.94L12.818 6.878L8.696 11V2Z"
/>
</svg>
</span>
foo
</div>
</div> </div>
`; `;
exports[`UnreadChannelIndicator should match snapshot when show is set 1`] = ` exports[`UnreadChannelIndicator should match snapshot when show is set 1`] = `
<div <div>
className="nav-pills__unread-indicator nav-pills__unread-indicator--visible" <div
id="unreadIndicatorundefined" class="nav-pills__unread-indicator nav-pills__unread-indicator--visible"
onClick={[MockFunction]} id="unreadIndicatorundefined"
> >
<UnreadBelowIcon <span
className="icon icon__unread" class="icon icon__unread"
/> >
<svg
aria-label="Down Arrow Icon"
height="16"
role="img"
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M8.696 2H7.184V11L3.062 6.878L2 7.94L7.94 13.88L13.88 7.94L12.818 6.878L8.696 11V2Z"
/>
</svg>
</span>
</div>
</div> </div>
`; `;

View file

@ -1,9 +1,10 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import {shallow} from 'enzyme';
import React from 'react'; import React from 'react';
import {render, screen, userEvent} from 'tests/react_testing_utils';
import UnreadChannelIndicator from './unread_channel_indicator'; import UnreadChannelIndicator from './unread_channel_indicator';
describe('UnreadChannelIndicator', () => { describe('UnreadChannelIndicator', () => {
@ -18,19 +19,19 @@ describe('UnreadChannelIndicator', () => {
show: false, show: false,
}; };
const wrapper = shallow( const {container} = render(
<UnreadChannelIndicator {...props}/>, <UnreadChannelIndicator {...props}/>,
); );
expect(wrapper).toMatchSnapshot(); expect(container).toMatchSnapshot();
}); });
test('should match snapshot when show is set', () => { test('should match snapshot when show is set', () => {
const wrapper = shallow( const {container} = render(
<UnreadChannelIndicator {...baseProps}/>, <UnreadChannelIndicator {...baseProps}/>,
); );
expect(wrapper).toMatchSnapshot(); expect(container).toMatchSnapshot();
}); });
test('should match snapshot when content is text', () => { test('should match snapshot when content is text', () => {
@ -39,11 +40,11 @@ describe('UnreadChannelIndicator', () => {
content: 'foo', content: 'foo',
}; };
const wrapper = shallow( const {container} = render(
<UnreadChannelIndicator {...props}/>, <UnreadChannelIndicator {...props}/>,
); );
expect(wrapper).toMatchSnapshot(); expect(container).toMatchSnapshot();
}); });
test('should match snapshot when content is an element', () => { test('should match snapshot when content is an element', () => {
@ -52,25 +53,26 @@ describe('UnreadChannelIndicator', () => {
content: <div>{'foo'}</div>, content: <div>{'foo'}</div>,
}; };
const wrapper = shallow( const {container} = render(
<UnreadChannelIndicator {...props}/>, <UnreadChannelIndicator {...props}/>,
); );
expect(wrapper).toMatchSnapshot(); expect(container).toMatchSnapshot();
}); });
test('should have called onClick', () => { test('should have called onClick', async () => {
const props = { const props = {
...baseProps, ...baseProps,
content: <div>{'foo'}</div>, content: <div>{'foo'}</div>,
name: 'name', name: 'name',
}; };
const wrapper = shallow( render(
<UnreadChannelIndicator {...props}/>, <UnreadChannelIndicator {...props}/>,
); );
wrapper.simulate('click'); const user = userEvent.setup();
await user.click(screen.getByText('foo'));
expect(props.onClick).toHaveBeenCalledTimes(1); expect(props.onClick).toHaveBeenCalledTimes(1);
}); });
}); });