Skip to content

Commit 41cc624

Browse files
committed
Extract the mapping functions from state to properties so they can be tested
One option for testing mapStateToProps directly would have required exporting private code as public, just for the purpose of testing. That's a bit of a code smell. Another option would be to pass a mock store into the component. That was something we could do in react-redux v5 and which was restored in v7, but it was removed in v6, which is what we are on. See reduxjs/react-redux#1161 This seems like the best way of testing this mapping without upgrading react-redux or unnecessarily breaking encapsulation.
1 parent 9971050 commit 41cc624

File tree

5 files changed

+84
-8
lines changed

5 files changed

+84
-8
lines changed

__tests__/selectors.test.js

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// Copyright 2019 Stanford University see LICENSE for license
2+
3+
import { getCurrentUser, getCurrentSession, getAuthenticationError, getAuthenticationState } from '../src/selectors';
4+
5+
describe('getCurrentUser', () => {
6+
const currentUser = { hello: 'world' }
7+
8+
const state = {
9+
authenticate: {
10+
authenticationState: {
11+
currentUser: currentUser
12+
}
13+
}
14+
};
15+
16+
it('returns user', () => {
17+
expect(getCurrentUser(state)).toBe(currentUser)
18+
})
19+
})
20+
21+
describe('getCurrentSession', () => {
22+
const currentSession = { hello: 'world' }
23+
24+
const state = {
25+
authenticate: {
26+
authenticationState: {
27+
currentSession: currentSession
28+
}
29+
}
30+
};
31+
32+
it('returns currentSession', () => {
33+
expect(getCurrentSession(state)).toBe(currentSession)
34+
})
35+
})
36+
37+
describe('getAuthenticationError', () => {
38+
const authenticationError = { hello: 'world' }
39+
40+
const state = {
41+
authenticate: {
42+
authenticationState: {
43+
authenticationError: authenticationError
44+
}
45+
}
46+
};
47+
48+
it('returns authentication error', () => {
49+
expect(getAuthenticationError(state)).toBe(authenticationError)
50+
})
51+
})
52+
53+
describe('getAuthenticationState', () => {
54+
const authenticationState = { authenticationError: 'broken' }
55+
56+
const state = {
57+
authenticate: {
58+
authenticationState: authenticationState
59+
}
60+
};
61+
62+
it('returns a copy of the authentication state', () => {
63+
const result = getAuthenticationState(state)
64+
expect(result).not.toBe(authenticationState)
65+
expect(result).toEqual(authenticationState)
66+
})
67+
})

src/components/LoginPanel.jsx

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import Config from '../Config'
66
import CognitoUtils from '../CognitoUtils'
77
import { connect } from 'react-redux'
88
import { authenticationFailure, authenticationSuccess, signOutSuccess } from '../actions/index'
9-
9+
import { getCurrentUser, getCurrentSession, getAuthenticationError } from '../selectors';
1010

1111
class LoginPanel extends Component {
1212
constructor(props){
@@ -126,13 +126,11 @@ LoginPanel.propTypes = {
126126
signout: PropTypes.func
127127
}
128128

129-
//TODO: make testing these part of the new tests you write (that is, unit test mapStateToProps and mapDispatchToProps, since
130-
// you're testing the bare WrappedComponent, and not the HoC generated by calling connect(...)(LoginPanel))
131129
const mapStateToProps = (state) => {
132130
return {
133-
currentUser: state.authenticate.authenticationState ? state.authenticate.authenticationState.currentUser : null,
134-
currentSession: state.authenticate.authenticationState ? state.authenticate.authenticationState.currentSession : null,
135-
authenticationError: state.authenticate.authenticationState ? state.authenticate.authenticationState.authenticationError : null
131+
currentUser: getCurrentUser(state),
132+
currentSession: getCurrentSession(state),
133+
authenticationError: getAuthenticationError(state)
136134
}
137135
}
138136

src/components/editor/Editor.jsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import PropTypes from 'prop-types'
77
import ResourceTemplate from './ResourceTemplate'
88
import Header from './Header'
99
import RDFModal from './RDFModal'
10+
import { getAuthenticationState } from '../../selectors';
11+
1012
const _ = require('lodash')
1113

1214
class Editor extends Component {
@@ -87,7 +89,7 @@ Editor.propTypes = {
8789

8890
const mapStateToProps = (state) => {
8991
return {
90-
authenticationState: Object.assign({}, state.authenticate.authenticationState)
92+
authenticationState: getAuthenticationState(state)
9193
}
9294
}
9395

src/components/editor/ImportResourceTemplate.jsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import SinopiaResourceTemplates from './SinopiaResourceTemplates'
88
import UpdateResourceModal from './UpdateResourceModal'
99
import { createResourceTemplate, updateResourceTemplate } from '../../sinopiaServer'
1010
import { connect } from 'react-redux'
11+
import { getAuthenticationState } from '../../selectors';
1112

1213
class ImportResourceTemplate extends Component {
1314
constructor(props) {
@@ -164,7 +165,7 @@ ImportResourceTemplate.propTypes = {
164165

165166
const mapStateToProps = (state) => {
166167
return {
167-
authenticationState: Object.assign({}, state.authenticate.authenticationState)
168+
authenticationState: getAuthenticationState(state)
168169
}
169170
}
170171

src/selectors.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
2+
export const getCurrentUser = (state) => (getAuthenticationState(state)?.currentUser)
3+
4+
export const getCurrentSession = (state) => (getAuthenticationState(state)?.currentSession)
5+
6+
export const getAuthenticationError = (state) => (getAuthenticationState(state)?.authenticationError)
7+
8+
export const getAuthenticationState = (state) => ({...state.authenticate.authenticationState})

0 commit comments

Comments
 (0)