diff --git a/app/components/payments/funds-recipient/personal-id-number.js b/app/components/payments/funds-recipient/personal-id-number.js new file mode 100644 index 000000000..34428636f --- /dev/null +++ b/app/components/payments/funds-recipient/personal-id-number.js @@ -0,0 +1,11 @@ +import Ember from 'ember'; + +const { + Component, + computed: { alias } +} = Ember; + +export default Component.extend({ + classNames: ['personal-id-number'], + status: alias('stripeConnectAccount.personalIdNumberStatus') +}); diff --git a/app/components/payments/funds-recipient/verification-document.js b/app/components/payments/funds-recipient/verification-document.js index b8b0d19e8..8cb27480b 100644 --- a/app/components/payments/funds-recipient/verification-document.js +++ b/app/components/payments/funds-recipient/verification-document.js @@ -5,6 +5,7 @@ const { computed, get, set + } = Ember; const VALIDATION_ERROR = 'The file you selected is invalid. Only .jpg and .png images of up to 8mb in size are supported.'; diff --git a/app/controllers/project/settings/donations/payments.js b/app/controllers/project/settings/donations/payments.js index 7afb5e1e9..2f62863c5 100644 --- a/app/controllers/project/settings/donations/payments.js +++ b/app/controllers/project/settings/donations/payments.js @@ -10,11 +10,11 @@ const { setProperties } = Ember; -const ACCOUNT_ADDING_ERROR = 'There was a problem submitting your bank account information.'; -const ACCOUNT_TOKEN_CREATION_ERROR = 'There was a problem in using your bank account information. Please check your input and try again.'; -const STRIPE_ACCOUNT_CREATION_ERROR = 'There was a problem with creating your account. Please check your input and try again.'; -const STRIPE_RECIPIENT_DETAILS_UPDATE_ERROR = 'There was a problem with your account information. Please check your input and try again.'; -const VERIFICATION_DOCUMENT_ERROR = 'There was a problem in attaching the verification document to your stripe account'; +const ACCOUNT_CREATION_ERROR = 'There was a problem with creating your account. Please check your input and try again.'; +const ACCOUNT_UPDATE_ERROR = 'There was a problem with your account information. Please check your input and try again.'; +const BANK_ACCOUNT_TOKEN_CREATION_ERROR = 'There was a problem in using your bank account information. Please check your input and try again.'; +const BANK_ACCOUNT_ADDING_ERROR = 'There was a problem submitting your bank account information.'; +const VERIFICATION_DOCUMENT_ERROR = 'There was a problem with attaching your document. Please try again.'; export default Controller.extend({ currentUser: service(), @@ -63,8 +63,13 @@ export default Controller.extend({ .finally(() => set(this, 'isBusy', false)); }, - onPersonalIdNumberSubmitted() { - console.log(arguments); + onPersonalIdNumberSubmitted(personalIdNumber) { + set(this, 'isBusy', true); + + get(this, 'stripeConnectAccount') + .then((account) => this._assignPersonalIdNumber(account, personalIdNumber)) + .catch((response) => this._handleError(response)) + .finally(() => set(this, 'isBusy', false)); } }, @@ -72,15 +77,10 @@ export default Controller.extend({ _createStripeAccount(organization, country) { return get(this, 'store') - .createRecord('stripe-connect-account', { organization, country }) - .save() - .then((account) => RSVP.resolve(account)) - .catch((reason) => this._handleAccountCreationError(reason)); - }, - - _handleAccountCreationError() { - let friendlyError = new FriendlyError(STRIPE_ACCOUNT_CREATION_ERROR); - return RSVP.reject(friendlyError); + .createRecord('stripe-connect-account', { organization, country }) + .save() + .then(RSVP.resolve) + .catch(() => this._wrapError(ACCOUNT_CREATION_ERROR)); }, // udating recipient info @@ -89,29 +89,31 @@ export default Controller.extend({ setProperties(stripeConnectAccount, recipientDetails); return stripeConnectAccount - .save() - .then((account) => RSVP.resolve(account)) - .catch((reason) => this._handleRecipientDetailsUpdateError(reason)); - }, - - _handleRecipientDetailsUpdateError() { - let friendlyError = new FriendlyError(STRIPE_RECIPIENT_DETAILS_UPDATE_ERROR); - return RSVP.reject(friendlyError); + .save() + .then(RSVP.resolve) + .catch(() => this._handleRecipientDetailsUpdateError(ACCOUNT_UPDATE_ERROR)); }, // uploading and assigning an id verification document - _assignIdentityVerificationDocument(account, stripeFileUploadId) { - set(account, 'identityDocumentId', stripeFileUploadId); + _assignIdentityVerificationDocument(stripeConnectAccount, stripeFileUploadId) { + set(stripeConnectAccount, 'identityDocumentId', stripeFileUploadId); - return account.save() - .then((account) => RSVP.resolve(account)) - .catch((reason) => this._handleIdentityVerificationDocumentError(reason)); + return stripeConnectAccount + .save() + .then(RSVP.resolve) + .catch(() => this._wrapError(VERIFICATION_DOCUMENT_ERROR)); }, - _handleIdentityVerificationDocumentError() { - let friendlyError = new FriendlyError(VERIFICATION_DOCUMENT_ERROR); - return RSVP.reject(friendlyError); + // assigning a personal id number + + _assignPersonalIdNumber(stripeConnectAccount, personalIdNumber) { + set(stripeConnectAccount, 'personalIdNumber', personalIdNumber); + + return stripeConnectAccount + .save() + .then(RSVP.resolve) + .catch(() => this._wrapError(ACCOUNT_UPDATE_ERROR)); }, // bank account - token step @@ -122,7 +124,7 @@ export default Controller.extend({ return stripe.bankAccount.createToken(params) .then((stripeResponse) => RSVP.resolve(stripeResponse)) - .catch((reason) => this._handleBankAccountTokenError(reason)); + .catch(() => this._wrapError(BANK_ACCOUNT_TOKEN_CREATION_ERROR)); }, _bankAccountTokenParams(accountNumber, routingNumber) { @@ -135,23 +137,21 @@ export default Controller.extend({ }; }, - _handleBankAccountTokenError() { - let friendlyError = new FriendlyError(ACCOUNT_TOKEN_CREATION_ERROR); - return RSVP.reject(friendlyError); - }, - // bank account - updating connect account record step _addBankAccount(tokenData, stripeConnectAccount) { set(stripeConnectAccount, 'externalAccount', tokenData.id); - return stripeConnectAccount.save() - .then((stripeConnectAccount) => RSVP.resolve(stripeConnectAccount)) - .catch((reason) => this._handleAddBankAccountError(reason)); + return stripeConnectAccount + .save() + .then(RSVP.resolve) + .catch(() => this._wrapError(BANK_ACCOUNT_ADDING_ERROR)); }, - _handleAddBankAccountError() { - let friendlyError = new FriendlyError(ACCOUNT_ADDING_ERROR); + // friendly error wrapping + + _wrapError(message) { + let friendlyError = new FriendlyError(message); return RSVP.reject(friendlyError); }, diff --git a/app/templates/components/payments/funds-recipient.hbs b/app/templates/components/payments/funds-recipient.hbs index 06c8b915a..bae3a1017 100644 --- a/app/templates/components/payments/funds-recipient.hbs +++ b/app/templates/components/payments/funds-recipient.hbs @@ -14,9 +14,10 @@ isBusy=isBusy onVerificationDocumentSubmitted=(action onVerificationDocumentSubmitted) stripeConnectAccount=stripeConnectAccount}} -
- TODO: personal-id-number component goes here -
+ {{payments/funds-recipient/personal-id-number + isBusy=isBusy + stripeConnectAccount=stripeConnectAccount + submit=(action onPersonalIdNumberSubmitted)}} {{/if}} {{#if (eq status 'verified')}} diff --git a/app/templates/components/payments/funds-recipient/personal-id-number.hbs b/app/templates/components/payments/funds-recipient/personal-id-number.hbs new file mode 100644 index 000000000..1be8b4546 --- /dev/null +++ b/app/templates/components/payments/funds-recipient/personal-id-number.hbs @@ -0,0 +1,16 @@ +{{#if (eq status 'required')}} +
+
We need your full personal ID number
+
+
+ + {{input type="text" name="personal-id-number" value=personalIdNumber disabled=isBusy}} +
+
+
+ +{{/if}} + +{{#if (eq status 'verifying')}} + We're verifying your ID number +{{/if}} \ No newline at end of file diff --git a/app/templates/components/payments/funds-recipient/verification-document.hbs b/app/templates/components/payments/funds-recipient/verification-document.hbs index 0f1ae7389..81c1db4f3 100644 --- a/app/templates/components/payments/funds-recipient/verification-document.hbs +++ b/app/templates/components/payments/funds-recipient/verification-document.hbs @@ -1,4 +1,3 @@ - {{#if (eq status 'required')}} {{#if isBusy}} Processing... @@ -23,4 +22,4 @@ {{/if}} {{#if (eq status 'verifying')}} Please be patient while we review the document you provided. -{{/if}} \ No newline at end of file +{{/if}} diff --git a/tests/integration/components/payments/funds-recipient-test.js b/tests/integration/components/payments/funds-recipient-test.js index b58484585..b9a644470 100644 --- a/tests/integration/components/payments/funds-recipient-test.js +++ b/tests/integration/components/payments/funds-recipient-test.js @@ -7,10 +7,7 @@ import fundsRecipientComponent from '../../../pages/components/payments/funds-re let page = PageObject.create(fundsRecipientComponent); -const { - setProperties, - K -} = Ember; +const { setProperties, K } = Ember; function setHandlers(context, { detailsHandler = K, documentHandler = K, idHandler = K }) { setProperties(context, { detailsHandler, documentHandler, idHandler }); @@ -61,7 +58,7 @@ test('it renders correctly when "required"', function(assert) { assert.ok(page.rendersDetailsForm, 'Component renders the details form subcomponent.'); }); -test('it renders correctly when "verifying"', function(assert) { +test('it renders correctly when "verifying" and document status "required"', function(assert) { assert.expect(3); let stripeConnectAccount = { recipientStatus: 'verifying' }; @@ -107,30 +104,52 @@ test('it renders correctly when "verified" for business', function(assert) { assert.ok(page.businessNameText, 'Company Inc.', 'Component renders the name of the registered business.'); }); -// TODO: These need to be implemented once subcomponents are done and pass out actions -// test('it passes out submit action from details subcomponent', function(assert) { -// assert.expect(1); +test('it passes out submit action from details subcomponent', function(assert) { + assert.expect(1); -// let stripeConnectAccount = { recipientStatus: 'required' }; -// this.set('stripeConnectAccount', stripeConnectAccount); + let stripeConnectAccount = { recipientStatus: 'required' }; + this.set('stripeConnectAccount', stripeConnectAccount); -// renderPage(); -// }); + function detailsHandler() { + assert.ok(true, 'Action got called'); + } + setHandlers(this, { detailsHandler }); + + renderPage(); + + page.detailsForm.clickSubmit(); +}); + +// TODO: Get this working // test('it passes out submit action from document upload subcomponent', function(assert) { // assert.expect(1); -// let stripeConnectAccount = { recipientStatus: 'verifying' }; +// let stripeConnectAccount = { recipientStatus: 'verifying', verificationDocumentStatus: 'required' }; // this.set('stripeConnectAccount', stripeConnectAccount); +// function documentHandler() { +// assert.ok(true, 'Action got called'); +// }; +// setHandlers(this, { documentHandler }); + // renderPage(); + +// page.verificationDocument.pickFile(this); // }); -// test('it passes out submit action from personal id number subcomponent', function(assert) { -// assert.expect(1); +test('it passes out submit action from personal id number subcomponent', function(assert) { + assert.expect(1); -// let stripeConnectAccount = { recipientStatus: 'verifying' }; -// this.set('stripeConnectAccount', stripeConnectAccount); + let stripeConnectAccount = { recipientStatus: 'verifying', personalIdNumberStatus: 'required' }; + this.set('stripeConnectAccount', stripeConnectAccount); -// renderPage(); -// }); + function idHandler() { + assert.ok(true, 'Action got called'); + } + setHandlers(this, { idHandler }); + + renderPage(); + + page.personalIdNumber.clickSubmit(); +}); diff --git a/tests/integration/components/payments/funds-recipient/personal-id-number-test.js b/tests/integration/components/payments/funds-recipient/personal-id-number-test.js new file mode 100644 index 000000000..970960e76 --- /dev/null +++ b/tests/integration/components/payments/funds-recipient/personal-id-number-test.js @@ -0,0 +1,111 @@ +import Ember from 'ember'; +import { moduleForComponent, test } from 'ember-qunit'; +import hbs from 'htmlbars-inline-precompile'; +import PageObject from 'ember-cli-page-object'; + +import personalIdNumberComponent from 'code-corps-ember/tests/pages/components/payments/funds-recipient/personal-id-number'; + +let page = PageObject.create(personalIdNumberComponent); + +const { + set, + K +} = Ember; + +function setHandler(context, submitHandler = K) { + set(context, 'submitHandler', submitHandler); +} + +function renderPage() { + page.render(hbs` + {{payments/funds-recipient/personal-id-number + isBusy=isBusy + stripeConnectAccount=stripeConnectAccount + submit=submitHandler}} + `); +} + +moduleForComponent('payments/funds-recipient/personal-id-number', 'Integration | Component | payments/funds recipient/personal id number', { + integration: true, + beforeEach() { + setHandler(this); + page.setContext(this); + }, + afterEach() { + page.removeContext(); + } +}); + +test('it renders correctly for "pending" status', function(assert) { + assert.expect(1); + + let stripeConnectAccount = { personalIdNumberStatus: 'pending_requirement' }; + set(this, 'stripeConnectAccount', stripeConnectAccount); + + renderPage(); + + assert.equal(page.text, '', 'Component renders nothing at all.'); +}); + +test('it renders correctly for "required" status', function(assert) { + assert.expect(2); + + let stripeConnectAccount = { personalIdNumberStatus: 'required' }; + set(this, 'stripeConnectAccount', stripeConnectAccount); + + renderPage(); + + assert.ok(page.rendersPersonalIdNumberField, 'Component renders the account number field.'); + assert.ok(page.rendersSubmitButton, 'Component renders the submit button.'); +}); + +test('it renders correctly for "verifying" status', function(assert) { + assert.expect(1); + + let stripeConnectAccount = { personalIdNumberStatus: 'verifying' }; + set(this, 'stripeConnectAccount', stripeConnectAccount); + + renderPage(); + + assert.equal(page.text, "We're verifying your ID number"); +}); + +test('it renders correctly for "verified" status', function(assert) { + assert.expect(1); + + let stripeConnectAccount = { personalIdNumberStatus: 'verified' }; + set(this, 'stripeConnectAccount', stripeConnectAccount); + + renderPage(); + + assert.equal(page.text, '', 'Component renders nothing at all.'); +}); + +test('it sends properties with submit action', function(assert) { + assert.expect(1); + + let stripeConnectAccount = { personalIdNumberStatus: 'required' }; + set(this, 'stripeConnectAccount', stripeConnectAccount); + + let personalIdNumber = '123456'; + + setHandler(this, (number) => { + assert.equal(personalIdNumber, number, 'Correct parameter was sent out with action.'); + }); + + renderPage(); + page.personalIdNumber(personalIdNumber).clickSubmit(); +}); + +test('it disables controls when busy', function(assert) { + assert.expect(2); + + let stripeConnectAccount = { personalIdNumberStatus: 'required' }; + set(this, 'isBusy', true); + set(this, 'stripeConnectAccount', stripeConnectAccount); + + renderPage(); + + assert.ok(page.personalIdNumberFieldIsDisabled, 'Personal ID number field is disabled when busy.'); + assert.ok(page.submitButtonIsDisabled, 'Submit button is disabled when busy.'); +}); diff --git a/tests/integration/components/payments/funds-recipient/verification-document-test.js b/tests/integration/components/payments/funds-recipient/verification-document-test.js index 75558209c..105c5f248 100644 --- a/tests/integration/components/payments/funds-recipient/verification-document-test.js +++ b/tests/integration/components/payments/funds-recipient/verification-document-test.js @@ -41,7 +41,6 @@ test('it renders file upload subcomponent if status is "required"', function(ass assert.expect(1); this.set('stripeConnectAccount', { verificationDocumentStatus: 'required' }); - renderPage(); assert.ok(page.rendersFileUpload, 'File upload subcomponent is rendered'); diff --git a/tests/pages/components/payments/funds-recipient.js b/tests/pages/components/payments/funds-recipient.js index 25e2e25c7..cd3dcb8ef 100644 --- a/tests/pages/components/payments/funds-recipient.js +++ b/tests/pages/components/payments/funds-recipient.js @@ -1,5 +1,9 @@ import { hasClass, isVisible, text } from 'ember-cli-page-object'; +import detailsForm from './funds-recipient/details-form'; +import verificationDocument from './funds-recipient/verification-document'; +import personalIdNumber from './funds-recipient/personal-id-number'; + export default { scope: '.funds-recipient', @@ -13,5 +17,9 @@ export default { rendersPersonalIdNumber: isVisible('.personal-id-number'), individualNameText: text('.funds-recipient__individual-name p'), - businessNameText: text('.funds-recipient__business-name p') + businessNameText: text('.funds-recipient__business-name p'), + + detailsForm, + verificationDocument, + personalIdNumber }; diff --git a/tests/pages/components/payments/funds-recipient/personal-id-number.js b/tests/pages/components/payments/funds-recipient/personal-id-number.js new file mode 100644 index 000000000..cd6745fa7 --- /dev/null +++ b/tests/pages/components/payments/funds-recipient/personal-id-number.js @@ -0,0 +1,15 @@ +import { clickable, fillable, is, isVisible } from 'ember-cli-page-object'; + +export default { + scope: '.personal-id-number', + + clickSubmit: clickable('button'), + + personalIdNumber: fillable('input[type=text]'), + personalIdNumberFieldIsDisabled: is(':disabled', 'input[type=text]'), + + rendersPersonalIdNumberField: isVisible('input[type=text]'), + rendersSubmitButton: isVisible('button'), + + submitButtonIsDisabled: is(':disabled', 'button') +}; diff --git a/tests/pages/components/payments/funds-recipient/verification-document.js b/tests/pages/components/payments/funds-recipient/verification-document.js index c80020e78..ae9eeb830 100644 --- a/tests/pages/components/payments/funds-recipient/verification-document.js +++ b/tests/pages/components/payments/funds-recipient/verification-document.js @@ -1,7 +1,9 @@ -import { isVisible, text } from 'ember-cli-page-object'; +import { clickable, isVisible, text } from 'ember-cli-page-object'; export default { + scope: '.verification-document', errorText: text('.error'), - rendersFileUpload: isVisible('input[type=file]') - + rendersFileUpload: isVisible('input[type=file]'), + clickSubmit: clickable('button') }; +