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