From 5443f9d37156b17ba70755f9896ea62a887bb754 Mon Sep 17 00:00:00 2001 From: abhinav700 <78839973+abhinav700@users.noreply.github.com> Date: Mon, 4 Sep 2023 14:15:00 +0530 Subject: [PATCH] MM-47110 : Migrate "components/advanced_create_comment/advanced_create_comment.test.jsx" to Typescript (#24257) --- ... => advanced_create_comment.test.tsx.snap} | 185 +++++- ...t.jsx => advanced_create_comment.test.tsx} | 624 +++++++++--------- .../advanced_create_comment.tsx | 12 +- webapp/channels/src/types/global.d.ts | 1 + webapp/channels/src/utils/test_helper.ts | 13 + 5 files changed, 512 insertions(+), 323 deletions(-) rename webapp/channels/src/components/advanced_create_comment/__snapshots__/{advanced_create_comment.test.jsx.snap => advanced_create_comment.test.tsx.snap} (64%) rename webapp/channels/src/components/advanced_create_comment/{advanced_create_comment.test.jsx => advanced_create_comment.test.tsx} (72%) diff --git a/webapp/channels/src/components/advanced_create_comment/__snapshots__/advanced_create_comment.test.jsx.snap b/webapp/channels/src/components/advanced_create_comment/__snapshots__/advanced_create_comment.test.tsx.snap similarity index 64% rename from webapp/channels/src/components/advanced_create_comment/__snapshots__/advanced_create_comment.test.jsx.snap rename to webapp/channels/src/components/advanced_create_comment/__snapshots__/advanced_create_comment.test.tsx.snap index 6f3b42ac39..4378cbf03f 100644 --- a/webapp/channels/src/components/advanced_create_comment/__snapshots__/advanced_create_comment.test.jsx.snap +++ b/webapp/channels/src/components/advanced_create_comment/__snapshots__/advanced_create_comment.test.tsx.snap @@ -14,15 +14,62 @@ exports[`components/AdvancedCreateComment should match snapshot when cannot post currentUserId="zaktnt8bpbgu8mb6ez9k64r7sa" draft={ Object { + "channelId": "", + "createAt": 0, "fileInfos": Array [ - Object {}, - Object {}, - Object {}, + Object { + "archived": false, + "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, + }, + Object { + "archived": false, + "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, + }, + Object { + "archived": false, + "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, + }, ], "message": "Test message", - "uploadsInProgress": Array [ - Object {}, - ], + "rootId": "", + "updateAt": 0, + "uploadsInProgress": Array [], } } emitTypingEvent={[Function]} @@ -87,8 +134,12 @@ exports[`components/AdvancedCreateComment should match snapshot, comment with me currentUserId="zaktnt8bpbgu8mb6ez9k64r7sa" draft={ Object { + "channelId": "", + "createAt": 0, "fileInfos": Array [], "message": "Test message", + "rootId": "", + "updateAt": 0, "uploadsInProgress": Array [], } } @@ -155,15 +206,62 @@ exports[`components/AdvancedCreateComment should match snapshot, emoji picker di currentUserId="zaktnt8bpbgu8mb6ez9k64r7sa" draft={ Object { + "channelId": "", + "createAt": 0, "fileInfos": Array [ - Object {}, - Object {}, - Object {}, + Object { + "archived": false, + "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, + }, + Object { + "archived": false, + "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, + }, + Object { + "archived": false, + "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, + }, ], "message": "Test message", - "uploadsInProgress": Array [ - Object {}, - ], + "rootId": "", + "updateAt": 0, + "uploadsInProgress": Array [], } } emitTypingEvent={[Function]} @@ -228,8 +326,12 @@ exports[`components/AdvancedCreateComment should match snapshot, empty comment 1 currentUserId="zaktnt8bpbgu8mb6ez9k64r7sa" draft={ Object { + "channelId": "", + "createAt": 0, "fileInfos": Array [], "message": "", + "rootId": "", + "updateAt": 0, "uploadsInProgress": Array [], } } @@ -296,15 +398,62 @@ exports[`components/AdvancedCreateComment should match snapshot, non-empty messa currentUserId="zaktnt8bpbgu8mb6ez9k64r7sa" draft={ Object { + "channelId": "", + "createAt": 0, "fileInfos": Array [ - Object {}, - Object {}, - Object {}, + Object { + "archived": false, + "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, + }, + Object { + "archived": false, + "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, + }, + Object { + "archived": false, + "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, + }, ], "message": "Test message", - "uploadsInProgress": Array [ - Object {}, - ], + "rootId": "", + "updateAt": 0, + "uploadsInProgress": Array [], } } emitTypingEvent={[Function]} diff --git a/webapp/channels/src/components/advanced_create_comment/advanced_create_comment.test.jsx b/webapp/channels/src/components/advanced_create_comment/advanced_create_comment.test.tsx similarity index 72% rename from webapp/channels/src/components/advanced_create_comment/advanced_create_comment.test.jsx rename to webapp/channels/src/components/advanced_create_comment/advanced_create_comment.test.tsx index 1c93ad3e2d..3eb5e72f5e 100644 --- a/webapp/channels/src/components/advanced_create_comment/advanced_create_comment.test.jsx +++ b/webapp/channels/src/components/advanced_create_comment/advanced_create_comment.test.tsx @@ -10,21 +10,26 @@ import {testComponentForMarkdownHotkeys} from 'tests/helpers/markdown_hotkey_hel import Constants, {ModalIdentifiers} from 'utils/constants'; import {execCommandInsertText} from 'utils/exec_commands'; -import AdvancedCreateComment from 'components/advanced_create_comment/advanced_create_comment'; +import AdvancedCreateComment, {Props} from 'components/advanced_create_comment/advanced_create_comment'; import AdvanceTextEditor from 'components/advanced_text_editor/advanced_text_editor'; - +import {ActionResult} from 'mattermost-redux/types/actions'; +import {PostDraft} from 'types/store/draft'; +import {ServerError} from '@mattermost/types/errors'; +import {TestHelper} from 'utils/test_helper'; +import {FileInfo} from '@mattermost/types/files'; jest.mock('utils/exec_commands', () => ({ execCommandInsertText: jest.fn(), })); describe('components/AdvancedCreateComment', () => { jest.useFakeTimers(); + let spy: jest.SpyInstance; beforeEach(() => { - jest.spyOn(window, 'requestAnimationFrame').mockImplementation((cb) => setTimeout(cb, 16)); + spy = jest.spyOn(window, 'requestAnimationFrame').mockImplementation((cb) => setTimeout(cb, 16)); }); afterEach(() => { - window.requestAnimationFrame.mockRestore(); + spy.mockRestore(); }); const currentTeamId = 'current-team-id'; @@ -33,18 +38,19 @@ describe('components/AdvancedCreateComment', () => { const latestPostId = '3498nv24823948v23m4nv34'; const currentUserId = 'zaktnt8bpbgu8mb6ez9k64r7sa'; - const baseProps = { + const emptyDraft: PostDraft = TestHelper.getPostDraftMock({message: ''}); + const defaultFileInfo: FileInfo = TestHelper.getFileInfoMock(); + + const baseProps: Props = { channelId, currentTeamId, currentUserId, rootId, rootDeleted: false, channelMembersCount: 3, - draft: { - message: 'Test message', - uploadsInProgress: [{}], - fileInfos: [{}, {}, {}], - }, + draft: TestHelper.getPostDraftMock({ + fileInfos: [defaultFileInfo, defaultFileInfo, defaultFileInfo], + }), isRemoteDraft: false, enableAddButton: true, ctrlSend: false, @@ -68,10 +74,9 @@ describe('components/AdvancedCreateComment', () => { maxPostSize: Constants.DEFAULT_CHARACTER_LIMIT, rhsExpanded: false, badConnection: false, - getChannelTimezones: jest.fn(() => Promise.resolve([])), + getChannelTimezones: jest.fn(() => Promise.resolve({data: '', error: ''})), isTimezoneEnabled: false, selectedPostFocussedAt: 0, - isMarkdownPreviewEnabled: true, canPost: true, canUploadFiles: true, isFormattingBarHidden: false, @@ -81,22 +86,28 @@ describe('components/AdvancedCreateComment', () => { useCustomGroupMentions: true, openModal: jest.fn(), postEditorActions: [], + emitShortcutReactToLastPostFrom(): void { + throw new Error('Function not implemented.'); + }, + groupsWithAllowReference: null, + channelMemberCountsByGroup: undefined as any, + savePreferences(): ActionResult { + throw new Error('Function not implemented.'); + }, }; - const emptyDraft = { - message: '', - uploadsInProgress: [], - fileInfos: [], - }; + const submitEvent = { + preventDefault: jest.fn(), + } as unknown as React.FormEvent; test('should match snapshot, empty comment', () => { - const draft = emptyDraft; + const draft: PostDraft = emptyDraft; const isRemoteDraft = false; const enableAddButton = false; const ctrlSend = true; - const props = {...baseProps, draft, isRemoteDraft, enableAddButton, ctrlSend}; + const props: any = {...baseProps, draft, isRemoteDraft, enableAddButton, ctrlSend}; - const wrapper = shallow( + const wrapper = shallow( , ); @@ -107,16 +118,12 @@ describe('components/AdvancedCreateComment', () => { const clearCommentDraftUploads = jest.fn(); const onResetHistoryIndex = jest.fn(); const getChannelMemberCountsByGroup = jest.fn(); - const draft = { - message: 'Test message', - uploadsInProgress: [], - fileInfos: [], - }; + const draft: PostDraft = TestHelper.getPostDraftMock(); const isRemoteDraft = false; const ctrlSend = true; - const props = {...baseProps, ctrlSend, draft, isRemoteDraft, clearCommentDraftUploads, onResetHistoryIndex, getChannelMemberCountsByGroup}; + const props: any = {...baseProps, ctrlSend, draft, isRemoteDraft, clearCommentDraftUploads, onResetHistoryIndex, getChannelMemberCountsByGroup}; - const wrapper = shallow( + const wrapper = shallow( , ); @@ -133,14 +140,12 @@ describe('components/AdvancedCreateComment', () => { }); test('should call searchAssociatedGroupsForReference if there is one mention in the draft', () => { - const draft = { + const draft: PostDraft = TestHelper.getPostDraftMock({ message: '@group', - uploadsInProgress: [], - fileInfos: [], - }; + }); - const searchAssociatedGroupsForReference = jest.fn(); - const props = {...baseProps, draft, searchAssociatedGroupsForReference}; + const searchAssociatedGroupsForReference: any = jest.fn(); + const props: any = {...baseProps, draft, searchAssociatedGroupsForReference}; shallow(); @@ -148,13 +153,12 @@ describe('components/AdvancedCreateComment', () => { }); test('should call getChannelMemberCountsByGroup if there is more than one mention in the draft', () => { - const draft = { + const draft: PostDraft = TestHelper.getPostDraftMock({ message: '@group @othergroup', - uploadsInProgress: [], - fileInfos: [], - }; + + }); const getChannelMemberCountsByGroup = jest.fn(); - const props = {...baseProps, draft, getChannelMemberCountsByGroup}; + const props: any = {...baseProps, draft, getChannelMemberCountsByGroup}; shallow(); @@ -164,29 +168,24 @@ describe('components/AdvancedCreateComment', () => { test('should not call getChannelMemberCountsByGroup, without group mentions permission or license', () => { const useLDAPGroupMentions = false; const useCustomGroupMentions = false; - const draft = { + const draft: PostDraft = TestHelper.getPostDraftMock({ message: '@group @othergroup', - uploadsInProgress: [], - fileInfos: [], - }; + + }); const getChannelMemberCountsByGroup = jest.fn(); - const props = {...baseProps, useLDAPGroupMentions, useCustomGroupMentions, getChannelMemberCountsByGroup, draft}; + const props: any = {...baseProps, useLDAPGroupMentions, useCustomGroupMentions, getChannelMemberCountsByGroup, draft}; - shallow(); + shallow(); // should not load channel member counts on mount without useGroupmentions expect(getChannelMemberCountsByGroup).not.toHaveBeenCalled(); }); test('should match snapshot, non-empty message and uploadsInProgress + fileInfos', () => { - const draft = { - message: 'Test message', - uploadsInProgress: [{}], - fileInfos: [{}, {}, {}], - }; + const draft: PostDraft = TestHelper.getPostDraftMock(); - const wrapper = shallow( + const wrapper = shallow( , ); @@ -195,7 +194,7 @@ describe('components/AdvancedCreateComment', () => { }); test('should correctly change state when toggleEmojiPicker is called', () => { - const wrapper = shallow( + const wrapper = shallow( , ); @@ -207,7 +206,7 @@ describe('components/AdvancedCreateComment', () => { }); test('should correctly change state when hideEmojiPicker is called', () => { - const wrapper = shallow( + const wrapper = shallow( , ); @@ -217,11 +216,11 @@ describe('components/AdvancedCreateComment', () => { test('should correctly update draft when handleEmojiClick is called', () => { const onUpdateCommentDraft = jest.fn(); - const draft = emptyDraft; + const draft: PostDraft = emptyDraft; const enableAddButton = false; - const props = {...baseProps, draft, onUpdateCommentDraft, enableAddButton}; + const props: any = {...baseProps, draft, onUpdateCommentDraft, enableAddButton}; - const wrapper = shallow( + const wrapper = shallow( , ); @@ -237,9 +236,9 @@ describe('components/AdvancedCreateComment', () => { return document.createElement('div'); }; - wrapper.instance().textboxRef.current = {getInputBox: jest.fn(mockImpl), getBoundingClientRect: jest.fn(), focus: jest.fn()}; + (wrapper.instance() as any).textboxRef.current = {getInputBox: jest.fn(mockImpl), getBoundingClientRect: jest.fn(), focus: jest.fn()}; - wrapper.instance().handleEmojiClick({name: 'smile'}); + wrapper.instance().handleEmojiClick({name: 'smile'} as any); jest.advanceTimersByTime(Constants.SAVE_DRAFT_TIMEOUT); expect(onUpdateCommentDraft).toHaveBeenCalled(); @@ -248,37 +247,37 @@ describe('components/AdvancedCreateComment', () => { expect(onUpdateCommentDraft.mock.calls[0][0]).toEqual( expect.objectContaining({message: ':smile: '}), ); - expect(wrapper.state().draft.message).toBe(':smile: '); + expect(wrapper.state().draft!.message).toBe(':smile: '); - wrapper.setState({draft: {message: 'test', uploadsInProgress: [], fileInfos: []}, + wrapper.setState({draft: TestHelper.getPostDraftMock({message: 'test', uploadsInProgress: [], fileInfos: []}), caretPosition: 'test'.length, // cursor is at the end }); - wrapper.instance().handleEmojiClick({name: 'smile'}); + wrapper.instance().handleEmojiClick({name: 'smile'} as any); // Message with no space at the end jest.advanceTimersByTime(Constants.SAVE_DRAFT_TIMEOUT); expect(onUpdateCommentDraft.mock.calls[1][0]).toEqual( expect.objectContaining({message: 'test :smile: '}), ); - expect(wrapper.state().draft.message).toBe('test :smile: '); + expect(wrapper.state().draft!.message).toBe('test :smile: '); - wrapper.setState({draft: {message: 'test ', uploadsInProgress: [], fileInfos: []}, + wrapper.setState({draft: TestHelper.getPostDraftMock({message: 'test ', uploadsInProgress: [], fileInfos: []}), caretPosition: 'test '.length, // cursor is at the end }); - wrapper.instance().handleEmojiClick({name: 'smile'}); + wrapper.instance().handleEmojiClick({name: 'smile'} as any); // Message with space at the end jest.advanceTimersByTime(Constants.SAVE_DRAFT_TIMEOUT); expect(onUpdateCommentDraft.mock.calls[2][0]).toEqual( expect.objectContaining({message: 'test :smile: '}), ); - expect(wrapper.state().draft.message).toBe('test :smile: '); + expect(wrapper.state().draft!.message).toBe('test :smile: '); expect(wrapper.state().showEmojiPicker).toBe(false); }); test('handlePostError should update state with the correct error', () => { - const wrapper = shallow( + const wrapper = shallow( , ); @@ -289,16 +288,18 @@ describe('components/AdvancedCreateComment', () => { expect(wrapper.state().postError).toBe('test error 2'); }); + // debug next test('handleUploadError should update state with the correct error', () => { const updateCommentDraftWithRootId = jest.fn(); - const draft = { + const fileInfoObject: FileInfo = TestHelper.getFileInfoMock(); + const draft: PostDraft = TestHelper.getPostDraftMock({ message: 'Test message', - uploadsInProgress: [1, 2, 3], - fileInfos: [{}, {}, {}], - }; - const props = {...baseProps, draft, updateCommentDraftWithRootId}; + uploadsInProgress: ['1', '2', '3'], + fileInfos: [fileInfoObject, fileInfoObject, fileInfoObject], + }); + const props: any = {...baseProps, draft, updateCommentDraftWithRootId}; - const wrapper = shallow( + const wrapper = shallow( , ); @@ -307,26 +308,26 @@ describe('components/AdvancedCreateComment', () => { const testError1 = 'test error 1'; wrapper.setState({draft}); instance.draftsForPost[props.rootId] = draft; - instance.handleUploadError(testError1, 1, null, props.rootId); + instance.handleUploadError(testError1, '1', undefined, props.rootId); expect(updateCommentDraftWithRootId).toHaveBeenCalled(); expect(updateCommentDraftWithRootId.mock.calls[0][0]).toEqual(props.rootId); expect(updateCommentDraftWithRootId.mock.calls[0][1]).toEqual( - expect.objectContaining({uploadsInProgress: [2, 3]}), + expect.objectContaining({uploadsInProgress: ['2', '3']}), ); - expect(wrapper.state().serverError.message).toBe(testError1); - expect(wrapper.state().draft.uploadsInProgress).toEqual([2, 3]); + expect(wrapper.state().serverError!.message).toBe(testError1); + expect(wrapper.state().draft!.uploadsInProgress).toEqual(['2', '3']); const testError2 = 'test error 2'; - instance.handleUploadError(testError2, '', null, props.rootId); + instance.handleUploadError(testError2, '', undefined, props.rootId); // should not call onUpdateCommentDraft expect(updateCommentDraftWithRootId.mock.calls.length).toBe(1); - expect(wrapper.state().serverError.message).toBe(testError2); + expect(wrapper.state().serverError!.message).toBe(testError2); }); test('should call openModal when showPostDeletedModal is called', () => { - const wrapper = shallow( + const wrapper = shallow( , ); @@ -337,74 +338,80 @@ describe('components/AdvancedCreateComment', () => { test('handleUploadStart should update comment draft correctly', () => { const onUpdateCommentDraft = jest.fn(); - const draft = { - message: 'Test message', - uploadsInProgress: [1, 2, 3], - fileInfos: [{}, {}, {}], - }; - const props = {...baseProps, onUpdateCommentDraft, draft}; + const draft: PostDraft = TestHelper.getPostDraftMock({ + uploadsInProgress: ['1', '2', '3'], + fileInfos: [TestHelper.getFileInfoMock(), TestHelper.getFileInfoMock(), TestHelper.getFileInfoMock()], + }); - const wrapper = shallow( + const props: any = {...baseProps, onUpdateCommentDraft, draft}; + + const wrapper = shallow( , ); const focusTextbox = jest.fn(); wrapper.setState({draft}); wrapper.instance().focusTextbox = focusTextbox; - wrapper.instance().handleUploadStart([4, 5]); + wrapper.instance().handleUploadStart(['4', '5']); expect(onUpdateCommentDraft).toHaveBeenCalled(); expect(onUpdateCommentDraft.mock.calls[0][0]).toEqual( - expect.objectContaining({uploadsInProgress: [1, 2, 3, 4, 5]}), + expect.objectContaining({uploadsInProgress: ['1', '2', '3', '4', '5']}), ); - expect(wrapper.state().draft.uploadsInProgress === [1, 2, 3, 4, 5]); + expect(wrapper.state().draft!.uploadsInProgress).toEqual(['1', '2', '3', '4', '5']); expect(focusTextbox).toHaveBeenCalled(); }); test('handleFileUploadComplete should update comment draft correctly', () => { - const updateCommentDraftWithRootId = jest.fn(); - const fileInfos = [{id: '1', name: 'aaa', create_at: 100}, {id: '2', name: 'bbb', create_at: 200}]; - const draft = { - message: 'Test message', - uploadsInProgress: [1, 2, 3], - fileInfos, - }; - const props = {...baseProps, updateCommentDraftWithRootId, draft}; + const updateCommentDraftWithRootId: any = jest.fn(); + const fileInfos = [ + TestHelper.getFileInfoMock({id: '1', name: 'aaa', create_at: 100}), + TestHelper.getFileInfoMock({id: '2', name: 'bbb', create_at: 200}), + ]; - const wrapper = shallow( + const draft: PostDraft = TestHelper.getPostDraftMock({ + uploadsInProgress: ['1', '2', '3'], + fileInfos, + }); + const props: any = {...baseProps, updateCommentDraftWithRootId, draft}; + + const wrapper: any = shallow( , ); - const instance = wrapper.instance(); + const instance: any = wrapper.instance(); wrapper.setState({draft}); instance.draftsForPost[props.rootId] = draft; - const uploadCompleteFileInfo = [{id: '3', name: 'ccc', create_at: 300}]; - const expectedNewFileInfos = fileInfos.concat(uploadCompleteFileInfo); - instance.handleFileUploadComplete(uploadCompleteFileInfo, [3], null, props.rootId); + const uploadCompleteFileInfo: any = [{id: '3', name: 'ccc', create_at: 300}]; + const expectedNewFileInfos: any = fileInfos.concat(uploadCompleteFileInfo); + instance.handleFileUploadComplete(uploadCompleteFileInfo, ['3'], null as any, props.rootId); jest.advanceTimersByTime(Constants.SAVE_DRAFT_TIMEOUT); expect(updateCommentDraftWithRootId).toHaveBeenCalled(); expect(updateCommentDraftWithRootId.mock.calls[0][0]).toEqual(props.rootId); expect(updateCommentDraftWithRootId.mock.calls[0][1]).toEqual( - expect.objectContaining({uploadsInProgress: [1, 2], fileInfos: expectedNewFileInfos}), + expect.objectContaining({uploadsInProgress: ['1', '2'], fileInfos: expectedNewFileInfos}), ); - expect(wrapper.state().draft.uploadsInProgress).toEqual([1, 2]); - expect(wrapper.state().draft.fileInfos).toEqual(expectedNewFileInfos); + expect(wrapper.state().draft!.uploadsInProgress).toEqual(['1', '2']); + expect(wrapper.state().draft!.fileInfos).toEqual(expectedNewFileInfos); }); test('should open PostDeletedModal when createPostErrorId === api.post.create_post.root_id.app_error', () => { const onUpdateCommentDraft = jest.fn(); - const draft = { + const draft: PostDraft = TestHelper.getPostDraftMock({ message: 'Test message', - uploadsInProgress: [1, 2, 3], - fileInfos: [{id: '1', name: 'aaa', create_at: 100}, {id: '2', name: 'bbb', create_at: 200}], - }; - const props = {...baseProps, onUpdateCommentDraft, draft}; + uploadsInProgress: ['1', '2', '3'], + fileInfos: [ + TestHelper.getFileInfoMock({id: '1', name: 'aaa', create_at: 100}), + TestHelper.getFileInfoMock({id: '2', name: 'bbb', create_at: 200}), + ], + }); + const props: any = {...baseProps, onUpdateCommentDraft, draft}; - const wrapper = shallow( + const wrapper = shallow( , ); @@ -418,19 +425,22 @@ describe('components/AdvancedCreateComment', () => { test('should open PostDeletedModal when message is submitted to deleted root', () => { const onUpdateCommentDraft = jest.fn(); - const draft = { + const draft: PostDraft = TestHelper.getPostDraftMock({ message: 'Test message', - uploadsInProgress: [1, 2, 3], - fileInfos: [{id: '1', name: 'aaa', create_at: 100}, {id: '2', name: 'bbb', create_at: 200}], - }; - const props = {...baseProps, onUpdateCommentDraft, draft}; + uploadsInProgress: ['1', '2', '3'], + fileInfos: [ + TestHelper.getFileInfoMock({id: '1', name: 'aaa', create_at: 100}), + TestHelper.getFileInfoMock({id: '2', name: 'bbb', create_at: 200}), + ], + }); + const props: any = {...baseProps, onUpdateCommentDraft, draft}; - const wrapper = shallow( + const wrapper = shallow( , ); wrapper.setProps({rootDeleted: true}); - wrapper.instance().handleSubmit({preventDefault: jest.fn()}); + wrapper.instance().handleSubmit(submitEvent); expect(props.openModal).toHaveBeenCalledTimes(1); expect(props.openModal.mock.calls[0][0]).toMatchObject({ @@ -439,15 +449,17 @@ describe('components/AdvancedCreateComment', () => { }); describe('focusTextbox', () => { - const draft = { - message: 'Test message', - uploadsInProgress: [1, 2, 3], - fileInfos: [{id: '1', name: 'aaa', create_at: 100}, {id: '2', name: 'bbb', create_at: 200}], - }; + const draft: PostDraft = TestHelper.getPostDraftMock({ + uploadsInProgress: ['1', '2', '3'], + fileInfos: [ + TestHelper.getFileInfoMock({id: '1', name: 'aaa', create_at: 100}), + TestHelper.getFileInfoMock({id: '2', name: 'bbb', create_at: 200}), + ], + }); it('is called when rootId changes', () => { - const props = {...baseProps, draft}; - const wrapper = shallow( + const props: any = {...baseProps, draft}; + const wrapper = shallow( , ); @@ -466,8 +478,8 @@ describe('components/AdvancedCreateComment', () => { }); it('is called when selectPostFocussedAt changes', () => { - const props = {...baseProps, draft, selectedPostFocussedAt: 1000}; - const wrapper = shallow( + const props: any = {...baseProps, draft, selectedPostFocussedAt: 1000}; + const wrapper = shallow( , ); @@ -486,8 +498,8 @@ describe('components/AdvancedCreateComment', () => { }); it('is not called when rootId and selectPostFocussedAt have not changed', () => { - const props = {...baseProps, draft, selectedPostFocussedAt: 1000}; - const wrapper = shallow( + const props: any = {...baseProps, draft, selectedPostFocussedAt: 1000}; + const wrapper = shallow( , ); @@ -503,22 +515,21 @@ describe('components/AdvancedCreateComment', () => { }); test('handleChange should update comment draft correctly', () => { - const draft = { - message: 'Test message', - uploadsInProgress: [1, 2, 3], - fileInfos: [{}, {}, {}], - }; + const draft: PostDraft = TestHelper.getPostDraftMock({ + uploadsInProgress: ['1', '2', '3'], + fileInfos: [TestHelper.getFileInfoMock(), TestHelper.getFileInfoMock(), TestHelper.getFileInfoMock()], + }); const scrollToBottom = jest.fn(); - const props = {...baseProps, draft, scrollToBottom}; + const props: any = {...baseProps, draft, scrollToBottom}; - const wrapper = shallow( + const wrapper = shallow( , ); const testMessage = 'new msg'; - wrapper.instance().handleChange({target: {value: testMessage}}); + wrapper.instance().handleChange({target: {value: testMessage}} as any); // The callback won't we called until after a short delay expect(baseProps.onUpdateCommentDraft).not.toHaveBeenCalled(); @@ -526,103 +537,108 @@ describe('components/AdvancedCreateComment', () => { jest.runOnlyPendingTimers(); jest.advanceTimersByTime(Constants.SAVE_DRAFT_TIMEOUT); expect(baseProps.onUpdateCommentDraft).toHaveBeenCalled(); - expect(baseProps.onUpdateCommentDraft.mock.calls[0][0]).toEqual( + + expect((baseProps.onUpdateCommentDraft as jest.Mock).mock.calls[0][0]).toEqual( expect.objectContaining({message: testMessage}), ); - expect(wrapper.state().draft.message).toBe(testMessage); + expect(wrapper.state().draft!.message).toBe(testMessage); expect(scrollToBottom).toHaveBeenCalled(); }); + // debug it('handleChange should throw away invalid command error if user resumes typing', async () => { const onUpdateCommentDraft = jest.fn(); - const error = new Error('No command found'); + const error: ServerError = {message: 'No command found'}; + error.server_error_id = 'api.command.execute_command.not_found.app_error'; const onSubmit = jest.fn(() => Promise.reject(error)); + const defaultFileInfo = TestHelper.getFileInfoMock(); - const draft = { + const draft: PostDraft = TestHelper.getPostDraftMock({ message: '/fakecommand other text', - uploadsInProgress: [1, 2, 3], - fileInfos: [{}, {}, {}], - }; - const props = {...baseProps, onUpdateCommentDraft, draft, onSubmit}; + uploadsInProgress: ['1', '2', '3'], + fileInfos: [defaultFileInfo, defaultFileInfo, defaultFileInfo], + }); + const props: any = {...baseProps, onUpdateCommentDraft, draft, onSubmit}; - const wrapper = shallow( + const wrapper = shallow( , ); - await wrapper.instance().handleSubmit({preventDefault: jest.fn()}); + await wrapper.instance().handleSubmit(submitEvent); - expect(onSubmit).toHaveBeenCalledWith({ + expect(onSubmit).toHaveBeenCalledWith(TestHelper.getPostDraftMock({ message: '/fakecommand other text', uploadsInProgress: [], - fileInfos: [{}, {}, {}], - }, {ignoreSlash: false}); + fileInfos: [defaultFileInfo, defaultFileInfo, defaultFileInfo], + }), {ignoreSlash: false}); wrapper.instance().handleChange({ target: {value: 'some valid text'}, - }); + } as any); - wrapper.instance().handleSubmit({preventDefault: jest.fn()}); + wrapper.instance().handleSubmit(submitEvent); - expect(onSubmit).toHaveBeenCalledWith({ + expect(onSubmit).toHaveBeenCalledWith(TestHelper.getPostDraftMock({ message: 'some valid text', uploadsInProgress: [], - fileInfos: [{}, {}, {}], - }, {ignoreSlash: false}); + fileInfos: [defaultFileInfo, defaultFileInfo, defaultFileInfo], + }), {ignoreSlash: false}); }); test('should scroll to bottom when uploadsInProgress increase', () => { - const draft = { - message: 'Test message', - uploadsInProgress: [1, 2, 3], - fileInfos: [{}, {}, {}], - }; + const draft: PostDraft = TestHelper.getPostDraftMock({ + uploadsInProgress: ['1', '2', '3'], + fileInfos: [TestHelper.getFileInfoMock(), TestHelper.getFileInfoMock(), TestHelper.getFileInfoMock()], + }); const scrollToBottom = jest.fn(); - const props = {...baseProps, draft, scrollToBottom}; + const props: any = {...baseProps, draft, scrollToBottom}; - const wrapper = shallow( + const wrapper = shallow( , ); - wrapper.setState({draft: {...draft, uploadsInProgress: [1, 2, 3, 4]}}); + wrapper.setState({draft: {...draft, uploadsInProgress: ['1', '2', '3', '4']}}); expect(scrollToBottom).toHaveBeenCalled(); }); test('handleSubmit should call onSubmit prop', () => { const onSubmit = jest.fn(); - const draft = { + const defaultFileInfo = TestHelper.getFileInfoMock(); + const draft: PostDraft = TestHelper.getPostDraftMock({ message: 'Test message', uploadsInProgress: [], - fileInfos: [{}, {}, {}], - }; - const props = {...baseProps, draft, onSubmit}; + fileInfos: [defaultFileInfo, defaultFileInfo, defaultFileInfo], + }); + const props: any = {...baseProps, draft, onSubmit}; - const wrapper = shallow( + const wrapper = shallow( , ); const preventDefault = jest.fn(); - wrapper.instance().handleSubmit({preventDefault}); + wrapper.instance().handleSubmit({...submitEvent, preventDefault}); expect(onSubmit).toHaveBeenCalled(); expect(preventDefault).toHaveBeenCalled(); }); describe('handleSubmit', () => { - let onSubmit; - let preventDefault; + let onSubmit: any; + let preventDefault: any; beforeEach(() => { onSubmit = jest.fn(); preventDefault = jest.fn(); + submitEvent.preventDefault = preventDefault; }); - ['channel', 'all', 'here'].forEach((mention) => { + ['channel', 'all', 'here'].forEach((mention: string) => { describe(`should not show Confirm Modal for @${mention} mentions`, () => { it('when channel member count too low', () => { - const props = { + const props: any = { ...baseProps, draft: { message: `Test message @${mention}`, @@ -634,18 +650,18 @@ describe('components/AdvancedCreateComment', () => { enableConfirmNotificationsToChannel: true, }; - const wrapper = shallow( + const wrapper = shallow( , ); - wrapper.instance().handleSubmit({preventDefault}); + wrapper.instance().handleSubmit(submitEvent); expect(onSubmit).toHaveBeenCalled(); expect(preventDefault).toHaveBeenCalled(); expect(props.openModal).not.toHaveBeenCalled(); }); it('when feature disabled', () => { - const props = { + const props: any = { ...baseProps, draft: { message: `Test message @${mention}`, @@ -657,18 +673,18 @@ describe('components/AdvancedCreateComment', () => { enableConfirmNotificationsToChannel: false, }; - const wrapper = shallow( + const wrapper = shallow( , ); - wrapper.instance().handleSubmit({preventDefault}); + wrapper.instance().handleSubmit(submitEvent); expect(onSubmit).toHaveBeenCalled(); expect(preventDefault).toHaveBeenCalled(); expect(props.openModal).not.toHaveBeenCalled(); }); it('when no mention', () => { - const props = { + const props: any = { ...baseProps, draft: { message: `Test message ${mention}`, @@ -680,18 +696,18 @@ describe('components/AdvancedCreateComment', () => { enableConfirmNotificationsToChannel: true, }; - const wrapper = shallow( + const wrapper = shallow( , ); - wrapper.instance().handleSubmit({preventDefault}); + wrapper.instance().handleSubmit(submitEvent); expect(onSubmit).toHaveBeenCalled(); expect(preventDefault).toHaveBeenCalled(); expect(props.openModal).not.toHaveBeenCalled(); }); it('when user has insufficient permissions', () => { - const props = { + const props: any = { ...baseProps, useChannelMentions: false, draft: { @@ -704,11 +720,11 @@ describe('components/AdvancedCreateComment', () => { enableConfirmNotificationsToChannel: true, }; - const wrapper = shallow( + const wrapper = shallow( , ); - wrapper.instance().handleSubmit({preventDefault}); + wrapper.instance().handleSubmit(submitEvent); expect(onSubmit).toHaveBeenCalled(); expect(preventDefault).toHaveBeenCalled(); expect(props.openModal).not.toHaveBeenCalled(); @@ -716,7 +732,7 @@ describe('components/AdvancedCreateComment', () => { }); it(`should show Confirm Modal for @${mention} mentions when needed`, () => { - const props = { + const props: any = { ...baseProps, draft: { message: `Test message @${mention}`, @@ -728,18 +744,18 @@ describe('components/AdvancedCreateComment', () => { enableConfirmNotificationsToChannel: true, }; - const wrapper = shallow( + const wrapper = shallow( , ); - wrapper.instance().handleSubmit({preventDefault}); + wrapper.instance().handleSubmit(submitEvent); expect(onSubmit).not.toHaveBeenCalled(); expect(preventDefault).toHaveBeenCalled(); expect(props.openModal).toHaveBeenCalled(); }); it(`should show Confirm Modal for @${mention} mentions when needed and timezone notification`, async () => { - const props = { + const props: any = { ...baseProps, draft: { message: `Test message @${mention}`, @@ -752,12 +768,12 @@ describe('components/AdvancedCreateComment', () => { enableConfirmNotificationsToChannel: true, }; - const wrapper = shallow( + const wrapper = shallow( , ); - await wrapper.instance().handleSubmit({preventDefault}); - wrapper.setState({channelTimezoneCount: 4}); + await wrapper.instance().handleSubmit(submitEvent); + wrapper.setState({channelTimezoneCount: 4} as any); expect(onSubmit).not.toHaveBeenCalled(); expect(preventDefault).toHaveBeenCalled(); @@ -767,7 +783,7 @@ describe('components/AdvancedCreateComment', () => { }); it(`should show Confirm Modal for @${mention} mentions when needed and no timezone notification`, async () => { - const props = { + const props: any = { ...baseProps, draft: { message: `Test message @${mention}`, @@ -780,12 +796,12 @@ describe('components/AdvancedCreateComment', () => { enableConfirmNotificationsToChannel: true, }; - const wrapper = shallow( + const wrapper = shallow( , ); - await wrapper.instance().handleSubmit({preventDefault}); - wrapper.setState({channelTimezoneCount: 0}); + await wrapper.instance().handleSubmit(submitEvent); + wrapper.setState({channelTimezoneCount: 0} as any); expect(onSubmit).not.toHaveBeenCalled(); expect(preventDefault).toHaveBeenCalled(); @@ -796,7 +812,7 @@ describe('components/AdvancedCreateComment', () => { }); it('should show Confirm Modal for @group mention when needed and no timezone notification', async () => { - const props = { + const props: any = { ...baseProps, draft: { message: 'Test message @developers', @@ -821,14 +837,13 @@ describe('components/AdvancedCreateComment', () => { enableConfirmNotificationsToChannel: true, }; - const wrapper = shallow( + const wrapper = shallow( , ); const showNotifyAllModal = wrapper.instance().showNotifyAllModal; wrapper.instance().showNotifyAllModal = jest.fn((mentions, channelTimezoneCount, memberNotifyCount) => showNotifyAllModal(mentions, channelTimezoneCount, memberNotifyCount)); - await wrapper.instance().handleSubmit({preventDefault}); - + await wrapper.instance().handleSubmit(submitEvent); expect(onSubmit).not.toHaveBeenCalled(); expect(preventDefault).toHaveBeenCalled(); expect(baseProps.getChannelTimezones).toHaveBeenCalledTimes(0); @@ -837,7 +852,7 @@ describe('components/AdvancedCreateComment', () => { }); it('should show Confirm Modal for @group mentions when needed and no timezone notification', async () => { - const props = { + const props: any = { ...baseProps, draft: { message: 'Test message @developers @boss @love @you @software-developers', @@ -894,15 +909,14 @@ describe('components/AdvancedCreateComment', () => { enableConfirmNotificationsToChannel: true, }; - const wrapper = shallow( + const wrapper = shallow( , ); const showNotifyAllModal = wrapper.instance().showNotifyAllModal; wrapper.instance().showNotifyAllModal = jest.fn((mentions, channelTimezoneCount, memberNotifyCount) => showNotifyAllModal(mentions, channelTimezoneCount, memberNotifyCount)); - await wrapper.instance().handleSubmit({preventDefault}); - + await wrapper.instance().handleSubmit(submitEvent); expect(onSubmit).not.toHaveBeenCalled(); expect(preventDefault).toHaveBeenCalled(); expect(baseProps.getChannelTimezones).toHaveBeenCalledTimes(0); @@ -911,7 +925,7 @@ describe('components/AdvancedCreateComment', () => { }); it('should show Confirm Modal for @group mention with timezone enabled', async () => { - const props = { + const props: any = { ...baseProps, draft: { message: 'Test message @developers', @@ -936,15 +950,14 @@ describe('components/AdvancedCreateComment', () => { enableConfirmNotificationsToChannel: true, }; - const wrapper = shallow( + const wrapper = shallow( , ); const showNotifyAllModal = wrapper.instance().showNotifyAllModal; wrapper.instance().showNotifyAllModal = jest.fn((mentions, channelTimezoneCount, memberNotifyCount) => showNotifyAllModal(mentions, channelTimezoneCount, memberNotifyCount)); - await wrapper.instance().handleSubmit({preventDefault}); - + await wrapper.instance().handleSubmit(submitEvent); expect(onSubmit).not.toHaveBeenCalled(); expect(preventDefault).toHaveBeenCalled(); expect(baseProps.getChannelTimezones).toHaveBeenCalledTimes(0); @@ -953,11 +966,11 @@ describe('components/AdvancedCreateComment', () => { }); it('should allow to force send invalid slash command as a message', async () => { - const error = new Error('No command found'); + const error: ServerError = {message: 'No command found'}; error.server_error_id = 'api.command.execute_command.not_found.app_error'; const onSubmitWithError = jest.fn(() => Promise.reject(error)); - const props = { + const props: any = { ...baseProps, draft: { message: '/fakecommand other text', @@ -967,12 +980,11 @@ describe('components/AdvancedCreateComment', () => { onSubmit: onSubmitWithError, }; - const wrapper = shallow( + const wrapper = shallow( , ); - await wrapper.instance().handleSubmit({preventDefault}); - + await wrapper.instance().handleSubmit(submitEvent); expect(onSubmitWithError).toHaveBeenCalledWith({ message: '/fakecommand other text', uploadsInProgress: [], @@ -981,8 +993,7 @@ describe('components/AdvancedCreateComment', () => { expect(preventDefault).toHaveBeenCalled(); wrapper.setProps({onSubmit}); - await wrapper.instance().handleSubmit({preventDefault}); - + await wrapper.instance().handleSubmit(submitEvent); expect(onSubmit).toHaveBeenCalledWith({ message: '/fakecommand other text', uploadsInProgress: [], @@ -992,11 +1003,11 @@ describe('components/AdvancedCreateComment', () => { }); it('should update global draft state if invalid slash command error occurs', async () => { - const error = new Error('No command found'); + const error: ServerError = {message: 'No command found'}; error.server_error_id = 'api.command.execute_command.not_found.app_error'; const onSubmitWithError = jest.fn(() => Promise.reject(error)); - const props = { + const props: any = { ...baseProps, draft: { message: '/fakecommand other text', @@ -1006,11 +1017,11 @@ describe('components/AdvancedCreateComment', () => { onSubmit: onSubmitWithError, }; - const wrapper = shallow( + const wrapper = shallow( , ); - const submitPromise = wrapper.instance().handleSubmit({preventDefault}); + const submitPromise = wrapper.instance().handleSubmit(submitEvent); expect(props.onUpdateCommentDraft).not.toHaveBeenCalled(); await submitPromise; @@ -1018,7 +1029,7 @@ describe('components/AdvancedCreateComment', () => { }); ['channel', 'all', 'here'].forEach((mention) => { it(`should set mentionHighlightDisabled when user does not have permission and message contains channel @${mention}`, async () => { - const props = { + const props: any = { ...baseProps, useChannelMentions: false, enableConfirmNotificationsToChannel: false, @@ -1030,17 +1041,17 @@ describe('components/AdvancedCreateComment', () => { onSubmit, }; - const wrapper = shallow( + const wrapper = shallow( , ); - wrapper.instance().handleSubmit({preventDefault}); + wrapper.instance().handleSubmit(submitEvent); expect(onSubmit).toHaveBeenCalled(); - expect(wrapper.state('draft').props.mentionHighlightDisabled).toBe(true); + expect(wrapper.state('draft')!.props.mentionHighlightDisabled).toBe(true); }); it(`should not set mentionHighlightDisabled when user does have permission and message contains channel channel @${mention}`, async () => { - const props = { + const props: any = { ...baseProps, useChannelMentions: true, enableConfirmNotificationsToChannel: false, @@ -1052,18 +1063,18 @@ describe('components/AdvancedCreateComment', () => { onSubmit, }; - const wrapper = shallow( + const wrapper = shallow( , ); - wrapper.instance().handleSubmit({preventDefault}); + wrapper.instance().handleSubmit(submitEvent); expect(onSubmit).toHaveBeenCalled(); - expect(wrapper.state('draft').props).toBe(undefined); + expect(wrapper.state('draft')!.props).toBe(undefined); }); }); it('should not set mentionHighlightDisabled when user does not have useChannelMentions permission and message contains no mention', async () => { - const props = { + const props: any = { ...baseProps, useChannelMentions: false, draft: { @@ -1074,86 +1085,99 @@ describe('components/AdvancedCreateComment', () => { onSubmit, }; - const wrapper = shallow( + const wrapper = shallow( , ); - wrapper.instance().handleSubmit({preventDefault}); + wrapper.instance().handleSubmit(submitEvent); expect(onSubmit).toHaveBeenCalled(); - expect(wrapper.state('draft').props).toBe(undefined); + expect(wrapper.state('draft')!.props).toBe(undefined); }); }); test('removePreview should remove file info and upload in progress with corresponding id', () => { const onUpdateCommentDraft = jest.fn(); - const draft = { + const draft: PostDraft = TestHelper.getPostDraftMock({ message: 'Test message', - uploadsInProgress: [4, 5, 6], - fileInfos: [{id: 1}, {id: 2}, {id: 3}], - }; - const props = {...baseProps, draft, onUpdateCommentDraft}; + uploadsInProgress: ['4', '5', '6'], + fileInfos: [ + TestHelper.getFileInfoMock({id: '1'}), + TestHelper.getFileInfoMock({id: '2'}), + TestHelper.getFileInfoMock({id: '3'}), + ], + }); + const props: any = {...baseProps, draft, onUpdateCommentDraft}; - const wrapper = shallow( + const wrapper = shallow( , ); wrapper.setState({draft}); - wrapper.instance().removePreview(3); + wrapper.instance().removePreview('3'); jest.advanceTimersByTime(Constants.SAVE_DRAFT_TIMEOUT); expect(onUpdateCommentDraft).toHaveBeenCalled(); expect(onUpdateCommentDraft.mock.calls[0][0]).toEqual( - expect.objectContaining({fileInfos: [{id: 1}, {id: 2}]}), + expect.objectContaining({fileInfos: [ + TestHelper.getFileInfoMock({id: '1'}), + TestHelper.getFileInfoMock({id: '2'}), + ]}), ); - expect(wrapper.state().draft.fileInfos).toEqual([{id: 1}, {id: 2}]); + expect(wrapper.state().draft!.fileInfos).toEqual([ + TestHelper.getFileInfoMock({id: '1'}), + TestHelper.getFileInfoMock({id: '2'}), + ]); - wrapper.instance().removePreview(5); + wrapper.instance().removePreview('5'); jest.advanceTimersByTime(Constants.SAVE_DRAFT_TIMEOUT); expect(onUpdateCommentDraft.mock.calls[1][0]).toEqual( - expect.objectContaining({uploadsInProgress: [4, 6]}), + expect.objectContaining({uploadsInProgress: ['4', '6']}), ); - expect(wrapper.state().draft.uploadsInProgress).toEqual([4, 6]); + expect(wrapper.state().draft!.uploadsInProgress).toEqual(['4', '6']); }); test('should match draft state on componentWillReceiveProps with change in messageInHistory', () => { - const draft = { - message: 'Test message', - uploadsInProgress: [], - fileInfos: [{}, {}, {}], - }; + const draft: PostDraft = TestHelper.getPostDraftMock({ + fileInfos: [defaultFileInfo, defaultFileInfo, defaultFileInfo], + }); - const wrapper = shallow( + const wrapper = shallow( , ); expect(wrapper.state('draft')).toEqual(draft); - const newDraft = {...draft, message: 'Test message edited'}; + const newDraft: PostDraft = TestHelper.getPostDraftMock({...draft, message: 'Test message edited'}); wrapper.setProps({draft: newDraft, messageInHistory: 'Test message edited'}); expect(wrapper.state('draft')).toEqual(newDraft); }); test('should match draft state on componentWillReceiveProps with new rootId', () => { - const draft = { + const defaultFileInfo = TestHelper.getFileInfoMock(); + const draft: PostDraft = TestHelper.getPostDraftMock({ message: 'Test message', - uploadsInProgress: [4, 5, 6], - fileInfos: [{id: 1}, {id: 2}, {id: 3}], - }; + uploadsInProgress: ['4', '5', '6'], + fileInfos: [ + TestHelper.getFileInfoMock({id: '1'}), + TestHelper.getFileInfoMock({id: '2'}), + TestHelper.getFileInfoMock({id: '3'}), + ], + }); - const wrapper = shallow( + const wrapper = shallow( , ); wrapper.setState({draft}); expect(wrapper.state('draft')).toEqual(draft); wrapper.setProps({rootId: 'new_root_id'}); - expect(wrapper.state('draft')).toEqual({...draft, uploadsInProgress: [], fileInfos: [{}, {}, {}]}); + expect(wrapper.state('draft')).toEqual(TestHelper.getPostDraftMock({...draft, uploadsInProgress: [], fileInfos: [defaultFileInfo, defaultFileInfo, defaultFileInfo]})); }); test('should match snapshot when cannot post', () => { - const props = {...baseProps, canPost: false}; - const wrapper = shallow( + const props: any = {...baseProps, canPost: false}; + const wrapper = shallow( , ); @@ -1161,8 +1185,8 @@ describe('components/AdvancedCreateComment', () => { }); test('should match snapshot, emoji picker disabled', () => { - const props = {...baseProps, enableEmojiPicker: false}; - const wrapper = shallow( + const props: any = {...baseProps, enableEmojiPicker: false}; + const wrapper = shallow( , ); @@ -1170,7 +1194,7 @@ describe('components/AdvancedCreateComment', () => { }); test('check for handleFileUploadChange callback for focus', () => { - const wrapper = shallow( + const wrapper = shallow( , ); const instance = wrapper.instance(); @@ -1186,7 +1210,7 @@ describe('components/AdvancedCreateComment', () => { const onEditLatestPost = jest.fn(). mockImplementationOnce(() => ({data: true})). mockImplementationOnce(() => ({data: false})); - const wrapper = shallow( + const wrapper = shallow( { return document.createElement('div'); }; - instance.textboxRef.current = {blur, focus, getInputBox: jest.fn(mockImpl)}; + (instance as any).textboxRef.current = {blur, focus, getInputBox: jest.fn(mockImpl)}; const mockTarget = { selectionStart: 0, @@ -1221,7 +1245,7 @@ describe('components/AdvancedCreateComment', () => { value: 'brown\nfox jumps over lazy dog', }; - const commentMsgKey = { + const commentMsgKey: any = { preventDefault: jest.fn(), ctrlKey: true, key: Constants.KeyCodes.ENTER[0], @@ -1231,7 +1255,7 @@ describe('components/AdvancedCreateComment', () => { instance.handleKeyDown(commentMsgKey); expect(instance.commentMsgKeyPress).toHaveBeenCalledTimes(1); - const upKey = { + const upKey: any = { preventDefault: jest.fn(), ctrlKey: true, key: Constants.KeyCodes.UP[0], @@ -1249,12 +1273,12 @@ describe('components/AdvancedCreateComment', () => { keyCode: Constants.KeyCodes.DOWN[1], target: mockTarget, }; - instance.handleKeyDown(downKey); + instance.handleKeyDown(downKey as any); expect(downKey.preventDefault).toHaveBeenCalledTimes(1); expect(onMoveHistoryIndexForward).toHaveBeenCalledTimes(1); - wrapper.setState({draft: {message: '', fileInfos: [], uploadsInProgress: []}}); - const upKeyForEdit = { + wrapper.setState({draft: emptyDraft}); + const upKeyForEdit: any = { preventDefault: jest.fn(), ctrlKey: false, key: Constants.KeyCodes.UP[0], @@ -1274,9 +1298,9 @@ describe('components/AdvancedCreateComment', () => { }); test('should the RHS thread scroll to bottom one time after mount when props.draft.message is not empty', () => { - const draft = emptyDraft; + const draft: PostDraft = emptyDraft; const scrollToBottom = jest.fn(); - const wrapper = shallow( + const wrapper = shallow( { }); test('should the RHS thread scroll to bottom when state.draft.uploadsInProgress increases but not when it decreases', () => { - const draft = emptyDraft; + const draft: PostDraft = emptyDraft; const scrollToBottom = jest.fn(); - const wrapper = shallow( + const wrapper = shallow( { expect(scrollToBottom).toBeCalledTimes(0); - wrapper.setState({draft: {...draft, uploadsInProgress: [1]}}); + wrapper.setState({draft: {...draft, uploadsInProgress: ['1']}}); expect(scrollToBottom).toBeCalledTimes(1); - wrapper.setState({draft: {...draft, uploadsInProgress: [1, 2]}}); + wrapper.setState({draft: {...draft, uploadsInProgress: ['1', '2']}}); expect(scrollToBottom).toBeCalledTimes(2); - wrapper.setState({draft: {...draft, uploadsInProgress: [2]}}); + wrapper.setState({draft: {...draft, uploadsInProgress: ['2']}}); expect(scrollToBottom).toBeCalledTimes(2); }); test('should be able to format a pasted markdown table', () => { - const draft = emptyDraft; - const wrapper = shallow( + const draft: PostDraft = emptyDraft; + const wrapper = shallow( { }; }; - wrapper.instance().textboxRef.current = {getInputBox: jest.fn(mockImpl), focus: jest.fn(), blur: jest.fn()}; + (wrapper.instance() as any).textboxRef.current = {getInputBox: jest.fn(mockImpl), focus: jest.fn(), blur: jest.fn()}; - const event = { + const event: any = { target: { id: 'reply_textbox', }, @@ -1364,8 +1388,8 @@ describe('components/AdvancedCreateComment', () => { }); test('should be able to format a pasted markdown table without headers', () => { - const draft = emptyDraft; - const wrapper = shallow( + const draft: PostDraft = emptyDraft; + const wrapper = shallow( { }; }; - wrapper.instance().textboxRef.current = {getInputBox: jest.fn(mockImpl), focus: jest.fn(), blur: jest.fn()}; + (wrapper.instance() as any).textboxRef.current = {getInputBox: jest.fn(mockImpl), focus: jest.fn(), blur: jest.fn()}; - const event = { + const event: any = { target: { id: 'reply_textbox', }, @@ -1407,8 +1431,8 @@ describe('components/AdvancedCreateComment', () => { }); test('should be able to format a pasted hyperlink', () => { - const draft = emptyDraft; - const wrapper = shallow( + const draft: PostDraft = emptyDraft; + const wrapper = shallow( { }; }; - wrapper.instance().textboxRef.current = {getInputBox: jest.fn(mockImpl), focus: jest.fn(), blur: jest.fn()}; + (wrapper.instance() as any).textboxRef.current = {getInputBox: jest.fn(mockImpl), focus: jest.fn(), blur: jest.fn()}; - const event = { + const event: any = { target: { id: 'reply_textbox', }, @@ -1450,8 +1474,8 @@ describe('components/AdvancedCreateComment', () => { }); test('should be able to format a github codeblock (pasted as a table)', () => { - const draft = emptyDraft; - const wrapper = shallow( + const draft: PostDraft = emptyDraft; + const wrapper = shallow( { }; }; - wrapper.instance().textboxRef.current = {getInputBox: jest.fn(mockImpl), focus: jest.fn(), blur: jest.fn()}; + (wrapper.instance() as any).textboxRef.current = {getInputBox: jest.fn(mockImpl), focus: jest.fn(), blur: jest.fn()}; - const event = { + const event: any = { target: { id: 'reply_textbox', }, @@ -1480,7 +1504,7 @@ describe('components/AdvancedCreateComment', () => { clipboardData: { items: [1], types: ['text/plain', 'text/html'], - getData: (type) => { + getData: (type: any) => { if (type === 'text/plain') { return '// a javascript codeblock example\nif (1 > 0) {\n return \'condition is true\';\n}'; } @@ -1496,7 +1520,7 @@ describe('components/AdvancedCreateComment', () => { }); test('should show preview and edit mode, and return focus on preview disable', () => { - const wrapper = shallow( + const wrapper = shallow( , ); const instance = wrapper.instance(); @@ -1514,7 +1538,7 @@ describe('components/AdvancedCreateComment', () => { expect(instance.focusTextbox).toBeCalled(); }); - testComponentForLineBreak((value) => ( + testComponentForLineBreak((value: any) => ( { }} ctrlSend={true} /> - ), (instance) => instance.state().draft.message, false); + ), (instance: any) => instance.state().draft.message, false); testComponentForMarkdownHotkeys( - (value) => ( + (value: any) => ( { ctrlSend={true} /> ), - (wrapper, setSelectionRangeFn) => { + (wrapper: any, setSelectionRangeFn: any) => { const mockTop = () => { return document.createElement('div'); }; @@ -1552,14 +1576,14 @@ describe('components/AdvancedCreateComment', () => { }, }; }, - (instance) => instance.find(AdvanceTextEditor), - (instance) => instance.state().draft.message, + (instance: any) => instance.find(AdvanceTextEditor), + (instance: any) => instance.state().draft.message, false, 'reply_textbox', ); it('should blur when ESCAPE is pressed', () => { - const wrapper = shallow( + const wrapper = shallow( , @@ -1574,7 +1598,7 @@ describe('components/AdvancedCreateComment', () => { }; }; - instance.textboxRef.current = {blur, getInputBox: jest.fn(mockImpl)}; + (instance as any).textboxRef.current = {blur, getInputBox: jest.fn(mockImpl)}; const mockTarget = { selectionStart: 0, @@ -1590,7 +1614,7 @@ describe('components/AdvancedCreateComment', () => { target: mockTarget, }; - instance.handleKeyDown(commentEscapeKey); + instance.handleKeyDown(commentEscapeKey as any); expect(blur).toHaveBeenCalledTimes(1); }); }); diff --git a/webapp/channels/src/components/advanced_create_comment/advanced_create_comment.tsx b/webapp/channels/src/components/advanced_create_comment/advanced_create_comment.tsx index f80da92306..a04bc9d297 100644 --- a/webapp/channels/src/components/advanced_create_comment/advanced_create_comment.tsx +++ b/webapp/channels/src/components/advanced_create_comment/advanced_create_comment.tsx @@ -60,7 +60,7 @@ import FileLimitStickyBanner from 'components/file_limit_sticky_banner'; const KeyCodes = Constants.KeyCodes; -type Props = { +export type Props = { currentTeamId: string; // The channel for which this comment is a part of @@ -219,10 +219,13 @@ function isDraftEmpty(draft: PostDraft): boolean { } class AdvancedCreateComment extends React.PureComponent { - private lastBlurAt = 0; - private draftsForPost: {[postID: string]: PostDraft | null} = {}; - private doInitialScrollToBottom = false; + // public because accessed in advanced_create_comment.test.tsx + public draftsForPost: {[postID: string]: PostDraft | null} = {}; + public doInitialScrollToBottom = false; + private readonly textboxRef: React.RefObject; + + private lastBlurAt = 0; private saveDraftFrame?: number | null; private isDraftSubmitting = false; @@ -230,7 +233,6 @@ class AdvancedCreateComment extends React.PureComponent { private isNonFormattedPaste = false; private timeoutId: number | null = null; - private readonly textboxRef: React.RefObject; private readonly fileUploadRef: React.RefObject; static defaultProps = { diff --git a/webapp/channels/src/types/global.d.ts b/webapp/channels/src/types/global.d.ts index 0cc9e15228..41fbe76fb7 100644 --- a/webapp/channels/src/types/global.d.ts +++ b/webapp/channels/src/types/global.d.ts @@ -2,3 +2,4 @@ // See LICENSE.txt for license information. declare const COMMIT_HASH: string; + diff --git a/webapp/channels/src/utils/test_helper.ts b/webapp/channels/src/utils/test_helper.ts index 045db99cc8..29f8d8f8e9 100644 --- a/webapp/channels/src/utils/test_helper.ts +++ b/webapp/channels/src/utils/test_helper.ts @@ -20,8 +20,21 @@ import {PreferenceType} from '@mattermost/types/preferences'; import {Reaction} from '@mattermost/types/reactions'; import {getPreferenceKey} from 'mattermost-redux/utils/preference_utils'; import {Invoice, Product, Subscription, CloudCustomer} from '@mattermost/types/cloud'; +import {PostDraft} from 'types/store/draft'; export class TestHelper { + public static getPostDraftMock(override?: Partial): PostDraft { + const defaultPostDraft: PostDraft = { + message: 'Test message', + fileInfos: [], + uploadsInProgress: [], + channelId: '', + rootId: '', + createAt: 0, + updateAt: 0, + }; + return Object.assign({}, defaultPostDraft, override); + } public static getUserMock(override: Partial = {}): UserProfile { const defaultUser: UserProfile = { id: 'user_id',