Skip to content

Commit

Permalink
Add selectors and tests for unreadReducers (#885)
Browse files Browse the repository at this point in the history
  • Loading branch information
borisyankov authored Jul 19, 2017
1 parent 51bc009 commit 81a481e
Show file tree
Hide file tree
Showing 12 changed files with 435 additions and 59 deletions.
3 changes: 2 additions & 1 deletion src/chat/Chat.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { OfflineNotice } from '../common';
import boundActions from '../boundActions';
import {
getAuth,
getActiveNarrow,
getIsFetching,
getCurrentTypingUsers,
getShownMessagesInActiveNarrow,
Expand Down Expand Up @@ -95,7 +96,7 @@ export default connect(
subscriptions: state.subscriptions,
flags: state.flags,
isFetching: getIsFetching(state),
narrow: state.chat.narrow,
narrow: getActiveNarrow(state),
mute: state.mute,
messages: getShownMessagesInActiveNarrow(state),
typingUsers: getCurrentTypingUsers(state),
Expand Down
13 changes: 10 additions & 3 deletions src/common/UnreadCount.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,28 @@ const styles = StyleSheet.create({
color: 'white',
fontSize: 12,
},
frameInverse: {
backgroundColor: 'white',
},
textInverse: {
color: BRAND_COLOR,
},
});

export default class UnreadCount extends PureComponent {
props: {
count?: number,
inverse?: boolean,
};

render() {
const { count } = this.props;
const { count, inverse } = this.props;

if (!count) return null;

return (
<View style={styles.frame}>
<Text style={styles.text}>
<View style={[styles.frame, inverse && styles.frameInverse]}>
<Text style={[styles.text, inverse && styles.textInverse]}>
{count < 100 ? count : '99+'}
</Text>
</View>
Expand Down
82 changes: 42 additions & 40 deletions src/nav/__tests__/navSelectors-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,65 +2,67 @@ import deepFreeze from 'deep-freeze';

import { getInitialRoute } from '../navSelectors';

test('if logged in, show main route', () => {
const state = deepFreeze({
accounts: [{ apiKey: '123' }],
});

const route = getInitialRoute(state);
describe('getInitialRoute', () => {
test('if logged in, show main route', () => {
const state = deepFreeze({
accounts: [{ apiKey: '123' }],
});

expect(route).toEqual('main');
});
const route = getInitialRoute(state);

test('if not logged in, and no previous accounts, show realm screen', () => {
const state = deepFreeze({
accounts: [],
expect(route).toEqual('main');
});

const route = getInitialRoute(state);
test('if not logged in, and no previous accounts, show realm screen', () => {
const state = deepFreeze({
accounts: [],
});

expect(route).toEqual('realm');
});
const route = getInitialRoute(state);

test('if more than one account and no active account, display account list', () => {
const state = deepFreeze({
accounts: [{}, {}],
expect(route).toEqual('realm');
});

const route = getInitialRoute(state);
test('if more than one account and no active account, display account list', () => {
const state = deepFreeze({
accounts: [{}, {}],
});

expect(route).toEqual('account');
});
const route = getInitialRoute(state);

test('when only a single account and no other properties, redirect to realm', () => {
const state = deepFreeze({
accounts: [{ realm: 'https://example.com' }],
expect(route).toEqual('account');
});

const route = getInitialRoute(state);
test('when only a single account and no other properties, redirect to realm', () => {
const state = deepFreeze({
accounts: [{ realm: 'https://example.com' }],
});

expect(route).toEqual('realm');
});
const route = getInitialRoute(state);

test('when multiple accounts and default one has realm and email, show account list', () => {
const state = deepFreeze({
accounts: [
{ realm: 'https://example.com', email: 'johndoe@example.com' },
{ realm: 'https://example.com', email: 'janedoe@example.com' },
],
expect(route).toEqual('realm');
});

const route = getInitialRoute(state);
test('when multiple accounts and default one has realm and email, show account list', () => {
const state = deepFreeze({
accounts: [
{ realm: 'https://example.com', email: 'johndoe@example.com' },
{ realm: 'https://example.com', email: 'janedoe@example.com' },
],
});

expect(route).toEqual('account');
});
const route = getInitialRoute(state);

test('when default account has server and email set, redirect to realm screen', () => {
const state = deepFreeze({
accounts: [{ realm: 'https://example.com', email: 'johndoe@example.com' }],
expect(route).toEqual('account');
});

const route = getInitialRoute(state);
test('when default account has server and email set, redirect to realm screen', () => {
const state = deepFreeze({
accounts: [{ realm: 'https://example.com', email: 'johndoe@example.com' }],
});

expect(route).toEqual('realm');
const route = getInitialRoute(state);

expect(route).toEqual('realm');
});
});
1 change: 1 addition & 0 deletions src/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ export * from './account/accountSelectors';
export * from './chat/chatSelectors';
export * from './subscriptions/subscriptionSelectors';
export * from './nav/navSelectors';
export * from './unread/unreadSelectors';
export * from './users/userSelectors';
7 changes: 5 additions & 2 deletions src/streams/StreamItem.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import React, { PureComponent } from 'react';
import { StyleSheet, View } from 'react-native';

import { BRAND_COLOR } from '../styles';
import { RawLabel, Touchable, ZulipSwitch } from '../common';
import { RawLabel, Touchable, UnreadCount, ZulipSwitch } from '../common';
import StreamIcon from './StreamIcon';

const styles = StyleSheet.create({
Expand Down Expand Up @@ -48,8 +48,9 @@ export default class StreamItem extends PureComponent {
isSelected?: boolean,
showSwitch?: boolean,
color: string,
onPress: (name: string) => void,
isSwitchedOn?: boolean,
unreadCount?: number,
onPress: (name: string) => void,
onSwitch?: (name: string, newValue: boolean) => void,
};

Expand All @@ -73,6 +74,7 @@ export default class StreamItem extends PureComponent {
isSelected,
showSwitch,
isSwitchedOn,
unreadCount,
} = this.props;
const iconWrapperCustomStyle = {
width: iconSize * 1.5,
Expand All @@ -94,6 +96,7 @@ export default class StreamItem extends PureComponent {
{!!description &&
<RawLabel numberOfLines={1} style={styles.description} text={description} />}
</View>
{unreadCount && <UnreadCount count={unreadCount} inverse={isSelected} />}
{showSwitch &&
<ZulipSwitch
defaultValue={isSwitchedOn}
Expand Down
15 changes: 13 additions & 2 deletions src/streams/StreamList.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,28 @@ export default class StreamList extends PureComponent {
selected?: boolean,
showDescriptions: boolean,
showSwitch: boolean,
unreadByStream: number[],
onPress?: (streamName: string) => void,
onSwitch?: (streamName: string, newValue: boolean) => void,
};

static defaultProps: {
static defaultProps = {
showSwitch: false,
showDescriptions: false,
selected: false,
unreadByStream: [],
};

render() {
const { streams, selected, showDescriptions, showSwitch, onPress, onSwitch } = this.props;
const {
streams,
selected,
showDescriptions,
showSwitch,
unreadByStream,
onPress,
onSwitch,
} = this.props;
const sortedStreams = streams.sort((a, b) => a.name.localeCompare(b.name));

return (
Expand All @@ -45,6 +55,7 @@ export default class StreamList extends PureComponent {
isPrivate={item.invite_only}
description={showDescriptions ? item.description : ''}
color={item.color}
unreadCount={unreadByStream[item.stream_id]}
isSelected={item.name === selected}
isMuted={item.in_home_view === false} // if 'undefined' is not muted
showSwitch={showSwitch}
Expand Down
14 changes: 11 additions & 3 deletions src/streams/SubscriptionsContainer.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { connect } from 'react-redux';

import boundActions from '../boundActions';
import type { Actions, Narrow, GlobalState, SubscriptionsState } from '../types';
import { getActiveNarrow, getUnreadByStream } from '../selectors';
import StreamList from './StreamList';
import { isStreamNarrow, streamNarrow } from '../utils/narrow';

Expand All @@ -20,26 +21,33 @@ class SubscriptionsContainer extends PureComponent {
actions: Actions,
narrow: Narrow,
subscriptions: SubscriptionsState,
unreadByStream: number[],
};

handleNarrow = (streamName: string) => this.props.actions.doNarrow(streamNarrow(streamName));

render() {
const { narrow, subscriptions } = this.props;
const { narrow, subscriptions, unreadByStream } = this.props;
const selected = isStreamNarrow(narrow) && narrow[0].operand;

return (
<View tabLabel="Streams" style={styles.container}>
<StreamList streams={subscriptions} selected={selected} onPress={this.handleNarrow} />
<StreamList
streams={subscriptions}
selected={selected}
unreadByStream={unreadByStream}
onPress={this.handleNarrow}
/>
</View>
);
}
}

export default connect(
(state: GlobalState) => ({
narrow: state.chat.narrow,
narrow: getActiveNarrow(state),
subscriptions: state.subscriptions,
unreadByStream: getUnreadByStream(state),
}),
boundActions,
)(SubscriptionsContainer);
12 changes: 5 additions & 7 deletions src/subscriptions/SubscriptionsScreen.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,12 @@ import { subscriptionAdd, subscriptionRemove } from '../api';
import StreamList from '../streams/StreamList';
import { getAuth } from '../selectors';

type Props = {
auth: Auth,
streams: [],
subscriptions: [],
};

class SubscriptionsScreen extends PureComponent {
props: Props;
props: {
auth: Auth,
streams: [],
subscriptions: [],
};

state: {
filter: string,
Expand Down
59 changes: 59 additions & 0 deletions src/unread/__tests__/unreadReducers-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import deepFreeze from 'deep-freeze';

import unreadReducers from '../unreadReducers';
import { REALM_INIT, ACCOUNT_SWITCH } from '../../actionConstants';

describe('unreadReducers', () => {
describe('ACCOUNT_SWITCH', () => {
test('resets state to initial state', () => {
const initialState = deepFreeze(['some_stream']);

const action = deepFreeze({
type: ACCOUNT_SWITCH,
});

const expectedState = {
streams: [],
huddles: [],
pms: [],
};

const actualState = unreadReducers(initialState, action);

expect(actualState).toEqual(expectedState);
});
});

describe('REALM_INIT', () => {
test('received data from "unread_msgs" key replaces the current state ', () => {
const initialState = deepFreeze({
streams: [],
huddles: [],
pms: [],
});

const unreadMsgsData = {
streams: [
{
stream_id: 1,
topic: 'some topic',
unread_message_ids: [1, 2, 3],
},
],
huddles: [],
pms: [],
};

const action = deepFreeze({
type: REALM_INIT,
data: {
unread_msgs: unreadMsgsData,
},
});

const actualState = unreadReducers(initialState, action);

expect(actualState).toBe(unreadMsgsData);
});
});
});
Loading

0 comments on commit 81a481e

Please sign in to comment.