Upgrading to hooks, simplifying unit test mocks

This commit is contained in:
James Barnsley
2022-09-29 15:32:11 +13:00
parent 875072dea8
commit 97a259187b
4 changed files with 71 additions and 73 deletions

View File

@ -13,8 +13,8 @@ jest.mock('redux-persist', () => ({
persistReducer: jest.fn().mockImplementation((config, reducers) => reducers),
}));
jest.mock('react-redux', () => ({
useSelector: jest.fn(() => mockState),
useDispatch: jest.fn(fn => fn()),
useSelector: jest.fn().mockImplementation(func => func(mockState)),
useDispatch: jest.fn(),
connect: jest.fn(fn => fn()),
}));
jest.mock('react-router-dom', () => ({
@ -30,7 +30,8 @@ jest.mock('react-router-dom', () => ({
}));
describe('<Album />', () => {
const album = state.core.items['local:album:md5:66fbea3593ba96a15a9d4188bebab50b'];
const album = mockState.core.items['local:album:md5:66fbea3593ba96a15a9d4188bebab50b'];
// Need to rebuild Album to functional component, at which point I'll copy previous
// snapshot testing approach from other project

View File

@ -0,0 +1,36 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<Album /> should render accurately 1`] = `
<div
className="error-message error-message--not-found"
data-qa-file="ErrorMessage"
data-qa-node="div"
>
<i
className="error-message__icon icon icon--material"
data-qa-file="ErrorMessage"
data-qa-node="i"
>
error
</i>
<h4
className="error-message__title"
data-qa-file="ErrorMessage"
data-qa-node="h4"
>
Not found
</h4>
<div
className="error-message__content"
data-qa-file="ErrorMessage"
data-qa-node="div"
>
<p
data-qa-file="Album"
data-qa-node="p"
>
Could not find anything with URI
</p>
</div>
</div>
`;

View File

@ -1,33 +1,29 @@
import React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as uiActions from '../../services/ui/actions';
import * as spotifyActions from '../../services/spotify/actions';
import { getFromUri } from '../../util/helpers';
import { createNotification } from '../../services/ui/actions';
import { following } from '../../services/spotify/actions';
import { i18n } from '../../locale';
import { Button } from '../Button';
import { makeLoadingSelector } from '../../util/selectors';
import { useSelector } from 'react-redux';
const loadingSelector = makeLoadingSelector(['(.*)(follow)|(me\/albums)(.*)']);
const FollowButton = ({
spotifyActions: {
following,
},
uiActions: {
createNotification,
},
uri,
addText,
removeText,
spotify_authorized,
is_following,
loading,
}) => {
const remove = () => following(uri, 'DELETE');
const add = () => following(uri, 'PUT');
const unauthorized = () => createNotification({
content: i18n('errors.authorization_required', { provider: i18n('services.spotify.title') }),
level: 'warning',
});
const loading = useSelector(loadingSelector);
const spotify_authorized = useSelector((state) => state.spotify.authorization);
const remove = () => dispatch(following(uri, 'DELETE'));
const add = () => dispatch(following(uri, 'PUT'));
const unauthorized = () => dispatch(
createNotification({
content: i18n('errors.authorization_required', { provider: i18n('services.spotify.title') }),
level: 'warning',
}),
);
if (!uri) return null;
@ -65,18 +61,4 @@ const FollowButton = ({
);
}
const mapStateToProps = (state) => {
const loadingSelector = makeLoadingSelector(['(.*)(follow)|(me\/albums)(.*)']);
return {
loading: loadingSelector(state),
spotify_authorized: state.spotify.authorization,
};
};
const mapDispatchToProps = (dispatch) => ({
uiActions: bindActionCreators(uiActions, dispatch),
spotifyActions: bindActionCreators(spotifyActions, dispatch),
});
export default connect(mapStateToProps, mapDispatchToProps)(FollowButton);
export default FollowButton;

View File

@ -1,33 +1,26 @@
import React, { useState, useEffect } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { range, uniqBy } from 'lodash';
import Track from './Track';
import * as mopidyActions from '../services/mopidy/actions';
import * as uiActions from '../services/ui/actions';
import { playURIs, changeTrack } from '../services/mopidy/actions';
import { createNotification, showContextMenu } from '../services/ui/actions';
import { isTouchDevice } from '../util/helpers';
import { arrayOf } from '../util/arrays';
import { SmartList } from './SmartList';
import { useSelector } from 'react-redux';
import { useDispatch } from 'react-redux';
const TrackList = ({
context,
className = '',
show_source_icon,
play_state,
slim_mode,
tracks,
playTracks,
removeTracks,
reorderTracks,
uiActions: {
createNotification,
showContextMenu,
},
mopidyActions: {
playURIs,
changeTrack,
},
}) => {
const dispatch = useDispatch();
const play_state = useSelector((state) => state.mopidy.play_state);
const { slim_mode } = useSelector((state) => state.ui);
const [selected, setSelected] = useState([]);
useEffect(() => {
@ -100,9 +93,9 @@ const TrackList = ({
onDoubleClick: (item, index) => {
setSelected([{ item, index }]);
if (context?.type === 'queue') {
changeTrack(item.tlid);
dispatch(changeTrack(item.tlid));
} else {
playURIs({ uris: [item.uri], from: context });
dispatch(playURIs({ uris: [item.uri], from: context }));
}
},
onContextMenu: (item, index, e) => {
@ -114,14 +107,14 @@ const TrackList = ({
({ index, item }) => ({ index, ...item }),
);
showContextMenu({
dispatch(showContextMenu({
e,
context,
...(items.length === 1
? { type: 'track', item: items[0] }
: { type: 'tracks', items }
),
});
}));
},
};
@ -132,7 +125,7 @@ const TrackList = ({
if (playTracks) {
playTracks(selectedTracks);
} else {
playURIs({ uris: arrayOf('uri', selectedTracks), from: context });
dispatch(playURIs({ uris: arrayOf('uri', selectedTracks), from: context }));
}
};
@ -140,10 +133,10 @@ const TrackList = ({
if (!selected || !selected.length) return;
if (!removeTracks) {
createNotification({
dispatch(createNotification({
content: `Cannot delete ${selected.length > 1 ? 'these tracks' : 'this track'}`,
level: 'error',
});
}));
return;
}
removeTracks(selected.map(({ index }) => index));
@ -233,18 +226,4 @@ const TrackList = ({
);
};
const mapStateToProps = (state) => ({
play_state: state.mopidy.play_state,
slim_mode: state.ui.slim_mode,
selected_tracks: state.ui.selected_tracks,
current_track: state.core.current_track,
context_menu: state.ui.context_menu,
stream_title: state.core.stream_title,
});
const mapDispatchToProps = (dispatch) => ({
mopidyActions: bindActionCreators(mopidyActions, dispatch),
uiActions: bindActionCreators(uiActions, dispatch),
});
export default connect(mapStateToProps, mapDispatchToProps)(TrackList);
export default TrackList;