Skip to content

Commit

Permalink
Merge pull request Lemoncode#157 from beatrizRG/Mymaster
Browse files Browse the repository at this point in the history
Edit student navigation
  • Loading branch information
nasdan authored Aug 30, 2017
2 parents 5ada23a + 803e5e5 commit 01f09d2
Show file tree
Hide file tree
Showing 15 changed files with 329 additions and 14 deletions.
1 change: 1 addition & 0 deletions src/common/actionEnums/admin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
export const adminActionEnums = {
GET_SUMMARY_STUDENT_REQUEST_COMPLETED: 'GET_SUMMARY_STUDENT_REQUEST_COMPLETED',
GET_SUMMARY_TRAINING_REQUEST_COMPLETED: 'GET_SUMMARY_TRAINING_REQUEST_COMPLETED',
GET_SUMMARY_STUDENT_BY_ID_REQUEST_COMPLETED: 'GET_SUMMARY_STUDENT_BY_ID_REQUEST_COMPLETED',
};
4 changes: 3 additions & 1 deletion src/common/routeEnums/admin/index.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
const defaultRoute = '/admin';
const studentRoute = `${defaultRoute}/student`;
const trainingRoute = `${defaultRoute}/training`;
const studentByIdRoute = `${studentRoute}/:id`;

export const adminRouteEnums = {
default: defaultRoute,
student: {
base: studentRoute,
list: `${studentRoute}/list`,
edit: `${studentRoute}/edit`,
edit: `${studentByIdRoute}/edit`,
},
training: {
list: `${trainingRoute}/list`,
Expand Down
2 changes: 1 addition & 1 deletion src/common/routeEnums/admin/spec/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ describe('adminRouteEnums', () => {
it('should have keys defined', () => {
expect(adminRouteEnums.default).to.be.equals('/admin');
expect(adminRouteEnums.student.list).to.be.equals('/admin/student/list');
expect(adminRouteEnums.student.edit).to.be.equals('/admin/student/edit');
expect(adminRouteEnums.student.edit).to.be.equals('/admin/student/:id/edit');
expect(adminRouteEnums.training.list).to.be.equals('/admin/training/list');
expect(adminRouteEnums.training.edit).to.be.equals('/admin/training/edit');
});
Expand Down
4 changes: 2 additions & 2 deletions src/pages/admin/routes.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as React from 'react';
import { Route } from 'react-router';
import { DashboardPage} from './dashboard/page';
import { EditStudentPage } from './student/edit/page';
import { EditStudentPageContainer } from './student/edit/pageContainer';
import { ListStudentPageContainer } from './student/list/pageContainer';
import { EditTrainingPage } from './training/edit/page';
import { ListTrainingPageContainer } from './training/list/pageContainer';
Expand All @@ -14,7 +14,7 @@ export const AdminRoutes = (
<div>
<Route path={adminRouteEnums.default} component={DashboardPage}/>
<Route path={adminRouteEnums.student.list} component={ListStudentPageContainer}/>
<Route path={adminRouteEnums.student.edit} component={EditStudentPage}/>
<Route path={adminRouteEnums.student.edit} component={EditStudentPageContainer} />
<Route path={adminRouteEnums.training.list} component={ListTrainingPageContainer}/>
<Route path={adminRouteEnums.training.edit} component={EditTrainingPage}/>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import thunk from 'redux-thunk';
import configureStore from 'redux-mock-store';

import { adminActionEnums } from '../../../../../../common/actionEnums/admin';
import { summaryStudentByIdRequestCompleted, summaryStudentByIdRequestStarted } from '../summaryStudentRequest';
import { StudentSummary } from '../../../../../../model/studentSummary';
import { studentApi } from '../../../../../../rest-api';

const mockStore = configureStore([thunk]);

describe('summaryStudentByIdRequestCompleted', () => {
it('should be a function', () => {
// Assert
expect(summaryStudentByIdRequestCompleted).to.be.a('function');
});

it('contains the expected type GET_SUMMARY_STUDENT_BY_ID_REQUEST_COMPLETED', () => {
// Arrange
const student = new StudentSummary();

// Act
const action = summaryStudentByIdRequestCompleted(student);

// Assert
expect(action.type).to.be.equals(adminActionEnums.GET_SUMMARY_STUDENT_BY_ID_REQUEST_COMPLETED);
});

it('contains the expected payload including the student summary', () => {
// Arrange
const student: StudentSummary = {
id: '2',
fullname: 'John Doe',
email: 'test@test.com',
isActive: true,
};

// Act
const actionResult = summaryStudentByIdRequestCompleted(student);

// Assert
expect(actionResult.payload.fullname).to.be.equal(student.fullname);
expect(actionResult.payload.email).to.be.equal(student.email);
expect(actionResult.payload.isActive).to.be.equal(student.isActive);
expect(actionResult.payload).eql(student);
});
});

describe('summaryStudentByIdRequestStarted', () => {
it('should return a function', () => {
// Arrange
const studentId = '1';

// Assert
expect(summaryStudentByIdRequestStarted(studentId)).to.be.a('function');
});

it('should return request action type completed', sinon.test(function(done) {
// Arrange
const sinon: sinon.SinonStatic = this;
const studentId = '1';

// Act
const store = mockStore([]);
store.dispatch(summaryStudentByIdRequestStarted(studentId)).then(() => {

// Assert
expect(store.getActions()[0].type).to.be.equal(adminActionEnums.GET_SUMMARY_STUDENT_BY_ID_REQUEST_COMPLETED);
done();
});
}));

it('should return expected student summary data', sinon.test(function(done) {
// Arrange
const sinon: sinon.SinonStatic = this;

const student: StudentSummary = {
id: '2',
fullname: 'John Doe',
email: 'test@test.com',
isActive: true,
};

const getSummaryStudentListStub = sinon.stub(studentApi, 'getStudentById');

getSummaryStudentListStub.returns({
then: (callback) => {
callback(student);
},
});

// Act
const store = mockStore([]);
store.dispatch(summaryStudentByIdRequestStarted(student.id)).then(() => {
// Assert
expect(store.getActions()[0].payload).to.be.equal(student);
expect(getSummaryStudentListStub.called).to.be.true;
done();
});
}));
});
24 changes: 24 additions & 0 deletions src/pages/admin/student/edit/actions/summaryStudentRequest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { adminActionEnums } from '../../../../../common/actionEnums/admin';
import { StudentSummary } from '../../../../../model/studentSummary';
import { studentApi } from '../../../../../rest-api';

export const summaryStudentByIdRequestStarted = (studentId: string) => {
return function(dispatcher) {
const promise = studentApi.getStudentById(studentId);

promise.then(
(data) => {
dispatcher(summaryStudentByIdRequestCompleted(data));
},
);

return promise;
};
};

export const summaryStudentByIdRequestCompleted = (student: StudentSummary) => {
return {
payload: student,
type: adminActionEnums.GET_SUMMARY_STUDENT_BY_ID_REQUEST_COMPLETED,
};
};
1 change: 1 addition & 0 deletions src/pages/admin/student/edit/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { EditStudentPageContainer } from './pageContainer';
32 changes: 24 additions & 8 deletions src/pages/admin/student/edit/page.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,33 @@
import * as React from 'react';
import {Link} from 'react-router';
import {adminRouteEnums} from '../../../../common/routeEnums/admin';
import { Link } from 'react-router';
import { adminRouteEnums } from '../../../../common/routeEnums/admin';
import { StudentSummary } from '../../../../model/studentSummary';

export class EditStudentPage extends React.Component<{}, {}> {
public render() {
interface Props {
student: StudentSummary;
getStudent: (id: string) => void;
studentId: string;
}

export class EditStudentPage extends React.Component<Props, {}> {
public componentDidMount() {
const studentId: string = this.props.studentId;
this.props.getStudent(studentId);
}

public render() {
return (
<div>
<span> Edit Student Page: </span>
<br/>
<div>
<span>Student name: {this.props.student.fullname}</span>
<br/>
<span>Email: {this.props.student.email}</span>
<br/>
<span>Is Active?: {this.props.student.isActive ? 'Yes' : 'No'}</span>
<br/>
<Link to={adminRouteEnums.student.list}>Back to student list</Link>
<Link to={adminRouteEnums.default}>Back to Dashboard</Link>
</div>

</div>
);
}
}
18 changes: 18 additions & 0 deletions src/pages/admin/student/edit/pageContainer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { connect } from 'react-redux';
import { IAppState } from '../../../../reducers';
import { EditStudentPage } from './page';
import { summaryStudentByIdRequestStarted } from './actions/summaryStudentRequest';

const mapStateToProps = (state: IAppState, ownProps) => ({
studentId: ownProps.params.id.toString(),
student: state.adminStudent.editingStudentSummary,
});

const mapDispatchToProps = (dispatch) => ({
getStudent: (studentId: string) => dispatch(summaryStudentByIdRequestStarted(studentId)),
});

export const EditStudentPageContainer = connect(
mapStateToProps,
mapDispatchToProps,
)(EditStudentPage);
111 changes: 111 additions & 0 deletions src/pages/admin/student/edit/spec/pageContainer.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import * as React from 'react';
import { mount } from 'enzyme';
import configureStore from 'redux-mock-store';
import { Provider } from 'react-redux';
import * as getStudent from '../actions/summaryStudentRequest';
import { EditStudentPageContainer } from '../pageContainer';

const createStore = configureStore();

describe('EditStudentPageContainer', () => {
it('should be defined', sinon.test(() => {
// Arrange
const sinon: sinon.SinonStatic = this;

const mockStore: any = createStore({
adminStudent: {
editingStudentSummary: {},
},
});

const getStudentStub = sinon.stub(getStudent,
'summaryStudentByIdRequestStarted', () => ({ type: 'dummy' }));

// Act
const container = mount(
<Provider store={mockStore}>
<EditStudentPageContainer params={{ id: ''}} />
</Provider>,
);

// Assert
expect(container).not.to.be.undefined;
}).bind(this));

it('should contain a studentId property equals to the conversion to string when params is not a string', sinon.test(() => {
// Arrange
const sinon: sinon.SinonStatic = this;

const mockStore: any = createStore({
adminStudent: {
editingStudentSummary: {},
},
});

const getStudentStub = sinon.stub(getStudent,
'summaryStudentByIdRequestStarted', () => ({ type: 'dummy' }));

// Act
const container = mount(
<Provider store={mockStore}>
<EditStudentPageContainer params={{ id: 2}} />
</Provider>,
);

// Assert
const presentational = container.find('EditStudentPage');
expect(presentational).not.to.be.undefined;
expect(presentational.prop('studentId')).to.equal('2');
}).bind(this));

it('should contain a studentId property equals 2 when params equals 2', sinon.test(() => {
// Arrange
const sinon: sinon.SinonStatic = this;

const mockStore: any = createStore({
adminStudent: {
editingStudentSummary: {},
},
});

const getStudentStub = sinon.stub(getStudent,
'summaryStudentByIdRequestStarted', () => ({ type: 'dummy' }));

// Act
const container = mount(
<Provider store={mockStore}>
<EditStudentPageContainer params={{ id: '2'}} />
</Provider>,
);

// Assert
const presentational = container.find('EditStudentPage');
expect(presentational).not.to.be.undefined;
expect(presentational.prop('studentId')).to.equal('2');
}).bind(this));

it('should call to getStudent with expected studentId', sinon.test(() => {
// Arrange
const sinon: sinon.SinonStatic = this;

const mockStore: any = createStore({
adminStudent: {
editingStudentSummary: {},
},
});

const getStudentStub = sinon.stub( getStudent ,
'summaryStudentByIdRequestStarted', () => ({ type: 'dummy' }));

// Act
const container = mount(
<Provider store={mockStore}>
<EditStudentPageContainer params={{ id: '2'}} />
</Provider>,
);

// Assert
expect(getStudentStub.calledOnce).to.be.true;
expect(getStudentStub.calledWith('2')).to.be.true;
}).bind(this));
});
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ describe('StudentRowComponent', () => {
// Assert
expect(link.type()).to.be.equals(Link);
expect(link.prop('className')).to.be.equals('btn btn-primary');
expect(link.prop('to')).to.be.equals(`${adminRouteEnums.student.edit}/32`);
expect(link.prop('to')).to.be.equals(`${adminRouteEnums.student.base}/32/edit`);
expect(icon.type()).to.be.equals('i');
expect(icon.prop('className')).to.be.equals('glyphicon glyphicon-pencil');
});
Expand Down
2 changes: 1 addition & 1 deletion src/pages/admin/student/list/components/studentRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export const StudentRowComponent: React.StatelessComponent<Props> = (props) => {
<span>{props.rowData.fullname}</span>
<span>{props.rowData.email}</span>
<div className={classNames.btnGroup}>
<Link to={`${adminRouteEnums.student.edit}/${props.rowData.id}`} className="btn btn-primary">
<Link to={`${adminRouteEnums.student.base}/${props.rowData.id}/edit`} className="btn btn-primary">
<i className="glyphicon glyphicon-pencil" />
</Link>
<button type="button" className="btn btn-danger">
Expand Down
11 changes: 11 additions & 0 deletions src/reducers/adminStudent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,20 @@ import { StudentSummary } from '../model/studentSummary';

export class AdminStudentState {
public studentSummaryList: StudentSummary[];
public editingStudentSummary: StudentSummary;

public constructor() {
this.studentSummaryList = [];
this.editingStudentSummary = new StudentSummary();
}
}

export const adminStudentReducer = (state: AdminStudentState = new AdminStudentState(), action) => {
switch (action.type) {
case adminActionEnums.GET_SUMMARY_STUDENT_REQUEST_COMPLETED:
return handleGetSummaryStudentRequestCompleted(state, action.payload);
case adminActionEnums.GET_SUMMARY_STUDENT_BY_ID_REQUEST_COMPLETED:
return handleGetSummaryStudentByIdRequestCompleted(state, action.payload);
default:
return state;
}
Expand All @@ -24,3 +28,10 @@ const handleGetSummaryStudentRequestCompleted = (state: AdminStudentState, paylo
studentSummaryList: payload,
};
};

const handleGetSummaryStudentByIdRequestCompleted = (state: AdminStudentState, payload: StudentSummary) => {
return {
...state,
editingStudentSummary: payload,
};
};
Loading

0 comments on commit 01f09d2

Please sign in to comment.