From 753c45a1d75276387716a9e3ff977e458743cfc9 Mon Sep 17 00:00:00 2001 From: "M. Reza Qorib" <rezaqorib96@gmail.com> Date: Wed, 29 Mar 2017 21:25:09 +0700 Subject: [PATCH] revised cancel application and tests --- assets/js/VacancyPage.jsx | 2 +- .../__test__/components/ApplyModal-test.jsx | 16 ++++ .../__test__/components/CancelModal-test.jsx | 62 ++++++++++++++ .../js/__test__/components/LoginForm-test.jsx | 2 + .../js/__test__/components/Vacancy-test.jsx | 36 ++++++-- .../__test__/components/VacancyList-test.jsx | 22 ++++- assets/js/components/ApplyModal.jsx | 6 +- assets/js/components/CancelModal.jsx | 83 +++++++++++++++++++ assets/js/components/ModalAlert.jsx | 40 +++++---- assets/js/components/Vacancy.jsx | 22 +++-- assets/js/components/VacancyList.jsx | 14 ++-- 11 files changed, 264 insertions(+), 41 deletions(-) create mode 100644 assets/js/__test__/components/CancelModal-test.jsx create mode 100644 assets/js/components/CancelModal.jsx diff --git a/assets/js/VacancyPage.jsx b/assets/js/VacancyPage.jsx index e1164609..27abae5b 100644 --- a/assets/js/VacancyPage.jsx +++ b/assets/js/VacancyPage.jsx @@ -29,7 +29,7 @@ export default class VacancyPage extends React.Component { <VacancyList key={1} studentId={student.id} url="/vacancies/" /> </Pane> <Pane label="Lamaran saya" > - <VacancyList key={2} status="batal" studentId={student.id} url={`/students/${student.id}/applied-vacancies/`} /> + <VacancyList key={2} status="Batal" studentId={student.id} url={`/students/${student.id}/applied-vacancies/`} /> </Pane> <Pane label="Lamaran Ditandai" > <VacancyList key={3} studentId={student.id} url={`/students/${student.id}/bookmarked-vacancies/`} /> diff --git a/assets/js/__test__/components/ApplyModal-test.jsx b/assets/js/__test__/components/ApplyModal-test.jsx index e0fbfe5e..58baa31d 100644 --- a/assets/js/__test__/components/ApplyModal-test.jsx +++ b/assets/js/__test__/components/ApplyModal-test.jsx @@ -18,4 +18,20 @@ describe('ApplyModal', () => { ReactTestUtils.Simulate.click(modal); }); + it('change without problem', () => { + const modalPendaftaran = ReactTestUtils.renderIntoDocument( + <ApplyModal id={4} data={{ key: 'value' }} buttonTitle="submit" />); + + modalPendaftaran.handleChange({ target: { value: 'duar' } }); + expect(modalPendaftaran.state.coverLetter).to.equal('duar'); + }); + + it('close without problem', () => { + const modalPendaftaran = ReactTestUtils.renderIntoDocument( + <ApplyModal id={4} data={{ key: 'value' }} buttonTitle="submit" />); + + modalPendaftaran.handleClose(); + expect(modalPendaftaran.state.modalOpen).to.equal(false); + }); + }); diff --git a/assets/js/__test__/components/CancelModal-test.jsx b/assets/js/__test__/components/CancelModal-test.jsx new file mode 100644 index 00000000..33a874af --- /dev/null +++ b/assets/js/__test__/components/CancelModal-test.jsx @@ -0,0 +1,62 @@ +/* eslint-disable no-unused-expressions */ +import React from 'react'; +import ReactTestUtils from 'react-addons-test-utils'; +import CancelModal from '../../components/CancelModal'; +import Storage from '../../lib/Storage'; + +describe('CancelModal', () => { + it('renders without problem', () => { + const modalPendaftaran = ReactTestUtils.renderIntoDocument( + <CancelModal id={4} />); + expect(modalPendaftaran).to.exist; + }); + + it('open without problem', () => { + const modalPendaftaran = ReactTestUtils.renderIntoDocument( + <CancelModal id={4} />); + + const modal = ReactTestUtils.findRenderedDOMComponentWithTag(modalPendaftaran, 'Button'); + ReactTestUtils.Simulate.click(modal); + expect(modalPendaftaran.state.modalOpen).to.equal(true); + }); + + it('close without problem', () => { + const modalPendaftaran = ReactTestUtils.renderIntoDocument( + <CancelModal id={4} />); + + modalPendaftaran.handleClose(); + expect(modalPendaftaran.state.modalOpen).to.equal(false); + }); + + it('remove vacancy without problem', () => { + const modalPendaftaran = ReactTestUtils.renderIntoDocument( + <CancelModal id={4} />); + + const response3 = { student: { id: 1, name: 2 } }; + Storage.set('user-data', response3); + modalPendaftaran.removeVacancy(); + expect(modalPendaftaran.state.header).to.exist; + }); + + it('confirm without problem', () => { + const fetchMock = require('fetch-mock'); + fetchMock.delete('*', { data: 'value' }); + const modalPendaftaran = ReactTestUtils.renderIntoDocument( + <CancelModal id={4} />); + + modalPendaftaran.confirm(); + expect(modalPendaftaran.state.header).to.equal('Permintaan gagal'); + }); + + it('render next modal without problem', () => { + const fetchMock = require('fetch-mock'); + fetchMock.delete('*', { data: 'value' }); + const modalPendaftaran = ReactTestUtils.renderIntoDocument( + <CancelModal id={4} />); + + modalPendaftaran.state.confirmed = true; + modalPendaftaran.forceUpdate() + expect(modalPendaftaran).to.exist; + }); + +}); diff --git a/assets/js/__test__/components/LoginForm-test.jsx b/assets/js/__test__/components/LoginForm-test.jsx index efacc1bb..c8df38af 100644 --- a/assets/js/__test__/components/LoginForm-test.jsx +++ b/assets/js/__test__/components/LoginForm-test.jsx @@ -4,6 +4,7 @@ import ReactTestUtils from 'react-addons-test-utils'; import LoginForm from '../../components/LoginForm'; describe('LoginForm', () => { + const fetchMock = require('fetch-mock'); it('created without problem', () => { const formLogin = new LoginForm({ url: 'tes' }); expect(formLogin).to.be.an.instanceof(LoginForm); @@ -52,6 +53,7 @@ describe('LoginForm', () => { }); it('submit form without problem', () => { + fetchMock.post('*', {data: 'value'}); const formLogin = ReactTestUtils.renderIntoDocument( <LoginForm url="" />); diff --git a/assets/js/__test__/components/Vacancy-test.jsx b/assets/js/__test__/components/Vacancy-test.jsx index 864a6fe0..a98d5565 100644 --- a/assets/js/__test__/components/Vacancy-test.jsx +++ b/assets/js/__test__/components/Vacancy-test.jsx @@ -2,9 +2,10 @@ import React from 'react'; import ReactTestUtils from 'react-addons-test-utils'; import Vacancy from '../../components/Vacancy'; -import Storage from '../../lib/Storage' +import Storage from '../../lib/Storage'; describe('Vacancy', () => { + const fetchMock = require('fetch-mock'); const response ={ close_time: '2019-03-28T05:55:42Z', company: { @@ -39,21 +40,44 @@ describe('Vacancy', () => { verified: true, }; - it('renders null picture without problem', () => { + it('renders with null picture and apply button without problem', () => { const lowongan = ReactTestUtils.renderIntoDocument( - <Vacancy data={response} />); + <Vacancy status="Daftar" data={response} />); expect(lowongan).to.exist; }); - it('renders with picture without problem', () => { + it('renders with null picture and cancel button without problem', () => { const lowongan = ReactTestUtils.renderIntoDocument( - <Vacancy data={response2} />); + <Vacancy status="Batal" data={response} />); + expect(lowongan).to.exist; + }); + + it('renders with picture and apply button without problem', () => { + const lowongan = ReactTestUtils.renderIntoDocument( + <Vacancy status="Daftar" data={response2} />); + expect(lowongan).to.exist; + }); + + it('renders with picture and cancel button without problem', () => { + const lowongan = ReactTestUtils.renderIntoDocument( + <Vacancy status="Batal" data={response2} />); expect(lowongan).to.exist; }); it('bookmarks without problem', () => { + fetchMock.post('*', response); + const lowongan = ReactTestUtils.renderIntoDocument( + <Vacancy status="Daftar" data={response2} />); + const response3 = { student: { id: 1, name: 2 } }; + expect(lowongan.props.data.id).to.equal(3); + Storage.set('user-data', response3); + expect(lowongan.bookmark()).to.be.undefined; + }); + + it('cancel bookmarks without problem', () => { + fetchMock.delete('*', { data: 'value' }); const lowongan = ReactTestUtils.renderIntoDocument( - <Vacancy data={response2} />); + <Vacancy status="Daftar" data={response2} bookmarked={1}/>); const response3 = { student: { id: 1, name: 2 } }; expect(lowongan.props.data.id).to.equal(3); Storage.set('user-data', response3); diff --git a/assets/js/__test__/components/VacancyList-test.jsx b/assets/js/__test__/components/VacancyList-test.jsx index 011045ba..f95f7ab9 100644 --- a/assets/js/__test__/components/VacancyList-test.jsx +++ b/assets/js/__test__/components/VacancyList-test.jsx @@ -25,15 +25,33 @@ describe('VacancyList', () => { const response2 = { hello: 'not-world' }; it('renders without problem', () => { + fetchMock.get('*', response); const vacancyList = ReactTestUtils.renderIntoDocument( - <VacancyList url="test" />); + <VacancyList studentId={1} url="test" />); expect(vacancyList).to.exist; }); + it('update bookmarks without problem', () => { + fetchMock.get('*', response); + const vacancyList = ReactTestUtils.renderIntoDocument( + <VacancyList studentId={1} url="test" />); + vacancyList.updateBookmarkList(); + expect(JSON.stringify(vacancyList.state.bookmarkList)).to.be.defined; + }); + + it('renders marked bookmarked vacancies without problem', () => { + fetchMock.get('*', response); + const vacancyList = ReactTestUtils.renderIntoDocument( + <VacancyList studentId={1} url="test" />); + vacancyList.state.vacancies = response; + vacancyList.state.bookmarkList = [{id: 5}, {id: 3}]; + expect(vacancyList.generateVacancies()).to.exist; + }); + it('success calling API', () => { fetchMock.get('*', response); const vacancyList = ReactTestUtils.renderIntoDocument( - <VacancyList url="test" />); + <VacancyList studentId={1} url="test" />); vacancyList.state.vacancies = response; expect(JSON.stringify(vacancyList.state.vacancies)).to.equal(JSON.stringify(response)); expect(vacancyList.generateVacancies()).to.exist; diff --git a/assets/js/components/ApplyModal.jsx b/assets/js/components/ApplyModal.jsx index 537eecd0..9bf420de 100644 --- a/assets/js/components/ApplyModal.jsx +++ b/assets/js/components/ApplyModal.jsx @@ -9,16 +9,11 @@ export default class ApplyModal extends React.Component { buttonTitle: React.PropTypes.string.isRequired, }; - static successResponse = 'Pendaftaran anda berhasil direkam. Harap menunggu kabar selanjutnya dari pihak yang terkait\n'; - static failedResponse = 'Maaf pendaftaran yang anda lakukan gagal. Harap ulangi pendaftaran atau hubungi administrator\n'; - constructor(props) { super(props); /* istanbul ignore next */ this.state = { modalOpen: false, - responseHeader: 'Menghubungkan ke Server', - responseText: 'Terima kasih sudah mendaftar!', coverLetter: '', }; this.handleChange = this.handleChange.bind(this); @@ -75,6 +70,7 @@ export default class ApplyModal extends React.Component { id={this.props.id} onChangeValue={this.handleClose} coverLetter={this.state.coverLetter} + status="Daftar" /> </Modal.Actions> </Modal> diff --git a/assets/js/components/CancelModal.jsx b/assets/js/components/CancelModal.jsx new file mode 100644 index 00000000..723bc3bc --- /dev/null +++ b/assets/js/components/CancelModal.jsx @@ -0,0 +1,83 @@ +import React from 'react'; +import { Modal, Button, Icon, Header } from 'semantic-ui-react'; +import Server from '../lib/Server'; +import Storage from '../lib/Storage'; + +export default class CancelModal extends React.Component { + + static propTypes = { + id: React.PropTypes.number.isRequired, + }; + + constructor(props) { + super(props); + /* istanbul ignore next */ + this.state = { + modalOpen: false, + confirmed: false, + header: 'Batalkan Pendaftaran?', + content: 'Aksi ini tidak dapat direka ulang. Pastikan anda benar-benar ingin membatalkan pendaftaran', + }; + this.open = this.open.bind(this); + this.confirm = this.confirm.bind(this); + this.removeVacancy = this.removeVacancy.bind(this); + } + + handleClose = () => this.setState({ + modalOpen: false, + }); + + removeVacancy() { + const studentId = Storage.get('user-data').student.id; + const batalSuccess = 'Pendaftaran anda berhasil dihapus dari sistem\n'; + const batalFailed = 'Maaf permintaan anda gagal diproses sistem. Harap ulangi pendaftaran atau hubungi administrator\n'; + + + Server.delete(`/students/${studentId}/applied-vacancies/${this.props.id}/`).then(() => { + this.setState({ + header: 'Pendaftaran Berhasil Dibatalkan', + content: batalSuccess, + confirmed: true, + }); + }, () => { + this.setState({ + header: 'Permintaan gagal', + content: batalFailed, + }); + }); + } + + confirm() { + this.setState({ + header: 'Permintaan gagal', + content: this.batalFailed, + }); + + } + + open = () => this.setState({ modalOpen: true }); + + render = () => ( + <Modal + trigger={<Button floated="right">Hapus</Button>} + basic size="small" open={this.state.modalOpen} + onOpen={this.open} + > + <Header icon="trash" content={this.state.header} /> + <Modal.Content> + <p>{this.state.content}</p> + </Modal.Content> + <Modal.Actions> + <Button onClick={this.handleClose} basic color="blue" inverted> + <Icon name="remove" /> Tutup + </Button> + { !this.state.confirmed ? + (<Button onClick={this.removeVacancy} basic color="red" inverted> + <Icon name="remove" /> Hapus + </Button>) + : + null } + </Modal.Actions> + </Modal> + ); +} \ No newline at end of file diff --git a/assets/js/components/ModalAlert.jsx b/assets/js/components/ModalAlert.jsx index b7a90bbd..3961001b 100644 --- a/assets/js/components/ModalAlert.jsx +++ b/assets/js/components/ModalAlert.jsx @@ -7,11 +7,13 @@ export default class ModalAlert extends React.Component { static propTypes = { id: React.PropTypes.number.isRequired, onChangeValue: React.PropTypes.func.isRequired, - coverLetter: React.PropTypes.string.isRequired, + coverLetter: React.PropTypes.string, + status: React.PropTypes.string.isRequired, }; - static successResponse = 'Pendaftaran anda berhasil direkam. Harap menunggu kabar selanjutnya dari pihak yang terkait\n'; - static failedResponse = 'Maaf pendaftaran yang anda lakukan gagal. Harap ulangi pendaftaran atau hubungi administrator\n'; + static defaultProps = { + coverLetter: '', + }; constructor(props) { super(props); @@ -29,26 +31,32 @@ export default class ModalAlert extends React.Component { handleOpen() { const studentId = Storage.get('user-data').student.id; - const requestData = {vacancy_id: this.props.id, cover_letter: this.props.coverLetter }; - Server.post(`/students/${studentId}/applied-vacancies/`, requestData).then((data) => { + const daftarSuccess = 'Pendaftaran anda berhasil direkam. Harap menunggu kabar selanjutnya dari pihak yang terkait\n'; + const daftarFailed = 'Maaf pendaftaran yang anda lakukan gagal. Harap ulangi pendaftaran atau hubungi administrator\n'; - this.setState({ - header: 'Pendaftaran Berhasil', - content: this.successResponse, - }); - }, (error) => { + if (this.props.status == 'Daftar') { + const requestData = { vacancy_id: this.props.id, cover_letter: this.props.coverLetter }; - this.setState({ - header: 'Pendaftaran Gagal', - content: this.failedResponse, + Server.post(`/students/${studentId}/applied-vacancies/`, requestData).then(() => { + this.setState({ + header: 'Pendaftaran Berhasil', + content: this.daftarSuccess, + }); + }, () => { + this.setState({ + header: 'Pendaftaran Gagal', + content: this.daftarFailed, + }); }); - }); + } else { + + } } render() { const { open } = this.state; - + const buttonColor = this.props.status === 'Daftar' ? 'blue' : 'red'; return ( <Modal open={open} @@ -56,7 +64,7 @@ export default class ModalAlert extends React.Component { onClose={this.close} size="small" basic - trigger={<Button color="blue" onClick={this.handleOpen}> Daftar <Icon name="right chevron" /></Button>} + trigger={<Button color={buttonColor} onClick={this.handleOpen}> {this.props.status} <Icon name="right chevron" /></Button>} > <Modal.Header>{this.state.header}</Modal.Header> <Modal.Content> diff --git a/assets/js/components/Vacancy.jsx b/assets/js/components/Vacancy.jsx index 9c7a1c35..8b98a8d2 100644 --- a/assets/js/components/Vacancy.jsx +++ b/assets/js/components/Vacancy.jsx @@ -1,8 +1,9 @@ import React from 'react'; -import { Button, Image as ImageComponent, Item, Rating } from 'semantic-ui-react'; +import { Item, Rating } from 'semantic-ui-react'; import ApplyModal from './ApplyModal'; import Storage from '../lib/Storage'; import Server from '../lib/Server'; +import CancelModal from './CancelModal'; const defaultImage = 'http://semantic-ui.com/images/wireframe/image.png'; @@ -10,6 +11,7 @@ export default class Vacancy extends React.Component { static propTypes = { data: React.PropTypes.object.isRequired, bookmarked: React.PropTypes.number, + status: React.PropTypes.string.isRequired, }; static defaultProps = { @@ -21,12 +23,13 @@ export default class Vacancy extends React.Component { /* istanbul ignore next */ this.state = { bookmarked: this.props.bookmarked }; this.bookmark = this.bookmark.bind(this); + this.generateAction = this.generateAction.bind(this); } bookmark() { const studentId = Storage.get('user-data').student.id; const data = { vacancy_id: this.props.data.id }; - if (this.state.bookmarked < 1){ + if (this.state.bookmarked < 1) { Server.post(`/students/${studentId}/bookmarked-vacancies/`, data); } else { Server.delete(`/students/${studentId}/bookmarked-vacancies/${this.props.data.id}/`); @@ -34,6 +37,16 @@ export default class Vacancy extends React.Component { this.state.bookmarked = 1 - this.state.bookmarked; } + generateAction() { + const applyModal = (<ApplyModal + id={this.props.data.id} data={{ header: this.props.data.name, + description: this.props.data.description }} buttonTitle="Detail" + />); + + const cancelModal = <CancelModal id={this.props.data.id} />; + return this.props.status === 'Daftar' ? applyModal : cancelModal; + } + render() { return ( <Item > @@ -48,10 +61,7 @@ export default class Vacancy extends React.Component { <h4>{ this.props.data.company.name }</h4> <h5>{ this.props.data.company.address }</h5> - <ApplyModal - id={this.props.data.id} data={{ header: this.props.data.name, - description: this.props.data.description }} buttonTitle="Detail" - /> + { this.generateAction() } </Item.Extra> </Item.Content> diff --git a/assets/js/components/VacancyList.jsx b/assets/js/components/VacancyList.jsx index c2e12720..5188044e 100644 --- a/assets/js/components/VacancyList.jsx +++ b/assets/js/components/VacancyList.jsx @@ -12,8 +12,7 @@ export default class VacancyList extends React.Component { }; static defaultProps = { - bookmarkList: false, - status: "daftar", + status: 'Daftar', }; constructor(props) { @@ -30,8 +29,8 @@ export default class VacancyList extends React.Component { } checkBookmark(id) { - for (let i = 0; i < this.state.bookmarkList.length; i++) { - if (id == this.state.bookmarkList[i].id) { return 1; } + for (let i = 0; i < this.state.bookmarkList.length; i += 1) { + if (id === this.state.bookmarkList[i].id) { return 1; } } return 0; } @@ -44,7 +43,12 @@ export default class VacancyList extends React.Component { generateVacancies() { return this.state.vacancies.map(vacancy => - <Vacancy key={vacancy.id} bookmarked={this.checkBookmark(vacancy.id)} data={vacancy} />, + <Vacancy + key={vacancy.id} + status={this.props.status} + bookmarked={this.checkBookmark(vacancy.id)} + data={vacancy} + />, ); } -- GitLab