Skip to content

Commit

Permalink
Extract the mapping functions from state to properties so they can be…
Browse files Browse the repository at this point in the history
… 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.
  • Loading branch information
jcoyne committed May 30, 2019
1 parent 9971050 commit 41cc624
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 8 deletions.
67 changes: 67 additions & 0 deletions __tests__/selectors.test.js
@@ -0,0 +1,67 @@
// Copyright 2019 Stanford University see LICENSE for license

import { getCurrentUser, getCurrentSession, getAuthenticationError, getAuthenticationState } from '../src/selectors';

describe('getCurrentUser', () => {
const currentUser = { hello: 'world' }

const state = {
authenticate: {
authenticationState: {
currentUser: currentUser
}
}
};

it('returns user', () => {
expect(getCurrentUser(state)).toBe(currentUser)
})
})

describe('getCurrentSession', () => {
const currentSession = { hello: 'world' }

const state = {
authenticate: {
authenticationState: {
currentSession: currentSession
}
}
};

it('returns currentSession', () => {
expect(getCurrentSession(state)).toBe(currentSession)
})
})

describe('getAuthenticationError', () => {
const authenticationError = { hello: 'world' }

const state = {
authenticate: {
authenticationState: {
authenticationError: authenticationError
}
}
};

it('returns authentication error', () => {
expect(getAuthenticationError(state)).toBe(authenticationError)
})
})

describe('getAuthenticationState', () => {
const authenticationState = { authenticationError: 'broken' }

const state = {
authenticate: {
authenticationState: authenticationState
}
};

it('returns a copy of the authentication state', () => {
const result = getAuthenticationState(state)
expect(result).not.toBe(authenticationState)
expect(result).toEqual(authenticationState)
})
})
10 changes: 4 additions & 6 deletions src/components/LoginPanel.jsx
Expand Up @@ -6,7 +6,7 @@ import Config from '../Config'
import CognitoUtils from '../CognitoUtils'
import { connect } from 'react-redux'
import { authenticationFailure, authenticationSuccess, signOutSuccess } from '../actions/index'

import { getCurrentUser, getCurrentSession, getAuthenticationError } from '../selectors';

class LoginPanel extends Component {
constructor(props){
Expand Down Expand Up @@ -126,13 +126,11 @@ LoginPanel.propTypes = {
signout: PropTypes.func
}

//TODO: make testing these part of the new tests you write (that is, unit test mapStateToProps and mapDispatchToProps, since
// you're testing the bare WrappedComponent, and not the HoC generated by calling connect(...)(LoginPanel))
const mapStateToProps = (state) => {
return {
currentUser: state.authenticate.authenticationState ? state.authenticate.authenticationState.currentUser : null,
currentSession: state.authenticate.authenticationState ? state.authenticate.authenticationState.currentSession : null,
authenticationError: state.authenticate.authenticationState ? state.authenticate.authenticationState.authenticationError : null
currentUser: getCurrentUser(state),
currentSession: getCurrentSession(state),
authenticationError: getAuthenticationError(state)
}
}

Expand Down
4 changes: 3 additions & 1 deletion src/components/editor/Editor.jsx
Expand Up @@ -7,6 +7,8 @@ import PropTypes from 'prop-types'
import ResourceTemplate from './ResourceTemplate'
import Header from './Header'
import RDFModal from './RDFModal'
import { getAuthenticationState } from '../../selectors';

const _ = require('lodash')

class Editor extends Component {
Expand Down Expand Up @@ -87,7 +89,7 @@ Editor.propTypes = {

const mapStateToProps = (state) => {
return {
authenticationState: Object.assign({}, state.authenticate.authenticationState)
authenticationState: getAuthenticationState(state)
}
}

Expand Down
3 changes: 2 additions & 1 deletion src/components/editor/ImportResourceTemplate.jsx
Expand Up @@ -8,6 +8,7 @@ import SinopiaResourceTemplates from './SinopiaResourceTemplates'
import UpdateResourceModal from './UpdateResourceModal'
import { createResourceTemplate, updateResourceTemplate } from '../../sinopiaServer'
import { connect } from 'react-redux'
import { getAuthenticationState } from '../../selectors';

class ImportResourceTemplate extends Component {
constructor(props) {
Expand Down Expand Up @@ -164,7 +165,7 @@ ImportResourceTemplate.propTypes = {

const mapStateToProps = (state) => {
return {
authenticationState: Object.assign({}, state.authenticate.authenticationState)
authenticationState: getAuthenticationState(state)
}
}

Expand Down
8 changes: 8 additions & 0 deletions src/selectors.js
@@ -0,0 +1,8 @@

export const getCurrentUser = (state) => (getAuthenticationState(state)?.currentUser)

export const getCurrentSession = (state) => (getAuthenticationState(state)?.currentSession)

export const getAuthenticationError = (state) => (getAuthenticationState(state)?.authenticationError)

export const getAuthenticationState = (state) => ({...state.authenticate.authenticationState})

0 comments on commit 41cc624

Please sign in to comment.