Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions app/components/donation/credit-card.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export default Component.extend({
cardInvalid: not('cardValid'),
cardValid: and('isCardNumberValid', 'isCVCValid', 'isExpiryValid'),

isBusyOrInvalid: or('isBusy', 'cardInvalid'),
isProcessingOrInvalid: or('isProcessing', 'cardInvalid'),

date: computed('month', 'year', function() {
let month = this.get('month');
Expand Down Expand Up @@ -81,7 +81,6 @@ export default Component.extend({

actions: {
submit() {
this.set('isSubmitting', true);
let cardAttrs = this.getProperties('cvc', 'cardNumber', 'year', 'month');
let onSubmit = this.get('submit');

Expand Down
24 changes: 22 additions & 2 deletions app/components/donation/donation-container.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,31 @@
import Ember from 'ember';

const {
Component
Component,
computed: {
and, empty, or
}
} = Ember;

export default Component.extend({
classNames: ['donation-container'],
donationAmount: 0,
projectTitle: null
projectTitle: null,
wasNewCard: true,

/**
* If there is no card for the user, this might be an ObjectProxy
* that has `null` content. This `card.id` approach was the least
* hacky way we could think to deal with this late one night.
*
* TODO: Find a better approach!
*/
isNewCard: empty('card.id'),

@begedin begedin Dec 2, 2016

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Someone might wonder why this isn't simply alias(cad.isNew), not realizing it's because card might be null to begin with. Maybe a comment would help?

At least, that's my assumption here. Maybe card.isNew would actually work? Does init get called if the component is not rendered?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@begedin this one is tricky because it's an ObjectProxy which has content: null if there is none. This was the least hacky way I could think to deal with this.

Should definitely comment, but would prefer a better fix.

I'm not super satisfied with my implementation over all, but hey: it works.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add a request for a better fix in the comment. Maybe someone gets inspired.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@begedin can you make an issue refing this PR for us to fix? And put under this milestone, I guess.

shouldShowNewForm: or('isNewCard', 'subscribingWithNewCard'),
subscribingWithNewCard: and('isProcessing', 'wasNewCard'),

init() {
this._super(...arguments);
this.set('wasNewCard', this.get('isNewCard'));
}
});
15 changes: 7 additions & 8 deletions app/controllers/project/donate.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ const SUBSCRIPTION_VALIDATION_ERROR = "The amount you've set for your monthly do

export default Controller.extend({
amount: null,
isAddingCard: false,
queryParams: ['amount'],

currentUser: service(),
Expand All @@ -28,29 +27,29 @@ export default Controller.extend({
project: alias('model'),
user: alias('currentUser.user'),

stripeCustomerCreated: bool('currentUser.user.stripePlatformCustomer.id'),
stripeCustomerCreated: bool('user.stripePlatformCustomer.id'),
shouldCreateCustomer: not('stripeCustomerCreated'),

actions: {
saveAndDonate(amount, cardParams) {
this._clearErrors();
this._updateIsLoading(true);
this._updateisProcessing(true);

return this._createCreditCardToken(cardParams)
.then((stripeResponse) => this._createCardForPlatformCustomer(stripeResponse))
.then((stripeCard) => this._createSubscription(amount, stripeCard))
.then(() => this._transitionToThankYou())
.catch((response) => this._handleError(response))
.finally(() => this._updateIsLoading(false));
.finally(() => this._updateisProcessing(false));
},

donate(amount, stripeCard) {
this._clearErrors();
this._updateIsLoading(true);
this._updateisProcessing(true);

return this._createSubscription(amount, stripeCard)
.then(() => this._transitionToThankYou())
.finally(() => this._updateIsLoading(false));
.finally(() => this._updateisProcessing(false));
}
},

Expand Down Expand Up @@ -163,8 +162,8 @@ export default Controller.extend({
};
},

_updateIsLoading(value) {
set(this, 'isLoading', value);
_updateisProcessing(value) {
set(this, 'isProcessing', value);
},

_clearErrors() {
Expand Down
15 changes: 10 additions & 5 deletions app/templates/components/donation/credit-card.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,18 @@
</div>

<button
class="button {{unless isBusyOrInvalid "default"}} large submit-card"
disabled={{isBusyOrInvalid}}
{{action "submit"}}>
{{#if isBusy}}Processing...{{else}}Donate{{/if}}
{{action "submit"}}
class="button {{unless isProcessingOrInvalid "default"}} large submit-card"
disabled={{isProcessingOrInvalid}}>

{{#if isProcessing}}
Processing...
{{else}}
Donate
{{/if}}
</button>

{{#unless isBusy}}
{{#unless isProcessing}}
{{!validation errors can be passed in from parent, using a block}}
{{yield}}
{{/unless}}
23 changes: 16 additions & 7 deletions app/templates/components/donation/donation-container.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,26 @@
</p>
</div>

{{#if card}}
{{donation/card-item card=card}}
{{else}}
{{#donation/credit-card isBusy=isBusy submit=(action saveAndDonate)}}
{{#if shouldShowNewForm}}
{{#donation/credit-card isProcessing=isProcessing submit=(action saveAndDonate)}}
{{!show errors}}
{{yield}}
{{/donation/credit-card}}
{{/if}}
{{else}}
{{donation/card-item card=card}}
<button
{{action donate}}
class="button {{unless isProcessing "default"}} large donate"
disabled={{isProcessing}}
data-test-selector="donate button">

{{#if isProcessing}}
Processing...
{{else}}
Donate
{{/if}}
</button>

{{#if card}}
<button class="button {{unless isBusy "default"}} large donate" {{action donate}} disabled={{isBusy}}>Donate</button>
{{!show errors}}
{{yield}}
{{/if}}
Expand Down
2 changes: 1 addition & 1 deletion app/templates/project/donate.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
card=user.stripePlatformCard
donate=(action 'donate' amount user.stripePlatformCard)
donationAmount=amount
isBusy=isLoading
isProcessing=isProcessing
projectTitle=project.title
saveAndDonate=(action 'saveAndDonate' amount)
}}
Expand Down
6 changes: 6 additions & 0 deletions mirage/scenarios/default.js
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,12 @@ export default function(server) {
server.create('project-category', { category, project });
});

organization.createStripeConnectAccount();

project.createStripeConnectPlan();

server.create('stripe-platform-customer', { user: owner });

server.create('stripe-platform-card', { user: owner });

server.create('donation-goal', {
Expand Down
4 changes: 2 additions & 2 deletions tests/integration/components/donation/credit-card-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ test('it sends submit with credit card fields when button is clicked', function(
page.clickSubmit();
});

test('it renders button as disabled and "Processing" when busy', function(assert) {
test('it renders button as disabled and "Processing" when processing', function(assert) {
assert.expect(2);

stubService(this, 'stripe', {
Expand All @@ -68,7 +68,7 @@ test('it renders button as disabled and "Processing" when busy', function(assert
}
});

page.render(hbs`{{donation/credit-card isBusy=true submit=submitHandler}}`);
page.render(hbs`{{donation/credit-card isProcessing=true submit=submitHandler}}`);

assert.ok(page.submitDisabled, 'Submit button is disabled');
assert.equal(page.submitButtonText, 'Processing...', 'Submit button changed text');
Expand Down
56 changes: 50 additions & 6 deletions tests/integration/components/donation/donation-container-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,18 +34,65 @@ test('it renders new card form when there is no card to begin with', function(as
assert.expect(2);

this.set('amount', 100);
this.set('isProcessing', false);
this.set('projectTitle', 'CodeCorps');
this.set('card', null);
this.set('card', Object.create({ content: null, isFulfilled: true }));

page.render(hbs`
{{donation/donation-container
card=card donate=donateHandler donationAmount=amount projectTitle=projectTitle saveAndDonate=saveAndDonateHandler }}
card=card donate=donateHandler donationAmount=amount
isProcessing=isProcessing projectTitle=projectTitle saveAndDonate=saveAndDonateHandler }}
`);

assert.ok(page.cardFormIsVisible, 'The new card form is rendered automatically.');
assert.notOk(page.cards(0).isVisible, 'The card item is not visible.');
});

test('it renders new card form when the card is added and is processing', function(assert) {
assert.expect(2);

this.set('amount', 100);
this.set('isProcessing', false);
this.set('projectTitle', 'CodeCorps');
this.set('card', Object.create({ content: null, isFulfilled: true }));

page.render(hbs`
{{donation/donation-container
card=card
donate=donateHandler
isProcessing=isProcessing
saveAndDonate=saveAndDonateHandler
}}
`);

this.set('card', visa);
this.set('isProcessing', true);

assert.ok(page.cardFormIsVisible, 'The new card form is rendered.');
assert.notOk(page.cards(0).isVisible, 'The card item is not visible.');
});

test('it renders the card item if there is a card and the form is processing', function(assert) {
assert.expect(4);

this.set('card', visa);
this.set('isProcessing', true);

page.render(hbs`
{{donation/donation-container
card=card
donate=donateHandler
isProcessing=isProcessing
saveAndDonate=saveAndDonateHandler
}}
`);

assert.notOk(page.cardFormIsVisible, 'The new card form is not rendered.');
assert.ok(page.donationButtonIsVisible, 'Donation button is rendered.');
assert.notOk(page.creditCard.submitButtonIsVisible, 'Card form button is not rendered.');
assert.ok(page.cards(0).isVisible, 'The card item is visible.');
});

test('it renders the card item and the "donate" button when a card exists', function(assert) {
assert.expect(3);

Expand Down Expand Up @@ -103,10 +150,7 @@ test('it handles adding a card correctly', function(assert) {
test('it handles donating correctly', function(assert) {
assert.expect(1);

let amount = 100;

this.set('amount', amount);

this.set('amount', 100);
this.set('card', visa);

function donateHandler() {
Expand Down
3 changes: 1 addition & 2 deletions tests/pages/components/donation/credit-card.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ import { clickable, fillable, is, isVisible, selectable, text, value } from 'emb
export default {
scope: '.credit-card-form',

isCancelVisible: isVisible('.cancel-add-card a'),

cardCVC: {
scope: '[name=card-cvc]',
fillIn: fillable(),
Expand Down Expand Up @@ -32,6 +30,7 @@ export default {
clickSubmit: clickable('button.submit-card'),
clickCancel: clickable('div.cancel-add-card a'),

submitButtonIsVisible: isVisible('button.submit-card'),
submitButtonText: text('button.submit-card'),
submitDisabled: is(':disabled', 'button')
};