From e27ed497b1cc5d2c344f82955c12a739a1bede6a Mon Sep 17 00:00:00 2001 From: Sudheer Date: Wed, 18 Jul 2018 14:16:02 +0530 Subject: [PATCH] MM-10924 Add forgot password screen (#1878) * MM-10924 Add forgot password screen * Add a new screen for the user to reset password. but can only verify the link through web UI * Update mattermost-redux version to include isEmail helper --- .../forgot_password.test.js.snap | 3367 +++++++++++++++++ .../forgot_password/forgot_password.js | 198 + .../forgot_password/forgot_password.test.js | 78 + app/screens/forgot_password/index.js | 18 + app/screens/index.js | 1 + app/screens/login/login.js | 34 + package-lock.json | 4 +- package.json | 2 +- 8 files changed, 3699 insertions(+), 3 deletions(-) create mode 100644 app/screens/forgot_password/__snapshots__/forgot_password.test.js.snap create mode 100644 app/screens/forgot_password/forgot_password.js create mode 100644 app/screens/forgot_password/forgot_password.test.js create mode 100644 app/screens/forgot_password/index.js diff --git a/app/screens/forgot_password/__snapshots__/forgot_password.test.js.snap b/app/screens/forgot_password/__snapshots__/forgot_password.test.js.snap new file mode 100644 index 000000000..9e4f5e487 --- /dev/null +++ b/app/screens/forgot_password/__snapshots__/forgot_password.test.js.snap @@ -0,0 +1,3367 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`ForgotPassword match snapshot after success of sendPasswordResetEmail 1`] = ` +ShallowWrapper { + "length": 1, + Symbol(enzyme.__root__): [Circular], + Symbol(enzyme.__unrendered__): , + Symbol(enzyme.__renderer__): Object { + "batchedUpdates": [Function], + "getNode": [Function], + "render": [Function], + "simulateEvent": [Function], + "unmount": [Function], + }, + Symbol(enzyme.__node__): Object { + "instance": null, + "key": undefined, + "nodeType": "class", + "props": Object { + "children": Array [ + , + + + + + + + + + + , + ], + "style": Object { + "backgroundColor": "#FFFFFF", + "flex": 1, + }, + }, + "ref": null, + "rendered": Array [ + Object { + "instance": null, + "key": undefined, + "nodeType": "class", + "props": Object {}, + "ref": null, + "rendered": null, + "type": [Function], + }, + Object { + "instance": null, + "key": undefined, + "nodeType": "class", + "props": Object { + "children": + + + + + + + , + "onPress": [Function], + }, + "ref": null, + "rendered": Object { + "instance": null, + "key": undefined, + "nodeType": "class", + "props": Object { + "children": Array [ + , + undefined, + + + + + , + ], + "style": Object { + "alignItems": "center", + "flexDirection": "column", + "justifyContent": "center", + "paddingHorizontal": 15, + "paddingVertical": 50, + }, + }, + "ref": null, + "rendered": Array [ + Object { + "instance": null, + "key": undefined, + "nodeType": "class", + "props": Object { + "source": Object { + "testUri": "../../../dist/assets/images/logo.png", + }, + }, + "ref": null, + "rendered": null, + "type": [Function], + }, + undefined, + Object { + "instance": null, + "key": undefined, + "nodeType": "class", + "props": Object { + "children": Array [ + , + , + , + ], + }, + "ref": null, + "rendered": Array [ + Object { + "instance": null, + "key": undefined, + "nodeType": "function", + "props": Object { + "defaultMessage": "To reset your password, enter the email address you used to sign up", + "id": "password_send.description", + "style": Array [ + Object { + "color": "#777", + "fontSize": 16, + "fontWeight": "300", + "lineHeight": 22, + "marginBottom": 15, + "textAlign": "center", + }, + Object { + "paddingTop": 15, + }, + ], + }, + "ref": null, + "rendered": null, + "type": undefined, + }, + Object { + "instance": null, + "key": undefined, + "nodeType": "class", + "props": Object { + "allowFontScaling": true, + "autoCapitalize": "none", + "autoCorrect": false, + "blurOnSubmit": false, + "disableFullscreenUI": true, + "keyboardType": "email-address", + "onChangeText": [Function], + "placeholder": undefined, + "style": Object { + "alignSelf": "stretch", + "borderColor": "gainsboro", + "borderRadius": 3, + "borderWidth": 1, + "fontSize": 16, + "height": 45, + "marginBottom": 5, + "marginTop": 5, + "paddingLeft": 10, + }, + "underlineColorAndroid": "transparent", + }, + "ref": [Function], + "rendered": null, + "type": [Function], + }, + Object { + "instance": null, + "key": undefined, + "nodeType": "class", + "props": Object { + "children": , + "containerStyle": Object { + "alignItems": "center", + "alignSelf": "stretch", + "borderColor": "#2389D7", + "borderRadius": 3, + "borderWidth": 1, + "marginTop": 10, + "padding": 15, + }, + "disabled": false, + "onPress": [Function], + }, + "ref": null, + "rendered": Object { + "instance": null, + "key": undefined, + "nodeType": "function", + "props": Object { + "defaultMessage": "Reset my password", + "id": "password_send.reset", + "style": Array [ + Object { + "color": "#2389D7", + "fontSize": 17, + "textAlign": "center", + }, + ], + }, + "ref": null, + "rendered": null, + "type": undefined, + }, + "type": [Function], + }, + ], + "type": [Function], + }, + ], + "type": [Function], + }, + "type": [Function], + }, + ], + "type": [Function], + }, + Symbol(enzyme.__nodes__): Array [ + Object { + "instance": null, + "key": undefined, + "nodeType": "class", + "props": Object { + "children": Array [ + , + + + + + + + + + + , + ], + "style": Object { + "backgroundColor": "#FFFFFF", + "flex": 1, + }, + }, + "ref": null, + "rendered": Array [ + Object { + "instance": null, + "key": undefined, + "nodeType": "class", + "props": Object {}, + "ref": null, + "rendered": null, + "type": [Function], + }, + Object { + "instance": null, + "key": undefined, + "nodeType": "class", + "props": Object { + "children": + + + + + + + , + "onPress": [Function], + }, + "ref": null, + "rendered": Object { + "instance": null, + "key": undefined, + "nodeType": "class", + "props": Object { + "children": Array [ + , + undefined, + + + + + , + ], + "style": Object { + "alignItems": "center", + "flexDirection": "column", + "justifyContent": "center", + "paddingHorizontal": 15, + "paddingVertical": 50, + }, + }, + "ref": null, + "rendered": Array [ + Object { + "instance": null, + "key": undefined, + "nodeType": "class", + "props": Object { + "source": Object { + "testUri": "../../../dist/assets/images/logo.png", + }, + }, + "ref": null, + "rendered": null, + "type": [Function], + }, + undefined, + Object { + "instance": null, + "key": undefined, + "nodeType": "class", + "props": Object { + "children": Array [ + , + , + , + ], + }, + "ref": null, + "rendered": Array [ + Object { + "instance": null, + "key": undefined, + "nodeType": "function", + "props": Object { + "defaultMessage": "To reset your password, enter the email address you used to sign up", + "id": "password_send.description", + "style": Array [ + Object { + "color": "#777", + "fontSize": 16, + "fontWeight": "300", + "lineHeight": 22, + "marginBottom": 15, + "textAlign": "center", + }, + Object { + "paddingTop": 15, + }, + ], + }, + "ref": null, + "rendered": null, + "type": undefined, + }, + Object { + "instance": null, + "key": undefined, + "nodeType": "class", + "props": Object { + "allowFontScaling": true, + "autoCapitalize": "none", + "autoCorrect": false, + "blurOnSubmit": false, + "disableFullscreenUI": true, + "keyboardType": "email-address", + "onChangeText": [Function], + "placeholder": undefined, + "style": Object { + "alignSelf": "stretch", + "borderColor": "gainsboro", + "borderRadius": 3, + "borderWidth": 1, + "fontSize": 16, + "height": 45, + "marginBottom": 5, + "marginTop": 5, + "paddingLeft": 10, + }, + "underlineColorAndroid": "transparent", + }, + "ref": [Function], + "rendered": null, + "type": [Function], + }, + Object { + "instance": null, + "key": undefined, + "nodeType": "class", + "props": Object { + "children": , + "containerStyle": Object { + "alignItems": "center", + "alignSelf": "stretch", + "borderColor": "#2389D7", + "borderRadius": 3, + "borderWidth": 1, + "marginTop": 10, + "padding": 15, + }, + "disabled": false, + "onPress": [Function], + }, + "ref": null, + "rendered": Object { + "instance": null, + "key": undefined, + "nodeType": "function", + "props": Object { + "defaultMessage": "Reset my password", + "id": "password_send.reset", + "style": Array [ + Object { + "color": "#2389D7", + "fontSize": 17, + "textAlign": "center", + }, + ], + }, + "ref": null, + "rendered": null, + "type": undefined, + }, + "type": [Function], + }, + ], + "type": [Function], + }, + ], + "type": [Function], + }, + "type": [Function], + }, + ], + "type": [Function], + }, + ], + Symbol(enzyme.__options__): Object { + "adapter": ReactSixteenAdapter { + "options": Object { + "enableComponentDidUpdateOnSetState": true, + }, + }, + "context": Object { + "intl": Object { + "formatMessage": [MockFunction] { + "calls": Array [ + Array [ + Object { + "defaultMessage": "Email", + "id": "login.email", + }, + ], + Array [ + Object { + "defaultMessage": "Email", + "id": "login.email", + }, + ], + ], + }, + }, + }, + }, +} +`; + +exports[`ForgotPassword should match snapshot 1`] = ` +ShallowWrapper { + "length": 1, + Symbol(enzyme.__root__): [Circular], + Symbol(enzyme.__unrendered__): , + Symbol(enzyme.__renderer__): Object { + "batchedUpdates": [Function], + "getNode": [Function], + "render": [Function], + "simulateEvent": [Function], + "unmount": [Function], + }, + Symbol(enzyme.__node__): Object { + "instance": null, + "key": undefined, + "nodeType": "class", + "props": Object { + "children": Array [ + , + + + + + + + + + + , + ], + "style": Object { + "backgroundColor": "#FFFFFF", + "flex": 1, + }, + }, + "ref": null, + "rendered": Array [ + Object { + "instance": null, + "key": undefined, + "nodeType": "class", + "props": Object {}, + "ref": null, + "rendered": null, + "type": [Function], + }, + Object { + "instance": null, + "key": undefined, + "nodeType": "class", + "props": Object { + "children": + + + + + + + , + "onPress": [Function], + }, + "ref": null, + "rendered": Object { + "instance": null, + "key": undefined, + "nodeType": "class", + "props": Object { + "children": Array [ + , + undefined, + + + + + , + ], + "style": Object { + "alignItems": "center", + "flexDirection": "column", + "justifyContent": "center", + "paddingHorizontal": 15, + "paddingVertical": 50, + }, + }, + "ref": null, + "rendered": Array [ + Object { + "instance": null, + "key": undefined, + "nodeType": "class", + "props": Object { + "source": Object { + "testUri": "../../../dist/assets/images/logo.png", + }, + }, + "ref": null, + "rendered": null, + "type": [Function], + }, + undefined, + Object { + "instance": null, + "key": undefined, + "nodeType": "class", + "props": Object { + "children": Array [ + , + , + , + ], + }, + "ref": null, + "rendered": Array [ + Object { + "instance": null, + "key": undefined, + "nodeType": "function", + "props": Object { + "defaultMessage": "To reset your password, enter the email address you used to sign up", + "id": "password_send.description", + "style": Array [ + Object { + "color": "#777", + "fontSize": 16, + "fontWeight": "300", + "lineHeight": 22, + "marginBottom": 15, + "textAlign": "center", + }, + Object { + "paddingTop": 15, + }, + ], + }, + "ref": null, + "rendered": null, + "type": undefined, + }, + Object { + "instance": null, + "key": undefined, + "nodeType": "class", + "props": Object { + "allowFontScaling": true, + "autoCapitalize": "none", + "autoCorrect": false, + "blurOnSubmit": false, + "disableFullscreenUI": true, + "keyboardType": "email-address", + "onChangeText": [Function], + "placeholder": undefined, + "style": Object { + "alignSelf": "stretch", + "borderColor": "gainsboro", + "borderRadius": 3, + "borderWidth": 1, + "fontSize": 16, + "height": 45, + "marginBottom": 5, + "marginTop": 5, + "paddingLeft": 10, + }, + "underlineColorAndroid": "transparent", + }, + "ref": [Function], + "rendered": null, + "type": [Function], + }, + Object { + "instance": null, + "key": undefined, + "nodeType": "class", + "props": Object { + "children": , + "containerStyle": Object { + "alignItems": "center", + "alignSelf": "stretch", + "borderColor": "#2389D7", + "borderRadius": 3, + "borderWidth": 1, + "marginTop": 10, + "padding": 15, + }, + "disabled": true, + "onPress": [Function], + }, + "ref": null, + "rendered": Object { + "instance": null, + "key": undefined, + "nodeType": "function", + "props": Object { + "defaultMessage": "Reset my password", + "id": "password_send.reset", + "style": Array [ + Object { + "color": "#2389D7", + "fontSize": 17, + "textAlign": "center", + }, + ], + }, + "ref": null, + "rendered": null, + "type": undefined, + }, + "type": [Function], + }, + ], + "type": [Function], + }, + ], + "type": [Function], + }, + "type": [Function], + }, + ], + "type": [Function], + }, + Symbol(enzyme.__nodes__): Array [ + Object { + "instance": null, + "key": undefined, + "nodeType": "class", + "props": Object { + "children": Array [ + , + + + + + + + + + + , + ], + "style": Object { + "backgroundColor": "#FFFFFF", + "flex": 1, + }, + }, + "ref": null, + "rendered": Array [ + Object { + "instance": null, + "key": undefined, + "nodeType": "class", + "props": Object {}, + "ref": null, + "rendered": null, + "type": [Function], + }, + Object { + "instance": null, + "key": undefined, + "nodeType": "class", + "props": Object { + "children": + + + + + + + , + "onPress": [Function], + }, + "ref": null, + "rendered": Object { + "instance": null, + "key": undefined, + "nodeType": "class", + "props": Object { + "children": Array [ + , + undefined, + + + + + , + ], + "style": Object { + "alignItems": "center", + "flexDirection": "column", + "justifyContent": "center", + "paddingHorizontal": 15, + "paddingVertical": 50, + }, + }, + "ref": null, + "rendered": Array [ + Object { + "instance": null, + "key": undefined, + "nodeType": "class", + "props": Object { + "source": Object { + "testUri": "../../../dist/assets/images/logo.png", + }, + }, + "ref": null, + "rendered": null, + "type": [Function], + }, + undefined, + Object { + "instance": null, + "key": undefined, + "nodeType": "class", + "props": Object { + "children": Array [ + , + , + , + ], + }, + "ref": null, + "rendered": Array [ + Object { + "instance": null, + "key": undefined, + "nodeType": "function", + "props": Object { + "defaultMessage": "To reset your password, enter the email address you used to sign up", + "id": "password_send.description", + "style": Array [ + Object { + "color": "#777", + "fontSize": 16, + "fontWeight": "300", + "lineHeight": 22, + "marginBottom": 15, + "textAlign": "center", + }, + Object { + "paddingTop": 15, + }, + ], + }, + "ref": null, + "rendered": null, + "type": undefined, + }, + Object { + "instance": null, + "key": undefined, + "nodeType": "class", + "props": Object { + "allowFontScaling": true, + "autoCapitalize": "none", + "autoCorrect": false, + "blurOnSubmit": false, + "disableFullscreenUI": true, + "keyboardType": "email-address", + "onChangeText": [Function], + "placeholder": undefined, + "style": Object { + "alignSelf": "stretch", + "borderColor": "gainsboro", + "borderRadius": 3, + "borderWidth": 1, + "fontSize": 16, + "height": 45, + "marginBottom": 5, + "marginTop": 5, + "paddingLeft": 10, + }, + "underlineColorAndroid": "transparent", + }, + "ref": [Function], + "rendered": null, + "type": [Function], + }, + Object { + "instance": null, + "key": undefined, + "nodeType": "class", + "props": Object { + "children": , + "containerStyle": Object { + "alignItems": "center", + "alignSelf": "stretch", + "borderColor": "#2389D7", + "borderRadius": 3, + "borderWidth": 1, + "marginTop": 10, + "padding": 15, + }, + "disabled": true, + "onPress": [Function], + }, + "ref": null, + "rendered": Object { + "instance": null, + "key": undefined, + "nodeType": "function", + "props": Object { + "defaultMessage": "Reset my password", + "id": "password_send.reset", + "style": Array [ + Object { + "color": "#2389D7", + "fontSize": 17, + "textAlign": "center", + }, + ], + }, + "ref": null, + "rendered": null, + "type": undefined, + }, + "type": [Function], + }, + ], + "type": [Function], + }, + ], + "type": [Function], + }, + "type": [Function], + }, + ], + "type": [Function], + }, + ], + Symbol(enzyme.__options__): Object { + "adapter": ReactSixteenAdapter { + "options": Object { + "enableComponentDidUpdateOnSetState": true, + }, + }, + "context": Object { + "intl": Object { + "formatMessage": [MockFunction] { + "calls": Array [ + Array [ + Object { + "defaultMessage": "Email", + "id": "login.email", + }, + ], + ], + }, + }, + }, + }, +} +`; + +exports[`ForgotPassword snapshot for error on failure of email regex 1`] = ` +ShallowWrapper { + "length": 1, + Symbol(enzyme.__root__): [Circular], + Symbol(enzyme.__unrendered__): , + Symbol(enzyme.__renderer__): Object { + "batchedUpdates": [Function], + "getNode": [Function], + "render": [Function], + "simulateEvent": [Function], + "unmount": [Function], + }, + Symbol(enzyme.__node__): Object { + "instance": null, + "key": undefined, + "nodeType": "class", + "props": Object { + "children": Array [ + , + + + + + + + + + + , + ], + "style": Object { + "backgroundColor": "#FFFFFF", + "flex": 1, + }, + }, + "ref": null, + "rendered": Array [ + Object { + "instance": null, + "key": undefined, + "nodeType": "class", + "props": Object {}, + "ref": null, + "rendered": null, + "type": [Function], + }, + Object { + "instance": null, + "key": undefined, + "nodeType": "class", + "props": Object { + "children": + + + + + + + , + "onPress": [Function], + }, + "ref": null, + "rendered": Object { + "instance": null, + "key": undefined, + "nodeType": "class", + "props": Object { + "children": Array [ + , + undefined, + + + + + , + ], + "style": Object { + "alignItems": "center", + "flexDirection": "column", + "justifyContent": "center", + "paddingHorizontal": 15, + "paddingVertical": 50, + }, + }, + "ref": null, + "rendered": Array [ + Object { + "instance": null, + "key": undefined, + "nodeType": "class", + "props": Object { + "source": Object { + "testUri": "../../../dist/assets/images/logo.png", + }, + }, + "ref": null, + "rendered": null, + "type": [Function], + }, + undefined, + Object { + "instance": null, + "key": undefined, + "nodeType": "class", + "props": Object { + "children": Array [ + , + , + , + ], + }, + "ref": null, + "rendered": Array [ + Object { + "instance": null, + "key": undefined, + "nodeType": "function", + "props": Object { + "defaultMessage": "To reset your password, enter the email address you used to sign up", + "id": "password_send.description", + "style": Array [ + Object { + "color": "#777", + "fontSize": 16, + "fontWeight": "300", + "lineHeight": 22, + "marginBottom": 15, + "textAlign": "center", + }, + Object { + "paddingTop": 15, + }, + ], + }, + "ref": null, + "rendered": null, + "type": undefined, + }, + Object { + "instance": null, + "key": undefined, + "nodeType": "class", + "props": Object { + "allowFontScaling": true, + "autoCapitalize": "none", + "autoCorrect": false, + "blurOnSubmit": false, + "disableFullscreenUI": true, + "keyboardType": "email-address", + "onChangeText": [Function], + "placeholder": undefined, + "style": Object { + "alignSelf": "stretch", + "borderColor": "gainsboro", + "borderRadius": 3, + "borderWidth": 1, + "fontSize": 16, + "height": 45, + "marginBottom": 5, + "marginTop": 5, + "paddingLeft": 10, + }, + "underlineColorAndroid": "transparent", + }, + "ref": [Function], + "rendered": null, + "type": [Function], + }, + Object { + "instance": null, + "key": undefined, + "nodeType": "class", + "props": Object { + "children": , + "containerStyle": Object { + "alignItems": "center", + "alignSelf": "stretch", + "borderColor": "#2389D7", + "borderRadius": 3, + "borderWidth": 1, + "marginTop": 10, + "padding": 15, + }, + "disabled": false, + "onPress": [Function], + }, + "ref": null, + "rendered": Object { + "instance": null, + "key": undefined, + "nodeType": "function", + "props": Object { + "defaultMessage": "Reset my password", + "id": "password_send.reset", + "style": Array [ + Object { + "color": "#2389D7", + "fontSize": 17, + "textAlign": "center", + }, + ], + }, + "ref": null, + "rendered": null, + "type": undefined, + }, + "type": [Function], + }, + ], + "type": [Function], + }, + ], + "type": [Function], + }, + "type": [Function], + }, + ], + "type": [Function], + }, + Symbol(enzyme.__nodes__): Array [ + Object { + "instance": null, + "key": undefined, + "nodeType": "class", + "props": Object { + "children": Array [ + , + + + + + + + + + + , + ], + "style": Object { + "backgroundColor": "#FFFFFF", + "flex": 1, + }, + }, + "ref": null, + "rendered": Array [ + Object { + "instance": null, + "key": undefined, + "nodeType": "class", + "props": Object {}, + "ref": null, + "rendered": null, + "type": [Function], + }, + Object { + "instance": null, + "key": undefined, + "nodeType": "class", + "props": Object { + "children": + + + + + + + , + "onPress": [Function], + }, + "ref": null, + "rendered": Object { + "instance": null, + "key": undefined, + "nodeType": "class", + "props": Object { + "children": Array [ + , + undefined, + + + + + , + ], + "style": Object { + "alignItems": "center", + "flexDirection": "column", + "justifyContent": "center", + "paddingHorizontal": 15, + "paddingVertical": 50, + }, + }, + "ref": null, + "rendered": Array [ + Object { + "instance": null, + "key": undefined, + "nodeType": "class", + "props": Object { + "source": Object { + "testUri": "../../../dist/assets/images/logo.png", + }, + }, + "ref": null, + "rendered": null, + "type": [Function], + }, + undefined, + Object { + "instance": null, + "key": undefined, + "nodeType": "class", + "props": Object { + "children": Array [ + , + , + , + ], + }, + "ref": null, + "rendered": Array [ + Object { + "instance": null, + "key": undefined, + "nodeType": "function", + "props": Object { + "defaultMessage": "To reset your password, enter the email address you used to sign up", + "id": "password_send.description", + "style": Array [ + Object { + "color": "#777", + "fontSize": 16, + "fontWeight": "300", + "lineHeight": 22, + "marginBottom": 15, + "textAlign": "center", + }, + Object { + "paddingTop": 15, + }, + ], + }, + "ref": null, + "rendered": null, + "type": undefined, + }, + Object { + "instance": null, + "key": undefined, + "nodeType": "class", + "props": Object { + "allowFontScaling": true, + "autoCapitalize": "none", + "autoCorrect": false, + "blurOnSubmit": false, + "disableFullscreenUI": true, + "keyboardType": "email-address", + "onChangeText": [Function], + "placeholder": undefined, + "style": Object { + "alignSelf": "stretch", + "borderColor": "gainsboro", + "borderRadius": 3, + "borderWidth": 1, + "fontSize": 16, + "height": 45, + "marginBottom": 5, + "marginTop": 5, + "paddingLeft": 10, + }, + "underlineColorAndroid": "transparent", + }, + "ref": [Function], + "rendered": null, + "type": [Function], + }, + Object { + "instance": null, + "key": undefined, + "nodeType": "class", + "props": Object { + "children": , + "containerStyle": Object { + "alignItems": "center", + "alignSelf": "stretch", + "borderColor": "#2389D7", + "borderRadius": 3, + "borderWidth": 1, + "marginTop": 10, + "padding": 15, + }, + "disabled": false, + "onPress": [Function], + }, + "ref": null, + "rendered": Object { + "instance": null, + "key": undefined, + "nodeType": "function", + "props": Object { + "defaultMessage": "Reset my password", + "id": "password_send.reset", + "style": Array [ + Object { + "color": "#2389D7", + "fontSize": 17, + "textAlign": "center", + }, + ], + }, + "ref": null, + "rendered": null, + "type": undefined, + }, + "type": [Function], + }, + ], + "type": [Function], + }, + ], + "type": [Function], + }, + "type": [Function], + }, + ], + "type": [Function], + }, + ], + Symbol(enzyme.__options__): Object { + "adapter": ReactSixteenAdapter { + "options": Object { + "enableComponentDidUpdateOnSetState": true, + }, + }, + "context": Object { + "intl": Object { + "formatMessage": [MockFunction] { + "calls": Array [ + Array [ + Object { + "defaultMessage": "Email", + "id": "login.email", + }, + ], + Array [ + Object { + "defaultMessage": "Email", + "id": "login.email", + }, + ], + Array [ + Object { + "defaultMessage": "Please enter a valid email address.", + "id": "password_send.error", + }, + ], + Array [ + Object { + "defaultMessage": "Email", + "id": "login.email", + }, + ], + ], + }, + }, + }, + }, +} +`; diff --git a/app/screens/forgot_password/forgot_password.js b/app/screens/forgot_password/forgot_password.js new file mode 100644 index 000000000..4684b2bf6 --- /dev/null +++ b/app/screens/forgot_password/forgot_password.js @@ -0,0 +1,198 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import React, {PureComponent} from 'react'; +import PropTypes from 'prop-types'; +import Button from 'react-native-button'; +import {intlShape} from 'react-intl'; + +import { + Image, + StyleSheet, + Text, + TextInput, + TouchableWithoutFeedback, + View, +} from 'react-native'; + +import {isEmail} from 'mattermost-redux/utils/helpers'; + +import {GlobalStyles} from 'app/styles'; + +import ErrorText from 'app/components/error_text'; +import FormattedText from 'app/components/formatted_text'; +import StatusBar from 'app/components/status_bar'; + +export default class ForgotPassword extends PureComponent { + static propTypes = { + actions: PropTypes.shape({ + sendPasswordResetEmail: PropTypes.func.isRequired, + }), + } + + static contextTypes = { + intl: intlShape.isRequired, + }; + + constructor(props) { + super(props); + + this.state = { + error: null, + email: '', + sentPasswordLink: false, + }; + } + + changeEmail = (email) => { + this.setState({ + email, + }); + } + + submitResetPassword = async () => { + if (!this.state.email || !isEmail(this.state.email)) { + const {formatMessage} = this.context.intl; + this.setState({ + error: formatMessage({id: 'password_send.error', defaultMessage: 'Please enter a valid email address.'}), + }); + return; + } + + const {data, error} = await this.props.actions.sendPasswordResetEmail(this.state.email); + if (error) { + this.setState({error}); + } else if (this.state.error) { + this.setState({error: ''}); + } + if (data) { + this.setState({sentPasswordLink: true}); + } + } + + emailIdRef = (ref) => { + this.emailId = ref; + }; + + blur = () => { + if (this.emailId) { + this.emailId.blur(); + } + } + + render() { + const {formatMessage} = this.context.intl; + let displayError; + if (this.state.error) { + displayError = ( + + ); + } + + let passwordFormView; + if (this.state.sentPasswordLink) { + passwordFormView = ( + + + + {this.state.email} + + + + ); + } else { + passwordFormView = ( + + + + + + ); + } + return ( + + + + + + {displayError} + {passwordFormView} + + + + ); + } +} + +const style = StyleSheet.create({ + container: { + backgroundColor: '#FFFFFF', + flex: 1, + }, + innerContainer: { + alignItems: 'center', + flexDirection: 'column', + justifyContent: 'center', + paddingHorizontal: 15, + paddingVertical: 50, + }, + forgotPasswordBtn: { + borderColor: 'transparent', + marginTop: 15, + }, + resetSuccessContainer: { + marginTop: 15, + padding: 10, + backgroundColor: '#dff0d8', + borderColor: '#d6e9c6', + }, + emailId: { + fontWeight: 'bold', + }, + successTxtColor: { + color: '#3c763d', + }, + defaultTopPadding: { + paddingTop: 15, + }, +}); diff --git a/app/screens/forgot_password/forgot_password.test.js b/app/screens/forgot_password/forgot_password.test.js new file mode 100644 index 000000000..da58dc1c6 --- /dev/null +++ b/app/screens/forgot_password/forgot_password.test.js @@ -0,0 +1,78 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import React from 'react'; +import {configure, shallow} from 'enzyme'; +import Adapter from 'enzyme-adapter-react-16'; +configure({adapter: new Adapter()}); + +import ForgotPassword from './forgot_password.js'; + +jest.mock('react-intl'); + +describe('ForgotPassword', () => { + const actions = { + sendPasswordResetEmail: jest.fn(), + }; + + const baseProps = { + actions, + }; + + const formatMessage = jest.fn(); + + test('should match snapshot', () => { + const wrapper = shallow( + , + {context: {intl: {formatMessage}}}, + ); + + expect(wrapper).toMatchSnapshot(); + }); + + test('snapshot for error on failure of email regex', () => { + const wrapper = shallow( + , + {context: {intl: {formatMessage}}}, + ); + + wrapper.setState({email: 'bar'}); + wrapper.instance().submitResetPassword(); + wrapper.update(); + expect(wrapper).toMatchSnapshot(); + }); + + test('Should call sendPasswordResetEmail', () => { + const wrapper = shallow( + , + {context: {intl: {formatMessage}}}, + ); + + wrapper.setState({email: 'test@test.com'}); + wrapper.instance().submitResetPassword(); + expect(actions.sendPasswordResetEmail).toBeCalledWith('test@test.com'); + }); + + test('match snapshot after success of sendPasswordResetEmail', async () => { + const sendPasswordResetEmail = async () => { + return { + data: {}, + }; + }; + const wrapper = shallow( + , + {context: {intl: {formatMessage}}}, + ); + + wrapper.setState({email: 'test@test.com'}); + wrapper.instance().submitResetPassword(); + await sendPasswordResetEmail(); + expect(wrapper).toMatchSnapshot(); + }); +}); diff --git a/app/screens/forgot_password/index.js b/app/screens/forgot_password/index.js new file mode 100644 index 000000000..612e7c00f --- /dev/null +++ b/app/screens/forgot_password/index.js @@ -0,0 +1,18 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {bindActionCreators} from 'redux'; +import {connect} from 'react-redux'; + +import {sendPasswordResetEmail} from 'mattermost-redux/actions/users'; +import ForgotPassword from './forgot_password'; + +function mapDispatchToProps(dispatch) { + return { + actions: bindActionCreators({ + sendPasswordResetEmail, + }, dispatch), + }; +} + +export default connect(null, mapDispatchToProps)(ForgotPassword); diff --git a/app/screens/index.js b/app/screens/index.js index eef346b2d..958bc1b77 100644 --- a/app/screens/index.js +++ b/app/screens/index.js @@ -28,6 +28,7 @@ export function registerScreens(store, Provider) { Navigation.registerComponent('Entry', () => Entry, store, Provider); Navigation.registerComponent('ExpandedAnnouncementBanner', () => wrapWithContextProvider(require('app/screens/expanded_announcement_banner').default), store, Provider); Navigation.registerComponent('FlaggedPosts', () => wrapWithContextProvider(require('app/screens/flagged_posts').default), store, Provider); + Navigation.registerComponent('ForgotPassword', () => wrapWithContextProvider(require('app/screens/forgot_password').default), store, Provider); Navigation.registerComponent('ImagePreview', () => wrapWithContextProvider(require('app/screens/image_preview').default), store, Provider); Navigation.registerComponent('Login', () => wrapWithContextProvider(require('app/screens/login').default), store, Provider); Navigation.registerComponent('LoginOptions', () => wrapWithContextProvider(require('app/screens/login_options').default), store, Provider); diff --git a/app/screens/login/login.js b/app/screens/login/login.js index dd3621611..f3cd29e8e 100644 --- a/app/screens/login/login.js +++ b/app/screens/login/login.js @@ -300,6 +300,23 @@ export default class Login extends PureComponent { this.scroll = ref; }; + forgotPassword = () => { + const {intl} = this.context; + const {navigator, theme} = this.props; + navigator.push({ + screen: 'ForgotPassword', + title: intl.formatMessage({id: 'password_form.title', defaultMessage: 'Password Reset'}), + animated: true, + backButtonTitle: '', + navigatorStyle: { + navBarTextColor: theme.sidebarHeaderTextColor, + navBarBackgroundColor: theme.sidebarHeaderBg, + navBarButtonColor: theme.sidebarHeaderTextColor, + screenBackgroundColor: theme.centerChannelBg, + }, + }); + } + render() { const isLoading = this.props.loginRequest.status === RequestStatus.STARTED || this.state.isLoading; @@ -394,6 +411,16 @@ export default class Login extends PureComponent { disableFullscreenUI={true} /> {proceed} + @@ -413,4 +440,11 @@ const style = StyleSheet.create({ paddingHorizontal: 15, paddingVertical: 50, }, + forgotPasswordBtn: { + borderColor: 'transparent', + marginTop: 15, + }, + forgotPasswordTxt: { + color: '#2389D7', + }, }); diff --git a/package-lock.json b/package-lock.json index cb253f765..f6d260081 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10100,8 +10100,8 @@ } }, "mattermost-redux": { - "version": "github:mattermost/mattermost-redux#ade5924a6e509ea76757bd78a2cc193043a584ce", - "from": "github:mattermost/mattermost-redux#ade5924a6e509ea76757bd78a2cc193043a584ce", + "version": "github:mattermost/mattermost-redux#1bc11c578b1dc2e5a508a00de65e4e9a549d72b1", + "from": "github:mattermost/mattermost-redux#1bc11c578b1dc2e5a508a00de65e4e9a549d72b1", "requires": { "deep-equal": "1.0.1", "eslint-plugin-header": "1.2.0", diff --git a/package.json b/package.json index 59daed94f..a4793c2a1 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "intl": "1.2.5", "jail-monkey": "1.0.0", "jsc-android": "216113.0.3", - "mattermost-redux": "github:mattermost/mattermost-redux#ade5924a6e509ea76757bd78a2cc193043a584ce", + "mattermost-redux": "github:mattermost/mattermost-redux#1bc11c578b1dc2e5a508a00de65e4e9a549d72b1", "mime-db": "1.33.0", "prop-types": "15.6.1", "react": "16.3.2",