From 403772bd0a6e27cf81d4ef597bddcf4c7c321725 Mon Sep 17 00:00:00 2001 From: Janki Salvi <117571355+js-jankisalvi@users.noreply.github.com> Date: Thu, 6 Jul 2023 15:49:47 +0200 Subject: [PATCH 1/5] UI validation for total number of comment characters --- .../components/add_comment/index.test.tsx | 199 +++++++++--------- .../public/components/add_comment/schema.tsx | 9 +- .../editable_markdown_footer.tsx | 3 +- .../editable_markdown_renderer.test.tsx | 58 +++-- .../public/components/user_actions/schema.ts | 11 +- 5 files changed, 163 insertions(+), 117 deletions(-) diff --git a/x-pack/plugins/cases/public/components/add_comment/index.test.tsx b/x-pack/plugins/cases/public/components/add_comment/index.test.tsx index 54b2219745b1f..cb00f85603faf 100644 --- a/x-pack/plugins/cases/public/components/add_comment/index.test.tsx +++ b/x-pack/plugins/cases/public/components/add_comment/index.test.tsx @@ -6,14 +6,14 @@ */ import React from 'react'; -import { mount } from 'enzyme'; -import { waitFor, act, fireEvent } from '@testing-library/react'; +import { waitFor, act, fireEvent, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; import { noop } from 'lodash/fp'; import { noCreateCasesPermissions, TestProviders, createAppMockRenderer } from '../../common/mock'; import { CommentType } from '../../../common/api'; -import { SECURITY_SOLUTION_OWNER } from '../../../common/constants'; +import { SECURITY_SOLUTION_OWNER, MAX_COMMENT_LENGTH } from '../../../common/constants'; import { useCreateAttachments } from '../../containers/use_create_attachments'; import type { AddCommentProps, AddCommentRefObject } from '.'; import { AddComment } from '.'; @@ -52,8 +52,11 @@ const appId = 'testAppId'; const draftKey = `cases.${appId}.${addCommentProps.caseId}.${addCommentProps.id}.markdownEditor`; describe('AddComment ', () => { + let appMockRender: AppMockRenderer; + beforeEach(() => { jest.clearAllMocks(); + appMockRender = createAppMockRenderer(); useCreateAttachmentsMock.mockImplementation(() => defaultResponse); }); @@ -61,22 +64,47 @@ describe('AddComment ', () => { sessionStorage.removeItem(draftKey); }); - it('should post comment on submit click', async () => { - const wrapper = mount( - - + it('renders correctly', () => { + appMockRender.render(); + + expect(screen.getByTestId('add-comment')).toBeInTheDocument(); + }); + + it('should render spinner and disable submit when loading', () => { + useCreateAttachmentsMock.mockImplementation(() => ({ + ...defaultResponse, + isLoading: true, + })); + appMockRender.render(); + + expect(screen.getByTestId('loading-spinner')).toBeInTheDocument(); + expect(screen.getByTestId('submit-comment')).toHaveAttribute('disabled'); + }); + + it('should hide the component when the user does not have create permissions', () => { + useCreateAttachmentsMock.mockImplementation(() => ({ + ...defaultResponse, + isLoading: true, + })); + + appMockRender.render( + + ); - wrapper - .find(`[data-test-subj="add-comment"] textarea`) - .first() - .simulate('change', { target: { value: sampleData.comment } }); + expect(screen.queryByTestId('loading-spinner')).not.toBeInTheDocument(); + }); + + it('should post comment on submit click', async () => { + appMockRender.render(); - expect(wrapper.find(`[data-test-subj="add-comment"]`).exists()).toBeTruthy(); - expect(wrapper.find(`[data-test-subj="loading-spinner"]`).exists()).toBeFalsy(); + const markdown = screen.getByTestId('euiMarkdownEditorTextArea'); + + userEvent.type(markdown, sampleData.comment); + + userEvent.click(screen.getByTestId('submit-comment')); - wrapper.find(`button[data-test-subj="submit-comment"]`).first().simulate('click'); await waitFor(() => { expect(onCommentSaving).toBeCalled(); expect(createAttachments).toBeCalledWith( @@ -94,105 +122,49 @@ describe('AddComment ', () => { }); await waitFor(() => { - expect(wrapper.find(`[data-test-subj="add-comment"] textarea`).text()).toBe(''); + expect(screen.getByTestId('euiMarkdownEditorTextArea')).toHaveTextContent(''); }); }); - it('should render spinner and disable submit when loading', () => { - useCreateAttachmentsMock.mockImplementation(() => ({ - ...defaultResponse, - isLoading: true, - })); - const wrapper = mount( - - - - ); - - expect(wrapper.find(`[data-test-subj="loading-spinner"]`).exists()).toBeTruthy(); - expect( - wrapper.find(`[data-test-subj="submit-comment"]`).first().prop('isDisabled') - ).toBeTruthy(); - }); - - it('should disable submit button when isLoading is true', () => { - useCreateAttachmentsMock.mockImplementation(() => ({ - ...defaultResponse, - isLoading: true, - })); - const wrapper = mount( - - - - ); - - expect( - wrapper.find(`[data-test-subj="submit-comment"]`).first().prop('isDisabled') - ).toBeTruthy(); - }); - - it('should hide the component when the user does not have create permissions', () => { - useCreateAttachmentsMock.mockImplementation(() => ({ - ...defaultResponse, - isLoading: true, - })); - const wrapper = mount( - - - - ); - - expect(wrapper.find(`[data-test-subj="add-comment"]`).exists()).toBeFalsy(); - }); - it('should insert a quote', async () => { const sampleQuote = 'what a cool quote \n with new lines'; const ref = React.createRef(); - const wrapper = mount( - - - - ); - wrapper - .find(`[data-test-subj="add-comment"] textarea`) - .first() - .simulate('change', { target: { value: sampleData.comment } }); + appMockRender.render(); + + userEvent.type(screen.getByTestId('euiMarkdownEditorTextArea'), sampleData.comment); await act(async () => { ref.current!.addQuote(sampleQuote); }); - expect(wrapper.find(`[data-test-subj="add-comment"] textarea`).text()).toBe( - `${sampleData.comment}\n\n> what a cool quote \n> with new lines \n\n` - ); + await waitFor(() => { + expect(screen.getByTestId('euiMarkdownEditorTextArea').textContent).toContain( + `${sampleData.comment}\n\n> what a cool quote \n> with new lines \n\n` + ); + }); }); it('should call onFocus when adding a quote', async () => { const ref = React.createRef(); - mount( - - - - ); + appMockRender.render(); ref.current!.editor!.textarea!.focus = jest.fn(); + await act(async () => { ref.current!.addQuote('a comment'); }); - expect(ref.current!.editor!.textarea!.focus).toHaveBeenCalled(); + await waitFor(() => { + expect(ref.current!.editor!.textarea!.focus).toHaveBeenCalled(); + }); }); it('should NOT call onFocus on mount', async () => { const ref = React.createRef(); - mount( - - - - ); + appMockRender.render(); ref.current!.editor!.textarea!.focus = jest.fn(); expect(ref.current!.editor!.textarea!.focus).not.toHaveBeenCalled(); @@ -208,12 +180,10 @@ describe('AddComment ', () => { const mockTimelineIntegration = { ...timelineIntegrationMock }; mockTimelineIntegration.hooks.useInsertTimeline = useInsertTimelineMock; - const wrapper = mount( - - - - - + appMockRender.render( + + + ); act(() => { @@ -221,7 +191,38 @@ describe('AddComment ', () => { }); await waitFor(() => { - expect(wrapper.find(`[data-test-subj="add-comment"] textarea`).text()).toBe('[title](url)'); + expect(screen.getByTestId('euiMarkdownEditorTextArea')).toHaveTextContent('[title](url)'); + }); + }); + + describe('errors', () => { + it('shows an error when comment is empty', async () => { + appMockRender.render(); + + const markdown = screen.getByTestId('euiMarkdownEditorTextArea'); + + userEvent.clear(markdown); + userEvent.type(markdown, ' '); + + await waitFor(() => { + expect(screen.getByText('Empty comments are not allowed.')).toBeInTheDocument(); + }); + }); + + it('shows an error when comment is too long', async () => { + const longComment = 'a'.repeat(MAX_COMMENT_LENGTH + 1); + + appMockRender.render(); + + const markdown = screen.getByTestId('euiMarkdownEditorTextArea'); + + userEvent.paste(markdown, longComment); + + await waitFor(() => { + expect( + screen.getByText('The length of the comment is too long. The maximum length is 30000.') + ).toBeInTheDocument(); + }); }); }); }); @@ -247,9 +248,9 @@ describe('draft comment ', () => { }); it('should clear session storage on submit', async () => { - const result = appMockRenderer.render(); + appMockRenderer.render(); - fireEvent.change(result.getByLabelText('caseComment'), { + fireEvent.change(screen.getByLabelText('caseComment'), { target: { value: sampleData.comment }, }); @@ -258,10 +259,10 @@ describe('draft comment ', () => { }); await waitFor(() => { - expect(result.getByLabelText('caseComment')).toHaveValue(sessionStorage.getItem(draftKey)); + expect(screen.getByLabelText('caseComment')).toHaveValue(sessionStorage.getItem(draftKey)); }); - fireEvent.click(result.getByTestId('submit-comment')); + fireEvent.click(screen.getByTestId('submit-comment')); await waitFor(() => { expect(onCommentSaving).toBeCalled(); @@ -280,7 +281,7 @@ describe('draft comment ', () => { }); await waitFor(() => { - expect(result.getByLabelText('caseComment').textContent).toBe(''); + expect(screen.getByLabelText('caseComment').textContent).toBe(''); expect(sessionStorage.getItem(draftKey)).toBe(''); }); }); @@ -295,9 +296,9 @@ describe('draft comment ', () => { }); it('should have draft comment same as existing session storage', async () => { - const result = appMockRenderer.render(); + appMockRenderer.render(); - expect(result.getByLabelText('caseComment')).toHaveValue('value set in storage'); + expect(screen.getByLabelText('caseComment')).toHaveValue('value set in storage'); }); }); }); diff --git a/x-pack/plugins/cases/public/components/add_comment/schema.tsx b/x-pack/plugins/cases/public/components/add_comment/schema.tsx index 980a03e76b772..91fc499baa74d 100644 --- a/x-pack/plugins/cases/public/components/add_comment/schema.tsx +++ b/x-pack/plugins/cases/public/components/add_comment/schema.tsx @@ -9,10 +9,11 @@ import type { FormSchema } from '@kbn/es-ui-shared-plugin/static/forms/hook_form import { FIELD_TYPES } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib'; import { fieldValidators } from '@kbn/es-ui-shared-plugin/static/forms/helpers'; import type { CommentRequestUserType } from '../../../common/api'; +import { MAX_COMMENT_LENGTH } from '../../../common/constants'; import * as i18n from './translations'; -const { emptyField } = fieldValidators; +const { emptyField, maxLengthField } = fieldValidators; export interface AddCommentFormSchema { comment: CommentRequestUserType['comment']; @@ -25,6 +26,12 @@ export const schema: FormSchema = { { validator: emptyField(i18n.EMPTY_COMMENTS_NOT_ALLOWED), }, + { + validator: maxLengthField({ + length: MAX_COMMENT_LENGTH, + message: i18n.MAX_LENGTH_ERROR('comment', MAX_COMMENT_LENGTH), + }), + }, ], }, }; diff --git a/x-pack/plugins/cases/public/components/markdown_editor/editable_markdown_footer.tsx b/x-pack/plugins/cases/public/components/markdown_editor/editable_markdown_footer.tsx index 86772136c18d0..adceb00fe8eb4 100644 --- a/x-pack/plugins/cases/public/components/markdown_editor/editable_markdown_footer.tsx +++ b/x-pack/plugins/cases/public/components/markdown_editor/editable_markdown_footer.tsx @@ -11,6 +11,7 @@ import React from 'react'; import { useFormData } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib'; import * as i18n from '../case_view/translations'; +import { MAX_COMMENT_LENGTH } from '../../../common/constants'; interface EditableMarkdownFooterProps { handleSaveAction: () => Promise; @@ -42,7 +43,7 @@ const EditableMarkdownFooterComponent: React.FC = ( fill iconType="save" onClick={handleSaveAction} - disabled={!content} + disabled={!content || content.length > MAX_COMMENT_LENGTH} size="s" > {i18n.SAVE} diff --git a/x-pack/plugins/cases/public/components/markdown_editor/editable_markdown_renderer.test.tsx b/x-pack/plugins/cases/public/components/markdown_editor/editable_markdown_renderer.test.tsx index 59d424fdd82c8..9738ba9640f04 100644 --- a/x-pack/plugins/cases/public/components/markdown_editor/editable_markdown_renderer.test.tsx +++ b/x-pack/plugins/cases/public/components/markdown_editor/editable_markdown_renderer.test.tsx @@ -14,6 +14,7 @@ import { EditableMarkdown } from '.'; import { TestProviders } from '../../common/mock'; import type { Content } from '../user_actions/schema'; import { schema } from '../user_actions/schema'; +import { MAX_COMMENT_LENGTH } from '../../../common/constants'; jest.mock('../../common/lib/kibana'); @@ -21,7 +22,6 @@ const onChangeEditable = jest.fn(); const onSaveContent = jest.fn(); const newValue = 'Hello from Tehas'; -const emptyValue = ''; const hyperlink = `[hyperlink](http://elastic.co)`; const draftStorageKey = `cases.testAppId.caseId.markdown-id.markdownEditor`; const content = `A link to a timeline ${hyperlink}`; @@ -100,32 +100,62 @@ describe('EditableMarkdown', () => { expect(onSaveContent).not.toHaveBeenCalled(); }); - it('Save button disabled if current text is empty', async () => { + it('Cancel button click calls only onChangeEditable', async () => { render( ); - fireEvent.change(screen.getByTestId('euiMarkdownEditorTextArea'), { value: emptyValue }); + userEvent.click(screen.getByTestId('editable-cancel-markdown')); await waitFor(() => { - expect(screen.getByTestId('editable-save-markdown')).toHaveProperty('disabled'); + expect(onSaveContent).not.toHaveBeenCalled(); + expect(onChangeEditable).toHaveBeenCalledWith(defaultProps.id); }); }); - it('Cancel button click calls only onChangeEditable', async () => { - render( - - - - ); + describe('errors', () => { + beforeAll(() => { + jest.useFakeTimers(); + }); - userEvent.click(screen.getByTestId('editable-cancel-markdown')); + it('Shows error message and save button disabled if current text is empty', async () => { + render( + + + + ); - await waitFor(() => { - expect(onSaveContent).not.toHaveBeenCalled(); - expect(onChangeEditable).toHaveBeenCalledWith(defaultProps.id); + userEvent.clear(screen.getByTestId('euiMarkdownEditorTextArea')); + + userEvent.type(screen.getByTestId('euiMarkdownEditorTextArea'), ' '); + + await waitFor(() => { + expect(screen.getByText('Empty comments are not allowed.')).toBeInTheDocument(); + expect(screen.getByTestId('editable-save-markdown')).toHaveProperty('disabled'); + }); + }); + + it('Shows error message and save button disabled if current text is too long', async () => { + const longComment = 'b'.repeat(MAX_COMMENT_LENGTH + 1); + + render( + + + + ); + + const markdown = screen.getByTestId('euiMarkdownEditorTextArea'); + + userEvent.paste(markdown, longComment); + + await waitFor(() => { + expect( + screen.getByText('The length of the comment is too long. The maximum length is 30000.') + ).toBeInTheDocument(); + expect(screen.getByTestId('editable-save-markdown')).toHaveProperty('disabled'); + }); }); }); diff --git a/x-pack/plugins/cases/public/components/user_actions/schema.ts b/x-pack/plugins/cases/public/components/user_actions/schema.ts index 8c47b700adeb5..2beedd80260e6 100644 --- a/x-pack/plugins/cases/public/components/user_actions/schema.ts +++ b/x-pack/plugins/cases/public/components/user_actions/schema.ts @@ -9,8 +9,9 @@ import type { FormSchema } from '@kbn/es-ui-shared-plugin/static/forms/hook_form import { FIELD_TYPES } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib'; import { fieldValidators } from '@kbn/es-ui-shared-plugin/static/forms/helpers'; import * as i18n from '../../common/translations'; +import { MAX_COMMENT_LENGTH } from '../../../common/constants'; -const { emptyField } = fieldValidators; +const { emptyField, maxLengthField } = fieldValidators; export interface Content { content: string; } @@ -19,7 +20,13 @@ export const schema: FormSchema = { type: FIELD_TYPES.TEXTAREA, validations: [ { - validator: emptyField(i18n.REQUIRED_FIELD), + validator: emptyField(i18n.EMPTY_COMMENTS_NOT_ALLOWED), + }, + { + validator: maxLengthField({ + length: MAX_COMMENT_LENGTH, + message: i18n.MAX_LENGTH_ERROR('comment', MAX_COMMENT_LENGTH), + }), }, ], }, From 24d36d2bf2f244659a160e2ecb877e0ca81eb96a Mon Sep 17 00:00:00 2001 From: Janki Salvi <117571355+js-jankisalvi@users.noreply.github.com> Date: Fri, 7 Jul 2023 10:22:51 +0200 Subject: [PATCH 2/5] Updating UI max length validation message --- x-pack/plugins/cases/public/common/translations.ts | 2 +- .../cases/public/components/add_comment/index.test.tsx | 2 +- .../components/case_view/components/edit_category.test.tsx | 2 +- .../components/case_view/components/edit_tags.test.tsx | 2 +- .../public/components/category/category_form_field.test.tsx | 2 +- .../cases/public/components/create/description.test.tsx | 2 +- .../cases/public/components/create/form_context.test.tsx | 2 +- x-pack/plugins/cases/public/components/create/tags.test.tsx | 2 +- .../cases/public/components/description/index.test.tsx | 2 +- .../public/components/header_page/editable_title.test.tsx | 4 ++-- .../markdown_editor/editable_markdown_renderer.test.tsx | 2 +- .../apps/cases/group1/create_case_form.ts | 6 +++--- .../functional_with_es_ssl/apps/cases/group1/view_case.ts | 6 +++--- 13 files changed, 18 insertions(+), 18 deletions(-) diff --git a/x-pack/plugins/cases/public/common/translations.ts b/x-pack/plugins/cases/public/common/translations.ts index a8fb655b06d54..8d4b687ed3b35 100644 --- a/x-pack/plugins/cases/public/common/translations.ts +++ b/x-pack/plugins/cases/public/common/translations.ts @@ -292,7 +292,7 @@ export const SELECT_CASE_TITLE = i18n.translate('xpack.cases.common.allCases.cas export const MAX_LENGTH_ERROR = (field: string, length: number) => i18n.translate('xpack.cases.createCase.maxLengthError', { values: { field, length }, - defaultMessage: 'The length of the {field} is too long. The maximum length is {length}.', + defaultMessage: 'The length of the {field} is too long. The maximum length is {length} characters.', }); export const MAX_TAGS_ERROR = (length: number) => diff --git a/x-pack/plugins/cases/public/components/add_comment/index.test.tsx b/x-pack/plugins/cases/public/components/add_comment/index.test.tsx index cb00f85603faf..ddffa7e3fe6cf 100644 --- a/x-pack/plugins/cases/public/components/add_comment/index.test.tsx +++ b/x-pack/plugins/cases/public/components/add_comment/index.test.tsx @@ -220,7 +220,7 @@ describe('AddComment ', () => { await waitFor(() => { expect( - screen.getByText('The length of the comment is too long. The maximum length is 30000.') + screen.getByText('The length of the comment is too long. The maximum length is 30000 characters.') ).toBeInTheDocument(); }); }); diff --git a/x-pack/plugins/cases/public/components/case_view/components/edit_category.test.tsx b/x-pack/plugins/cases/public/components/case_view/components/edit_category.test.tsx index 9e5db12f98ff1..1d2c6599be52e 100644 --- a/x-pack/plugins/cases/public/components/case_view/components/edit_category.test.tsx +++ b/x-pack/plugins/cases/public/components/case_view/components/edit_category.test.tsx @@ -194,7 +194,7 @@ describe('EditCategory ', () => { await waitFor(() => { expect( - screen.getByText('The length of the category is too long. The maximum length is 50.') + screen.getByText('The length of the category is too long. The maximum length is 50 characters.') ).toBeInTheDocument(); }); diff --git a/x-pack/plugins/cases/public/components/case_view/components/edit_tags.test.tsx b/x-pack/plugins/cases/public/components/case_view/components/edit_tags.test.tsx index 68749e30a5983..45f470cba70bb 100644 --- a/x-pack/plugins/cases/public/components/case_view/components/edit_tags.test.tsx +++ b/x-pack/plugins/cases/public/components/case_view/components/edit_tags.test.tsx @@ -132,7 +132,7 @@ describe('EditTags ', () => { userEvent.keyboard('{enter}'); await waitFor(() => { - expect(screen.getByText('The length of the tag is too long. The maximum length is 256.')); + expect(screen.getByText('The length of the tag is too long. The maximum length is 256 characters.')); }); }); diff --git a/x-pack/plugins/cases/public/components/category/category_form_field.test.tsx b/x-pack/plugins/cases/public/components/category/category_form_field.test.tsx index 3e6eb6472bf9a..5f9b033c7646f 100644 --- a/x-pack/plugins/cases/public/components/category/category_form_field.test.tsx +++ b/x-pack/plugins/cases/public/components/category/category_form_field.test.tsx @@ -120,7 +120,7 @@ describe('Category', () => { expect(onSubmit).toBeCalledWith({}, false); }); - expect(screen.getByText('The length of the category is too long. The maximum length is 50.')); + expect(screen.getByText('The length of the category is too long. The maximum length is 50 characters.')); }); it('can set a category from existing ones', async () => { diff --git a/x-pack/plugins/cases/public/components/create/description.test.tsx b/x-pack/plugins/cases/public/components/create/description.test.tsx index dfd8d7b4f8ae3..d54ace3910bab 100644 --- a/x-pack/plugins/cases/public/components/create/description.test.tsx +++ b/x-pack/plugins/cases/public/components/create/description.test.tsx @@ -106,7 +106,7 @@ describe('Description', () => { await waitFor(() => { expect( - screen.getByText('The length of the description is too long. The maximum length is 30000.') + screen.getByText('The length of the description is too long. The maximum length is 30000 characters.') ).toBeInTheDocument(); }); }); diff --git a/x-pack/plugins/cases/public/components/create/form_context.test.tsx b/x-pack/plugins/cases/public/components/create/form_context.test.tsx index 13bf3c04ebfc7..6c4893cd3ce73 100644 --- a/x-pack/plugins/cases/public/components/create/form_context.test.tsx +++ b/x-pack/plugins/cases/public/components/create/form_context.test.tsx @@ -325,7 +325,7 @@ describe('Create case', () => { await waitFor(() => { expect( - screen.getByText('The length of the name is too long. The maximum length is 160.') + screen.getByText('The length of the name is too long. The maximum length is 160 characters.') ).toBeInTheDocument(); }); diff --git a/x-pack/plugins/cases/public/components/create/tags.test.tsx b/x-pack/plugins/cases/public/components/create/tags.test.tsx index 2f0f35e7024ab..ea4554cfd1d39 100644 --- a/x-pack/plugins/cases/public/components/create/tags.test.tsx +++ b/x-pack/plugins/cases/public/components/create/tags.test.tsx @@ -103,7 +103,7 @@ describe('Tags', () => { userEvent.keyboard('{enter}'); await waitFor(() => { - expect(screen.getByText('The length of the tag is too long. The maximum length is 256.')); + expect(screen.getByText('The length of the tag is too long. The maximum length is 256 characters.')); }); }); }); diff --git a/x-pack/plugins/cases/public/components/description/index.test.tsx b/x-pack/plugins/cases/public/components/description/index.test.tsx index e943e266ea9bd..a180cc1ef3a59 100644 --- a/x-pack/plugins/cases/public/components/description/index.test.tsx +++ b/x-pack/plugins/cases/public/components/description/index.test.tsx @@ -141,7 +141,7 @@ describe('Description', () => { await waitFor(() => { expect( - screen.getByText('The length of the description is too long. The maximum length is 30000.') + screen.getByText('The length of the description is too long. The maximum length is 30000 characters.') ).toBeInTheDocument(); }); }); diff --git a/x-pack/plugins/cases/public/components/header_page/editable_title.test.tsx b/x-pack/plugins/cases/public/components/header_page/editable_title.test.tsx index 97f5b302b9cc9..b8486e67dff75 100644 --- a/x-pack/plugins/cases/public/components/header_page/editable_title.test.tsx +++ b/x-pack/plugins/cases/public/components/header_page/editable_title.test.tsx @@ -209,7 +209,7 @@ describe('EditableTitle', () => { wrapper.find('button[data-test-subj="editable-title-submit-btn"]').simulate('click'); wrapper.update(); expect(wrapper.find('.euiFormErrorText').text()).toBe( - 'The length of the title is too long. The maximum length is 160.' + 'The length of the title is too long. The maximum length is 160 characters.' ); expect(submitTitle).not.toHaveBeenCalled(); @@ -263,7 +263,7 @@ describe('EditableTitle', () => { wrapper.find('button[data-test-subj="editable-title-submit-btn"]').simulate('click'); wrapper.update(); expect(wrapper.find('.euiFormErrorText').text()).toBe( - 'The length of the title is too long. The maximum length is 160.' + 'The length of the title is too long. The maximum length is 160 characters.' ); // write a shorter one diff --git a/x-pack/plugins/cases/public/components/markdown_editor/editable_markdown_renderer.test.tsx b/x-pack/plugins/cases/public/components/markdown_editor/editable_markdown_renderer.test.tsx index 9738ba9640f04..bd83c23269f0f 100644 --- a/x-pack/plugins/cases/public/components/markdown_editor/editable_markdown_renderer.test.tsx +++ b/x-pack/plugins/cases/public/components/markdown_editor/editable_markdown_renderer.test.tsx @@ -152,7 +152,7 @@ describe('EditableMarkdown', () => { await waitFor(() => { expect( - screen.getByText('The length of the comment is too long. The maximum length is 30000.') + screen.getByText('The length of the comment is too long. The maximum length is 30000 characters.') ).toBeInTheDocument(); expect(screen.getByTestId('editable-save-markdown')).toHaveProperty('disabled'); }); diff --git a/x-pack/test/functional_with_es_ssl/apps/cases/group1/create_case_form.ts b/x-pack/test/functional_with_es_ssl/apps/cases/group1/create_case_form.ts index a5bf796105eea..55874757be096 100644 --- a/x-pack/test/functional_with_es_ssl/apps/cases/group1/create_case_form.ts +++ b/x-pack/test/functional_with_es_ssl/apps/cases/group1/create_case_form.ts @@ -82,7 +82,7 @@ export default ({ getService, getPageObject }: FtrProviderContext) => { const title = await find.byCssSelector('[data-test-subj="caseTitle"]'); expect(await title.getVisibleText()).contain( - 'The length of the name is too long. The maximum length is 160.' + 'The length of the name is too long. The maximum length is 160 characters.' ); const description = await testSubjects.find('caseDescription'); @@ -90,12 +90,12 @@ export default ({ getService, getPageObject }: FtrProviderContext) => { const tags = await testSubjects.find('caseTags'); expect(await tags.getVisibleText()).contain( - 'The length of the tag is too long. The maximum length is 256.' + 'The length of the tag is too long. The maximum length is 256 characters.' ); const category = await testSubjects.find('case-create-form-category'); expect(await category.getVisibleText()).contain( - 'The length of the category is too long. The maximum length is 50.' + 'The length of the category is too long. The maximum length is 50 characters.' ); }); diff --git a/x-pack/test/functional_with_es_ssl/apps/cases/group1/view_case.ts b/x-pack/test/functional_with_es_ssl/apps/cases/group1/view_case.ts index 94f9fc520ca17..72a7c3ac605c1 100644 --- a/x-pack/test/functional_with_es_ssl/apps/cases/group1/view_case.ts +++ b/x-pack/test/functional_with_es_ssl/apps/cases/group1/view_case.ts @@ -82,7 +82,7 @@ export default ({ getPageObject, getService }: FtrProviderContext) => { const error = await find.byCssSelector('.euiFormErrorText'); expect(await error.getVisibleText()).equal( - 'The length of the title is too long. The maximum length is 160.' + 'The length of the title is too long. The maximum length is 160 characters.' ); await testSubjects.click('editable-title-cancel-btn'); @@ -135,7 +135,7 @@ export default ({ getPageObject, getService }: FtrProviderContext) => { const error = await find.byCssSelector('.euiFormErrorText'); expect(await error.getVisibleText()).equal( - 'The length of the category is too long. The maximum length is 50.' + 'The length of the category is too long. The maximum length is 50 characters.' ); await testSubjects.click('edit-category-cancel'); @@ -167,7 +167,7 @@ export default ({ getPageObject, getService }: FtrProviderContext) => { const error = await find.byCssSelector('.euiFormErrorText'); expect(await error.getVisibleText()).equal( - 'The length of the tag is too long. The maximum length is 256.' + 'The length of the tag is too long. The maximum length is 256 characters.' ); await testSubjects.click('edit-tags-cancel'); From f91c242be54579a5b6cc7f313b92bf4f12602105 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Fri, 7 Jul 2023 08:27:45 +0000 Subject: [PATCH 3/5] [CI] Auto-commit changed files from 'node scripts/precommit_hook.js --ref HEAD~1..HEAD --fix' --- x-pack/plugins/cases/public/common/translations.ts | 3 ++- .../cases/public/components/add_comment/index.test.tsx | 4 +++- .../components/case_view/components/edit_category.test.tsx | 4 +++- .../components/case_view/components/edit_tags.test.tsx | 4 +++- .../public/components/category/category_form_field.test.tsx | 6 +++++- .../cases/public/components/create/description.test.tsx | 4 +++- .../cases/public/components/create/form_context.test.tsx | 4 +++- x-pack/plugins/cases/public/components/create/tags.test.tsx | 4 +++- .../cases/public/components/description/index.test.tsx | 4 +++- .../markdown_editor/editable_markdown_renderer.test.tsx | 4 +++- 10 files changed, 31 insertions(+), 10 deletions(-) diff --git a/x-pack/plugins/cases/public/common/translations.ts b/x-pack/plugins/cases/public/common/translations.ts index 8d4b687ed3b35..9dea7a3413f95 100644 --- a/x-pack/plugins/cases/public/common/translations.ts +++ b/x-pack/plugins/cases/public/common/translations.ts @@ -292,7 +292,8 @@ export const SELECT_CASE_TITLE = i18n.translate('xpack.cases.common.allCases.cas export const MAX_LENGTH_ERROR = (field: string, length: number) => i18n.translate('xpack.cases.createCase.maxLengthError', { values: { field, length }, - defaultMessage: 'The length of the {field} is too long. The maximum length is {length} characters.', + defaultMessage: + 'The length of the {field} is too long. The maximum length is {length} characters.', }); export const MAX_TAGS_ERROR = (length: number) => diff --git a/x-pack/plugins/cases/public/components/add_comment/index.test.tsx b/x-pack/plugins/cases/public/components/add_comment/index.test.tsx index ddffa7e3fe6cf..562dc407aed2f 100644 --- a/x-pack/plugins/cases/public/components/add_comment/index.test.tsx +++ b/x-pack/plugins/cases/public/components/add_comment/index.test.tsx @@ -220,7 +220,9 @@ describe('AddComment ', () => { await waitFor(() => { expect( - screen.getByText('The length of the comment is too long. The maximum length is 30000 characters.') + screen.getByText( + 'The length of the comment is too long. The maximum length is 30000 characters.' + ) ).toBeInTheDocument(); }); }); diff --git a/x-pack/plugins/cases/public/components/case_view/components/edit_category.test.tsx b/x-pack/plugins/cases/public/components/case_view/components/edit_category.test.tsx index 1d2c6599be52e..e41067dddc1ca 100644 --- a/x-pack/plugins/cases/public/components/case_view/components/edit_category.test.tsx +++ b/x-pack/plugins/cases/public/components/case_view/components/edit_category.test.tsx @@ -194,7 +194,9 @@ describe('EditCategory ', () => { await waitFor(() => { expect( - screen.getByText('The length of the category is too long. The maximum length is 50 characters.') + screen.getByText( + 'The length of the category is too long. The maximum length is 50 characters.' + ) ).toBeInTheDocument(); }); diff --git a/x-pack/plugins/cases/public/components/case_view/components/edit_tags.test.tsx b/x-pack/plugins/cases/public/components/case_view/components/edit_tags.test.tsx index 45f470cba70bb..28198f7f9c28c 100644 --- a/x-pack/plugins/cases/public/components/case_view/components/edit_tags.test.tsx +++ b/x-pack/plugins/cases/public/components/case_view/components/edit_tags.test.tsx @@ -132,7 +132,9 @@ describe('EditTags ', () => { userEvent.keyboard('{enter}'); await waitFor(() => { - expect(screen.getByText('The length of the tag is too long. The maximum length is 256 characters.')); + expect( + screen.getByText('The length of the tag is too long. The maximum length is 256 characters.') + ); }); }); diff --git a/x-pack/plugins/cases/public/components/category/category_form_field.test.tsx b/x-pack/plugins/cases/public/components/category/category_form_field.test.tsx index 5f9b033c7646f..cdaae6f49c5cc 100644 --- a/x-pack/plugins/cases/public/components/category/category_form_field.test.tsx +++ b/x-pack/plugins/cases/public/components/category/category_form_field.test.tsx @@ -120,7 +120,11 @@ describe('Category', () => { expect(onSubmit).toBeCalledWith({}, false); }); - expect(screen.getByText('The length of the category is too long. The maximum length is 50 characters.')); + expect( + screen.getByText( + 'The length of the category is too long. The maximum length is 50 characters.' + ) + ); }); it('can set a category from existing ones', async () => { diff --git a/x-pack/plugins/cases/public/components/create/description.test.tsx b/x-pack/plugins/cases/public/components/create/description.test.tsx index d54ace3910bab..5d678a6592638 100644 --- a/x-pack/plugins/cases/public/components/create/description.test.tsx +++ b/x-pack/plugins/cases/public/components/create/description.test.tsx @@ -106,7 +106,9 @@ describe('Description', () => { await waitFor(() => { expect( - screen.getByText('The length of the description is too long. The maximum length is 30000 characters.') + screen.getByText( + 'The length of the description is too long. The maximum length is 30000 characters.' + ) ).toBeInTheDocument(); }); }); diff --git a/x-pack/plugins/cases/public/components/create/form_context.test.tsx b/x-pack/plugins/cases/public/components/create/form_context.test.tsx index 6c4893cd3ce73..17bacdf683c4e 100644 --- a/x-pack/plugins/cases/public/components/create/form_context.test.tsx +++ b/x-pack/plugins/cases/public/components/create/form_context.test.tsx @@ -325,7 +325,9 @@ describe('Create case', () => { await waitFor(() => { expect( - screen.getByText('The length of the name is too long. The maximum length is 160 characters.') + screen.getByText( + 'The length of the name is too long. The maximum length is 160 characters.' + ) ).toBeInTheDocument(); }); diff --git a/x-pack/plugins/cases/public/components/create/tags.test.tsx b/x-pack/plugins/cases/public/components/create/tags.test.tsx index ea4554cfd1d39..65e94e8cc204e 100644 --- a/x-pack/plugins/cases/public/components/create/tags.test.tsx +++ b/x-pack/plugins/cases/public/components/create/tags.test.tsx @@ -103,7 +103,9 @@ describe('Tags', () => { userEvent.keyboard('{enter}'); await waitFor(() => { - expect(screen.getByText('The length of the tag is too long. The maximum length is 256 characters.')); + expect( + screen.getByText('The length of the tag is too long. The maximum length is 256 characters.') + ); }); }); }); diff --git a/x-pack/plugins/cases/public/components/description/index.test.tsx b/x-pack/plugins/cases/public/components/description/index.test.tsx index a180cc1ef3a59..37ea48158bb61 100644 --- a/x-pack/plugins/cases/public/components/description/index.test.tsx +++ b/x-pack/plugins/cases/public/components/description/index.test.tsx @@ -141,7 +141,9 @@ describe('Description', () => { await waitFor(() => { expect( - screen.getByText('The length of the description is too long. The maximum length is 30000 characters.') + screen.getByText( + 'The length of the description is too long. The maximum length is 30000 characters.' + ) ).toBeInTheDocument(); }); }); diff --git a/x-pack/plugins/cases/public/components/markdown_editor/editable_markdown_renderer.test.tsx b/x-pack/plugins/cases/public/components/markdown_editor/editable_markdown_renderer.test.tsx index bd83c23269f0f..f0f95363c4102 100644 --- a/x-pack/plugins/cases/public/components/markdown_editor/editable_markdown_renderer.test.tsx +++ b/x-pack/plugins/cases/public/components/markdown_editor/editable_markdown_renderer.test.tsx @@ -152,7 +152,9 @@ describe('EditableMarkdown', () => { await waitFor(() => { expect( - screen.getByText('The length of the comment is too long. The maximum length is 30000 characters.') + screen.getByText( + 'The length of the comment is too long. The maximum length is 30000 characters.' + ) ).toBeInTheDocument(); expect(screen.getByTestId('editable-save-markdown')).toHaveProperty('disabled'); }); From a12a48acb9d094b7e990cb13ad286dd2fab5509a Mon Sep 17 00:00:00 2001 From: Janki Salvi <117571355+js-jankisalvi@users.noreply.github.com> Date: Fri, 7 Jul 2023 15:52:31 +0200 Subject: [PATCH 4/5] disable save button on empty characters of string, maximum length errors --- .../components/add_comment/index.test.tsx | 18 +++++++++++++++++- .../public/components/add_comment/index.tsx | 6 +++++- .../components/description/index.test.tsx | 18 ++++++++++++++++++ .../editable_markdown_footer.tsx | 4 +++- .../editable_markdown_renderer.test.tsx | 19 ++++++++++++++++++- 5 files changed, 61 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/cases/public/components/add_comment/index.test.tsx b/x-pack/plugins/cases/public/components/add_comment/index.test.tsx index 562dc407aed2f..ac255f266620e 100644 --- a/x-pack/plugins/cases/public/components/add_comment/index.test.tsx +++ b/x-pack/plugins/cases/public/components/add_comment/index.test.tsx @@ -201,11 +201,26 @@ describe('AddComment ', () => { const markdown = screen.getByTestId('euiMarkdownEditorTextArea'); + userEvent.type(markdown, 'test'); userEvent.clear(markdown); - userEvent.type(markdown, ' '); await waitFor(() => { expect(screen.getByText('Empty comments are not allowed.')).toBeInTheDocument(); + expect(screen.getByTestId('submit-comment')).toHaveAttribute('disabled'); + }); + }); + + it('shows an error when comment is of empty characters', async () => { + appMockRender.render(); + + const markdown = screen.getByTestId('euiMarkdownEditorTextArea'); + + userEvent.clear(markdown); + userEvent.type(markdown, ' '); + + await waitFor(() => { + expect(screen.getByText('Empty comments are not allowed.')).toBeInTheDocument(); + expect(screen.getByTestId('submit-comment')).toHaveAttribute('disabled'); }); }); @@ -224,6 +239,7 @@ describe('AddComment ', () => { 'The length of the comment is too long. The maximum length is 30000 characters.' ) ).toBeInTheDocument(); + expect(screen.getByTestId('submit-comment')).toHaveAttribute('disabled'); }); }); }); diff --git a/x-pack/plugins/cases/public/components/add_comment/index.tsx b/x-pack/plugins/cases/public/components/add_comment/index.tsx index 94f45f129c0f3..59fc1fcde0fac 100644 --- a/x-pack/plugins/cases/public/components/add_comment/index.tsx +++ b/x-pack/plugins/cases/public/components/add_comment/index.tsx @@ -36,6 +36,7 @@ import type { AddCommentFormSchema } from './schema'; import { schema } from './schema'; import { InsertTimeline } from '../insert_timeline'; import { useCasesContext } from '../cases_context/use_cases_context'; +import { MAX_COMMENT_LENGTH } from '../../../common/constants'; const MySpinner = styled(EuiLoadingSpinner)` position: absolute; @@ -174,6 +175,9 @@ export const AddComment = React.memo( // eslint-disable-next-line react-hooks/exhaustive-deps }, [focusOnContext]); + const isDisabled = + isLoading || !comment?.trim().length || comment.trim().length > MAX_COMMENT_LENGTH; + return ( {isLoading && showLoading && } @@ -200,7 +204,7 @@ export const AddComment = React.memo( data-test-subj="submit-comment" fill iconType="plusInCircle" - isDisabled={!comment || isLoading} + isDisabled={isDisabled} isLoading={isLoading} onClick={onSubmit} > diff --git a/x-pack/plugins/cases/public/components/description/index.test.tsx b/x-pack/plugins/cases/public/components/description/index.test.tsx index 37ea48158bb61..5515753736274 100644 --- a/x-pack/plugins/cases/public/components/description/index.test.tsx +++ b/x-pack/plugins/cases/public/components/description/index.test.tsx @@ -122,6 +122,23 @@ describe('Description', () => { await waitFor(() => { expect(screen.getByText('A description is required.')).toBeInTheDocument(); + expect(screen.getByTestId('editable-save-markdown')).toHaveAttribute('disabled'); + }); + }); + + it('shows an error when description is a sting of empty characters', async () => { + const res = appMockRender.render( + + ); + + userEvent.click(res.getByTestId('description-edit-icon')); + + userEvent.clear(screen.getByTestId('euiMarkdownEditorTextArea')); + userEvent.type(screen.getByTestId('euiMarkdownEditorTextArea'), ' '); + + await waitFor(() => { + expect(screen.getByText('A description is required.')).toBeInTheDocument(); + expect(screen.getByTestId('editable-save-markdown')).toHaveAttribute('disabled'); }); }); @@ -145,6 +162,7 @@ describe('Description', () => { 'The length of the description is too long. The maximum length is 30000 characters.' ) ).toBeInTheDocument(); + expect(screen.getByTestId('editable-save-markdown')).toHaveAttribute('disabled'); }); }); diff --git a/x-pack/plugins/cases/public/components/markdown_editor/editable_markdown_footer.tsx b/x-pack/plugins/cases/public/components/markdown_editor/editable_markdown_footer.tsx index adceb00fe8eb4..18e52af5cde5a 100644 --- a/x-pack/plugins/cases/public/components/markdown_editor/editable_markdown_footer.tsx +++ b/x-pack/plugins/cases/public/components/markdown_editor/editable_markdown_footer.tsx @@ -24,6 +24,8 @@ const EditableMarkdownFooterComponent: React.FC = ( }) => { const [{ content }] = useFormData<{ content: string }>({ watch: ['content'] }); + const isDisabled = !content?.trim().length || content.trim().length > MAX_COMMENT_LENGTH; + return ( @@ -43,7 +45,7 @@ const EditableMarkdownFooterComponent: React.FC = ( fill iconType="save" onClick={handleSaveAction} - disabled={!content || content.length > MAX_COMMENT_LENGTH} + disabled={isDisabled} size="s" > {i18n.SAVE} diff --git a/x-pack/plugins/cases/public/components/markdown_editor/editable_markdown_renderer.test.tsx b/x-pack/plugins/cases/public/components/markdown_editor/editable_markdown_renderer.test.tsx index f0f95363c4102..7a266652576e0 100644 --- a/x-pack/plugins/cases/public/components/markdown_editor/editable_markdown_renderer.test.tsx +++ b/x-pack/plugins/cases/public/components/markdown_editor/editable_markdown_renderer.test.tsx @@ -129,7 +129,24 @@ describe('EditableMarkdown', () => { userEvent.clear(screen.getByTestId('euiMarkdownEditorTextArea')); - userEvent.type(screen.getByTestId('euiMarkdownEditorTextArea'), ' '); + userEvent.type(screen.getByTestId('euiMarkdownEditorTextArea'), ''); + + await waitFor(() => { + expect(screen.getByText('Empty comments are not allowed.')).toBeInTheDocument(); + expect(screen.getByTestId('editable-save-markdown')).toHaveProperty('disabled'); + }); + }); + + it('Shows error message and save button disabled if current text is of empty characters', async () => { + render( + + + + ); + + userEvent.clear(screen.getByTestId('euiMarkdownEditorTextArea')); + + userEvent.type(screen.getByTestId('euiMarkdownEditorTextArea'), ' '); await waitFor(() => { expect(screen.getByText('Empty comments are not allowed.')).toBeInTheDocument(); From 0097ebfebb03c4d0c18e0a190619b069df5c6706 Mon Sep 17 00:00:00 2001 From: Janki Salvi <117571355+js-jankisalvi@users.noreply.github.com> Date: Fri, 7 Jul 2023 18:25:44 +0200 Subject: [PATCH 5/5] PR feedback --- .../editable_markdown_footer.tsx | 11 ++--- .../editable_markdown_renderer.test.tsx | 46 ++++++++++++------ .../editable_markdown_renderer.tsx | 3 +- .../user_actions/markdown_form.test.tsx | 48 +++++++++++++++++++ 4 files changed, 84 insertions(+), 24 deletions(-) diff --git a/x-pack/plugins/cases/public/components/markdown_editor/editable_markdown_footer.tsx b/x-pack/plugins/cases/public/components/markdown_editor/editable_markdown_footer.tsx index 18e52af5cde5a..5ce43423597de 100644 --- a/x-pack/plugins/cases/public/components/markdown_editor/editable_markdown_footer.tsx +++ b/x-pack/plugins/cases/public/components/markdown_editor/editable_markdown_footer.tsx @@ -8,24 +8,19 @@ import { EuiFlexGroup, EuiFlexItem, EuiButtonEmpty, EuiButton } from '@elastic/eui'; import React from 'react'; -import { useFormData } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib'; - import * as i18n from '../case_view/translations'; -import { MAX_COMMENT_LENGTH } from '../../../common/constants'; interface EditableMarkdownFooterProps { handleSaveAction: () => Promise; handleCancelAction: () => void; + isSaveDisabled: boolean; } const EditableMarkdownFooterComponent: React.FC = ({ handleSaveAction, handleCancelAction, + isSaveDisabled, }) => { - const [{ content }] = useFormData<{ content: string }>({ watch: ['content'] }); - - const isDisabled = !content?.trim().length || content.trim().length > MAX_COMMENT_LENGTH; - return ( @@ -45,7 +40,7 @@ const EditableMarkdownFooterComponent: React.FC = ( fill iconType="save" onClick={handleSaveAction} - disabled={isDisabled} + disabled={isSaveDisabled} size="s" > {i18n.SAVE} diff --git a/x-pack/plugins/cases/public/components/markdown_editor/editable_markdown_renderer.test.tsx b/x-pack/plugins/cases/public/components/markdown_editor/editable_markdown_renderer.test.tsx index 7a266652576e0..af114e8ec473e 100644 --- a/x-pack/plugins/cases/public/components/markdown_editor/editable_markdown_renderer.test.tsx +++ b/x-pack/plugins/cases/public/components/markdown_editor/editable_markdown_renderer.test.tsx @@ -6,15 +6,17 @@ */ import React from 'react'; -import { useForm, Form } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib'; +import type { FormSchema } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib'; +import { useForm, Form, FIELD_TYPES } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib'; import { waitFor, fireEvent, screen, render, act } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; +import { fieldValidators } from '@kbn/es-ui-shared-plugin/static/forms/helpers'; +import * as i18n from '../../common/translations'; + +const { emptyField, maxLengthField } = fieldValidators; import { EditableMarkdown } from '.'; import { TestProviders } from '../../common/mock'; -import type { Content } from '../user_actions/schema'; -import { schema } from '../user_actions/schema'; -import { MAX_COMMENT_LENGTH } from '../../../common/constants'; jest.mock('../../common/lib/kibana'); @@ -25,6 +27,24 @@ const newValue = 'Hello from Tehas'; const hyperlink = `[hyperlink](http://elastic.co)`; const draftStorageKey = `cases.testAppId.caseId.markdown-id.markdownEditor`; const content = `A link to a timeline ${hyperlink}`; +const maxLength = 5000; + +const mockSchema: FormSchema<{ content: string }> = { + content: { + type: FIELD_TYPES.TEXTAREA, + validations: [ + { + validator: emptyField(i18n.REQUIRED_FIELD), + }, + { + validator: maxLengthField({ + length: maxLength, + message: i18n.MAX_LENGTH_ERROR('textarea', maxLength), + }), + }, + ], + }, +}; const editorRef: React.MutableRefObject = { current: null }; const defaultProps = { @@ -36,7 +56,7 @@ const defaultProps = { onChangeEditable, onSaveContent, fieldName: 'content', - formSchema: schema, + formSchema: mockSchema, editorRef, }; @@ -45,10 +65,10 @@ describe('EditableMarkdown', () => { children, testProviderProps = {}, }) => { - const { form } = useForm({ + const { form } = useForm<{ content: string }>({ defaultValue: { content }, options: { stripEmptyFields: false }, - schema, + schema: mockSchema, }); return ( @@ -116,10 +136,6 @@ describe('EditableMarkdown', () => { }); describe('errors', () => { - beforeAll(() => { - jest.useFakeTimers(); - }); - it('Shows error message and save button disabled if current text is empty', async () => { render( @@ -132,7 +148,7 @@ describe('EditableMarkdown', () => { userEvent.type(screen.getByTestId('euiMarkdownEditorTextArea'), ''); await waitFor(() => { - expect(screen.getByText('Empty comments are not allowed.')).toBeInTheDocument(); + expect(screen.getByText('Required field')).toBeInTheDocument(); expect(screen.getByTestId('editable-save-markdown')).toHaveProperty('disabled'); }); }); @@ -149,13 +165,13 @@ describe('EditableMarkdown', () => { userEvent.type(screen.getByTestId('euiMarkdownEditorTextArea'), ' '); await waitFor(() => { - expect(screen.getByText('Empty comments are not allowed.')).toBeInTheDocument(); + expect(screen.getByText('Required field')).toBeInTheDocument(); expect(screen.getByTestId('editable-save-markdown')).toHaveProperty('disabled'); }); }); it('Shows error message and save button disabled if current text is too long', async () => { - const longComment = 'b'.repeat(MAX_COMMENT_LENGTH + 1); + const longComment = 'b'.repeat(maxLength + 1); render( @@ -170,7 +186,7 @@ describe('EditableMarkdown', () => { await waitFor(() => { expect( screen.getByText( - 'The length of the comment is too long. The maximum length is 30000 characters.' + `The length of the textarea is too long. The maximum length is ${maxLength} characters.` ) ).toBeInTheDocument(); expect(screen.getByTestId('editable-save-markdown')).toHaveProperty('disabled'); diff --git a/x-pack/plugins/cases/public/components/markdown_editor/editable_markdown_renderer.tsx b/x-pack/plugins/cases/public/components/markdown_editor/editable_markdown_renderer.tsx index 1706e30fcc8f4..6cbaca3378242 100644 --- a/x-pack/plugins/cases/public/components/markdown_editor/editable_markdown_renderer.tsx +++ b/x-pack/plugins/cases/public/components/markdown_editor/editable_markdown_renderer.tsx @@ -46,7 +46,7 @@ const EditableMarkDownRenderer = forwardRef< options: { stripEmptyFields: false }, schema: formSchema, }); - const { submit, setFieldValue } = form; + const { submit, setFieldValue, isValid: isFormValid } = form; const setComment = useCallback( (newComment) => { @@ -90,6 +90,7 @@ const EditableMarkDownRenderer = forwardRef< ), initialValue: content, diff --git a/x-pack/plugins/cases/public/components/user_actions/markdown_form.test.tsx b/x-pack/plugins/cases/public/components/user_actions/markdown_form.test.tsx index a52d1bf221119..15a8094561332 100644 --- a/x-pack/plugins/cases/public/components/user_actions/markdown_form.test.tsx +++ b/x-pack/plugins/cases/public/components/user_actions/markdown_form.test.tsx @@ -12,6 +12,7 @@ import userEvent from '@testing-library/user-event'; import type { AppMockRenderer } from '../../common/mock'; import { createAppMockRenderer } from '../../common/mock'; import { UserActionMarkdown } from './markdown_form'; +import { MAX_COMMENT_LENGTH } from '../../../common/constants'; jest.mock('../../common/lib/kibana'); jest.mock('../../common/navigation/hooks'); @@ -58,6 +59,53 @@ describe('UserActionMarkdown ', () => { expect(screen.getByTestId('editable-cancel-markdown')).toBeInTheDocument(); }); + describe('errors', () => { + it('Shows error message and save button disabled if current text is empty', async () => { + appMockRenderer.render(); + + userEvent.clear(screen.getByTestId('euiMarkdownEditorTextArea')); + + userEvent.type(screen.getByTestId('euiMarkdownEditorTextArea'), ''); + + await waitFor(() => { + expect(screen.getByText('Empty comments are not allowed.')).toBeInTheDocument(); + expect(screen.getByTestId('editable-save-markdown')).toHaveProperty('disabled'); + }); + }); + + it('Shows error message and save button disabled if current text is of empty characters', async () => { + appMockRenderer.render(); + + userEvent.clear(screen.getByTestId('euiMarkdownEditorTextArea')); + + userEvent.type(screen.getByTestId('euiMarkdownEditorTextArea'), ' '); + + await waitFor(() => { + expect(screen.getByText('Empty comments are not allowed.')).toBeInTheDocument(); + expect(screen.getByTestId('editable-save-markdown')).toHaveProperty('disabled'); + }); + }); + + it('Shows error message and save button disabled if current text is too long', async () => { + const longComment = 'b'.repeat(MAX_COMMENT_LENGTH + 1); + + appMockRenderer.render(); + + const markdown = screen.getByTestId('euiMarkdownEditorTextArea'); + + userEvent.paste(markdown, longComment); + + await waitFor(() => { + expect( + screen.getByText( + 'The length of the comment is too long. The maximum length is 30000 characters.' + ) + ).toBeInTheDocument(); + expect(screen.getByTestId('editable-save-markdown')).toHaveProperty('disabled'); + }); + }); + }); + describe('useForm stale state bug', () => { const oldContent = defaultProps.content; const appendContent = ' appended content';