Unverified Commit 2ac2dc30 authored by Daniel's avatar Daniel Committed by GitHub

Update Dependencies based on Security Issues (#354)

* Update dependencies to resolve security issues
* Fixes `getDerivedStateFromProps` usage
* Refactor TagsList behavior, fix tests
parent dbb33e1d
...@@ -34,7 +34,6 @@ module.exports = { ...@@ -34,7 +34,6 @@ module.exports = {
transform: { transform: {
'^.+\\.tsx?$': 'ts-jest', '^.+\\.tsx?$': 'ts-jest',
'^.+\\.js$': 'babel-jest', '^.+\\.js$': 'babel-jest',
'^.+\\.(css|scss)$': '<rootDir>/node_modules/jest-css-modules',
}, },
testRegex: '(/tests/.*|(\\.|/)(test|spec))\\.(j|t)sx?$', testRegex: '(/tests/.*|(\\.|/)(test|spec))\\.(j|t)sx?$',
moduleDirectories: ['node_modules', 'js'], moduleDirectories: ['node_modules', 'js'],
...@@ -45,6 +44,9 @@ module.exports = { ...@@ -45,6 +44,9 @@ module.exports = {
'jsx', 'jsx',
'json', 'json',
], ],
moduleNameMapper: {
'^.+\\.(css|scss)$': '<rootDir>/node_modules/jest-css-modules',
},
globals: { globals: {
'ts-jest': { 'ts-jest': {
diagnostics: false, diagnostics: false,
......
...@@ -2,7 +2,7 @@ import * as React from 'react'; ...@@ -2,7 +2,7 @@ import * as React from 'react';
import * as DocumentTitle from 'react-document-title'; import * as DocumentTitle from 'react-document-title';
import SanitizedHTML from 'react-sanitized-html'; import SanitizedHTML from 'react-sanitized-html';
import { mount, shallow } from 'enzyme'; import { shallow } from 'enzyme';
import { AnnouncementPage, AnnouncementPageProps, mapDispatchToProps, mapStateToProps } from '../'; import { AnnouncementPage, AnnouncementPageProps, mapDispatchToProps, mapStateToProps } from '../';
...@@ -26,7 +26,7 @@ describe('AnnouncementPage', () => { ...@@ -26,7 +26,7 @@ describe('AnnouncementPage', () => {
html_content: '<div>Just kidding</div>', html_content: '<div>Just kidding</div>',
}], }],
}; };
subject = mount(<AnnouncementPage {...props} />); subject = shallow(<AnnouncementPage {...props} />);
}); });
describe('componentDidMount', () => { describe('componentDidMount', () => {
......
...@@ -25,7 +25,6 @@ export interface DispatchFromProps { ...@@ -25,7 +25,6 @@ export interface DispatchFromProps {
export interface ComponentProps { export interface ComponentProps {
errorText?: string | null; errorText?: string | null;
readOnly: boolean;
} }
interface OwnerAvatarLabelProps extends AvatarLabelProps { interface OwnerAvatarLabelProps extends AvatarLabelProps {
...@@ -42,7 +41,6 @@ type OwnerEditorProps = ComponentProps & DispatchFromProps & StateFromProps & Ed ...@@ -42,7 +41,6 @@ type OwnerEditorProps = ComponentProps & DispatchFromProps & StateFromProps & Ed
interface OwnerEditorState { interface OwnerEditorState {
errorText: string | null; errorText: string | null;
isLoading: boolean;
itemProps: { [id: string]: OwnerAvatarLabelProps }; itemProps: { [id: string]: OwnerAvatarLabelProps };
readOnly: boolean; readOnly: boolean;
tempItemProps: { [id: string]: AvatarLabelProps }; tempItemProps: { [id: string]: AvatarLabelProps };
...@@ -56,20 +54,13 @@ export class OwnerEditor extends React.Component<OwnerEditorProps, OwnerEditorSt ...@@ -56,20 +54,13 @@ export class OwnerEditor extends React.Component<OwnerEditorProps, OwnerEditorSt
isLoading: false, isLoading: false,
itemProps: {}, itemProps: {},
onUpdateList: () => undefined, onUpdateList: () => undefined,
readOnly: true,
}; };
static getDerivedStateFromProps(nextProps) {
const { isLoading, itemProps, readOnly } = nextProps;
return { isLoading, itemProps, readOnly, tempItemProps: itemProps };
}
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = {
errorText: props.errorText, errorText: props.errorText,
isLoading: props.isLoading,
itemProps: props.itemProps, itemProps: props.itemProps,
readOnly: props.readOnly, readOnly: props.readOnly,
tempItemProps: props.itemProps, tempItemProps: props.itemProps,
...@@ -78,6 +69,13 @@ export class OwnerEditor extends React.Component<OwnerEditorProps, OwnerEditorSt ...@@ -78,6 +69,13 @@ export class OwnerEditor extends React.Component<OwnerEditorProps, OwnerEditorSt
this.inputRef = React.createRef(); this.inputRef = React.createRef();
} }
componentDidUpdate(prevProps) {
// TODO - itemProps is a new object and this check needs to be fixed
if (prevProps.itemProps !== this.props.itemProps) {
this.setState({ itemProps: this.props.itemProps, tempItemProps: this.props.itemProps });
}
}
handleShow = () => { handleShow = () => {
this.props.setEditMode(true) this.props.setEditMode(true)
}; };
...@@ -140,7 +138,7 @@ export class OwnerEditor extends React.Component<OwnerEditorProps, OwnerEditorSt ...@@ -140,7 +138,7 @@ export class OwnerEditor extends React.Component<OwnerEditorProps, OwnerEditorSt
return null; return null;
} }
if (this.state.isLoading) { if (this.props.isLoading) {
return ( return (
<Modal.Body> <Modal.Body>
<LoadingSpinner/> <LoadingSpinner/>
......
...@@ -177,7 +177,7 @@ class TableDetail extends React.Component<TableDetailProps & RouteComponentProps ...@@ -177,7 +177,7 @@ class TableDetail extends React.Component<TableDetailProps & RouteComponentProps
</EditableSection> </EditableSection>
<EditableSection title="Owners"> <EditableSection title="Owners">
<OwnerEditor readOnly={false}/> <OwnerEditor />
</EditableSection> </EditableSection>
</section> </section>
......
...@@ -69,22 +69,23 @@ export class SearchBar extends React.Component<SearchBarProps, SearchBarState> { ...@@ -69,22 +69,23 @@ export class SearchBar extends React.Component<SearchBarProps, SearchBarState> {
}; };
} }
static getDerivedStateFromProps(props, state) {
const { searchTerm } = props;
return { searchTerm };
}
clearSearchTerm = () : void => { clearSearchTerm = () : void => {
this.setState({ showTypeAhead: false, searchTerm: '' }); this.setState({ showTypeAhead: false, searchTerm: '' });
}; };
componentDidMount = () => { componentDidMount = () => {
document.addEventListener('mousedown', this.updateTypeAhead, false); document.addEventListener('mousedown', this.updateTypeAhead, false);
} };
componentWillUnmount = () => { componentWillUnmount = () => {
document.removeEventListener('mousedown', this.updateTypeAhead, false); document.removeEventListener('mousedown', this.updateTypeAhead, false);
} };
componentDidUpdate = (prevProps: SearchBarProps) => {
if (this.props.searchTerm !== prevProps.searchTerm) {
this.setState({ searchTerm: this.props.searchTerm });
}
};
handleValueChange = (event: React.SyntheticEvent<HTMLInputElement>) : void => { handleValueChange = (event: React.SyntheticEvent<HTMLInputElement>) : void => {
const searchTerm = (event.target as HTMLInputElement).value.toLowerCase(); const searchTerm = (event.target as HTMLInputElement).value.toLowerCase();
...@@ -107,7 +108,7 @@ export class SearchBar extends React.Component<SearchBarProps, SearchBarState> { ...@@ -107,7 +108,7 @@ export class SearchBar extends React.Component<SearchBarProps, SearchBarState> {
hideTypeAhead = () : void => { hideTypeAhead = () : void => {
this.setState({ showTypeAhead: false }); this.setState({ showTypeAhead: false });
} };
isFormValid = (searchTerm: string) : boolean => { isFormValid = (searchTerm: string) : boolean => {
if (searchTerm.length === 0) { if (searchTerm.length === 0) {
......
...@@ -50,20 +50,6 @@ describe('SearchBar', () => { ...@@ -50,20 +50,6 @@ describe('SearchBar', () => {
}); });
}); });
describe('getDerivedStateFromProps', () => {
it('sets searchTerm on state from props', () => {
const { props, wrapper } = setup();
const prevState = wrapper.state();
props.searchTerm = 'newTerm';
// @ts-ignore: Why does this work in other tests but complain here
wrapper.setProps(props);
expect(wrapper.state()).toMatchObject({
...prevState,
searchTerm: 'newTerm',
});
});
});
describe('clearSearchTerm', () => { describe('clearSearchTerm', () => {
it('sets the searchTerm to an empty string', () => { it('sets the searchTerm to an empty string', () => {
setStateSpy.mockClear(); setStateSpy.mockClear();
...@@ -90,6 +76,20 @@ describe('SearchBar', () => { ...@@ -90,6 +76,20 @@ describe('SearchBar', () => {
}); });
}); });
describe('componentDidUpdate', () => {
it('sets the searchTerm state when props update', () => {
const { props, wrapper } = setup();
const prevState = wrapper.state();
props.searchTerm = 'newTerm';
// @ts-ignore: Why does this work in other tests but complain here
wrapper.setProps(props);
expect(wrapper.state()).toMatchObject({
...prevState,
searchTerm: 'newTerm',
});
});
});
describe('handleValueChange', () => { describe('handleValueChange', () => {
let shouldShowTypeAheadSpy; let shouldShowTypeAheadSpy;
......
...@@ -4,7 +4,6 @@ import { bindActionCreators } from 'redux'; ...@@ -4,7 +4,6 @@ import { bindActionCreators } from 'redux';
import './styles.scss'; import './styles.scss';
import AppConfig from 'config/config';
import LoadingSpinner from 'components/common/LoadingSpinner'; import LoadingSpinner from 'components/common/LoadingSpinner';
import TagInfo from 'components/Tags/TagInfo'; import TagInfo from 'components/Tags/TagInfo';
import { Tag } from 'interfaces'; import { Tag } from 'interfaces';
...@@ -12,9 +11,11 @@ import { Tag } from 'interfaces'; ...@@ -12,9 +11,11 @@ import { Tag } from 'interfaces';
import { GlobalState } from 'ducks/rootReducer'; import { GlobalState } from 'ducks/rootReducer';
import { getAllTags } from 'ducks/allTags/reducer'; import { getAllTags } from 'ducks/allTags/reducer';
import { GetAllTagsRequest } from 'ducks/allTags/types'; import { GetAllTagsRequest } from 'ducks/allTags/types';
import { getCuratedTags, showAllTags } from 'config/config-utils';
export interface StateFromProps { export interface StateFromProps {
allTags: Tag[]; curatedTags: Tag[];
otherTags: Tag[];
isLoading: boolean; isLoading: boolean;
} }
...@@ -22,40 +23,11 @@ export interface DispatchFromProps { ...@@ -22,40 +23,11 @@ export interface DispatchFromProps {
getAllTags: () => GetAllTagsRequest; getAllTags: () => GetAllTagsRequest;
} }
interface TagsListState {
curatedTags: Tag[];
otherTags: Tag[];
}
export type TagsListProps = StateFromProps & DispatchFromProps; export type TagsListProps = StateFromProps & DispatchFromProps;
export class TagsList extends React.Component<TagsListProps, TagsListState> { export class TagsList extends React.Component<TagsListProps> {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = {
curatedTags: [],
otherTags: [],
};
}
static getDerivedStateFromProps(nextProps, prevState) {
const { allTags, isLoading } = nextProps;
if (isLoading) {
return {
...prevState,
isLoading,
};
}
const curatedTagsList = AppConfig.browse.curatedTags;
const curatedTags = allTags.filter((tag) => curatedTagsList.indexOf(tag.tag_name) !== -1);
let otherTags = [];
if (AppConfig.browse.showAllTags) {
otherTags = allTags.filter((tag) => curatedTagsList.indexOf(tag.tag_name) === -1);
}
return { curatedTags, otherTags, isLoading };
} }
componentDidMount() { componentDidMount() {
...@@ -68,28 +40,34 @@ export class TagsList extends React.Component<TagsListProps, TagsListState> { ...@@ -68,28 +40,34 @@ export class TagsList extends React.Component<TagsListProps, TagsListState> {
} }
render() { render() {
let innerContent;
if (this.props.isLoading) { if (this.props.isLoading) {
innerContent = <LoadingSpinner/>; return <LoadingSpinner/>;
} else {
innerContent = (
<div id="browse-body" className="browse-body">
{this.generateTagInfo(this.state.curatedTags)}
{
this.state.curatedTags.length > 0 && this.state.otherTags.length > 0 &&
<hr />
}
{this.generateTagInfo(this.state.otherTags)}
</div>
);
} }
return (innerContent); return (
<div id="tags-list" className="tags-list">
{ this.generateTagInfo(this.props.curatedTags) }
{
showAllTags() && this.props.curatedTags.length > 0 && this.props.otherTags.length > 0 &&
<hr />
}
{
showAllTags() && this.props.otherTags.length > 0 &&
this.generateTagInfo(this.props.otherTags)
}
</div>
);
} }
} }
export const mapStateToProps = (state: GlobalState) => { export const mapStateToProps = (state: GlobalState) => {
const curatedTagsList = getCuratedTags();
const allTags = state.allTags.allTags;
const curatedTags = allTags.filter((tag) => curatedTagsList.indexOf(tag.tag_name) !== -1);
const otherTags = allTags.filter((tag) => curatedTagsList.indexOf(tag.tag_name) === -1);
return { return {
allTags: state.allTags.allTags, curatedTags,
otherTags,
isLoading: state.allTags.isLoading, isLoading: state.allTags.isLoading,
}; };
}; };
......
...@@ -4,6 +4,6 @@ hr.header-hr { ...@@ -4,6 +4,6 @@ hr.header-hr {
border: 2px solid $brand-color-4; border: 2px solid $brand-color-4;
} }
.browse-body { .tags-list {
margin: 0 -4px; margin: 0 -4px;
} }
...@@ -10,17 +10,27 @@ import globalState from 'fixtures/globalState'; ...@@ -10,17 +10,27 @@ import globalState from 'fixtures/globalState';
import AppConfig from 'config/config'; import AppConfig from 'config/config';
AppConfig.browse.curatedTags = ['test1']; AppConfig.browse.curatedTags = ['test1'];
jest.mock('config/config-utils', () => (
{
showAllTags: jest.fn(),
getCuratedTags: () => { return ['curated_tag_1']; },
}
));
import { getCuratedTags, showAllTags } from 'config/config-utils';
describe('TagsList', () => { describe('TagsList', () => {
let props: TagsListProps;
let subject;
beforeEach(() => { const setup = (propOverrides?: Partial<TagsListProps>) => {
props = { const props: TagsListProps = {
allTags: [ curatedTags: [
{ {
tag_count: 2, tag_count: 2,
tag_name: 'test1', tag_name: 'test1',
}, },
],
otherTags: [
{ {
tag_count: 1, tag_count: 1,
tag_name: 'test2', tag_name: 'test2',
...@@ -28,78 +38,58 @@ describe('TagsList', () => { ...@@ -28,78 +38,58 @@ describe('TagsList', () => {
], ],
isLoading: false, isLoading: false,
getAllTags: jest.fn(), getAllTags: jest.fn(),
...propOverrides,
}; };
subject = shallow(<TagsList {...props} />);
});
describe('getDerivedStateFromProps', () => {
it('returns correct state if props.isLoading', () => {
const prevState = subject.state();
props.isLoading = true;
subject.setProps(props);
expect(subject.state()).toMatchObject({
...prevState,
isLoading: true,
});
});
it('returns correct state if !props.isLoading', () => { const wrapper = shallow(<TagsList {...props} />);
props.isLoading = false; return { props, wrapper };
subject.setProps(props); };
expect(subject.state()).toMatchObject({
curatedTags: [{ tag_count: 2, tag_name: 'test1'}],
otherTags: [{ tag_count: 1, tag_name: 'test2'}],
isLoading: false,
});
});
it('returns correct state if !props.isLoading and !AppConfig.browse.showAllTags', () => {
AppConfig.browse.showAllTags = false;
subject = shallow(<TagsList {...props} />);
expect(subject.state()).toMatchObject({
curatedTags: [{tag_count: 2, tag_name: 'test1'}],
otherTags: [],
isLoading: false,
});
AppConfig.browse.showAllTags = true; // reset so other tests aren't affected
});
});
describe('componentDidMount', () => { describe('componentDidMount', () => {
it('calls props.getAllTags', () => { it('calls props.getAllTags', () => {
expect(props.getAllTags).toHaveBeenCalled(); const { props, wrapper } = setup();
}); expect(props.getAllTags).toHaveBeenCalled();
});
}); });
describe('render', () => { describe('render', () => {
let spy; it('renders LoadingSpinner if props.isLoading is true', () => {
beforeEach(() => { const { props, wrapper } = setup({ isLoading: true });
spy = jest.spyOn(TagsList.prototype, 'generateTagInfo'); expect(wrapper.find(LoadingSpinner).exists()).toBe(true);
}); });
it('renders LoadingSpinner if state.isLoading', () => {
/* Note: For some reason setState is not updating the component in this case */ it('renders <hr> if curatedTags.length > 0 & otherTags.length > 0 & showAllTags == true', () => {
props.isLoading = true; // @ts-ignore
subject.setProps(props); showAllTags.mockImplementation(() => true);
expect(subject.find(LoadingSpinner).exists()).toBeTruthy(); const { props, wrapper } = setup();
expect(wrapper.find('hr').exists()).toBe(true);
}); });
it('renders <hr> in if curatedTags.length > 0 & otherTags.length > 0 ', () => { it('does not render <hr> if showAllTags is false', () => {
expect(subject.find('#browse-body').find('hr').exists()).toBeTruthy(); // @ts-ignore
showAllTags.mockImplementation(() => false);
const { props, wrapper } = setup();
expect(wrapper.find('hr').exists()).toBe(false);
}); });
it('does not render <hr> if !(curatedTags.length > 0 & otherTags.length > 0) ', () => { it('does not render an <hr> if otherTags is empty', () => {
AppConfig.browse.curatedTags = ['test1', 'test2']; // @ts-ignore
subject = shallow(<TagsList {...props} />); showAllTags.mockImplementation(() => true);
expect(subject.find('#browse-body').find('hr').exists()).toBeFalsy(); const { props, wrapper } = setup();
AppConfig.browse.curatedTags = ['test1']; // reset so other tests aren't affected
expect(wrapper.find('#tags-list').find('hr').exists()).toBe(true);
}); });
it('calls generateTagInfo with curatedTags', () => { it('calls generateTagInfo with curatedTags', () => {
expect(spy).toHaveBeenCalledWith(subject.state().curatedTags); const generateTagInfoSpy = jest.spyOn(TagsList.prototype, 'generateTagInfo');
const { props, wrapper } = setup();
expect(generateTagInfoSpy).toHaveBeenCalledWith(props.curatedTags)
}); });
it('call generateTagInfo with otherTags', () => { it('call generateTagInfo with otherTags', () => {
expect(spy).toHaveBeenCalledWith(subject.state().otherTags); const generateTagInfoSpy = jest.spyOn(TagsList.prototype, 'generateTagInfo');
const { props, wrapper } = setup();
expect(generateTagInfoSpy).toHaveBeenCalledWith(props.otherTags)
}); });
}); });
}); });
...@@ -120,12 +110,22 @@ describe('mapDispatchToProps', () => { ...@@ -120,12 +110,22 @@ describe('mapDispatchToProps', () => {
describe('mapStateToProps', () => { describe('mapStateToProps', () => {
let result; let result;
let expectedCuratedTags;
let expectedOtherTags;
beforeEach(() => { beforeEach(() => {
result = mapStateToProps(globalState); result = mapStateToProps(globalState);
const allTags = globalState.allTags.allTags;
const curatedTagsList = getCuratedTags();
expectedCuratedTags = allTags.filter((tag) => curatedTagsList.indexOf(tag.tag_name) !== -1);
expectedOtherTags = allTags.filter((tag) => curatedTagsList.indexOf(tag.tag_name) === -1);
});
it('sets curatedTags on the props', () => {
expect(result.curatedTags).toEqual(expectedCuratedTags);
}); });
it('sets allTags on the props', () => { it('sets otherTags on the props', () => {
expect(result.allTags).toEqual(globalState.allTags.allTags); expect(result.otherTags).toEqual(expectedOtherTags);
}); });
it('sets isLoading on the props', () => { it('sets isLoading on the props', () => {
......
...@@ -67,3 +67,17 @@ export function indexUsersEnabled(): boolean { ...@@ -67,3 +67,17 @@ export function indexUsersEnabled(): boolean {
export function notificationsEnabled(): boolean { export function notificationsEnabled(): boolean {
return AppConfig.mailClientFeatures.notificationsEnabled; return AppConfig.mailClientFeatures.notificationsEnabled;
} }
/**
* Returns whether or not to show all tags
*/
export function showAllTags(): boolean {
return AppConfig.browse.showAllTags;
}
/**
* Returns a list of curated tag names
*/
export function getCuratedTags(): string[] {
return AppConfig.browse.curatedTags;
}
...@@ -72,3 +72,19 @@ describe('notificationsEnabled', () => { ...@@ -72,3 +72,19 @@ describe('notificationsEnabled', () => {
expect(ConfigUtils.notificationsEnabled()).toBe(AppConfig.mailClientFeatures.notificationsEnabled); expect(ConfigUtils.notificationsEnabled()).toBe(AppConfig.mailClientFeatures.notificationsEnabled);
}); });
}); });
describe('showAllTags', () => {
it('returns whether or not to show all tags', () => {
AppConfig.browse.showAllTags = true;
expect(ConfigUtils.showAllTags()).toBe(AppConfig.browse.showAllTags);
AppConfig.browse.showAllTags = false;
expect(ConfigUtils.showAllTags()).toBe(AppConfig.browse.showAllTags);
});
});
describe('getCuratedTags', () => {
it('returns a list of curated tags', () => {
AppConfig.browse.curatedTags = ['one', 'two', 'three'];
expect(ConfigUtils.getCuratedTags()).toBe(AppConfig.browse.curatedTags);
});
});
...@@ -7,10 +7,10 @@ export function getAllTags(): GetAllTagsRequest { ...@@ -7,10 +7,10 @@ export function getAllTags(): GetAllTagsRequest {
return { type: GetAllTags.REQUEST }; return { type: GetAllTags.REQUEST };
}; };
export function getAllTagsFailure(): GetAllTagsResponse { export function getAllTagsFailure(): GetAllTagsResponse {
return { type: GetAllTags.FAILURE, payload: { tags: [] } }; return { type: GetAllTags.FAILURE, payload: { allTags: [] } };
}; };
export function getAllTagsSuccess(tags: Tag[]): GetAllTagsResponse { export function getAllTagsSuccess(allTags: Tag[]): GetAllTagsResponse {
return { type: GetAllTags.SUCCESS, payload: { tags } }; return { type: GetAllTags.SUCCESS, payload: { allTags } };
}; };
/* REDUCER */ /* REDUCER */
...@@ -31,7 +31,11 @@ export default function reducer(state: AllTagsReducerState = initialState, actio ...@@ -31,7 +31,11 @@ export default function reducer(state: AllTagsReducerState = initialState, actio
case GetAllTags.FAILURE: case GetAllTags.FAILURE:
return initialState; return initialState;
case GetAllTags.SUCCESS: case GetAllTags.SUCCESS:
return { ...state, allTags: (<GetAllTagsResponse>action).payload.tags, isLoading: false }; return {
...state,
allTags: (<GetAllTagsResponse>action).payload.allTags,
isLoading: false,
};
default: default:
return state; return state;
} }
......
...@@ -7,8 +7,9 @@ import { GetAllTags } from './types'; ...@@ -7,8 +7,9 @@ import { GetAllTags } from './types';
export function* getAllTagsWorker(): SagaIterator { export function* getAllTagsWorker(): SagaIterator {
try { try {
const tags = yield call(API.getAllTags); const allTags = yield call(API.getAllTags);
yield put(getAllTagsSuccess(tags));
yield put(getAllTagsSuccess(allTags));
} catch (e) { } catch (e) {
yield put(getAllTagsFailure()); yield put(getAllTagsFailure());
} }
......
...@@ -21,7 +21,7 @@ describe('allTags ducks', () => { ...@@ -21,7 +21,7 @@ describe('allTags ducks', () => {
const action = getAllTagsFailure(); const action = getAllTagsFailure();
const { payload } = action; const { payload } = action;
expect(action.type).toBe(GetAllTags.FAILURE); expect(action.type).toBe(GetAllTags.FAILURE);
expect(payload.tags).toEqual([]); expect(payload.allTags).toEqual([]);
}); });
it('getAllTagsSuccess - returns the action to process success', () => { it('getAllTagsSuccess - returns the action to process success', () => {
...@@ -29,7 +29,7 @@ describe('allTags ducks', () => { ...@@ -29,7 +29,7 @@ describe('allTags ducks', () => {
const action = getAllTagsSuccess(expectedTags); const action = getAllTagsSuccess(expectedTags);
const { payload } = action; const { payload } = action;
expect(action.type).toBe(GetAllTags.SUCCESS); expect(action.type).toBe(GetAllTags.SUCCESS);
expect(payload.tags).toBe(expectedTags); expect(payload.allTags).toBe(expectedTags);
}); });
}); });
......
...@@ -11,6 +11,6 @@ export interface GetAllTagsRequest { ...@@ -11,6 +11,6 @@ export interface GetAllTagsRequest {
export interface GetAllTagsResponse { export interface GetAllTagsResponse {
type: GetAllTags.SUCCESS | GetAllTags.FAILURE; type: GetAllTags.SUCCESS | GetAllTags.FAILURE;
payload: { payload: {
tags: Tag[]; allTags: Tag[];
}; };
} }
import { GlobalState } from 'ducks/rootReducer'; import { GlobalState } from 'ducks/rootReducer';
import { RequestMetadataType, ResourceType, SendingState } from 'interfaces'; import { ResourceType, SendingState } from 'interfaces';
const globalState: GlobalState = { const globalState: GlobalState = {
announcements: { announcements: {
...@@ -135,7 +135,16 @@ const globalState: GlobalState = { ...@@ -135,7 +135,16 @@ const globalState: GlobalState = {
}, },
}, },
allTags: { allTags: {
allTags: [], allTags: [
{
tag_name: 'curated_tag_1',
tag_count: 20,
},
{
tag_name: 'other_tag_1',
tag_count: 15,
}
],
isLoading: false, isLoading: false,
}, },
user: { user: {
......
...@@ -25,9 +25,9 @@ ...@@ -25,9 +25,9 @@
"author": "", "author": "",
"license": "ISC", "license": "ISC",
"devDependencies": { "devDependencies": {
"@types/enzyme": "^3.1.14", "@types/enzyme": "^3.10.3",
"@types/jasmine-matchers": "^0.2.31", "@types/jasmine-matchers": "^0.2.31",
"@types/jest": "^23.3.9", "@types/jest": "^24.0.23",
"@types/node": "^10.12.10", "@types/node": "^10.12.10",
"@types/react-redux": "^6.0.0", "@types/react-redux": "^6.0.0",
"@types/react-router": "^4.0.25", "@types/react-router": "^4.0.25",
...@@ -35,19 +35,19 @@ ...@@ -35,19 +35,19 @@
"@types/webpack": "^4.4.19", "@types/webpack": "^4.4.19",
"babel-cli": "^6.26.0", "babel-cli": "^6.26.0",
"babel-core": "^6.26.0", "babel-core": "^6.26.0",
"babel-jest": "^23.6.0", "babel-jest": "^24.9.0",
"babel-loader": "^7.1.4", "babel-loader": "^7.1.4",
"babel-polyfill": "^6.0.16", "babel-polyfill": "^6.0.16",
"babel-preset-env": "^1.7.0", "babel-preset-env": "^1.7.0",
"babel-preset-es2015": "^6.24.1", "babel-preset-es2015": "^6.24.1",
"babel-preset-react": "^6.24.1", "babel-preset-react": "^6.24.1",
"babel-preset-stage-0": "^6.0.15", "babel-preset-stage-0": "^6.0.15",
"bootstrap-sass": "^3.3.7", "bootstrap-sass": "^3.4.1",
"clean-webpack-plugin": "^0.1.19", "clean-webpack-plugin": "^0.1.19",
"cross-env": "^5.2.1", "cross-env": "^5.2.1",
"css-loader": "^0.28.11", "css-loader": "^3.2.0",
"enzyme": "^3.7.0", "enzyme": "^3.10.0",
"enzyme-adapter-react-16": "^1.6.0", "enzyme-adapter-react-16": "^1.15.1",
"enzyme-to-json": "^3.3.4", "enzyme-to-json": "^3.3.4",
"eslint": "^4.9.0", "eslint": "^4.9.0",
"eslint-config-airbnb": "^16.1.0", "eslint-config-airbnb": "^16.1.0",
...@@ -55,8 +55,8 @@ ...@@ -55,8 +55,8 @@
"eslint-plugin-jsx-a11y": "^6.0.2", "eslint-plugin-jsx-a11y": "^6.0.2",
"eslint-plugin-react": "^7.4.0", "eslint-plugin-react": "^7.4.0",
"html-webpack-plugin": "^3.2.0", "html-webpack-plugin": "^3.2.0",
"jest": "^24.0.0", "jest": "^24.9.0",
"jest-css-modules": "^1.1.0", "jest-css-modules": "^2.1.0",
"mini-css-extract-plugin": "^0.4.5", "mini-css-extract-plugin": "^0.4.5",
"node-sass": "^4.10.0", "node-sass": "^4.10.0",
"postcss": "^7.0.6", "postcss": "^7.0.6",
...@@ -67,18 +67,18 @@ ...@@ -67,18 +67,18 @@
"sass-loader": "^7.1.0", "sass-loader": "^7.1.0",
"style-loader": "^0.20.3", "style-loader": "^0.20.3",
"terser-webpack-plugin": "^1.1.0", "terser-webpack-plugin": "^1.1.0",
"ts-jest": "^24.0.0", "ts-jest": "^24.1.0",
"ts-loader": "4.0.0", "ts-loader": "^6.2.1",
"ts-node": "^7.0.1", "ts-node": "^8.5.2",
"tsconfig-paths": "^3.7.0", "tsconfig-paths": "^3.9.0",
"tslint": "^5.10.0", "tslint": "^5.20.1",
"tslint-config-airbnb": "^5.8.0", "tslint-config-airbnb": "^5.8.0",
"tslint-config-prettier": "^1.12.0", "tslint-config-prettier": "^1.12.0",
"tslint-eslint-rules": "^5.2.0", "tslint-eslint-rules": "^5.2.0",
"tslint-react": "^3.6.0", "tslint-react": "^3.6.0",
"typescript": "^3.1.1", "typescript": "^3.1.1",
"uglifyjs-webpack-plugin": "^1.1.0", "uglifyjs-webpack-plugin": "^1.1.0",
"webpack": "^4.19.0", "webpack": "^4.41.3",
"webpack-cli": "^3.1.1", "webpack-cli": "^3.1.1",
"webpack-merge": "^4.1.4", "webpack-merge": "^4.1.4",
"webpack-sources": "^1.1.0", "webpack-sources": "^1.1.0",
...@@ -88,13 +88,13 @@ ...@@ -88,13 +88,13 @@
"autosize": "^4.0.2", "autosize": "^4.0.2",
"axios": "0.19.0", "axios": "0.19.0",
"form-serialize": "^0.7.2", "form-serialize": "^0.7.2",
"jquery": "^3.3.1", "jquery": "^3.4.0",
"moment-timezone": "^0.5.21", "moment-timezone": "^0.5.21",
"react": "^16.3.1", "react": "^16.3.3",
"react-avatar": "^2.5.1", "react-avatar": "^2.5.1",
"react-bootstrap": "^0.32.1", "react-bootstrap": "^0.32.1",
"react-document-title": "^2.0.3", "react-document-title": "^2.0.3",
"react-dom": "^16.3.1", "react-dom": "^16.3.3",
"react-js-pagination": "^3.0.2", "react-js-pagination": "^3.0.2",
"react-linkify": "^0.2.2", "react-linkify": "^0.2.2",
"react-markdown": "^4.2.2", "react-markdown": "^4.2.2",
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment