diff --git a/app/components/markdown/markdown_code_block/markdown_code_block.js b/app/components/markdown/markdown_code_block/markdown_code_block.js
index cea4808cb..f2341cf30 100644
--- a/app/components/markdown/markdown_code_block/markdown_code_block.js
+++ b/app/components/markdown/markdown_code_block/markdown_code_block.js
@@ -3,7 +3,7 @@
import {PropTypes} from 'prop-types';
import React from 'react';
-import {injectIntl, intlShape} from 'react-intl';
+import {intlShape} from 'react-intl';
import {
Clipboard,
StyleSheet,
@@ -21,9 +21,8 @@ import mattermostManaged from 'app/mattermost_managed';
const MAX_LINES = 4;
-class MarkdownCodeBlock extends React.PureComponent {
+export default class MarkdownCodeBlock extends React.PureComponent {
static propTypes = {
- intl: intlShape.isRequired,
navigator: PropTypes.object.isRequired,
theme: PropTypes.object.isRequired,
language: PropTypes.string,
@@ -36,8 +35,13 @@ class MarkdownCodeBlock extends React.PureComponent {
language: '',
};
+ static contextTypes = {
+ intl: intlShape,
+ };
+
handlePress = preventDoubleTap(() => {
- const {intl, navigator, theme} = this.props;
+ const {navigator, theme} = this.props;
+ const {intl} = this.context;
const languageDisplayName = getDisplayNameForLanguage(this.props.language);
let title;
@@ -76,7 +80,7 @@ class MarkdownCodeBlock extends React.PureComponent {
});
handleLongPress = async () => {
- const {formatMessage} = this.props.intl;
+ const {formatMessage} = this.context.intl;
const config = await mattermostManaged.getLocalConfig();
@@ -238,5 +242,3 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
},
};
});
-
-export default injectIntl(MarkdownCodeBlock);
diff --git a/app/screens/thread/__snapshots__/thread.test.js.snap b/app/screens/thread/__snapshots__/thread.test.js.snap
new file mode 100644
index 000000000..e78b7839e
--- /dev/null
+++ b/app/screens/thread/__snapshots__/thread.test.js.snap
@@ -0,0 +1,147 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`thread should match snapshot, has root post 1`] = `
+
+
+
+
+
+
+
+`;
+
+exports[`thread should match snapshot, no root post, loading 1`] = `
+
+
+
+
+
+
+`;
+
+exports[`thread should match snapshot, render footer 1`] = `
+
+`;
+
+exports[`thread should match snapshot, render footer 2`] = `null`;
+
+exports[`thread should match snapshot, render footer 3`] = `
+
+`;
diff --git a/app/screens/thread/thread.js b/app/screens/thread/thread.js
index 9c6d9d8e9..ee568d1b7 100644
--- a/app/screens/thread/thread.js
+++ b/app/screens/thread/thread.js
@@ -4,7 +4,7 @@
import React, {PureComponent} from 'react';
import PropTypes from 'prop-types';
import {Platform} from 'react-native';
-import {injectIntl, intlShape} from 'react-intl';
+import {intlShape} from 'react-intl';
import {General, RequestStatus} from 'mattermost-redux/constants';
import Loading from 'app/components/loading';
@@ -16,7 +16,7 @@ import StatusBar from 'app/components/status_bar';
import {makeStyleSheetFromTheme, setNavigatorStyles} from 'app/utils/theme';
import DeletedPost from 'app/components/deleted_post';
-class Thread extends PureComponent {
+export default class Thread extends PureComponent {
static propTypes = {
actions: PropTypes.shape({
selectPost: PropTypes.func.isRequired,
@@ -24,7 +24,6 @@ class Thread extends PureComponent {
channelId: PropTypes.string.isRequired,
channelType: PropTypes.string,
displayName: PropTypes.string,
- intl: intlShape.isRequired,
navigator: PropTypes.object,
myMember: PropTypes.object.isRequired,
rootId: PropTypes.string.isRequired,
@@ -36,8 +35,13 @@ class Thread extends PureComponent {
state = {};
+ static contextTypes = {
+ intl: intlShape,
+ };
+
componentWillMount() {
- const {channelType, displayName, intl} = this.props;
+ const {channelType, displayName} = this.props;
+ const {intl} = this.context;
let title;
if (channelType === General.DM_CHANNEL) {
@@ -182,5 +186,3 @@ const getStyle = makeStyleSheetFromTheme((theme) => {
},
};
});
-
-export default injectIntl(Thread);
diff --git a/app/screens/thread/thread.test.js b/app/screens/thread/thread.test.js
new file mode 100644
index 000000000..9557f5693
--- /dev/null
+++ b/app/screens/thread/thread.test.js
@@ -0,0 +1,103 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+
+import React from 'react';
+import {shallow} from 'enzyme';
+
+import Preferences from 'mattermost-redux/constants/preferences';
+import {General, RequestStatus} from 'mattermost-redux/constants';
+
+import Thread from './thread.js';
+
+jest.mock('react-intl');
+
+describe('thread', () => {
+ const navigator = {
+ dismissModal: jest.fn(),
+ pop: jest.fn(),
+ resetTo: jest.fn(),
+ setTitle: jest.fn(),
+ };
+ const baseProps = {
+ actions: {
+ selectPost: jest.fn(),
+ },
+ channelId: 'channel_id',
+ channelType: General.OPEN_CHANNEL,
+ displayName: 'channel_display_name',
+ navigator,
+ myMember: {last_viewed_at: 0, user_id: 'member_user_id'},
+ rootId: 'root_id',
+ theme: Preferences.THEMES.default,
+ postIds: ['root_id', 'post_id_1', 'post_id_2'],
+ channelIsArchived: false,
+ threadLoadingStatus: {status: RequestStatus.STARTED},
+ };
+
+ test('should match snapshot, has root post', () => {
+ const wrapper = shallow(
+ ,
+ {context: {intl: {formatMessage: jest.fn()}}},
+ );
+ expect(wrapper.getElement()).toMatchSnapshot();
+ });
+
+ test('should match snapshot, no root post, loading', () => {
+ const newPostIds = ['post_id_1', 'post_id_2'];
+ const wrapper = shallow(
+ ,
+ {context: {intl: {formatMessage: jest.fn()}}},
+ );
+ expect(wrapper.getElement()).toMatchSnapshot();
+ });
+
+ test('should call props.navigator on onCloseChannel', () => {
+ const channelScreen = {
+ screen: 'Channel',
+ title: '',
+ animated: false,
+ backButtonTitle: '',
+ navigatorStyle: {
+ animated: true,
+ animationType: 'fade',
+ navBarHidden: true,
+ statusBarHidden: false,
+ statusBarHideWithNavBar: false,
+ screenBackgroundColor: 'transparent',
+ },
+ };
+ const newNavigator = {...navigator};
+ const wrapper = shallow(
+ ,
+ {context: {intl: {formatMessage: jest.fn()}}},
+ );
+ wrapper.instance().onCloseChannel();
+ expect(newNavigator.resetTo).toHaveBeenCalledTimes(1);
+ expect(newNavigator.resetTo).toBeCalledWith(channelScreen);
+ });
+
+ test('should match snapshot, render footer', () => {
+ const wrapper = shallow(
+ ,
+ {context: {intl: {formatMessage: jest.fn()}}},
+ );
+
+ // return loading
+ expect(wrapper.instance().renderFooter()).toMatchSnapshot();
+
+ // return null
+ wrapper.setProps({threadLoadingStatus: {status: RequestStatus.SUCCESS}});
+ expect(wrapper.instance().renderFooter()).toMatchSnapshot();
+
+ // return deleted post
+ const newPostIds = ['post_id_1', 'post_id_2'];
+ wrapper.setProps({postIds: newPostIds});
+ expect(wrapper.instance().renderFooter()).toMatchSnapshot();
+ });
+});
diff --git a/package.json b/package.json
index 88798e15a..c1441a9eb 100644
--- a/package.json
+++ b/package.json
@@ -131,6 +131,9 @@
],
"moduleNameMapper": {
"assets/images/video_player/(.*).png": "/dist/assets/images/video_player/$1@2x.png"
- }
+ },
+ "transformIgnorePatterns": [
+ "node_modules/(?!react-native|jail-monkey)"
+ ]
}
}
diff --git a/test/setup.js b/test/setup.js
index 1484fe689..8c416b1dd 100644
--- a/test/setup.js
+++ b/test/setup.js
@@ -7,6 +7,16 @@ configure({adapter: new Adapter()});
/* eslint-disable no-console */
+jest.mock('NativeModules', () => {
+ return {
+ BlurAppScreen: () => true,
+ MattermostManaged: {
+ getConfig: jest.fn(),
+ },
+ };
+});
+jest.mock('NativeEventEmitter');
+
let logs;
let warns;
let errors;