From 25e15cac0a16fc86ec6bb0d885d6134dfdbbfc9d Mon Sep 17 00:00:00 2001 From: Muhammad Aditya Hilmy Date: Tue, 13 Oct 2020 14:24:37 +0700 Subject: [PATCH] [MM-20455] Migrate 'components/integrations/edit_incoming_webhook' module and associated tests to TypeScript (#6596) --- ...ap => edit_incoming_webhook.test.tsx.snap} | 116 ++++++++++++++++-- ...est.jsx => edit_incoming_webhook.test.tsx} | 69 +++++------ ..._webhook.jsx => edit_incoming_webhook.tsx} | 115 +++++++++-------- .../{index.js => index.ts} | 25 +++- 4 files changed, 218 insertions(+), 107 deletions(-) rename components/integrations/edit_incoming_webhook/__snapshots__/{edit_incoming_webhook.test.jsx.snap => edit_incoming_webhook.test.tsx.snap} (52%) rename components/integrations/edit_incoming_webhook/{edit_incoming_webhook.test.jsx => edit_incoming_webhook.test.tsx} (72%) rename components/integrations/edit_incoming_webhook/{edit_incoming_webhook.jsx => edit_incoming_webhook.tsx} (55%) rename components/integrations/edit_incoming_webhook/{index.js => index.ts} (57%) diff --git a/components/integrations/edit_incoming_webhook/__snapshots__/edit_incoming_webhook.test.jsx.snap b/components/integrations/edit_incoming_webhook/__snapshots__/edit_incoming_webhook.test.tsx.snap similarity index 52% rename from components/integrations/edit_incoming_webhook/__snapshots__/edit_incoming_webhook.test.jsx.snap rename to components/integrations/edit_incoming_webhook/__snapshots__/edit_incoming_webhook.test.tsx.snap index 152b348e4029..1ac76a62bdbe 100644 --- a/components/integrations/edit_incoming_webhook/__snapshots__/edit_incoming_webhook.test.jsx.snap +++ b/components/integrations/edit_incoming_webhook/__snapshots__/edit_incoming_webhook.test.tsx.snap @@ -19,8 +19,18 @@ exports[`components/integrations/EditIncomingWebhook should have called submitHo } initialHook={ Object { + "channel_id": "channel_id", + "channel_locked": false, + "create_at": 0, + "delete_at": 20, + "description": "description", + "display_name": "display_name", + "icon_url": "http://test/icon.png", "id": "id", - "token": "token", + "team_id": "team_id", + "update_at": 10, + "user_id": "user_id", + "username": "username", } } loading={ @@ -32,8 +42,21 @@ exports[`components/integrations/EditIncomingWebhook should have called submitHo serverError="" team={ Object { - "id": "testteamid", - "name": "test", + "allow_open_invite": false, + "allowed_domains": "", + "company_name": "", + "create_at": 0, + "delete_at": 0, + "description": "", + "display_name": "name", + "email": "", + "group_constrained": false, + "id": "team_id", + "invite_id": "", + "name": "DN", + "scheme_id": "id", + "type": "O", + "update_at": 0, } } /> @@ -58,8 +81,18 @@ exports[`components/integrations/EditIncomingWebhook should have called submitHo } initialHook={ Object { + "channel_id": "channel_id", + "channel_locked": false, + "create_at": 0, + "delete_at": 20, + "description": "description", + "display_name": "display_name", + "icon_url": "http://test/icon.png", "id": "id", - "token": "token", + "team_id": "team_id", + "update_at": 10, + "user_id": "user_id", + "username": "username", } } loading={ @@ -71,8 +104,21 @@ exports[`components/integrations/EditIncomingWebhook should have called submitHo serverError="" team={ Object { - "id": "testteamid", - "name": "test", + "allow_open_invite": false, + "allowed_domains": "", + "company_name": "", + "create_at": 0, + "delete_at": 0, + "description": "", + "display_name": "name", + "email": "", + "group_constrained": false, + "id": "team_id", + "invite_id": "", + "name": "DN", + "scheme_id": "id", + "type": "O", + "update_at": 0, } } /> @@ -97,8 +143,18 @@ exports[`components/integrations/EditIncomingWebhook should have called submitHo } initialHook={ Object { + "channel_id": "channel_id", + "channel_locked": false, + "create_at": 0, + "delete_at": 20, + "description": "description", + "display_name": "display_name", + "icon_url": "http://test/icon.png", "id": "id", - "token": "token", + "team_id": "team_id", + "update_at": 10, + "user_id": "user_id", + "username": "username", } } loading={ @@ -110,8 +166,21 @@ exports[`components/integrations/EditIncomingWebhook should have called submitHo serverError="" team={ Object { - "id": "testteamid", - "name": "test", + "allow_open_invite": false, + "allowed_domains": "", + "company_name": "", + "create_at": 0, + "delete_at": 0, + "description": "", + "display_name": "name", + "email": "", + "group_constrained": false, + "id": "team_id", + "invite_id": "", + "name": "DN", + "scheme_id": "id", + "type": "O", + "update_at": 0, } } /> @@ -143,8 +212,18 @@ exports[`components/integrations/EditIncomingWebhook should show AbstractIncomin } initialHook={ Object { + "channel_id": "channel_id", + "channel_locked": false, + "create_at": 0, + "delete_at": 20, + "description": "description", + "display_name": "display_name", + "icon_url": "http://test/icon.png", "id": "id", - "token": "token", + "team_id": "team_id", + "update_at": 10, + "user_id": "user_id", + "username": "username", } } loading={ @@ -156,8 +235,21 @@ exports[`components/integrations/EditIncomingWebhook should show AbstractIncomin serverError="" team={ Object { - "id": "testteamid", - "name": "test", + "allow_open_invite": false, + "allowed_domains": "", + "company_name": "", + "create_at": 0, + "delete_at": 0, + "description": "", + "display_name": "name", + "email": "", + "group_constrained": false, + "id": "team_id", + "invite_id": "", + "name": "DN", + "scheme_id": "id", + "type": "O", + "update_at": 0, } } /> diff --git a/components/integrations/edit_incoming_webhook/edit_incoming_webhook.test.jsx b/components/integrations/edit_incoming_webhook/edit_incoming_webhook.test.tsx similarity index 72% rename from components/integrations/edit_incoming_webhook/edit_incoming_webhook.test.jsx rename to components/integrations/edit_incoming_webhook/edit_incoming_webhook.test.tsx index 95819bf9be24..f0dd4c1bd3e5 100644 --- a/components/integrations/edit_incoming_webhook/edit_incoming_webhook.test.jsx +++ b/components/integrations/edit_incoming_webhook/edit_incoming_webhook.test.tsx @@ -2,28 +2,42 @@ // See LICENSE.txt for license information. import React from 'react'; -import {shallow} from 'enzyme'; +import {shallow, ShallowWrapper} from 'enzyme'; + +import {IncomingWebhook} from 'mattermost-redux/types/integrations'; +import {ActionResult} from 'mattermost-redux/types/actions'; import {browserHistory} from 'utils/browser_history'; -import EditIncomingWebhook from 'components/integrations/edit_incoming_webhook/edit_incoming_webhook.jsx'; +import EditIncomingWebhook from 'components/integrations/edit_incoming_webhook/edit_incoming_webhook'; +import {TestHelper} from '../../../utils/test_helper'; describe('components/integrations/EditIncomingWebhook', () => { const hook = { id: 'id', - token: 'token', + create_at: 0, + update_at: 10, + delete_at: 20, + user_id: 'user_id', + channel_id: 'channel_id', + team_id: 'team_id', + display_name: 'display_name', + description: 'description', + username: 'username', + icon_url: 'http://test/icon.png', + channel_locked: false, }; - let updateIncomingHook = null; - let getIncomingHook = null; - let actions = {}; + const updateIncomingHook = jest.fn(); + const getIncomingHook = jest.fn(); + const actions = { + updateIncomingHook: updateIncomingHook as (hook: IncomingWebhook) => Promise, + getIncomingHook: getIncomingHook as (hookId: string) => Promise, + }; const requiredProps = { hookId: 'somehookid', teamId: 'testteamid', - team: { - id: 'testteamid', - name: 'test', - }, + team: TestHelper.getTeamMock(), updateIncomingHookRequest: { status: 'not_started', error: null, @@ -34,18 +48,8 @@ describe('components/integrations/EditIncomingWebhook', () => { }; afterEach(() => { - updateIncomingHook = null; - getIncomingHook = null; - actions = {}; - }); - - beforeEach(() => { - updateIncomingHook = jest.fn(); - getIncomingHook = jest.fn(); - actions = { - updateIncomingHook, - getIncomingHook, - }; + updateIncomingHook.mockReset(); + getIncomingHook.mockReset(); }); test('should show Loading screen when no hook is provided', () => { @@ -75,12 +79,9 @@ describe('components/integrations/EditIncomingWebhook', () => { test('should have called submitHook when editIncomingHook is initiated (no server error)', async () => { const newUpdateIncomingHook = jest.fn().mockReturnValue({data: ''}); const newActions = {...actions, updateIncomingHook: newUpdateIncomingHook}; - const asyncHook = { - id: 'id', - token: 'token', - }; + const asyncHook = {...hook}; const props = {...requiredProps, actions: newActions, hook}; - const wrapper = shallow(); + const wrapper: ShallowWrapper = shallow(); const instance = wrapper.instance(); await instance.editIncomingHook(asyncHook); @@ -93,12 +94,9 @@ describe('components/integrations/EditIncomingWebhook', () => { test('should have called submitHook when editIncomingHook is initiated (with server error)', async () => { const newUpdateIncomingHook = jest.fn().mockReturnValue({data: ''}); const newActions = {...actions, updateIncomingHook: newUpdateIncomingHook}; - const asyncHook = { - id: 'id', - token: 'token', - }; + const asyncHook = {...hook}; const props = {...requiredProps, actions: newActions, hook}; - const wrapper = shallow(); + const wrapper: ShallowWrapper = shallow(); const instance = wrapper.instance(); await instance.editIncomingHook(asyncHook); @@ -112,12 +110,9 @@ describe('components/integrations/EditIncomingWebhook', () => { const newUpdateIncomingHook = jest.fn().mockReturnValue({data: 'data'}); const newActions = {...actions, updateIncomingHook: newUpdateIncomingHook}; browserHistory.push = jest.fn(); - const asyncHook = { - id: 'id', - token: 'token', - }; + const asyncHook = {...hook}; const props = {...requiredProps, actions: newActions, hook}; - const wrapper = shallow(); + const wrapper: ShallowWrapper = shallow(); const instance = wrapper.instance(); await instance.editIncomingHook(asyncHook); diff --git a/components/integrations/edit_incoming_webhook/edit_incoming_webhook.jsx b/components/integrations/edit_incoming_webhook/edit_incoming_webhook.tsx similarity index 55% rename from components/integrations/edit_incoming_webhook/edit_incoming_webhook.jsx rename to components/integrations/edit_incoming_webhook/edit_incoming_webhook.tsx index 0daf49b7506c..1c1db73759ae 100644 --- a/components/integrations/edit_incoming_webhook/edit_incoming_webhook.jsx +++ b/components/integrations/edit_incoming_webhook/edit_incoming_webhook.tsx @@ -1,66 +1,76 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import PropTypes from 'prop-types'; import React from 'react'; +import {Team} from 'mattermost-redux/types/teams'; +import {IncomingWebhook} from 'mattermost-redux/types/integrations'; +import {ActionResult} from 'mattermost-redux/types/actions'; + import {browserHistory} from 'utils/browser_history'; import {t} from 'utils/i18n'; -import AbstractIncomingWebhook from 'components/integrations/abstract_incoming_webhook.jsx'; +import AbstractIncomingWebhook from 'components/integrations/abstract_incoming_webhook'; import LoadingScreen from 'components/loading_screen'; const HEADER = {id: t('integrations.edit'), defaultMessage: 'Edit'}; const FOOTER = {id: t('update_incoming_webhook.update'), defaultMessage: 'Update'}; const LOADING = {id: t('update_incoming_webhook.updating'), defaultMessage: 'Updating...'}; -export default class EditIncomingWebhook extends React.PureComponent { - static propTypes = { +type Props = { - /** - * The current team - */ - team: PropTypes.object.isRequired, + /** + * The current team + */ + team: Team; - /** - * The incoming webhook to edit - */ - hook: PropTypes.object, + /** + * The incoming webhook to edit + */ + hook?: IncomingWebhook; - /** - * The id of the incoming webhook to edit - */ - hookId: PropTypes.string.isRequired, + /** + * The id of the incoming webhook to edit + */ + hookId: string; - /** - * Whether or not incoming webhooks are enabled. - */ - enableIncomingWebhooks: PropTypes.bool.isRequired, + /** + * Whether or not incoming webhooks are enabled. + */ + enableIncomingWebhooks: boolean; + + /** + * Whether to allow configuration of the default post username. + */ + enablePostUsernameOverride: boolean; + + /** + * Whether to allow configuration of the default post icon. + */ + enablePostIconOverride: boolean; + + actions: { /** - * Whether to allow configuration of the default post username. - */ - enablePostUsernameOverride: PropTypes.bool.isRequired, + * The function to call to update an incoming webhook + */ + updateIncomingHook: (hook: IncomingWebhook) => Promise; /** - * Whether to allow configuration of the default post icon. - */ - enablePostIconOverride: PropTypes.bool.isRequired, - - actions: PropTypes.shape({ - - /** - * The function to call to update an incoming webhook - */ - updateIncomingHook: PropTypes.func.isRequired, - - /** - * The function to call to get an incoming webhook - */ - getIncomingHook: PropTypes.func.isRequired, - }).isRequired, - } + * The function to call to get an incoming webhook + */ + getIncomingHook: (hookId: string) => Promise; + }; +}; - constructor(props) { +type State = { + showConfirmModal: boolean; + serverError: string; +}; + +export default class EditIncomingWebhook extends React.PureComponent { + private newHook?: IncomingWebhook; + + constructor(props: Props) { super(props); this.state = { @@ -75,34 +85,35 @@ export default class EditIncomingWebhook extends React.PureComponent { } } - editIncomingHook = async (hook) => { + editIncomingHook = async (hook: IncomingWebhook) => { this.newHook = hook; - if (this.props.hook.id) { + if (this.props.hook?.id) { hook.id = this.props.hook.id; } - if (this.props.hook.token) { - hook.token = this.props.hook.token; - } - await this.submitHook(); - } + }; submitHook = async () => { this.setState({serverError: ''}); - const {data, error} = await this.props.actions.updateIncomingHook(this.newHook); + if (!this.newHook) { + return; + } - if (data) { + const result = await this.props.actions.updateIncomingHook(this.newHook); + + if ('data' in result) { browserHistory.push(`/${this.props.team.name}/integrations/incoming_webhooks`); return; } - if (error) { + if ('error' in result) { + const {error} = result; this.setState({serverError: error.message}); } - } + }; render() { if (!this.props.hook) { diff --git a/components/integrations/edit_incoming_webhook/index.js b/components/integrations/edit_incoming_webhook/index.ts similarity index 57% rename from components/integrations/edit_incoming_webhook/index.js rename to components/integrations/edit_incoming_webhook/index.ts index 6ee338375876..72e1e924d496 100644 --- a/components/integrations/edit_incoming_webhook/index.js +++ b/components/integrations/edit_incoming_webhook/index.ts @@ -2,18 +2,31 @@ // See LICENSE.txt for license information. import {connect} from 'react-redux'; -import {bindActionCreators} from 'redux'; +import {ActionCreatorsMapObject, bindActionCreators, Dispatch} from 'redux'; import {getIncomingHook, updateIncomingHook} from 'mattermost-redux/actions/integrations'; import {getConfig} from 'mattermost-redux/selectors/entities/general'; -import EditIncomingWebhook from './edit_incoming_webhook.jsx'; +import {GlobalState} from 'mattermost-redux/types/store'; +import {ActionFunc, ActionResult, GenericAction} from 'mattermost-redux/types/actions'; +import {IncomingWebhook} from 'mattermost-redux/types/integrations'; -function mapStateToProps(state, ownProps) { +import EditIncomingWebhook from './edit_incoming_webhook'; + +type Props = { + location: Location; +} + +type Actions = { + updateIncomingHook: (hook: IncomingWebhook) => Promise; + getIncomingHook: (hookId: string) => Promise; +} + +function mapStateToProps(state: GlobalState, ownProps: Props) { const config = getConfig(state); const enableIncomingWebhooks = config.EnableIncomingWebhooks === 'true'; const enablePostUsernameOverride = config.EnablePostUsernameOverride === 'true'; const enablePostIconOverride = config.EnablePostIconOverride === 'true'; - const hookId = (new URLSearchParams(ownProps.location.search)).get('id'); + const hookId = (new URLSearchParams(ownProps.location.search)).get('id') || ''; return { hookId, @@ -24,9 +37,9 @@ function mapStateToProps(state, ownProps) { }; } -function mapDispatchToProps(dispatch) { +function mapDispatchToProps(dispatch: Dispatch) { return { - actions: bindActionCreators({ + actions: bindActionCreators, Actions>({ updateIncomingHook, getIncomingHook, }, dispatch),