diff --git a/src/components/Gradebook/index.jsx b/src/components/Gradebook/index.jsx index 182ca79..8551cdf 100644 --- a/src/components/Gradebook/index.jsx +++ b/src/components/Gradebook/index.jsx @@ -208,7 +208,7 @@ export default class Gradebook extends React.Component { - {PageButtons(this.props)} +

* available for learners in the Master's track only

{ - const assertPageButtonsSnapshot = function assertPageButtonsSnapshot(input) { - const pb = renderer.create(PageButtons(input)); - const tree = pb.toJSON(); - expect(tree).toMatchSnapshot(); - }; +import { PageButtons, mapStateToProps, mapDispatchToProps } from '.'; - it('prev null, next null', () => { - assertPageButtonsSnapshot(createInput(null, null)); +jest.mock('@edx/paragon', () => ({ + Button: () => 'Button', +})); +jest.mock('data/selectors', () => ({ + __esModule: true, + default: { + grades: { + nextPage: jest.fn(state => ({ nextPage: state })), + prevPage: jest.fn(state => ({ prevPage: state })), + }, + }, +})); + +jest.mock('data/thunkActions', () => ({ + __esModule: true, + default: { + grades: { + fetchPrevNextGrades: jest.fn(), + }, + }, +})); + +let props; +let el; +describe('PageButtons component', () => { + beforeEach(() => { + props = { + match: { params: { courseId: 'hogwarts-minerva-txmog101' } }, + selectedAssignmentType: 'Transmogrification Exams', + selectedCohort: { name: 'Slytherin' }, + selectedTrack: { name: 'Death Eater' }, + getPrevNextGrades: jest.fn(), + nextPage: 'NEXT PAGE', + prevPage: 'prev PAGE', + }; }); - - it('prev null, next not null', () => { - assertPageButtonsSnapshot(createInput(null, 'np')); + describe('snapshots', () => { + beforeEach(() => { + el = shallow(); + el.instance.fetchNextGrades = jest.fn().mockName('fetchNextGrades'); + el.instance.fetchPrevGrades = jest.fn().mockName('fetchPrevGrades'); + }); + test('buttons enabled with both endpoints provided', () => { + expect(el.instance().render()).toMatchSnapshot(); + }); + test('nextPage disabled if not provided', () => { + el.setProps({ nextPage: undefined }); + expect(el.instance().render()).toMatchSnapshot(); + }); + test('prevPage disabled if not provided', () => { + el.setProps({ prevPage: undefined }); + expect(el.instance().render()).toMatchSnapshot(); + }); }); - - it('prev not null, next null', () => { - assertPageButtonsSnapshot(createInput('pp', null)); + describe('behavior', () => { + beforeEach(() => { + el = shallow(); + }); + describe('getPrevGrades', () => { + it('calls props.getPrevNextGrades with props.prevPage', () => { + el.instance().getPrevGrades(); + expect(props.getPrevNextGrades).toHaveBeenCalledWith( + props.prevPage, + props.match.params.courseId, + props.selectedCohort, + props.selectedTrack, + props.selectedAssignmentType, + ); + }); + }); + describe('getNextGrades', () => { + it('calls props.getPrevNextGrades with props.nextPage', () => { + el.instance().getNextGrades(); + expect(props.getPrevNextGrades).toHaveBeenCalledWith( + props.nextPage, + props.match.params.courseId, + props.selectedCohort, + props.selectedTrack, + props.selectedAssignmentType, + ); + }); + }); }); - - it('prev not null, next not null', () => { - assertPageButtonsSnapshot(createInput('pp', 'np')); + describe('mapStateToProps', () => { + const testState = { l: 'eeeerroooooy', j: 'jjjjeeeeeeenkins' }; + let mapped; + beforeEach(() => { + mapped = mapStateToProps(testState); + }); + test('nextPage from grades.nextPage', () => { + expect(mapped.nextPage).toEqual(selectors.grades.nextPage(testState)); + }); + test('prevPage from grades.prevPage', () => { + expect(mapped.prevPage).toEqual(selectors.grades.prevPage(testState)); + }); + }); + describe('mapDispatchToProps', () => { + test('getPrevNextGrades from thunkActions.grades.fetchPrevNextGrades', () => { + expect( + mapDispatchToProps.getPrevNextGrades, + ).toEqual(thunkActions.grades.fetchPrevNextGrades); + }); }); }); diff --git a/src/components/PageButtons/__snapshots__/PageButtons.test.jsx.snap b/src/components/PageButtons/__snapshots__/PageButtons.test.jsx.snap index 9225c65..3630d0d 100644 --- a/src/components/PageButtons/__snapshots__/PageButtons.test.jsx.snap +++ b/src/components/PageButtons/__snapshots__/PageButtons.test.jsx.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`PageButtons prev not null, next not null 1`] = ` +exports[`PageButtons component snapshots buttons enabled with both endpoints provided 1`] = `
- - +
`; -exports[`PageButtons prev not null, next null 1`] = ` +exports[`PageButtons component snapshots nextPage disabled if not provided 1`] = `
- - +
`; -exports[`PageButtons prev null, next not null 1`] = ` +exports[`PageButtons component snapshots prevPage disabled if not provided 1`] = `
- - -
-`; - -exports[`PageButtons prev null, next null 1`] = ` -
- - +
`; diff --git a/src/components/PageButtons/index.jsx b/src/components/PageButtons/index.jsx index 89824af..ac8da60 100644 --- a/src/components/PageButtons/index.jsx +++ b/src/components/PageButtons/index.jsx @@ -1,75 +1,99 @@ import React from 'react'; import PropTypes from 'prop-types'; +import { connect } from 'react-redux'; + import { Button } from '@edx/paragon'; -export default function PageButtons({ - prevPage, nextPage, selectedTrack, selectedCohort, selectedAssignmentType, - getPrevNextGrades, match, -}) { - return ( -
- - -
- ); + + + + ); + } } PageButtons.defaultProps = { match: { - params: { - courseId: '', - }, + params: { courseId: '' }, }, - nextPage: '', - prevPage: '', + selectedAssignmentType: null, selectedCohort: null, selectedTrack: null, - selectedAssignmentType: null, + nextPage: '', + prevPage: '', }; PageButtons.propTypes = { - getPrevNextGrades: PropTypes.func.isRequired, match: PropTypes.shape({ params: PropTypes.shape({ courseId: PropTypes.string, }), }), + selectedAssignmentType: PropTypes.string, + selectedCohort: PropTypes.shape({ name: PropTypes.string }), + selectedTrack: PropTypes.shape({ name: PropTypes.string }), + // redux + getPrevNextGrades: PropTypes.func.isRequired, nextPage: PropTypes.string, prevPage: PropTypes.string, - selectedAssignmentType: PropTypes.string, - selectedCohort: PropTypes.shape({ - name: PropTypes.string, - }), - selectedTrack: PropTypes.shape({ - name: PropTypes.string, - }), }; + +export const mapStateToProps = (state) => ({ + nextPage: selectors.grades.nextPage(state), + prevPage: selectors.grades.prevPage(state), +}); + +export const mapDispatchToProps = { + getPrevNextGrades: thunkActions.grades.fetchPrevNextGrades, +}; + +export default connect(mapStateToProps, mapDispatchToProps)(PageButtons); diff --git a/src/data/selectors/grades.js b/src/data/selectors/grades.js index 02504fe..16d220c 100644 --- a/src/data/selectors/grades.js +++ b/src/data/selectors/grades.js @@ -205,6 +205,8 @@ const simpleSelectors = simpleSelectorFactory( 'filteredUsersCount', 'totalUsersCount', 'gradeFormat', + 'nextPage', + 'prevPage', 'showSpinner', 'gradeOverrideCurrentEarnedGradedOverride', 'gradeOverrideHistoryError', diff --git a/src/data/selectors/grades.test.js b/src/data/selectors/grades.test.js index ce0c559..8cc88bb 100644 --- a/src/data/selectors/grades.test.js +++ b/src/data/selectors/grades.test.js @@ -287,6 +287,8 @@ describe('grades selectors', () => { testSimpleSelector('filteredUsersCount'); testSimpleSelector('totalUsersCount'); testSimpleSelector('gradeFormat'); + testSimpleSelector('nextPage'); + testSimpleSelector('prevPage'); testSimpleSelector('showSpinner'); testSimpleSelector('gradeOverrideCurrentEarnedGradedOverride'); testSimpleSelector('gradeOverrideHistoryError');