Skip to content

Commit d286571

Browse files
borisyankovneerajwahi
authored andcommitted
Extract userlist sort and filter as selectors, add unit tests and wallaby config
1 parent 717a62e commit d286571

File tree

8 files changed

+113
-38
lines changed

8 files changed

+113
-38
lines changed

Diff for: src/common/__tests__/__snapshots__/ZulipButton-test.js.snap

+14-14
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@ exports[`test renders correctly 1`] = `
99
"borderRadius": 5,
1010
"height": 44,
1111
"justifyContent": "center",
12-
"marginBottom": 10
12+
"marginBottom": 10,
1313
},
1414
Object {
15-
"backgroundColor": "#22693F"
16-
}
15+
"backgroundColor": "#22693F",
16+
},
1717
]
1818
}>
1919
<View
@@ -23,23 +23,23 @@ exports[`test renders correctly 1`] = `
2323
accessible={true}
2424
hitSlop={undefined}
2525
onLayout={undefined}
26-
onResponderGrant={[Function bound touchableHandleResponderGrant]}
27-
onResponderMove={[Function bound touchableHandleResponderMove]}
28-
onResponderRelease={[Function bound touchableHandleResponderRelease]}
29-
onResponderTerminate={[Function bound touchableHandleResponderTerminate]}
30-
onResponderTerminationRequest={[Function bound touchableHandleResponderTerminationRequest]}
31-
onStartShouldSetResponder={[Function bound touchableHandleStartShouldSetResponder]}
26+
onResponderGrant={[Function]}
27+
onResponderMove={[Function]}
28+
onResponderRelease={[Function]}
29+
onResponderTerminate={[Function]}
30+
onResponderTerminationRequest={[Function]}
31+
onStartShouldSetResponder={[Function]}
3232
style={
3333
Array [
3434
Object {
35-
"backgroundColor": "transparent"
35+
"backgroundColor": "transparent",
3636
},
3737
Object {
3838
"alignItems": "center",
3939
"alignSelf": "stretch",
4040
"flex": 1,
41-
"justifyContent": "center"
42-
}
41+
"justifyContent": "center",
42+
},
4343
]
4444
}
4545
testID={undefined}>
@@ -50,9 +50,9 @@ exports[`test renders correctly 1`] = `
5050
style={
5151
Array [
5252
Object {
53-
"color": "#FFFFFF"
53+
"color": "#FFFFFF",
5454
},
55-
undefined
55+
undefined,
5656
]
5757
} />
5858
</View>

Diff for: src/events/eventActions.js

+3
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@ const processEvent = (dispatch, event) => {
5151
case 'subscription':
5252
// TODO
5353
break;
54+
case 'update_message':
55+
// TODO
56+
break;
5457
case 'heartbeat':
5558
// ignore, no need to handle
5659
break;

Diff for: src/userlist/UserList.js

+2-20
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
ListView,
55
} from 'react-native';
66
import UserItem from './UserItem';
7+
import { sortUserList, filterUserList } from './userListSelectors';
78

89
const styles = StyleSheet.create({
910
container: {
@@ -12,25 +13,6 @@ const styles = StyleSheet.create({
1213
},
1314
});
1415

15-
const filterUsers = (users: any[], filter: string): any[] =>
16-
users.filter(x =>
17-
filter === '' ||
18-
x.get('fullName').toLowerCase().includes(filter.toLowerCase()) ||
19-
x.get('email').toLowerCase().includes(filter.toLowerCase())
20-
);
21-
22-
const statusOrder = {
23-
active: 1,
24-
idle: 2,
25-
offline: 3,
26-
};
27-
28-
const sortUsers = (users: any[]): any[] =>
29-
users.sort((x1, x2) =>
30-
statusOrder[x1.get('status')] - statusOrder[x2.get('status')] ||
31-
x1.get('fullName').localeCompare(x2.get('fullName'))
32-
);
33-
3416
export default class UserList extends Component {
3517

3618
props: {
@@ -42,7 +24,7 @@ export default class UserList extends Component {
4224
render() {
4325
const { users, filter, onNarrow } = this.props;
4426
const ds = new ListView.DataSource({ rowHasChanged: (r1, r2) => r1 !== r2 });
45-
const shownUsers = sortUsers(filterUsers(users, filter));
27+
const shownUsers = sortUserList(filterUserList(users, filter));
4628
const dataSource = ds.cloneWithRows(shownUsers.toJS());
4729

4830
return (

Diff for: src/userlist/__tests__/userListReducers-test.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,5 @@ it('on unrecognized action, returns input state unchanged', () => {
1616
it('on GET_USER_RESPONSE stores user data', () => {
1717
const users = [{ full_name: 'user1' }, { full_name: 'user2' }];
1818
const newState = userListReducers(fromJS([]), { type: GET_USER_RESPONSE, users });
19-
expect(newState).toEqual(users);
19+
expect(newState.size).toEqual(2);
2020
});

Diff for: src/userlist/__tests__/userListSelectors-test.js

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { fromJS } from 'immutable';
2+
import { sortUserList, filterUserList } from '../userListSelectors';
3+
4+
it('empty input to filterUserList results in empty list', () => {
5+
const users = fromJS([]);
6+
const filteredUsers = filterUserList(users, 'some filter');
7+
expect(filteredUsers).toEqual(fromJS([]));
8+
});
9+
10+
it('filterUserList returns same list if no filter', () => {
11+
const users = fromJS([{ fullName: 'user1' }, { fullName: 'user2' }]);
12+
const filteredUsers = filterUserList(users);
13+
expect(filteredUsers).toEqual(users);
14+
});
15+
16+
it('filterUserList searches in name, email and is case insensitive', () => {
17+
const allUsers = fromJS([
18+
{ fullName: 'match', email: '[email protected]' },
19+
{ fullName: 'partial match', email: '[email protected]' },
20+
{ fullName: 'Case Insensitive MaTcH', email: '[email protected]' },
21+
{ fullName: 'Any Name', email: '[email protected]' },
22+
{ fullName: 'some name', email: '[email protected]' },
23+
]);
24+
const shouldMatch = fromJS([
25+
{ fullName: 'match', email: '[email protected]' },
26+
{ fullName: 'partial match', email: '[email protected]' },
27+
{ fullName: 'Case Insensitive MaTcH', email: '[email protected]' },
28+
{ fullName: 'Any Name', email: '[email protected]' },
29+
]);
30+
const filteredUsers = filterUserList(allUsers, 'match');
31+
expect(filteredUsers).toEqual(shouldMatch);
32+
});
33+
34+
it('sortUserList sorts by name', () => {
35+
const users = fromJS([{ fullName: 'abc' }, { fullName: 'xyz' }, { fullName: 'jkl' }]);
36+
const shouldMatch = fromJS([{ fullName: 'abc' }, { fullName: 'jkl' }, { fullName: 'xyz' }]);
37+
const sortedUsers = sortUserList(users);
38+
expect(sortedUsers).toEqual(shouldMatch);
39+
});
40+
41+
it('sortUserList prioritizes status', () => {
42+
const users = fromJS([
43+
{ fullName: 'abc', status: 'offline' },
44+
{ fullName: 'xyz', status: 'idle' },
45+
{ fullName: 'jkl', status: 'active' },
46+
{ fullName: 'abc', status: 'active' },
47+
]);
48+
const shouldMatch = fromJS([
49+
{ fullName: 'abc', status: 'active' },
50+
{ fullName: 'jkl', status: 'active' },
51+
{ fullName: 'xyz', status: 'idle' },
52+
{ fullName: 'abc', status: 'offline' },
53+
]);
54+
const sortedUsers = sortUserList(users);
55+
expect(sortedUsers).toEqual(shouldMatch);
56+
});

Diff for: src/userlist/userListReducers.js

+1-3
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ const initialState = fromJS([]);
3535
const activityFromPresence = (presence: PresenceMap): UserStatus =>
3636
'active';
3737

38-
const reducer = (state = initialState, action) => {
38+
export default (state = initialState, action) => {
3939
switch (action.type) {
4040
case PRESENCE_RESPONSE: {
4141
let newState = state;
@@ -64,5 +64,3 @@ const reducer = (state = initialState, action) => {
6464
return state;
6565
}
6666
};
67-
68-
export default reducer;

Diff for: src/userlist/userListSelectors.js

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
const statusOrder = {
2+
active: 1,
3+
idle: 2,
4+
offline: 3,
5+
};
6+
7+
export const sortUserList = (users: any[]): any[] =>
8+
users.sort((x1, x2) =>
9+
statusOrder[x1.get('status')] - statusOrder[x2.get('status')] ||
10+
x1.get('fullName').localeCompare(x2.get('fullName'))
11+
);
12+
13+
export const filterUserList = (users: any[], filter: string = ''): any[] =>
14+
users.filter(x =>
15+
filter === '' ||
16+
x.get('fullName').toLowerCase().includes(filter.toLowerCase()) ||
17+
x.get('email').toLowerCase().includes(filter.toLowerCase())
18+
);

Diff for: wallaby.js

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
module.exports = (wallaby) => ({
2+
files: ['src/**/*.js', '!src/**/__tests__/*.js'],
3+
tests: ['src/**/__tests__/*.js'],
4+
env: {
5+
type: 'node',
6+
runner: 'node',
7+
},
8+
testFramework: 'jest',
9+
compilers: {
10+
'src/**/*.js': wallaby.compilers.babel(),
11+
},
12+
setup: () =>
13+
wallaby.testFramework.configure({
14+
// https://facebook.github.io/jest/docs/api.html#config-options
15+
// you may just pass `require('./package.json').jest`, if you use it for your Jest config
16+
// don't forget to include package.json in the files list in this case
17+
}),
18+
});

0 commit comments

Comments
 (0)