Merge pull request #7785 from mabashian/convert-NotificationList-functional

Converts NotificationList to functional component and now uses useRequest

Reviewed-by: https://github.com/apps/softwarefactory-project-zuul
This commit is contained in:
softwarefactory-project-zuul[bot]
2020-07-31 19:29:56 +00:00
committed by GitHub
2 changed files with 266 additions and 381 deletions

View File

@@ -1,15 +1,14 @@
import React, { Component, Fragment } from 'react'; import React, { useEffect, useCallback, useState } from 'react';
import { number, shape, string, bool } from 'prop-types'; import { useLocation } from 'react-router-dom';
import { withRouter } from 'react-router-dom'; import { number, shape, bool } from 'prop-types';
import { withI18n } from '@lingui/react'; import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro'; import { t } from '@lingui/macro';
import AlertModal from '../AlertModal'; import AlertModal from '../AlertModal';
import ErrorDetail from '../ErrorDetail'; import ErrorDetail from '../ErrorDetail';
import NotificationListItem from './NotificationListItem'; import NotificationListItem from './NotificationListItem';
import PaginatedDataList from '../PaginatedDataList'; import PaginatedDataList from '../PaginatedDataList';
import { getQSConfig, parseQueryString } from '../../util/qs'; import { getQSConfig, parseQueryString } from '../../util/qs';
import useRequest from '../../util/useRequest';
import { NotificationTemplatesAPI } from '../../api'; import { NotificationTemplatesAPI } from '../../api';
const QS_CONFIG = getQSConfig('notification', { const QS_CONFIG = getQSConfig('notification', {
@@ -18,64 +17,49 @@ const QS_CONFIG = getQSConfig('notification', {
order_by: 'name', order_by: 'name',
}); });
class NotificationList extends Component { function NotificationList({ apiModel, canToggleNotifications, id, i18n }) {
constructor(props) { const location = useLocation();
super(props); const [isToggleLoading, setIsToggleLoading] = useState(false);
this.state = { const [toggleError, setToggleError] = useState(null);
contentError: null,
hasContentLoading: true,
toggleError: false,
loadingToggleIds: [],
itemCount: 0,
notifications: [],
startedTemplateIds: [],
successTemplateIds: [],
errorTemplateIds: [],
typeLabels: null,
};
this.handleNotificationToggle = this.handleNotificationToggle.bind(this);
this.handleNotificationErrorClose = this.handleNotificationErrorClose.bind(
this
);
this.loadNotifications = this.loadNotifications.bind(this);
}
componentDidMount() { const {
this.loadNotifications(); result: fetchNotificationsResult,
} result: {
notifications,
itemCount,
startedTemplateIds,
successTemplateIds,
errorTemplateIds,
typeLabels,
},
error: contentError,
isLoading,
request: fetchNotifications,
setValue,
} = useRequest(
useCallback(async () => {
const params = parseQueryString(QS_CONFIG, location.search);
const [
{
data: { results: notificationsResults, count: notificationsCount },
},
{
data: { actions },
},
] = await Promise.all([
NotificationTemplatesAPI.read(params),
NotificationTemplatesAPI.readOptions(),
]);
componentDidUpdate(prevProps) { const labels = actions.GET.notification_type.choices.reduce(
const { location } = this.props; (map, notifType) => ({ ...map, [notifType[0]]: notifType[1] }),
if (location !== prevProps.location) { {}
this.loadNotifications(); );
}
}
async loadNotifications() { const idMatchParams =
const { id, location, apiModel } = this.props; notificationsResults.length > 0
const { typeLabels } = this.state; ? { id__in: notificationsResults.map(n => n.id).join(',') }
const params = parseQueryString(QS_CONFIG, location.search); : {};
const promises = [NotificationTemplatesAPI.read(params)];
if (!typeLabels) {
promises.push(NotificationTemplatesAPI.readOptions());
}
this.setState({ contentError: null, hasContentLoading: true });
try {
const {
data: { count: itemCount = 0, results: notifications = [] },
} = await NotificationTemplatesAPI.read(params);
const optionsResponse = await NotificationTemplatesAPI.readOptions();
let idMatchParams;
if (notifications.length > 0) {
idMatchParams = { id__in: notifications.map(n => n.id).join(',') };
} else {
idMatchParams = {};
}
const [ const [
{ data: startedTemplates }, { data: startedTemplates },
@@ -87,69 +71,35 @@ class NotificationList extends Component {
apiModel.readNotificationTemplatesError(id, idMatchParams), apiModel.readNotificationTemplatesError(id, idMatchParams),
]); ]);
const stateToUpdate = { return {
itemCount, notifications: notificationsResults,
notifications, itemCount: notificationsCount,
startedTemplateIds: startedTemplates.results.map(st => st.id), startedTemplateIds: startedTemplates.results.map(st => st.id),
successTemplateIds: successTemplates.results.map(su => su.id), successTemplateIds: successTemplates.results.map(su => su.id),
errorTemplateIds: errorTemplates.results.map(e => e.id), errorTemplateIds: errorTemplates.results.map(e => e.id),
typeLabels: labels,
}; };
}, [apiModel, id, location]),
if (!typeLabels) { {
const { notifications: [],
data: { itemCount: 0,
actions: { startedTemplateIds: [],
GET: { successTemplateIds: [],
notification_type: { choices }, errorTemplateIds: [],
}, typeLabels: {},
},
},
} = optionsResponse;
// The structure of choices looks like [['slack', 'Slack'], ['email', 'Email'], ...]
stateToUpdate.typeLabels = choices.reduce(
(map, notifType) => ({ ...map, [notifType[0]]: notifType[1] }),
{}
);
}
this.setState(stateToUpdate);
} catch (err) {
this.setState({ contentError: err });
} finally {
this.setState({ hasContentLoading: false });
} }
} );
async handleNotificationToggle(notificationId, isCurrentlyOn, status) { useEffect(() => {
const { id, apiModel } = this.props; fetchNotifications();
}, [fetchNotifications]);
let stateArrayName; const handleNotificationToggle = async (
if (status === 'success') { notificationId,
stateArrayName = 'successTemplateIds'; isCurrentlyOn,
} else if (status === 'error') { status
stateArrayName = 'errorTemplateIds'; ) => {
} else if (status === 'started') { setIsToggleLoading(true);
stateArrayName = 'startedTemplateIds';
}
let stateUpdateFunction;
if (isCurrentlyOn) {
// when switching off, remove the toggled notification id from the array
stateUpdateFunction = prevState => ({
[stateArrayName]: prevState[stateArrayName].filter(
i => i !== notificationId
),
});
} else {
// when switching on, add the toggled notification id to the array
stateUpdateFunction = prevState => ({
[stateArrayName]: prevState[stateArrayName].concat(notificationId),
});
}
this.setState(({ loadingToggleIds }) => ({
loadingToggleIds: loadingToggleIds.concat([notificationId]),
}));
try { try {
if (isCurrentlyOn) { if (isCurrentlyOn) {
await apiModel.disassociateNotificationTemplate( await apiModel.disassociateNotificationTemplate(
@@ -157,128 +107,111 @@ class NotificationList extends Component {
notificationId, notificationId,
status status
); );
setValue({
...fetchNotificationsResult,
[`${status}TemplateIds`]: fetchNotificationsResult[
`${status}TemplateIds`
].filter(i => i !== notificationId),
});
} else { } else {
await apiModel.associateNotificationTemplate( await apiModel.associateNotificationTemplate(
id, id,
notificationId, notificationId,
status status
); );
setValue({
...fetchNotificationsResult,
[`${status}TemplateIds`]: fetchNotificationsResult[
`${status}TemplateIds`
].concat(notificationId),
});
} }
this.setState(stateUpdateFunction);
} catch (err) { } catch (err) {
this.setState({ toggleError: err }); setToggleError(err);
} finally { } finally {
this.setState(({ loadingToggleIds }) => ({ setIsToggleLoading(false);
loadingToggleIds: loadingToggleIds.filter(
item => item !== notificationId
),
}));
} }
} };
handleNotificationErrorClose() { return (
this.setState({ toggleError: false }); <>
} <PaginatedDataList
contentError={contentError}
render() { hasContentLoading={isLoading}
const { canToggleNotifications, i18n } = this.props; items={notifications}
const { itemCount={itemCount}
contentError, pluralizedItemName={i18n._(t`Notifications`)}
hasContentLoading, qsConfig={QS_CONFIG}
toggleError, toolbarSearchColumns={[
loadingToggleIds, {
itemCount, name: i18n._(t`Name`),
notifications, key: 'name',
startedTemplateIds, isDefault: true,
successTemplateIds, },
errorTemplateIds, {
typeLabels, name: i18n._(t`Type`),
} = this.state; key: 'type',
options: [
return ( ['email', i18n._(t`Email`)],
<Fragment> ['grafana', i18n._(t`Grafana`)],
<PaginatedDataList ['hipchat', i18n._(t`Hipchat`)],
contentError={contentError} ['irc', i18n._(t`IRC`)],
hasContentLoading={hasContentLoading} ['mattermost', i18n._(t`Mattermost`)],
items={notifications} ['pagerduty', i18n._(t`Pagerduty`)],
itemCount={itemCount} ['rocketchat', i18n._(t`Rocket.Chat`)],
pluralizedItemName={i18n._(t`Notifications`)} ['slack', i18n._(t`Slack`)],
qsConfig={QS_CONFIG} ['twilio', i18n._(t`Twilio`)],
toolbarSearchColumns={[ ['webhook', i18n._(t`Webhook`)],
{ ],
name: i18n._(t`Name`), },
key: 'name', {
isDefault: true, name: i18n._(t`Created by (username)`),
}, key: 'created_by__username',
{ },
name: i18n._(t`Type`), {
key: 'type', name: i18n._(t`Modified by (username)`),
options: [ key: 'modified_by__username',
['email', i18n._(t`Email`)], },
['grafana', i18n._(t`Grafana`)], ]}
['hipchat', i18n._(t`Hipchat`)], toolbarSortColumns={[
['irc', i18n._(t`IRC`)], {
['mattermost', i18n._(t`Mattermost`)], name: i18n._(t`Name`),
['pagerduty', i18n._(t`Pagerduty`)], key: 'name',
['rocketchat', i18n._(t`Rocket.Chat`)], },
['slack', i18n._(t`Slack`)], ]}
['twilio', i18n._(t`Twilio`)], renderItem={notification => (
['webhook', i18n._(t`Webhook`)], <NotificationListItem
], key={notification.id}
}, notification={notification}
{ detailUrl={`/notifications/${notification.id}`}
name: i18n._(t`Created By (Username)`), canToggleNotifications={canToggleNotifications && !isToggleLoading}
key: 'created_by__username', toggleNotification={handleNotificationToggle}
}, errorTurnedOn={errorTemplateIds.includes(notification.id)}
{ startedTurnedOn={startedTemplateIds.includes(notification.id)}
name: i18n._(t`Modified By (Username)`), successTurnedOn={successTemplateIds.includes(notification.id)}
key: 'modified_by__username', typeLabels={typeLabels}
}, />
]} )}
toolbarSortColumns={[ />
{ {toggleError && (
name: i18n._(t`Name`),
key: 'name',
},
]}
renderItem={notification => (
<NotificationListItem
key={notification.id}
notification={notification}
detailUrl={`/notifications/${notification.id}`}
canToggleNotifications={
canToggleNotifications &&
!loadingToggleIds.includes(notification.id)
}
toggleNotification={this.handleNotificationToggle}
errorTurnedOn={errorTemplateIds.includes(notification.id)}
startedTurnedOn={startedTemplateIds.includes(notification.id)}
successTurnedOn={successTemplateIds.includes(notification.id)}
typeLabels={typeLabels}
/>
)}
/>
<AlertModal <AlertModal
variant="error" variant="error"
title={i18n._(t`Error!`)} title={i18n._(t`Error!`)}
isOpen={toggleError && loadingToggleIds.length === 0} isOpen={!isToggleLoading}
onClose={this.handleNotificationErrorClose} onClose={() => setToggleError(null)}
> >
{i18n._(t`Failed to toggle notification.`)} {i18n._(t`Failed to toggle notification.`)}
<ErrorDetail error={toggleError} /> <ErrorDetail error={toggleError} />
</AlertModal> </AlertModal>
</Fragment> )}
); </>
} );
} }
NotificationList.propTypes = { NotificationList.propTypes = {
apiModel: shape({}).isRequired,
id: number.isRequired, id: number.isRequired,
canToggleNotifications: bool.isRequired, canToggleNotifications: bool.isRequired,
location: shape({
search: string.isRequired,
}).isRequired,
}; };
export { NotificationList as _NotificationList }; export default withI18n()(NotificationList);
export default withI18n()(withRouter(NotificationList));

View File

@@ -1,15 +1,13 @@
import React from 'react'; import React from 'react';
import { act } from 'react-dom/test-utils';
import { mountWithContexts } from '../../../testUtils/enzymeHelpers'; import { mountWithContexts } from '../../../testUtils/enzymeHelpers';
import { sleep } from '../../../testUtils/testUtils';
import { NotificationTemplatesAPI } from '../../api'; import { NotificationTemplatesAPI } from '../../api';
import NotificationList from './NotificationList'; import NotificationList from './NotificationList';
jest.mock('../../api'); jest.mock('../../api');
describe('<NotificationList />', () => { describe('<NotificationList />', () => {
let wrapper;
const data = { const data = {
count: 2, count: 2,
results: [ results: [
@@ -58,220 +56,185 @@ describe('<NotificationList />', () => {
}, },
}); });
beforeEach(() => { NotificationTemplatesAPI.read.mockReturnValue({ data });
NotificationTemplatesAPI.read.mockReturnValue({ data });
MockModelAPI.readNotificationTemplatesSuccess.mockReturnValue({ MockModelAPI.readNotificationTemplatesSuccess.mockReturnValue({
data: { results: [{ id: 1 }] }, data: { results: [{ id: 1 }] },
}); });
MockModelAPI.readNotificationTemplatesError.mockReturnValue({
data: { results: [{ id: 2 }] }, MockModelAPI.readNotificationTemplatesError.mockReturnValue({
}); data: { results: [{ id: 2 }] },
MockModelAPI.readNotificationTemplatesStarted.mockReturnValue({ });
data: { results: [{ id: 3 }] },
MockModelAPI.readNotificationTemplatesStarted.mockReturnValue({
data: { results: [{ id: 3 }] },
});
beforeEach(async () => {
await act(async () => {
wrapper = mountWithContexts(
<NotificationList
id={1}
canToggleNotifications
apiModel={MockModelAPI}
/>
);
}); });
wrapper.update();
}); });
afterEach(() => { afterEach(() => {
jest.clearAllMocks(); wrapper.unmount();
}); });
test('initially renders succesfully', async () => { test('initially renders succesfully', () => {
const wrapper = mountWithContexts( expect(wrapper.find('PaginatedDataList')).toHaveLength(1);
<NotificationList id={1} canToggleNotifications apiModel={MockModelAPI} />
);
await sleep(0);
wrapper.update();
const dataList = wrapper.find('PaginatedDataList');
expect(dataList).toHaveLength(1);
expect(dataList.prop('items')).toEqual(data.results);
}); });
test('should render list fetched of items', async () => { test('should render list fetched of items', () => {
const wrapper = mountWithContexts(
<NotificationList id={1} canToggleNotifications apiModel={MockModelAPI} />
);
await sleep(0);
wrapper.update();
expect(NotificationTemplatesAPI.read).toHaveBeenCalled(); expect(NotificationTemplatesAPI.read).toHaveBeenCalled();
expect(wrapper.find('NotificationList').state('notifications')).toEqual( expect(NotificationTemplatesAPI.readOptions).toHaveBeenCalled();
data.results expect(MockModelAPI.readNotificationTemplatesSuccess).toHaveBeenCalled();
); expect(MockModelAPI.readNotificationTemplatesError).toHaveBeenCalled();
const items = wrapper.find('NotificationListItem'); expect(MockModelAPI.readNotificationTemplatesStarted).toHaveBeenCalled();
expect(items).toHaveLength(3); expect(wrapper.find('NotificationListItem').length).toBe(3);
expect(items.at(0).prop('successTurnedOn')).toEqual(true); expect(
expect(items.at(0).prop('errorTurnedOn')).toEqual(false); wrapper.find('input#notification-1-success-toggle').props().checked
expect(items.at(0).prop('startedTurnedOn')).toEqual(false); ).toBe(true);
expect(items.at(1).prop('successTurnedOn')).toEqual(false); expect(
expect(items.at(1).prop('errorTurnedOn')).toEqual(true); wrapper.find('input#notification-1-error-toggle').props().checked
expect(items.at(1).prop('startedTurnedOn')).toEqual(false); ).toBe(false);
expect(items.at(2).prop('successTurnedOn')).toEqual(false); expect(
expect(items.at(2).prop('errorTurnedOn')).toEqual(false); wrapper.find('input#notification-1-started-toggle').props().checked
expect(items.at(2).prop('startedTurnedOn')).toEqual(true); ).toBe(false);
expect(
wrapper.find('input#notification-2-success-toggle').props().checked
).toBe(false);
expect(
wrapper.find('input#notification-2-error-toggle').props().checked
).toBe(true);
expect(
wrapper.find('input#notification-2-started-toggle').props().checked
).toBe(false);
expect(
wrapper.find('input#notification-3-success-toggle').props().checked
).toBe(false);
expect(
wrapper.find('input#notification-3-error-toggle').props().checked
).toBe(false);
expect(
wrapper.find('input#notification-3-started-toggle').props().checked
).toBe(true);
}); });
test('should enable success notification', async () => { test('should enable success notification', async () => {
const wrapper = mountWithContexts(
<NotificationList id={1} canToggleNotifications apiModel={MockModelAPI} />
);
await sleep(0);
wrapper.update();
expect( expect(
wrapper.find('NotificationList').state('successTemplateIds') wrapper.find('input#notification-2-success-toggle').props().checked
).toEqual([1]); ).toBe(false);
const items = wrapper.find('NotificationListItem'); await act(async () => {
items wrapper.find('Switch#notification-2-success-toggle').prop('onChange')();
.at(1) });
.find('Switch[aria-label="Toggle notification success"]') wrapper.update();
.prop('onChange')();
expect(MockModelAPI.associateNotificationTemplate).toHaveBeenCalledWith( expect(MockModelAPI.associateNotificationTemplate).toHaveBeenCalledWith(
1, 1,
2, 2,
'success' 'success'
); );
await sleep(0);
wrapper.update();
expect( expect(
wrapper.find('NotificationList').state('successTemplateIds') wrapper.find('input#notification-2-success-toggle').props().checked
).toEqual([1, 2]); ).toBe(true);
}); });
test('should enable error notification', async () => { test('should enable error notification', async () => {
const wrapper = mountWithContexts( expect(
<NotificationList id={1} canToggleNotifications apiModel={MockModelAPI} /> wrapper.find('input#notification-1-error-toggle').props().checked
); ).toBe(false);
await sleep(0); await act(async () => {
wrapper.find('Switch#notification-1-error-toggle').prop('onChange')();
});
wrapper.update(); wrapper.update();
expect(wrapper.find('NotificationList').state('errorTemplateIds')).toEqual([
2,
]);
const items = wrapper.find('NotificationListItem');
items
.at(0)
.find('Switch[aria-label="Toggle notification failure"]')
.prop('onChange')();
expect(MockModelAPI.associateNotificationTemplate).toHaveBeenCalledWith( expect(MockModelAPI.associateNotificationTemplate).toHaveBeenCalledWith(
1, 1,
1, 1,
'error' 'error'
); );
await sleep(0); expect(
wrapper.update(); wrapper.find('input#notification-1-error-toggle').props().checked
expect(wrapper.find('NotificationList').state('errorTemplateIds')).toEqual([ ).toBe(true);
2,
1,
]);
}); });
test('should enable start notification', async () => { test('should enable start notification', async () => {
const wrapper = mountWithContexts(
<NotificationList id={1} canToggleNotifications apiModel={MockModelAPI} />
);
await sleep(0);
wrapper.update();
expect( expect(
wrapper.find('NotificationList').state('startedTemplateIds') wrapper.find('input#notification-1-started-toggle').props().checked
).toEqual([3]); ).toBe(false);
const items = wrapper.find('NotificationListItem'); await act(async () => {
items wrapper.find('Switch#notification-1-started-toggle').prop('onChange')();
.at(0) });
.find('Switch[aria-label="Toggle notification start"]') wrapper.update();
.prop('onChange')();
expect(MockModelAPI.associateNotificationTemplate).toHaveBeenCalledWith( expect(MockModelAPI.associateNotificationTemplate).toHaveBeenCalledWith(
1, 1,
1, 1,
'started' 'started'
); );
await sleep(0);
wrapper.update();
expect( expect(
wrapper.find('NotificationList').state('startedTemplateIds') wrapper.find('input#notification-1-started-toggle').props().checked
).toEqual([3, 1]); ).toBe(true);
}); });
test('should disable success notification', async () => { test('should disable success notification', async () => {
const wrapper = mountWithContexts(
<NotificationList id={1} canToggleNotifications apiModel={MockModelAPI} />
);
await sleep(0);
wrapper.update();
expect( expect(
wrapper.find('NotificationList').state('successTemplateIds') wrapper.find('input#notification-1-success-toggle').props().checked
).toEqual([1]); ).toBe(true);
const items = wrapper.find('NotificationListItem'); await act(async () => {
items wrapper.find('Switch#notification-1-success-toggle').prop('onChange')();
.at(0) });
.find('Switch[aria-label="Toggle notification success"]') wrapper.update();
.prop('onChange')();
expect(MockModelAPI.disassociateNotificationTemplate).toHaveBeenCalledWith( expect(MockModelAPI.disassociateNotificationTemplate).toHaveBeenCalledWith(
1, 1,
1, 1,
'success' 'success'
); );
await sleep(0);
wrapper.update();
expect( expect(
wrapper.find('NotificationList').state('successTemplateIds') wrapper.find('input#notification-1-success-toggle').props().checked
).toEqual([]); ).toBe(false);
}); });
test('should disable error notification', async () => { test('should disable error notification', async () => {
const wrapper = mountWithContexts( expect(
<NotificationList id={1} canToggleNotifications apiModel={MockModelAPI} /> wrapper.find('input#notification-2-error-toggle').props().checked
); ).toBe(true);
await sleep(0); await act(async () => {
wrapper.find('Switch#notification-2-error-toggle').prop('onChange')();
});
wrapper.update(); wrapper.update();
expect(wrapper.find('NotificationList').state('errorTemplateIds')).toEqual([
2,
]);
const items = wrapper.find('NotificationListItem');
items
.at(1)
.find('Switch[aria-label="Toggle notification failure"]')
.prop('onChange')();
expect(MockModelAPI.disassociateNotificationTemplate).toHaveBeenCalledWith( expect(MockModelAPI.disassociateNotificationTemplate).toHaveBeenCalledWith(
1, 1,
2, 2,
'error' 'error'
); );
await sleep(0); expect(
wrapper.update(); wrapper.find('input#notification-2-error-toggle').props().checked
expect(wrapper.find('NotificationList').state('errorTemplateIds')).toEqual( ).toBe(false);
[]
);
}); });
test('should disable start notification', async () => { test('should disable start notification', async () => {
const wrapper = mountWithContexts(
<NotificationList id={1} canToggleNotifications apiModel={MockModelAPI} />
);
await sleep(0);
wrapper.update();
expect( expect(
wrapper.find('NotificationList').state('startedTemplateIds') wrapper.find('input#notification-3-started-toggle').props().checked
).toEqual([3]); ).toBe(true);
const items = wrapper.find('NotificationListItem'); await act(async () => {
items wrapper.find('Switch#notification-3-started-toggle').prop('onChange')();
.at(2) });
.find('Switch[aria-label="Toggle notification start"]') wrapper.update();
.prop('onChange')();
expect(MockModelAPI.disassociateNotificationTemplate).toHaveBeenCalledWith( expect(MockModelAPI.disassociateNotificationTemplate).toHaveBeenCalledWith(
1, 1,
3, 3,
'started' 'started'
); );
await sleep(0);
wrapper.update();
expect( expect(
wrapper.find('NotificationList').state('startedTemplateIds') wrapper.find('input#notification-3-started-toggle').props().checked
).toEqual([]); ).toBe(false);
}); });
test('should throw toggle error', async () => { test('should throw toggle error', async () => {
MockModelAPI.associateNotificationTemplate.mockRejectedValue( MockModelAPI.associateNotificationTemplate.mockRejectedValue(
new Error({ new Error({
@@ -284,27 +247,16 @@ describe('<NotificationList />', () => {
}, },
}) })
); );
const wrapper = mountWithContexts( expect(wrapper.find('ErrorDetail').length).toBe(0);
<NotificationList id={1} canToggleNotifications apiModel={MockModelAPI} /> await act(async () => {
); wrapper.find('Switch#notification-1-started-toggle').prop('onChange')();
await sleep(0); });
wrapper.update(); wrapper.update();
expect(
wrapper.find('NotificationList').state('startedTemplateIds')
).toEqual([3]);
const items = wrapper.find('NotificationListItem');
items
.at(0)
.find('Switch[aria-label="Toggle notification start"]')
.prop('onChange')();
expect(MockModelAPI.associateNotificationTemplate).toHaveBeenCalledWith( expect(MockModelAPI.associateNotificationTemplate).toHaveBeenCalledWith(
1, 1,
1, 1,
'started' 'started'
); );
await sleep(0);
wrapper.update();
expect(wrapper.find('ErrorDetail').length).toBe(1); expect(wrapper.find('ErrorDetail').length).toBe(1);
}); });
}); });