diff --git a/app/components/channel_link/__snapshots__/channel_link.test.js.snap b/app/components/channel_link/__snapshots__/channel_link.test.js.snap new file mode 100644 index 000000000..56727ab3f --- /dev/null +++ b/app/components/channel_link/__snapshots__/channel_link.test.js.snap @@ -0,0 +1,25 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`ChannelLink should match snapshot 1`] = ` + + + ~First Channel + + + +`; diff --git a/app/components/channel_link/channel_link.js b/app/components/channel_link/channel_link.js index f06dfc600..839c0330b 100644 --- a/app/components/channel_link/channel_link.js +++ b/app/components/channel_link/channel_link.js @@ -5,6 +5,8 @@ import React from 'react'; import PropTypes from 'prop-types'; import {Text} from 'react-native'; +import {getChannelFromChannelName} from './channel_link_utils'; + import CustomPropTypes from 'app/constants/custom_prop_types'; export default class ChannelLink extends React.PureComponent { @@ -24,32 +26,14 @@ export default class ChannelLink extends React.PureComponent { super(props); this.state = { - channel: this.getChannelFromChannelName(props), + channel: getChannelFromChannelName(props.channelName, props.channelsByName), }; } - componentWillReceiveProps(nextProps) { - if (nextProps.channelName !== this.props.channelName || nextProps.channelsByName !== this.props.channelsByName) { - this.setState({ - channel: this.getChannelFromChannelName(nextProps), - }); - } - } - - getChannelFromChannelName(props) { - let channelName = props.channelName; - - while (channelName.length > 0) { - if (props.channelsByName[channelName]) { - return props.channelsByName[channelName]; - } - - // Repeatedly trim off trailing punctuation in case this is at the end of a sentence - if ((/[_-]$/).test(channelName)) { - channelName = channelName.substring(0, channelName.length - 1); - } else { - break; - } + static getDerivedStateFromProps(nextProps, prevState) { + const nextChannel = getChannelFromChannelName(nextProps.channelName, nextProps.channelsByName); + if (nextChannel !== prevState.channel) { + return {channel: nextChannel}; } return null; diff --git a/app/components/channel_link/channel_link.test.js b/app/components/channel_link/channel_link.test.js new file mode 100644 index 000000000..7d1732646 --- /dev/null +++ b/app/components/channel_link/channel_link.test.js @@ -0,0 +1,75 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import React from 'react'; +import {shallow} from 'enzyme'; +import {Text} from 'react-native'; + +import ChannelLink from './channel_link'; + +describe('ChannelLink', () => { + const channelsByName = { + firstChannel: {id: 'channel_id_1', name: 'firstChannel', display_name: 'First Channel'}, + secondChannel: {id: 'channel_id_2', name: 'secondChannel', display_name: 'Second Channel'}, + }; + const baseProps = { + channelName: 'firstChannel', + linkStyle: {color: '#2389d7'}, + onChannelLinkPress: jest.fn(), + textStyle: {color: '#3d3c40', fontSize: 15, lineHeight: 20}, + channelsByName, + actions: { + handleSelectChannel: jest.fn(), + setChannelDisplayName: jest.fn(), + }, + }; + + test('should match snapshot', () => { + const wrapper = shallow( + + ); + + expect(wrapper.getElement()).toMatchSnapshot(); + expect(wrapper.find(Text).length).toBe(2); + + // inner text with highlight + let innerText = wrapper.find(Text).last(); + expect(innerText.props().onPress).toBeDefined(); + expect(innerText.props().style.color).toBe('#2389d7'); + expect(innerText.props().children).toContain(channelsByName.firstChannel.display_name); + + // text for channel name with punctuation + const punctuation = '-_'; + wrapper.setProps({channelName: baseProps.channelName + punctuation}); + expect(wrapper.find(Text).length).toBe(2); + innerText = wrapper.find(Text).last(); + const outerText = wrapper.find(Text).first(); + expect(innerText.props().onPress).toBeDefined(); + expect(innerText.props().children).toContain(channelsByName.firstChannel.display_name); + expect(outerText.props().children).toContain(punctuation); + + // text for not found channel + const notFoundChannel = 'notFoundChannel'; + wrapper.setProps({channelName: notFoundChannel}); + expect(wrapper.find(Text).length).toBe(1); + innerText = wrapper.find(Text).first(); + expect(innerText.props().children).toContain(notFoundChannel); + expect(innerText.props().onPress).not.toBeDefined(); + }); + + test('should call props.actions and onChannelLinkPress on handlePress', () => { + const wrapper = shallow( + + ); + + const channel = channelsByName.firstChannel; + wrapper.setState({channel}); + wrapper.instance().handlePress(); + expect(baseProps.actions.handleSelectChannel).toHaveBeenCalledTimes(1); + expect(baseProps.actions.handleSelectChannel).toBeCalledWith(channel.id); + expect(baseProps.actions.setChannelDisplayName).toHaveBeenCalledTimes(1); + expect(baseProps.actions.setChannelDisplayName).toBeCalledWith(channel.display_name); + expect(baseProps.onChannelLinkPress).toHaveBeenCalledTimes(1); + expect(baseProps.onChannelLinkPress).toBeCalledWith(channel); + }); +}); diff --git a/app/components/channel_link/channel_link_utils.js b/app/components/channel_link/channel_link_utils.js new file mode 100644 index 000000000..1d71e57d4 --- /dev/null +++ b/app/components/channel_link/channel_link_utils.js @@ -0,0 +1,21 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +export function getChannelFromChannelName(name, channelsByName) { + let channelName = name; + + while (channelName.length > 0) { + if (channelsByName[channelName]) { + return channelsByName[channelName]; + } + + // Repeatedly trim off trailing punctuation in case this is at the end of a sentence + if ((/[_-]$/).test(channelName)) { + channelName = channelName.substring(0, channelName.length - 1); + } else { + break; + } + } + + return null; +} diff --git a/app/components/channel_link/channel_link_utils.test.js b/app/components/channel_link/channel_link_utils.test.js new file mode 100644 index 000000000..b70a08359 --- /dev/null +++ b/app/components/channel_link/channel_link_utils.test.js @@ -0,0 +1,46 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {getChannelFromChannelName} from './channel_link_utils'; + +describe('should match return value on getChannelFromChannelName', () => { + const channelsByName = { + firstChannel: {id: 'channel_id_1', name: 'firstChannel', display_name: 'First Channel'}, + secondChannel: {id: 'channel_id_2', name: 'secondChannel', display_name: 'Second Channel'}, + }; + + const testCases = [ + { + name: 'regular mention', + input: {channelName: 'firstChannel', channelsByName}, + output: channelsByName.firstChannel, + }, + { + name: 'another mention', + input: {channelName: 'secondChannel', channelsByName}, + output: channelsByName.secondChannel, + }, + { + name: 'channel mention with trailing punctuation', + input: {channelName: 'firstChannel-', channelsByName}, + output: channelsByName.firstChannel, + }, + { + name: 'channel mention with trailing punctuations', + input: {channelName: 'firstChannel-_', channelsByName}, + output: channelsByName.firstChannel, + }, + { + name: 'channel mention not found in channelsByName', + input: {channelName: 'otherChannel', channelsByName}, + output: null, + }, + ]; + + for (const testCase of testCases) { + const {name, input, output} = testCase; + test(name, () => { + expect(getChannelFromChannelName(input.channelName, input.channelsByName)).toEqual(output); + }); + } +});