diff --git a/awx/ui/src/screens/NotificationTemplate/NotificationTemplateDetail/NotificationTemplateDetail.test.js b/awx/ui/src/screens/NotificationTemplate/NotificationTemplateDetail/NotificationTemplateDetail.test.js index 792aabc8b..e10edc090 100644 --- a/awx/ui/src/screens/NotificationTemplate/NotificationTemplateDetail/NotificationTemplateDetail.test.js +++ b/awx/ui/src/screens/NotificationTemplate/NotificationTemplateDetail/NotificationTemplateDetail.test.js @@ -1,9 +1,10 @@ import React from 'react'; -import { act } from 'react-dom/test-utils'; +import { screen } from '@testing-library/react'; + import { - mountWithContexts, - waitForElement, -} from '../../../../testUtils/enzymeHelpers'; + renderWithContexts, + assertDetail, +} from '../../../../testUtils/rtlContexts'; import NotificationTemplateDetail from './NotificationTemplateDetail'; import defaultMessages from '../shared/notification-template-default-messages.json'; @@ -23,28 +24,10 @@ const mockTemplate = { organization: '/api/v2/organizations/1/', }, summary_fields: { - organization: { - id: 1, - name: 'Default', - description: '', - }, - created_by: { - id: 2, - username: 'test', - first_name: '', - last_name: '', - }, - modified_by: { - id: 2, - username: 'test', - first_name: '', - last_name: '', - }, - user_capabilities: { - edit: true, - delete: true, - copy: true, - }, + organization: { id: 1, name: 'Default', description: '' }, + created_by: { id: 2, username: 'test', first_name: '', last_name: '' }, + modified_by: { id: 2, username: 'test', first_name: '', last_name: '' }, + user_capabilities: { edit: true, delete: true, copy: true }, recent_notifications: [{ status: 'success' }], }, created: '2021-06-16T18:52:23.811374Z', @@ -68,61 +51,33 @@ const mockTemplate = { }; describe('', () => { - let wrapper; - afterEach(() => { jest.clearAllMocks(); }); - test('should render Details', async () => { - await act(async () => { - wrapper = mountWithContexts( - - ); - }); - await waitForElement(wrapper, 'ContentLoading', (el) => el.length === 0); - function assertDetail(label, value) { - expect(wrapper.find(`Detail[label="${label}"] dt`).text()).toBe(label); - expect(wrapper.find(`Detail[label="${label}"] dd`).text()).toBe(value); - } + const assertCommonDetails = async () => { + expect(await screen.findByText('Name')).toBeInTheDocument(); assertDetail('Name', mockTemplate.name); assertDetail('Description', mockTemplate.description); - expect( - wrapper - .find('Detail[label="Email Options"]') - .containsAllMatchingElements([
  • Use SSL
  • ,
  • Use TLS
  • ]) - ).toEqual(true); - expect( - wrapper.find('Detail[label="Email Options"]').prop('helpText') - ).toBeDefined(); + // Email Options renders the SSL/TLS flags as list items + expect(screen.getByText('Use SSL')).toBeInTheDocument(); + expect(screen.getByText('Use TLS')).toBeInTheDocument(); + }; + + test('should render Details', async () => { + renderWithContexts( + + ); + await assertCommonDetails(); }); test('should render Details when defaultMessages is missing', async () => { - await act(async () => { - wrapper = mountWithContexts( - - ); - }); - await waitForElement(wrapper, 'ContentLoading', (el) => el.length === 0); - function assertDetail(label, value) { - expect(wrapper.find(`Detail[label="${label}"] dt`).text()).toBe(label); - expect(wrapper.find(`Detail[label="${label}"] dd`).text()).toBe(value); - } - assertDetail('Name', mockTemplate.name); - assertDetail('Description', mockTemplate.description); - expect( - wrapper - .find('Detail[label="Email Options"]') - .containsAllMatchingElements([
  • Use SSL
  • ,
  • Use TLS
  • ]) - ).toEqual(true); - expect( - wrapper.find('Detail[label="Email Options"]').prop('helpText') - ).toBeDefined(); + renderWithContexts( + + ); + await assertCommonDetails(); }); }); diff --git a/awx/ui/src/screens/NotificationTemplate/NotificationTemplateList/NotificationTemplateList.test.js b/awx/ui/src/screens/NotificationTemplate/NotificationTemplateList/NotificationTemplateList.test.js index 98493f4b5..d8f30751a 100644 --- a/awx/ui/src/screens/NotificationTemplate/NotificationTemplateList/NotificationTemplateList.test.js +++ b/awx/ui/src/screens/NotificationTemplate/NotificationTemplateList/NotificationTemplateList.test.js @@ -1,11 +1,9 @@ import React from 'react'; -import { act } from 'react-dom/test-utils'; -import { - NotificationsAPI, - NotificationTemplatesAPI, - OrganizationsAPI, -} from 'api'; -import { mountWithContexts } from '../../../../testUtils/enzymeHelpers'; +import { screen, waitFor, fireEvent, act } from '@testing-library/react'; + +import { NotificationsAPI, NotificationTemplatesAPI } from 'api'; +import { renderWithContexts } from '../../../../testUtils/rtlContexts'; + import NotificationTemplateList from './NotificationTemplateList'; jest.mock('../../../api'); @@ -20,19 +18,9 @@ const mockTemplates = { url: '/notification_templates/1', type: 'slack', summary_fields: { - organization: { - id: 1, - name: 'Foo', - }, - recent_notifications: [ - { - status: 'success', - }, - ], - user_capabilities: { - delete: true, - edit: true, - }, + organization: { id: 1, name: 'Foo' }, + recent_notifications: [{ status: 'success' }], + user_capabilities: { delete: true, edit: true }, }, }, { @@ -40,15 +28,9 @@ const mockTemplates = { id: 2, url: '/notification_templates/2', summary_fields: { - organization: { - id: 2, - name: 'Bar', - }, + organization: { id: 2, name: 'Bar' }, recent_notifications: [], - user_capabilities: { - delete: true, - edit: true, - }, + user_capabilities: { delete: true, edit: true }, }, }, { @@ -56,22 +38,9 @@ const mockTemplates = { id: 3, url: '/notification_templates/3', summary_fields: { - organization: { - id: 3, - name: 'Test', - }, - recent_notifications: [ - { - status: 'failed', - }, - { - status: 'success', - }, - ], - user_capabilities: { - delete: true, - edit: true, - }, + organization: { id: 3, name: 'Test' }, + recent_notifications: [{ status: 'failed' }, { status: 'success' }], + user_capabilities: { delete: true, edit: true }, }, }, ], @@ -79,180 +48,105 @@ const mockTemplates = { }; describe('', () => { - let wrapper; beforeEach(() => { - OrganizationsAPI.read.mockResolvedValue(mockTemplates); - OrganizationsAPI.readOptions.mockResolvedValue({ - data: { - actions: { - GET: {}, - POST: {}, - }, - }, + NotificationTemplatesAPI.read.mockResolvedValue(mockTemplates); + NotificationTemplatesAPI.readOptions.mockResolvedValue({ + data: { actions: { GET: {}, POST: {} } }, }); }); + afterEach(() => { jest.resetAllMocks(); }); test('should load notifications', async () => { - await act(async () => { - wrapper = mountWithContexts(); - }); - wrapper.update(); - expect(OrganizationsAPI.read).toHaveBeenCalledTimes(1); - expect(wrapper.find('NotificationTemplateListItem').length).toBe(3); + renderWithContexts(); + expect(await screen.findByText('Boston')).toBeInTheDocument(); + expect(screen.getByText('Minneapolis')).toBeInTheDocument(); + expect(screen.getByText('Philidelphia')).toBeInTheDocument(); + expect(NotificationTemplatesAPI.read).toHaveBeenCalledTimes(1); }); - test('should select item', async () => { - await act(async () => { - wrapper = mountWithContexts(); - }); - wrapper.update(); - expect( - wrapper.find('.pf-c-table__check').first().find('input').prop('checked') - ).toEqual(false); - await act(async () => { - wrapper - .find('.pf-c-table__check') - .first() - .find('input') - .props() - .onChange(); - }); - wrapper.update(); - expect( - wrapper.find('.pf-c-table__check').first().find('input').prop('checked') - ).toEqual(true); + test('should select a row', async () => { + const { user } = renderWithContexts(); + await screen.findByText('Boston'); + const checkboxes = screen.getAllByRole('checkbox'); + expect(checkboxes[1]).not.toBeChecked(); + await user.click(checkboxes[1]); + expect(checkboxes[1]).toBeChecked(); }); test('should delete notifications', async () => { - await act(async () => { - wrapper = mountWithContexts(); - }); - wrapper.update(); - expect(OrganizationsAPI.read).toHaveBeenCalledTimes(1); - await act(async () => { - wrapper.find('Checkbox#select-all').props().onChange(true); - }); - wrapper.update(); - await act(async () => { - wrapper.find('button[aria-label="Delete"]').simulate('click'); - wrapper.update(); - }); - const deleteButton = global.document.querySelector( - 'body div[role="dialog"] button[aria-label="confirm delete"]' + const { user } = renderWithContexts(); + await screen.findByText('Boston'); + await user.click(screen.getAllByRole('checkbox')[0]); // select all + await user.click(screen.getByRole('button', { name: 'Delete' })); + fireEvent.click(await screen.findByLabelText('confirm delete')); + await waitFor(() => + expect(NotificationTemplatesAPI.destroy).toHaveBeenCalledTimes(3) + ); + await waitFor(() => + expect(NotificationTemplatesAPI.read).toHaveBeenCalledTimes(2) ); - expect(deleteButton).not.toEqual(null); - await act(async () => { - deleteButton.click(); - }); - expect(OrganizationsAPI.destroy).toHaveBeenCalledTimes(3); - expect(OrganizationsAPI.read).toHaveBeenCalledTimes(2); }); - test('should show error dialog shown for failed deletion', async () => { - OrganizationsAPI.destroy.mockRejectedValue( - new Error({ - response: { - config: { - method: 'delete', - url: '/api/v2/organizations/1', - }, - data: 'An error occurred', - }, - }) - ); - await act(async () => { - wrapper = mountWithContexts(); - }); - wrapper.update(); - await act(async () => { - wrapper - .find('.pf-c-table__check') - .first() - .find('input') - .props() - .onChange(); - }); - wrapper.update(); - await act(async () => { - wrapper.find('button[aria-label="Delete"]').simulate('click'); - wrapper.update(); - }); - const deleteButton = global.document.querySelector( - 'body div[role="dialog"] button[aria-label="confirm delete"]' - ); - expect(deleteButton).not.toEqual(null); - await act(async () => { - deleteButton.click(); - }); - wrapper.update(); + test('should show an error dialog for a failed deletion', async () => { + NotificationTemplatesAPI.destroy.mockRejectedValue(new Error('nope')); + const { user } = renderWithContexts(); + await screen.findByText('Boston'); + await user.click(screen.getAllByRole('checkbox')[1]); + await user.click(screen.getByRole('button', { name: 'Delete' })); + fireEvent.click(await screen.findByLabelText('confirm delete')); + expect(await screen.findByText('Error!')).toBeInTheDocument(); + }); - const modal = wrapper.find('Modal'); - expect(modal.prop('isOpen')).toEqual(true); - expect(modal.prop('title')).toEqual('Error!'); + test('should show the add button', async () => { + renderWithContexts(); + await screen.findByText('Boston'); + expect(screen.getByRole('link', { name: 'Add' })).toBeInTheDocument(); }); - test('should show add button', async () => { - await act(async () => { - wrapper = mountWithContexts(); + test('should hide the add button without POST capability', async () => { + NotificationTemplatesAPI.readOptions.mockResolvedValue({ + data: { actions: { GET: {} } }, }); - wrapper.update(); - expect(wrapper.find('ToolbarAddButton').length).toBe(1); + renderWithContexts(); + await screen.findByText('Boston'); + expect(screen.queryByRole('link', { name: 'Add' })).not.toBeInTheDocument(); }); - test('should show toast after test resolves', async () => { + test('should show a toast after a test notification resolves', async () => { jest.useFakeTimers(); NotificationTemplatesAPI.test.mockResolvedValue({ - data: { - notification: 9182, - }, + data: { notification: 9182 }, }); NotificationsAPI.readDetail.mockResolvedValue({ data: { id: 9182, status: 'failed', error: 'There was an error with the notification', - summary_fields: { - notification_template: { - name: 'foobar', - }, - }, + summary_fields: { notification_template: { name: 'foobar' } }, }, }); - await act(async () => { - wrapper = mountWithContexts(); - }); - wrapper.update(); - expect(wrapper.find('Alert').length).toBe(0); - await act(async () => { - wrapper - .find('button[aria-label="Test Notification"]') - .at(0) - .simulate('click'); - }); - wrapper.update(); - await act(async () => { - jest.runAllTimers(); - }); - wrapper.update(); - expect(wrapper.find('Alert').length).toBe(1); - }); + renderWithContexts(); + // wait for the list rows/actions to render before interacting + await screen.findByText('Boston'); - test('should hide add button (rbac)', async () => { - OrganizationsAPI.readOptions.mockResolvedValue({ - data: { - actions: { - GET: {}, - }, - }, - }); + expect(screen.queryByText('foobar')).not.toBeInTheDocument(); + fireEvent.click( + screen.getAllByRole('button', { name: 'Test Notification' })[0] + ); + + // runAllTimersAsync flushes the microtask queue between timers, so the + // test() -> setTimeout(poll) -> readDetail() -> onAddToast chain resolves. await act(async () => { - wrapper = mountWithContexts(); + await jest.runAllTimersAsync(); }); - wrapper.update(); - expect(wrapper.find('ToolbarAddButton').length).toBe(0); + + // the toast carries the notification template name as its title + expect(NotificationsAPI.readDetail).toHaveBeenCalledWith(9182); + expect(screen.getByText('foobar')).toBeInTheDocument(); + jest.useRealTimers(); }); }); diff --git a/awx/ui/src/screens/NotificationTemplate/NotificationTemplateList/NotificationTemplateListItem.test.js b/awx/ui/src/screens/NotificationTemplate/NotificationTemplateList/NotificationTemplateListItem.test.js index bd3e07c46..b96b3d248 100644 --- a/awx/ui/src/screens/NotificationTemplate/NotificationTemplateList/NotificationTemplateListItem.test.js +++ b/awx/ui/src/screens/NotificationTemplate/NotificationTemplateList/NotificationTemplateListItem.test.js @@ -1,7 +1,9 @@ import React from 'react'; -import { act } from 'react-dom/test-utils'; +import { screen, waitFor } from '@testing-library/react'; + import { NotificationTemplatesAPI } from 'api'; -import { mountWithContexts } from '../../../../testUtils/enzymeHelpers'; +import { renderWithContexts } from '../../../../testUtils/rtlContexts'; + import NotificationTemplateListItem from './NotificationTemplateListItem'; jest.mock('../../../api/models/NotificationTemplates'); @@ -12,147 +14,81 @@ const template = { notification_type: 'slack', name: 'Test Notification', summary_fields: { - organization: { - id: 1, - name: 'Foo', - }, - user_capabilities: { - edit: true, - copy: true, - }, - recent_notifications: [ - { - status: 'success', - }, - ], + organization: { id: 1, name: 'Foo' }, + user_capabilities: { edit: true, copy: true }, + recent_notifications: [{ status: 'success' }], }, }; +const renderItem = (props = {}) => + renderWithContexts( + + + + +
    + ); + describe('', () => { - test('should render template row', () => { - const wrapper = mountWithContexts( - - - - -
    - ); + afterEach(() => { + jest.clearAllMocks(); + }); - const cells = wrapper.find('Td'); - expect(cells).toHaveLength(6); - expect(cells.at(1).text()).toEqual('Test Notification'); - expect(cells.at(2).text()).toEqual('Success'); - expect(cells.at(3).text()).toEqual('Slack'); + test('should render template row', () => { + renderItem(); + expect(screen.getByText('Test Notification')).toBeInTheDocument(); + expect(screen.getByText('Success')).toBeInTheDocument(); + expect(screen.getByText('Slack')).toBeInTheDocument(); }); test('should send test notification', async () => { NotificationTemplatesAPI.test.mockResolvedValue({ data: { notification: 1 }, }); - - const wrapper = mountWithContexts( - - - - -
    + const { user } = renderItem(); + await user.click(screen.getByRole('button', { name: 'Test Notification' })); + await waitFor(() => + expect(NotificationTemplatesAPI.test).toHaveBeenCalledTimes(1) ); - await act(async () => { - wrapper.find('Button').at(0).invoke('onClick')(); - }); - expect(NotificationTemplatesAPI.test).toHaveBeenCalledTimes(1); - expect(wrapper.find('Td').at(2).text()).toEqual('Running'); + expect(screen.getByText('Running')).toBeInTheDocument(); }); - test('should call api to copy inventory', async () => { + test('should call api to copy template', async () => { NotificationTemplatesAPI.copy.mockResolvedValue({ name: 'Foo' }); - - const wrapper = mountWithContexts( - - - - -
    + const { user } = renderItem(); + await user.click(screen.getByRole('button', { name: 'Copy' })); + await waitFor(() => + expect(NotificationTemplatesAPI.copy).toHaveBeenCalled() ); - - await act(async () => - wrapper.find('Button[aria-label="Copy"]').prop('onClick')() - ); - expect(NotificationTemplatesAPI.copy).toHaveBeenCalled(); - jest.clearAllMocks(); }); - test('should render proper alert modal on copy error', async () => { - NotificationTemplatesAPI.copy.mockRejectedValue( - new Error({ - response: { - config: { - method: 'post', - url: '/api/v2/notification_templates/3/copy', - }, - data: 'An error ocurred', - status: 403, - }, - }) - ); - - const wrapper = mountWithContexts( - - - - -
    - ); - expect(wrapper.find('Modal').length).toBe(0); - await act(async () => - wrapper.find('Button[aria-label="Copy"]').prop('onClick')() - ); - wrapper.update(); - expect(wrapper.find('Modal').length).toBe(1); - expect(wrapper.find('Modal').prop('isOpen')).toBe(true); - jest.clearAllMocks(); + test('should render an error modal on copy failure', async () => { + NotificationTemplatesAPI.copy.mockRejectedValue(new Error('nope')); + const { user } = renderItem(); + await user.click(screen.getByRole('button', { name: 'Copy' })); + expect( + await screen.findByText('Failed to copy template.') + ).toBeInTheDocument(); }); - test('should not render copy button', async () => { - const wrapper = mountWithContexts( - - - - -
    - ); - expect(wrapper.find('CopyButton').length).toBe(0); + test('should not render copy button without copy capability', () => { + renderItem({ + template: { + ...template, + summary_fields: { + organization: { id: 3, name: 'Test' }, + user_capabilities: { copy: false, edit: false }, + }, + }, + }); + expect( + screen.queryByRole('button', { name: 'Copy' }) + ).not.toBeInTheDocument(); }); }); diff --git a/awx/ui/src/screens/NotificationTemplate/shared/NotificationTemplateForm.test.js b/awx/ui/src/screens/NotificationTemplate/shared/NotificationTemplateForm.test.js index 1fca957f2..d293d335c 100644 --- a/awx/ui/src/screens/NotificationTemplate/shared/NotificationTemplateForm.test.js +++ b/awx/ui/src/screens/NotificationTemplate/shared/NotificationTemplateForm.test.js @@ -1,11 +1,36 @@ import React from 'react'; -import { act } from 'react-dom/test-utils'; -import { mountWithContexts } from '../../../../testUtils/enzymeHelpers'; +import { screen, waitFor, act } from '@testing-library/react'; + +import { renderWithContexts } from '../../../../testUtils/rtlContexts'; import NotificationTemplateForm from './NotificationTemplateForm'; jest.mock('../../../api/models/NotificationTemplates'); jest.mock('../../../api/models/Organizations'); +// react-ace (CodeEditor) does not expose its value as queryable text in jsdom, +// so render the editor value as plain text to allow content assertions. The +// custom-message fields use CodeEditorField, which is rendered from its formik +// field value. +jest.mock('components/CodeEditor', () => { + const ReactLib = require('react'); + return { + __esModule: true, + ...jest.requireActual('components/CodeEditor'), + default: ({ value }) => ReactLib.createElement('div', null, value), + }; +}); +jest.mock('components/CodeEditor/CodeEditorField', () => { + const ReactLib = require('react'); + const { useField } = require('formik'); + return { + __esModule: true, + default: ({ name }) => { + const [field] = useField(name); + return ReactLib.createElement('div', null, field.value); + }, + }; +}); + const template = { id: 3, notification_type: 'slack', @@ -14,18 +39,9 @@ const template = { url: '/notification_templates/3', organization: 1, summary_fields: { - user_capabilities: { - edit: true, - }, - recent_notifications: [ - { - status: 'success', - }, - ], - organization: { - id: 1, - name: 'The Organization', - }, + user_capabilities: { edit: true }, + recent_notifications: [{ status: 'success' }], + organization: { id: 1, name: 'The Organization' }, }, }; @@ -45,10 +61,7 @@ const emailTemplate = { }, }; -const messageDef = { - message: 'default message', - body: 'default body', -}; +const messageDef = { message: 'default message', body: 'default body' }; const defaults = { started: messageDef, success: messageDef, @@ -60,12 +73,7 @@ const defaults = { timed_out: messageDef, }, }; -const defaultMessages = { - email: defaults, - slack: defaults, - twilio: defaults, -}; - +const defaultMessages = { email: defaults, slack: defaults, twilio: defaults }; const allDefaultMessages = { ...defaultMessages, grafana: defaults, @@ -150,203 +158,135 @@ const secretTemplates = [ }, ]; -describe('', () => { - let wrapper; - test('should render form fields', async () => { - await act(async () => { - wrapper = mountWithContexts( - - ); - }); +const renderForm = (props = {}) => + renderWithContexts( + + ); - expect(wrapper.find('input#notification-name').prop('value')).toEqual( +describe('', () => { + test('should render fields and reveal email options on type change', async () => { + const { container, user } = renderForm(); + expect(container.querySelector('#notification-name')).toHaveValue( 'Test Notification' ); - expect( - wrapper.find('input#notification-description').prop('value') - ).toEqual('a sample notification'); - expect(wrapper.find('OrganizationLookup').prop('value')).toEqual({ - id: 1, - name: 'The Organization', - }); - expect(wrapper.find('AnsibleSelect').prop('value')).toEqual('slack'); - expect(wrapper.find('TypeInputsSubForm').prop('type')).toEqual('slack'); - expect(wrapper.find('CustomMessagesSubForm').prop('type')).toEqual('slack'); - expect( - wrapper.find('CustomMessagesSubForm').prop('defaultMessages') - ).toEqual(defaultMessages); - - expect(wrapper.find('input#option-use-ssl').length).toBe(0); - expect(wrapper.find('input#option-use-tls').length).toBe(0); - - await act(async () => { - wrapper.find('AnsibleSelect#notification-type').invoke('onChange')( - { - target: { - name: 'notification_type', - value: 'email', - }, - }, - 'email' - ); - }); + expect(container.querySelector('#notification-description')).toHaveValue( + 'a sample notification' + ); + expect(screen.getByDisplayValue('The Organization')).toBeInTheDocument(); + expect(container.querySelector('#notification-type')).toHaveValue('slack'); + expect(container.querySelector('#option-use-ssl')).toBeNull(); + expect(container.querySelector('#option-use-tls')).toBeNull(); - wrapper.update(); + await user.selectOptions( + container.querySelector('#notification-type'), + 'email' + ); - expect(wrapper.find('input#option-use-ssl').length).toBe(1); - expect(wrapper.find('input#option-use-tls').length).toBe(1); - expect( - wrapper.find('FormGroup[label="Email Options"]').find('HelpIcon').length - ).toBe(1); + await waitFor(() => + expect(container.querySelector('#option-use-ssl')).toBeInTheDocument() + ); + expect(container.querySelector('#option-use-tls')).toBeInTheDocument(); }); - test('should render custom messages fields', async () => { + test('should render existing custom messages', async () => { await act(async () => { - wrapper = mountWithContexts( - - ); + renderForm({ + template: { + ...template, + messages: { started: { message: 'Started', body: null } }, + }, + }); }); - - expect(wrapper.find('CodeEditor').at(0).prop('value')).toEqual('Started'); + expect(screen.getByText('Started')).toBeInTheDocument(); }); - test('should submit', async () => { - const handleSubmit = jest.fn(); - await act(async () => { - wrapper = mountWithContexts( - - ); + test('should submit the assembled values', async () => { + const onSubmit = jest.fn(); + const { user } = renderForm({ + template: { + ...template, + notification_configuration: { channels: ['#foo'], token: 'abc123' }, + }, + onSubmit, }); - await act(async () => { - wrapper.find('FormActionGroup').invoke('onSubmit')(); - }); - wrapper.update(); + await user.click(screen.getByRole('button', { name: 'Save' })); - expect(handleSubmit).toHaveBeenCalledWith({ - name: 'Test Notification', - description: 'a sample notification', - organization: 1, - notification_type: 'slack', - notification_configuration: { - channels: ['#foo'], - hex_color: '', - token: 'abc123', - }, - messages: null, - }); + await waitFor(() => + expect(onSubmit).toHaveBeenCalledWith({ + name: 'Test Notification', + description: 'a sample notification', + organization: 1, + notification_type: 'slack', + notification_configuration: { + channels: ['#foo'], + hex_color: '', + token: 'abc123', + }, + messages: null, + }) + ); }); test('should clear the email password when reverted', async () => { - const handleSubmit = jest.fn(); - await act(async () => { - wrapper = mountWithContexts( - - ); - }); - - await act(async () => { - wrapper - .find('Button') - .filterWhere( - (node) => node.prop('ouiaId') === 'notification_configuration.password-revert' - ) - .simulate('click'); + const onSubmit = jest.fn(); + const { container, user } = renderForm({ + template: emailTemplate, + onSubmit, }); - wrapper.update(); - await act(async () => { - wrapper.find('FormActionGroup').invoke('onSubmit')(); - }); - wrapper.update(); + await user.click( + container.querySelector( + '[data-ouia-component-id="notification_configuration.password-revert"]' + ) + ); + await user.click(screen.getByRole('button', { name: 'Save' })); - expect(handleSubmit).toHaveBeenCalledWith( - expect.objectContaining({ - notification_configuration: expect.objectContaining({ - password: '', - }), - }) + await waitFor(() => + expect(onSubmit).toHaveBeenCalledWith( + expect.objectContaining({ + notification_configuration: expect.objectContaining({ + password: '', + }), + }) + ) ); }); test.each(secretTemplates)( 'should clear the $type secret when reverted', async ({ template: secretTemplate, fieldName }) => { - const handleSubmit = jest.fn(); - await act(async () => { - wrapper = mountWithContexts( - - ); + const onSubmit = jest.fn(); + const { container, user } = renderForm({ + template: secretTemplate, + defaultMessages: allDefaultMessages, + onSubmit, }); - const revertButton = wrapper - .find('Button') - .filterWhere( - (node) => node.prop('ouiaId') === `${fieldName}-revert` - ); - - expect(revertButton.exists()).toBe(true); - - await act(async () => { - revertButton.simulate('click'); - }); - wrapper.update(); + const revertButton = container.querySelector( + `[data-ouia-component-id="${fieldName}-revert"]` + ); + expect(revertButton).not.toBeNull(); - await act(async () => { - wrapper.find('FormActionGroup').invoke('onSubmit')(); - }); - wrapper.update(); + await user.click(revertButton); + await user.click(screen.getByRole('button', { name: 'Save' })); - expect(handleSubmit).toHaveBeenCalledWith( - expect.objectContaining({ - notification_configuration: expect.objectContaining({ - [fieldName.split('.').pop()]: '', - }), - }) + const key = fieldName.split('.').pop(); + await waitFor(() => + expect(onSubmit).toHaveBeenCalledWith( + expect.objectContaining({ + notification_configuration: expect.objectContaining({ + [key]: '', + }), + }) + ) ); } );