From e88e963b4cd1303f00d605bdded38202afbb539c Mon Sep 17 00:00:00 2001 From: Rob Harrington Date: Thu, 19 Apr 2018 16:28:53 +1000 Subject: [PATCH] Alter cards interface to allow updating of default card --- .../credit_cards_controller.js.coffee | 3 +- .../darkswarm/services/credit_cards.js.coffee | 13 ++++- app/views/spree/users/_saved_cards.html.haml | 3 ++ config/locales/en.yml | 2 + spec/features/consumer/account/cards_spec.rb | 33 ++++++++++-- .../services/credit_cards_spec.js.coffee | 50 +++++++++++++++++++ 6 files changed, 97 insertions(+), 7 deletions(-) create mode 100644 spec/javascripts/unit/darkswarm/services/credit_cards_spec.js.coffee diff --git a/app/assets/javascripts/darkswarm/controllers/credit_cards_controller.js.coffee b/app/assets/javascripts/darkswarm/controllers/credit_cards_controller.js.coffee index 7fa3e4f8c4..868057b22c 100644 --- a/app/assets/javascripts/darkswarm/controllers/credit_cards_controller.js.coffee +++ b/app/assets/javascripts/darkswarm/controllers/credit_cards_controller.js.coffee @@ -1,6 +1,7 @@ -Darkswarm.controller "CreditCardsCtrl", ($scope, $timeout, CreditCard, CreditCards, Dates) -> +Darkswarm.controller "CreditCardsCtrl", ($scope, CreditCard, CreditCards) -> angular.extend(this, new FieldsetMixin($scope)) $scope.savedCreditCards = CreditCards.saved + $scope.setDefault = CreditCards.setDefault $scope.CreditCard = CreditCard $scope.secrets = CreditCard.secrets $scope.showForm = CreditCard.show diff --git a/app/assets/javascripts/darkswarm/services/credit_cards.js.coffee b/app/assets/javascripts/darkswarm/services/credit_cards.js.coffee index c588ef681c..d217a7a7bd 100644 --- a/app/assets/javascripts/darkswarm/services/credit_cards.js.coffee +++ b/app/assets/javascripts/darkswarm/services/credit_cards.js.coffee @@ -1,6 +1,15 @@ -Darkswarm.factory 'CreditCards', (savedCreditCards)-> +Darkswarm.factory 'CreditCards', ($http, $filter, savedCreditCards, RailsFlashLoader)-> new class CreditCard - saved: savedCreditCards + saved: $filter('orderBy')(savedCreditCards,'-is_default') add: (card) -> @saved.push card + + setDefault: (card) => + card.is_default = true + for othercard in @saved when othercard != card + othercard.is_default = false + $http.put("/credit_cards/#{card.id}", is_default: true).then (data) -> + RailsFlashLoader.loadFlash({success: t('js.default_card_updated')}) + , (response) -> + RailsFlashLoader.loadFlash({error: response.data.flash.error}) diff --git a/app/views/spree/users/_saved_cards.html.haml b/app/views/spree/users/_saved_cards.html.haml index d8c710607b..55d4d83442 100644 --- a/app/views/spree/users/_saved_cards.html.haml +++ b/app/views/spree/users/_saved_cards.html.haml @@ -3,11 +3,14 @@ %th= t(:card_type) %th= t(:card_number) %th= t(:card_expiry_date) + %th= t(:default?) %th= t(:delete?) %tr.card{ id: "card{{ card.id }}", ng: { repeat: "card in savedCreditCards" } } %td.brand{ ng: { bind: '::card.brand' } } %td.number{ ng: { bind: '::card.number' } } %td.expiry{ ng: { bind: '::card.expiry' } } + %td.is-default + %input{ type: 'radio', name: 'default_card', ng: { model: 'card.is_default', change: 'setDefault(card)', value: "true"} } %td.actions %a{"rel" => "nofollow", "data-method" => "delete", "ng-href" => "{{card.delete_link}}" } = t(:delete) diff --git a/config/locales/en.yml b/config/locales/en.yml index 8946c62b82..00631faf55 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1232,6 +1232,7 @@ en: saving_credit_card: Saving credit card... card_has_been_removed: "Your card has been removed (number: %{number})" card_could_not_be_removed: Sorry, the card could not be removed + default?: Default? ie_warning_headline: "Your browser is out of date :-(" ie_warning_text: "For the best Open Food Network experience, we strongly recommend upgrading your browser:" @@ -2344,6 +2345,7 @@ See the %{link} to find out more about %{sitename}'s features and to start using choose: Choose resolve_errors: Please resolve the following errors more_items: "+ %{count} More" + default_card_updated: Default Card Updated admin: enterprise_limit_reached: "You have reached the standard limit of enterprises per account. Write to %{contact_email} if you need to increase it." modals: diff --git a/spec/features/consumer/account/cards_spec.rb b/spec/features/consumer/account/cards_spec.rb index 3a9e32ddd6..6412322eaa 100644 --- a/spec/features/consumer/account/cards_spec.rb +++ b/spec/features/consumer/account/cards_spec.rb @@ -4,7 +4,8 @@ feature "Credit Cards", js: true do include AuthenticationWorkflow describe "as a logged in user" do let(:user) { create(:user) } - let!(:card) { create(:credit_card, user_id: user.id, gateway_customer_profile_id: 'cus_AZNMJ') } + let!(:card) { create(:credit_card, user_id: user.id, gateway_customer_profile_id: 'cus_AZNMJ', is_default: true) } + let!(:card2) { create(:credit_card, user_id: user.id, gateway_customer_profile_id: 'cus_FDTG') } before do quick_login_as user @@ -20,28 +21,52 @@ feature "Credit Cards", js: true do to_return(:status => 200, :body => JSON.generate(deleted: true, id: "cus_AZNMJ")) end - it "lists saved cards, shows interface for adding new cards" do + it "passes the smoke test" do visit "/account" click_link I18n.t('spree.users.show.tabs.cards') expect(page).to have_content I18n.t(:saved_cards) + # Lists saved cards within(".card#card#{card.id}") do expect(page).to have_content card.cc_type.capitalize expect(page).to have_content card.last_digits + expect(find_field('default_card')).to be_checked end + within(".card#card#{card2.id}") do + expect(page).to have_content card2.cc_type.capitalize + expect(page).to have_content card2.last_digits + expect(find_field('default_card')).to_not be_checked + end + + # Allows switching of default card + within(".card#card#{card2.id}") do + find_field('default_card').click + expect(find_field('default_card')).to be_checked + end + + expect(page).to have_content I18n.t('js.default_card_updated') + + within(".card#card#{card.id}") do + expect(find_field('default_card')).to_not be_checked + end + expect(card.reload.is_default).to be false + expect(card2.reload.is_default).to be true + # Shows the interface for adding a card click_button I18n.t(:add_a_card) expect(page).to have_field 'first_name' expect(page).to have_selector '#card-element.StripeElement' # Allows deletion of cards - click_link I18n.t(:delete) + within(".card#card#{card.id}") do + click_link I18n.t(:delete) + end expect(page).to have_content I18n.t(:card_has_been_removed, number: "x-#{card.last_digits}") - expect(page).to have_content I18n.t(:you_have_no_saved_cards) + expect(page).to_not have_selector ".card#card#{card.id}" end end end diff --git a/spec/javascripts/unit/darkswarm/services/credit_cards_spec.js.coffee b/spec/javascripts/unit/darkswarm/services/credit_cards_spec.js.coffee new file mode 100644 index 0000000000..be3106f89c --- /dev/null +++ b/spec/javascripts/unit/darkswarm/services/credit_cards_spec.js.coffee @@ -0,0 +1,50 @@ +describe 'CreditCards service', -> + CreditCards = $httpBackend = RailsFlashLoader = null + + beforeEach -> + module 'Darkswarm' + module ($provide)-> + $provide.value "savedCreditCards", [] + $provide.value "railsFlash", null + null + + inject (_CreditCards_, _$httpBackend_, _RailsFlashLoader_)-> + CreditCards = _CreditCards_ + $httpBackend = _$httpBackend_ + RailsFlashLoader = _RailsFlashLoader_ + + describe "setDefault", -> + card1 = { last4: "1234", is_default: true } + card2 = { last4: "4321", is_default: false } + card3 = { last4: "5555", is_default: false } + ajax = null + + beforeEach -> + CreditCards.saved = [card1, card2, card3] + ajax = $httpBackend.expectPUT("/credit_cards/#{card2.id}") + + it "resets the default value on other cards to false", -> + CreditCards.setDefault(card2) + expect(card1.is_default).toBe false + expect(card2.is_default).toBe true + expect(card3.is_default).toBe false + + describe "when the update request succeeds", -> + beforeEach -> + spyOn(RailsFlashLoader,"loadFlash") + ajax.respond(200) + + it "loads a success flash", -> + CreditCards.setDefault(card2) + $httpBackend.flush() + expect(RailsFlashLoader.loadFlash).toHaveBeenCalledWith({success: t('js.default_card_updated')}) + + describe "when the update request fails", -> + beforeEach -> + spyOn(RailsFlashLoader,"loadFlash") + ajax.respond(400, flash: { error: 'Some error message'}) + + it "loads a error flash", -> + CreditCards.setDefault(card2) + $httpBackend.flush() + expect(RailsFlashLoader.loadFlash).toHaveBeenCalledWith({error: 'Some error message'})