From 13d76a0cb2cd08357b7667020410e4615732aabd Mon Sep 17 00:00:00 2001 From: n1aba Date: Fri, 18 Aug 2017 14:06:32 -0500 Subject: PLT-6451 Migrate installed_oauth_app.jsx to be pure and use Redux (#7190) * Migrate installed_oauth_app.jsx to be pure and use Redux, add test * Fix behavior for the error case --- webapp/actions/admin_actions.jsx | 14 -- .../components/installed_oauth_app.jsx | 79 ++++++----- .../components/installed_oauth_apps/index.js | 4 +- .../installed_oauth_apps/installed_oauth_apps.jsx | 12 ++ .../installed_oauth_app.test.jsx.snap | 155 +++++++++++++++++++++ .../installed_oauth_apps.test.jsx.snap | 2 + .../integrations/installed_oauth_app.test.jsx | 71 ++++++++++ .../integrations/installed_oauth_apps.test.jsx | 6 +- 8 files changed, 294 insertions(+), 49 deletions(-) create mode 100644 webapp/tests/components/integrations/__snapshots__/installed_oauth_app.test.jsx.snap create mode 100644 webapp/tests/components/integrations/installed_oauth_app.test.jsx (limited to 'webapp') diff --git a/webapp/actions/admin_actions.jsx b/webapp/actions/admin_actions.jsx index 85184a23a..4dd0f848f 100644 --- a/webapp/actions/admin_actions.jsx +++ b/webapp/actions/admin_actions.jsx @@ -9,7 +9,6 @@ const getState = store.getState; import * as AdminActions from 'mattermost-redux/actions/admin'; import * as UserActions from 'mattermost-redux/actions/users'; -import * as IntegrationActions from 'mattermost-redux/actions/integrations'; import {Client4} from 'mattermost-redux/client'; export function saveConfig(config, success, error) { @@ -238,19 +237,6 @@ export function oauthToEmail(currentService, email, password, success, error) { ); } -export function regenerateOAuthAppSecret(oauthAppId, success, error) { - IntegrationActions.regenOAuthAppSecret(oauthAppId)(dispatch, getState).then( - (data) => { - if (data && success) { - success(data); - } else if (data == null && error) { - const serverError = getState().requests.admin.updateOAuthApp.error; - error({id: serverError.server_error_id, ...serverError}); - } - } - ); -} - export function uploadBrandImage(brandImage, success, error) { AdminActions.uploadBrandImage(brandImage)(dispatch, getState).then( (data) => { diff --git a/webapp/components/integrations/components/installed_oauth_app.jsx b/webapp/components/integrations/components/installed_oauth_app.jsx index a59bf8e47..bcb7a7c96 100644 --- a/webapp/components/integrations/components/installed_oauth_app.jsx +++ b/webapp/components/integrations/components/installed_oauth_app.jsx @@ -4,70 +4,83 @@ import React from 'react'; import PropTypes from 'prop-types'; -import FormError from 'components/form_error.jsx'; - import * as Utils from 'utils/utils.jsx'; import {FormattedMessage, FormattedHTMLMessage} from 'react-intl'; -import {regenerateOAuthAppSecret} from 'actions/admin_actions.jsx'; - +import FormError from 'components/form_error.jsx'; import DeleteIntegration from './delete_integration.jsx'; const FAKE_SECRET = '***************'; -export default class InstalledOAuthApp extends React.Component { - static get propTypes() { - return { - oauthApp: PropTypes.object.isRequired, - onDelete: PropTypes.func.isRequired, - filter: PropTypes.string - }; +export default class InstalledOAuthApp extends React.PureComponent { + static propTypes = { + + /** + * The oauthApp data + */ + oauthApp: PropTypes.object.isRequired, + + /** + * The request state for regenOAuthAppSecret action. Contains status and error + */ + regenOAuthAppSecretRequest: PropTypes.object.isRequired, + + /** + * The function to call when Regenerate Secret link is clicked + */ + onRegenerateSecret: PropTypes.func.isRequired, + + /** + * The function to call when Delete link is clicked + */ + onDelete: PropTypes.func.isRequired, + + /** + * Set to filter OAuthApp + */ + filter: PropTypes.string } constructor(props) { super(props); - this.handleShowClientSecret = this.handleShowClientSecret.bind(this); - this.handleHideClientScret = this.handleHideClientScret.bind(this); - this.handleRegenerate = this.handleRegenerate.bind(this); - this.handleDelete = this.handleDelete.bind(this); - - this.matchesFilter = this.matchesFilter.bind(this); - this.state = { clientSecret: FAKE_SECRET }; } - handleShowClientSecret(e) { - e.preventDefault(); + handleShowClientSecret = (e) => { + if (e && e.preventDefault) { + e.preventDefault(); + } this.setState({clientSecret: this.props.oauthApp.client_secret}); } - handleHideClientScret(e) { + handleHideClientSecret = (e) => { e.preventDefault(); this.setState({clientSecret: FAKE_SECRET}); } - handleRegenerate(e) { + handleRegenerate = (e) => { e.preventDefault(); - - regenerateOAuthAppSecret( - this.props.oauthApp.id, + this.props.onRegenerateSecret(this.props.oauthApp.id).then( () => { - this.handleShowClientSecret(e); - }, - (err) => { - this.setState({error: err.message}); + const {error} = this.props.regenOAuthAppSecretRequest; + if (error) { + this.setState({error: error.message}); + } else { + this.setState({error: null}); + this.handleShowClientSecret(); + } } ); } - handleDelete() { + handleDelete = () => { this.props.onDelete(this.props.oauthApp); } - matchesFilter(oauthApp, filter) { + matchesFilter = (oauthApp, filter) => { if (!filter) { return true; } @@ -152,7 +165,7 @@ export default class InstalledOAuthApp extends React.Component { showHide = ( ); } -} +} \ No newline at end of file diff --git a/webapp/components/integrations/components/installed_oauth_apps/index.js b/webapp/components/integrations/components/installed_oauth_apps/index.js index 85d2a5ba7..bfeed6d66 100644 --- a/webapp/components/integrations/components/installed_oauth_apps/index.js +++ b/webapp/components/integrations/components/installed_oauth_apps/index.js @@ -13,7 +13,8 @@ function mapStateToProps(state, ownProps) { return { ...ownProps, oauthApps: getOAuthApps(state), - isSystemAdmin: isCurrentUserSystemAdmin(state) + isSystemAdmin: isCurrentUserSystemAdmin(state), + regenOAuthAppSecretRequest: state.requests.integrations.updateOAuthApp }; } @@ -21,6 +22,7 @@ function mapDispatchToProps(dispatch) { return { actions: bindActionCreators({ getOAuthApps: Actions.getOAuthApps, + regenOAuthAppSecret: Actions.regenOAuthAppSecret, deleteOAuthApp: Actions.deleteOAuthApp }, dispatch) }; diff --git a/webapp/components/integrations/components/installed_oauth_apps/installed_oauth_apps.jsx b/webapp/components/integrations/components/installed_oauth_apps/installed_oauth_apps.jsx index 45dd56310..4cd27ab57 100644 --- a/webapp/components/integrations/components/installed_oauth_apps/installed_oauth_apps.jsx +++ b/webapp/components/integrations/components/installed_oauth_apps/installed_oauth_apps.jsx @@ -27,6 +27,11 @@ export default class InstalledOAuthApps extends React.PureComponent { */ isSystemAdmin: PropTypes.bool, + /** + * The request state for regenOAuthAppSecret action. Contains status and error + */ + regenOAuthAppSecretRequest: PropTypes.object.isRequired, + actions: PropTypes.shape({ /** @@ -34,6 +39,11 @@ export default class InstalledOAuthApps extends React.PureComponent { */ getOAuthApps: PropTypes.func.isRequired, + /** + * The function to call when Regenerate Secret link is clicked + */ + regenOAuthAppSecret: PropTypes.func.isRequired, + /** * The function to call when Delete link is clicked */ @@ -80,6 +90,8 @@ export default class InstalledOAuthApps extends React.PureComponent { ); diff --git a/webapp/tests/components/integrations/__snapshots__/installed_oauth_app.test.jsx.snap b/webapp/tests/components/integrations/__snapshots__/installed_oauth_app.test.jsx.snap new file mode 100644 index 000000000..f4e8fb464 --- /dev/null +++ b/webapp/tests/components/integrations/__snapshots__/installed_oauth_app.test.jsx.snap @@ -0,0 +1,155 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`components/integrations/InstalledOAuthApp should filter out OAuthApp 1`] = `null`; + +exports[`components/integrations/InstalledOAuthApp should match snapshot 1`] = ` +
+
+ +
+
+
+ + testApp + +
+
+ + testing + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+
+ + + + - + + + + - + +
+
+`; diff --git a/webapp/tests/components/integrations/__snapshots__/installed_oauth_apps.test.jsx.snap b/webapp/tests/components/integrations/__snapshots__/installed_oauth_apps.test.jsx.snap index 022bc1476..c6ef12112 100644 --- a/webapp/tests/components/integrations/__snapshots__/installed_oauth_apps.test.jsx.snap +++ b/webapp/tests/components/integrations/__snapshots__/installed_oauth_apps.test.jsx.snap @@ -72,6 +72,7 @@ exports[`components/integrations/InstalledOAuthApps should match snapshot 1`] = } } onDelete={[Function]} + onRegenerateSecret={[Function]} /> `; diff --git a/webapp/tests/components/integrations/installed_oauth_app.test.jsx b/webapp/tests/components/integrations/installed_oauth_app.test.jsx new file mode 100644 index 000000000..ff27a5768 --- /dev/null +++ b/webapp/tests/components/integrations/installed_oauth_app.test.jsx @@ -0,0 +1,71 @@ +// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +import React from 'react'; +import {shallow} from 'enzyme'; + +import InstalledOAuthApp from 'components/integrations/components/installed_oauth_app.jsx'; + +describe('components/integrations/InstalledOAuthApp', () => { + const emptyFunction = jest.fn(); + const app = { + id: 'facxd9wpzpbpfp8pad78xj75pr', + name: 'testApp', + client_secret: '88cxd9wpzpbpfp8pad78xj75pr', + create_at: 1501365458934, + creator_id: '88oybd1dwfdoxpkpw1h5kpbyco', + description: 'testing', + homepage: 'https://test.com', + icon_url: 'https://test.com/icon', + is_trusted: true, + update_at: 1501365458934, + callback_urls: ['https://test.com/callback', 'https://test.com/callback2'] + }; + + test('should match snapshot', () => { + const wrapper = shallow( + + ); + expect(wrapper).toMatchSnapshot(); + }); + + test('should call onRegenerateSecret function', () => { + const onRegenerateSecret = jest.genMockFunction().mockImplementation( + () => { + return new Promise((resolve) => { + process.nextTick(() => resolve()); + }); + } + ); + + const wrapper = shallow( + + ); + wrapper.find('div.item-actions a').at(1).simulate('click', {preventDefault() { + return jest.fn(); + }}); + expect(onRegenerateSecret).toBeCalled(); + }); + + test('should filter out OAuthApp', () => { + const wrapper = shallow( + + ); + expect(wrapper).toMatchSnapshot(); + }); +}); \ No newline at end of file diff --git a/webapp/tests/components/integrations/installed_oauth_apps.test.jsx b/webapp/tests/components/integrations/installed_oauth_apps.test.jsx index 4f3ca6ba8..2e5ccad7a 100644 --- a/webapp/tests/components/integrations/installed_oauth_apps.test.jsx +++ b/webapp/tests/components/integrations/installed_oauth_apps.test.jsx @@ -44,7 +44,11 @@ describe('components/integrations/InstalledOAuthApps', () => { team={{name: 'test'}} oauthApps={oauthApps} isSystemAdmin={true} - actions={{getOAuthApps: emptyFunction, deleteOAuthApp: emptyFunction}} + actions={{ + getOAuthApps: emptyFunction, + regenOAuthAppSecret: emptyFunction, + deleteOAuthApp: emptyFunction + }} /> ); expect(wrapper.find('InstalledOAuthApp').length).toBe(2); -- cgit v1.2.3-1-g7c22