Fixing regressions and refactoring our tests

This commit is contained in:
Will Marshall
2014-07-16 16:00:44 +10:00
parent cd033c300e
commit 2b1ab53d8e
14 changed files with 304 additions and 289 deletions

View File

@@ -1,12 +1,22 @@
Darkswarm.controller "CheckoutCtrl", ($scope, storage, Order, CurrentUser) ->
$scope.Order = Order
Order.bindFieldsToLocalStorage($scope)
Darkswarm.controller "CheckoutCtrl", ($scope, storage, Checkout, CurrentUser, CurrentHub) ->
$scope.Checkout = Checkout
$scope.order = Order.order # Ordering is important
$scope.secrets = Order.secrets
# Bind to local storage
$scope.fieldsToBind = ["bill_address", "email", "payment_method_id", "shipping_method_id", "ship_address"]
prefix = "order_#{Checkout.order.id}#{Checkout.order.user_id}#{CurrentHub.hub.id}"
for field in $scope.fieldsToBind
storage.bind $scope, "Checkout.order.#{field}",
storeName: "#{prefix}_#{field}"
storage.bind $scope, "Checkout.ship_address_same_as_billing",
storeName: "#{prefix}_sameasbilling"
defaultValue: true
$scope.order = Checkout.order # Ordering is important
$scope.secrets = Checkout.secrets
$scope.enabled = if CurrentUser then true else false
$scope.purchase = (event)->
event.preventDefault()
$scope.Order.submit()
$scope.Checkout.submit()

View File

@@ -1,3 +1,5 @@
# TODO this SUCKS. Fix it
Darkswarm.controller "OrderCycleCtrl", ($scope, OrderCycle, $timeout) ->
$scope.order_cycle = OrderCycle.order_cycle
$scope.OrderCycle = OrderCycle

View File

@@ -0,0 +1,5 @@
Darkswarm.factory 'Cart', (Order)->
# Handles syncing of current cart/order state to server
new class Cart
order: Order.order

View File

@@ -0,0 +1,62 @@
Darkswarm.factory 'Checkout', (Order, $http, Navigation, CurrentHub, RailsFlashLoader, Loading)->
new class Checkout
errors: {}
secrets: {}
order: Order.order
ship_address_same_as_billing: true
submit: ->
Loading.message = "Submitting your order: please wait"
$http.put('/checkout', {order: @preprocess()}).success (data, status)=>
Navigation.go data.pat
.error (response, status)=>
Loading.clear()
@errors = response.errors
RailsFlashLoader.loadFlash(response.flash)
# Rails wants our Spree::Address data to be provided with _attributes
preprocess: ->
munged_order = {}
for name, value of @order # Clone all data from the order JSON object
switch name
when "bill_address"
munged_order["bill_address_attributes"] = value
when "ship_address"
munged_order["ship_address_attributes"] = value
when "payment_method_id"
munged_order["payments_attributes"] = [{payment_method_id: value}]
when "form_state" # don't keep this shit
else
munged_order[name] = value
if @ship_address_same_as_billing
munged_order.ship_address_attributes = munged_order.bill_address_attributes
if @paymentMethod()?.method_type == 'gateway'
angular.extend munged_order.payments_attributes[0], {
source_attributes:
number: @secrets.card_number
month: @secrets.card_month
year: @secrets.card_year
verification_value: @secrets.card_verification_value
first_name: @order.bill_address.firstname
last_name: @order.bill_address.lastname
}
munged_order
shippingMethod: ->
@order.shipping_methods[@order.shipping_method_id] if @order.shipping_method_id
requireShipAddress: ->
@shippingMethod()?.require_ship_address
shippingPrice: ->
@shippingMethod()?.price || 0.0
paymentMethod: ->
@order.payment_methods[@order.payment_method_id]
cartTotal: ->
@shippingPrice() + @order.display_total

View File

@@ -1,77 +1,5 @@
Darkswarm.factory 'Order', ($resource, order, $http, Navigation, storage, CurrentHub, RailsFlashLoader, Loading)->
Darkswarm.factory 'Order', (order)->
new class Order
errors: {}
secrets: {}
order: order
ship_address_same_as_billing: true
#Whitelist of fields from Order.order to bind into localStorage
fieldsToBind: ["bill_address", "email", "payment_method_id", "shipping_method_id", "ship_address"]
# Bind all the fields from fieldsToBind, + anything on the Order class
bindFieldsToLocalStorage: (scope)=>
prefix = "order_#{@order.id}#{@order.user_id}#{CurrentHub.hub.id}"
for field in @fieldsToBind
storage.bind scope, "Order.order.#{field}",
storeName: "#{prefix}_#{field}"
storage.bind scope, "Order.ship_address_same_as_billing",
storeName: "#{prefix}_sameasbilling"
defaultValue: true
submit: ->
Loading.message = "Submitting your order: please wait"
$http.put('/checkout', {order: @preprocess()}).success (data, status)=>
Navigation.go data.path
.error (response, status)=>
Loading.clear()
@errors = response.errors
RailsFlashLoader.loadFlash(response.flash)
# Rails wants our Spree::Address data to be provided with _attributes
preprocess: ->
munged_order = {}
for name, value of @order # Clone all data from the order JSON object
switch name
when "bill_address"
munged_order["bill_address_attributes"] = value
when "ship_address"
munged_order["ship_address_attributes"] = value
when "payment_method_id"
munged_order["payments_attributes"] = [{payment_method_id: value}]
when "form_state" # don't keep this shit
else
munged_order[name] = value
if @ship_address_same_as_billing
munged_order.ship_address_attributes = munged_order.bill_address_attributes
if @paymentMethod()?.method_type == 'gateway'
angular.extend munged_order.payments_attributes[0], {
source_attributes:
number: @secrets.card_number
month: @secrets.card_month
year: @secrets.card_year
verification_value: @secrets.card_verification_value
first_name: @order.bill_address.firstname
last_name: @order.bill_address.lastname
}
munged_order
shippingMethod: ->
@order.shipping_methods[@order.shipping_method_id] if @order.shipping_method_id
requireShipAddress: ->
@shippingMethod()?.require_ship_address
shippingPrice: ->
@shippingMethod()?.price || 0.0
paymentMethod: ->
@order.payment_methods[@order.payment_method_id]
cartTotal: ->
@shippingPrice() + @order.display_total

View File

@@ -23,8 +23,8 @@ Darkswarm.factory 'Product', ($resource, Enterprises, Dereferencer, Taxons) ->
extend: ->
for product in @products
if product.variants.length > 0
if product.variants?.length > 0
prices = (v.price for v in product.variants)
product.price = Math.min.apply(null, prices)
product.hasVariants = product.variants.length > 0
product.hasVariants = product.variants?.length > 0

View File

@@ -10,29 +10,15 @@ describe "AccordionCtrl", ->
module ($provide)->
$provide.value "CurrentHub", CurrentHubMock
null
localStorage.clear()
inject ($controller, $rootScope) ->
scope = $rootScope.$new()
scope.order =
id: 129
ctrl = $controller 'AccordionCtrl', {$scope: scope}
describe "loading incomplete form", ->
beforeEach ->
inject ($controller, $rootScope) ->
scope = $rootScope.$new()
scope.order =
id: 129
ctrl = $controller 'AccordionCtrl', {$scope: scope}
it "defaults the details accordion to visible", ->
expect(scope.accordion.details).toEqual true
it "defaults the details accordion to visible", ->
expect(scope.accordion.details).toEqual true
it "changes accordion", ->
scope.show "shipping"
expect(scope.accordion["shipping"]).toEqual true
describe "loading complete form", ->
beforeEach ->
inject ($controller, $rootScope) ->
scope = $rootScope.$new()
scope.checkout =
$valid: true
scope.order =
id: 129
ctrl = $controller 'AccordionCtrl', {$scope: scope}
it "changes accordion", ->
scope.show "shipping"
expect(scope.accordion["shipping"]).toEqual true

View File

@@ -1,47 +1,72 @@
describe "CheckoutCtrl", ->
ctrl = null
scope = null
Order = null
Checkout = null
CurrentUser = null
CurrentHubMock =
hub:
id: 1
storage = null
beforeEach ->
module("Darkswarm")
angular.module('Darkswarm').value('user', {})
Order =
angular.module('Darkswarm').value('currentHub', {id: 1})
module ($provide)->
$provide.value "CurrentHub", CurrentHubMock
null
Checkout =
submit: ->
navigate: ->
bindFieldsToLocalStorage: ->
order:
id: 1
email: "public"
user_id: 1
secrets:
card_number: "this is a secret"
describe "with user", ->
beforeEach ->
inject ($controller, $rootScope) ->
inject ($controller, $rootScope, _storage_) ->
storage = _storage_
spyOn(storage, "bind").andCallThrough()
scope = $rootScope.$new()
spyOn(Order, "bindFieldsToLocalStorage")
ctrl = $controller 'CheckoutCtrl', {$scope: scope, Order: Order, CurrentUser: {}}
ctrl = $controller 'CheckoutCtrl', {$scope: scope, Checkout: Checkout, CurrentUser: {}}
it "delegates to the service on submit", ->
event =
preventDefault: ->
spyOn(Order, "submit")
spyOn(Checkout, "submit")
scope.purchase(event)
expect(Order.submit).toHaveBeenCalled()
expect(Checkout.submit).toHaveBeenCalled()
it "is enabled", ->
expect(scope.enabled).toEqual true
it "triggers localStorage binding", ->
expect(Order.bindFieldsToLocalStorage).toHaveBeenCalled()
describe "Local storage", ->
it "binds to localStorage when given a scope", ->
prefix = "order_#{scope.order.id}#{scope.order.user_id}#{CurrentHubMock.hub.id}"
field = scope.fieldsToBind[0]
expect(storage.bind).toHaveBeenCalledWith(scope, "Checkout.order.#{field}", {storeName: "#{prefix}_#{field}"})
expect(storage.bind).toHaveBeenCalledWith(scope, "Checkout.ship_address_same_as_billing", {storeName: "#{prefix}_sameasbilling", defaultValue: true})
it "it can retrieve data from localstorage", ->
prefix = "order_#{scope.order.id}#{scope.order.user_id}#{CurrentHubMock.hub.id}"
expect(localStorage.getItem("#{prefix}_email")).toMatch "public"
it "does not store secrets in local storage", ->
Checkout.secrets =
card_number: "superfuckingsecret"
keys = (localStorage.key(i) for i in [0..localStorage.length])
for key in keys
expect(localStorage.getItem(key)).not.toMatch Checkout.secrets.card_number
describe "without user", ->
beforeEach ->
inject ($controller, $rootScope) ->
scope = $rootScope.$new()
ctrl = $controller 'CheckoutCtrl', {$scope: scope, Order: Order, CurrentUser: undefined}
ctrl = $controller 'CheckoutCtrl', {$scope: scope, Checkout: Checkout, CurrentUser: undefined}
it "is disabled", ->
expect(scope.enabled).toEqual false
@@ -49,4 +74,4 @@ describe "CheckoutCtrl", ->
it "does not store secrets in local storage", ->
keys = (localStorage.key(i) for i in [0..localStorage.length])
for key in keys
expect(localStorage.getItem(key)).not.toMatch Order.secrets.card_number
expect(localStorage.getItem(key)).not.toMatch Checkout.secrets.card_number

View File

@@ -14,5 +14,6 @@ describe 'OrderCycleCtrl', ->
scope = {}
ctrl = $controller 'OrderCycleCtrl', {$scope: scope, OrderCycle: OrderCycle}
#it "puts the order cycle in scope", ->
#expect(scope.order_cycle).toEqual "test"
it "puts the order cycle in scope", ->
expect(scope.order_cycle).toEqual "test"

View File

@@ -5,6 +5,7 @@ describe "ProductNodeCtrl", ->
id: 99
price: 10.00
variants: []
supplier: {}
beforeEach ->
module('Darkswarm')
@@ -13,12 +14,5 @@ describe "ProductNodeCtrl", ->
product: product
ctrl = $controller 'ProductNodeCtrl', {$scope: scope}
describe "determining the price to display for a product", ->
it "displays the product price when the product does not have variants", ->
expect(scope.price()).toEqual 10.00
it "displays the minimum variant price when the product has variants", ->
scope.product =
price: 11
variants: [{price: 22}, {price: 33}]
expect(scope.price()).toEqual 22
it "puts a reference to supplier in the scope", ->
expect(scope.enterprise).toBe product.supplier

View File

@@ -9,7 +9,7 @@ describe 'ProductsCtrl', ->
Product =
all: ->
update: ->
products: "testy mctest"
products: ["testy mctest"]
OrderCycle =
order_cycle: {}
@@ -18,4 +18,19 @@ describe 'ProductsCtrl', ->
ctrl = $controller 'ProductsCtrl', {$scope: scope, Product: Product, OrderCycle: OrderCycle}
it 'fetches products from Product', ->
expect(scope.Product.products).toEqual 'testy mctest'
expect(scope.Product.products).toEqual ['testy mctest']
it "increments the limit up to the number of products", ->
scope.limit = 0
scope.incrementLimit()
expect(scope.limit).toEqual 1
scope.incrementLimit()
expect(scope.limit).toEqual 1
it "blocks keypresses on code 13", ->
event =
keyCode: 13
preventDefault: ->
spyOn(event, 'preventDefault')
scope.searchKeypress(event)
expect(event.preventDefault).toHaveBeenCalled()

View File

@@ -0,0 +1,133 @@
describe 'Checkout service', ->
Checkout = null
orderData = null
$httpBackend = null
Navigation = null
flash = null
scope = null
FlashLoaderMock =
loadFlash: (arg)->
beforeEach ->
orderData =
id: 3102
payment_method_id: null
email: "test@test.com"
bill_address:
test: "foo"
firstname: "Robert"
lastname: "Harrington"
ship_address: {test: "bar"}
user_id: 901
shipping_methods:
7:
require_ship_address: true
price: 0.0
25:
require_ship_address: false
price: 13
payment_methods:
99:
test: "foo"
method_type: "gateway"
123:
test: "bar"
method_type: "check"
angular.module('Darkswarm').value('order', orderData)
module 'Darkswarm'
module ($provide)->
$provide.value "RailsFlashLoader", FlashLoaderMock
null
inject ($injector, _$httpBackend_, $rootScope)->
$httpBackend = _$httpBackend_
Checkout = $injector.get("Checkout")
scope = $rootScope.$new()
scope.Checkout = Checkout
Navigation = $injector.get("Navigation")
flash = $injector.get("flash")
spyOn(Navigation, "go") # Stubbing out writes to window.location
it "defaults to no shipping method", ->
expect(Checkout.order.shipping_method_id).toEqual null
expect(Checkout.shippingMethod()).toEqual undefined
it "has a shipping price of zero with no shipping method", ->
expect(Checkout.shippingPrice()).toEqual 0.0
describe "with shipping method", ->
beforeEach ->
Checkout.order.shipping_method_id = 7
it 'Tracks whether a ship address is required', ->
expect(Checkout.requireShipAddress()).toEqual true
Checkout.order.shipping_method_id = 25
expect(Checkout.requireShipAddress()).toEqual false
it 'Gets the current shipping price', ->
expect(Checkout.shippingPrice()).toEqual 0.0
Checkout.order.shipping_method_id = 25
expect(Checkout.shippingPrice()).toEqual 13
it 'Gets the current payment method', ->
expect(Checkout.paymentMethod()).toEqual null
Checkout.order.payment_method_id = 99
expect(Checkout.paymentMethod()).toEqual {test: "foo", method_type: "gateway"}
it "Posts the Checkout to the server", ->
$httpBackend.expectPUT("/checkout", {order: Checkout.preprocess()}).respond 200, {path: "test"}
Checkout.submit()
$httpBackend.flush()
it "sends flash messages to the flash service", ->
spyOn(FlashLoaderMock, "loadFlash") # Stubbing out writes to window.location
$httpBackend.expectPUT("/checkout").respond 400, {flash: {error: "frogs"}}
Checkout.submit()
$httpBackend.flush()
expect(FlashLoaderMock.loadFlash).toHaveBeenCalledWith {error: "frogs"}
it "puts errors into the scope", ->
$httpBackend.expectPUT("/checkout").respond 400, {errors: {error: "frogs"}}
Checkout.submit()
$httpBackend.flush()
expect(Checkout.errors).toEqual {error: "frogs"}
describe "data preprocessing", ->
beforeEach ->
Checkout.order.payment_method_id = 99
Checkout.secrets =
card_number: "1234567890123456"
card_month: "10"
card_year: "2015"
card_verification_value: "123"
it "munges the order attributes to add _attributes as Rails needs", ->
expect(Checkout.preprocess().bill_address_attributes).not.toBe(undefined)
expect(Checkout.preprocess().bill_address).toBe(undefined)
expect(Checkout.preprocess().ship_address_attributes).not.toBe(undefined)
expect(Checkout.preprocess().ship_address).toBe(undefined)
it "munges the order attributes to clone ship address from bill address", ->
Checkout.ship_address_same_as_billing = false
expect(Checkout.preprocess().ship_address_attributes).toEqual(orderData.ship_address)
Checkout.ship_address_same_as_billing = true
expect(Checkout.preprocess().ship_address_attributes).toEqual(orderData.bill_address)
it "creates attributes for card fields", ->
source_attributes = Checkout.preprocess().payments_attributes[0].source_attributes
expect(source_attributes).toBeDefined()
expect(source_attributes.number).toBe Checkout.secrets.card_number
expect(source_attributes.month).toBe Checkout.secrets.card_month
expect(source_attributes.year).toBe Checkout.secrets.card_year
expect(source_attributes.verification_value).toBe Checkout.secrets.card_verification_value
expect(source_attributes.first_name).toBe Checkout.order.bill_address.firstname
expect(source_attributes.last_name).toBe Checkout.order.bill_address.lastname
it "does not create attributes for card fields when no card is supplied", ->
Checkout.order.payment_method_id = 123
source_attributes = Checkout.preprocess().payments_attributes[0].source_attributes
expect(source_attributes).not.toBeDefined()

View File

@@ -1,159 +0,0 @@
describe 'Order service', ->
Order = null
orderData = null
$httpBackend = null
Navigation = null
flash = null
storage = null
scope = null
CurrentHubMock =
hub:
id: 1
FlashLoaderMock =
loadFlash: (arg)->
beforeEach ->
orderData =
id: 3102
payment_method_id: null
email: "test@test.com"
bill_address:
test: "foo"
firstname: "Robert"
lastname: "Harrington"
ship_address: {test: "bar"}
user_id: 901
shipping_methods:
7:
require_ship_address: true
price: 0.0
25:
require_ship_address: false
price: 13
payment_methods:
99:
test: "foo"
method_type: "gateway"
123:
test: "bar"
method_type: "check"
angular.module('Darkswarm').value('order', orderData)
module 'Darkswarm'
module ($provide)->
$provide.value "CurrentHub", CurrentHubMock
$provide.value "RailsFlashLoader", FlashLoaderMock
null
inject ($injector, _$httpBackend_, _storage_, $rootScope)->
$httpBackend = _$httpBackend_
storage = _storage_
Order = $injector.get("Order")
scope = $rootScope.$new()
scope.Order = Order
Navigation = $injector.get("Navigation")
flash = $injector.get("flash")
spyOn(Navigation, "go") # Stubbing out writes to window.location
it "defaults to no shipping method", ->
expect(Order.order.shipping_method_id).toEqual null
expect(Order.shippingMethod()).toEqual undefined
it "has a shipping price of zero with no shipping method", ->
expect(Order.shippingPrice()).toEqual 0.0
it "binds to localStorage when given a scope", ->
spyOn(storage, "bind")
Order.fieldsToBind = ["testy"]
Order.bindFieldsToLocalStorage({})
prefix = "order_#{Order.order.id}#{Order.order.user_id}#{CurrentHubMock.hub.id}"
expect(storage.bind).toHaveBeenCalledWith({}, "Order.order.testy", {storeName: "#{prefix}_testy"})
expect(storage.bind).toHaveBeenCalledWith({}, "Order.ship_address_same_as_billing", {storeName: "#{prefix}_sameasbilling", defaultValue: true})
it "binds order to local storage", ->
Order.bindFieldsToLocalStorage(scope)
prefix = "order_#{Order.order.id}#{Order.order.user_id}#{CurrentHubMock.hub.id}"
expect(localStorage.getItem("#{prefix}_email")).toMatch "test@test.com"
it "does not store secrets in local storage", ->
Order.secrets =
card_number: "superfuckingsecret"
Order.bindFieldsToLocalStorage(scope)
keys = (localStorage.key(i) for i in [0..localStorage.length])
for key in keys
expect(localStorage.getItem(key)).not.toMatch Order.secrets.card_number
describe "with shipping method", ->
beforeEach ->
Order.order.shipping_method_id = 7
it 'Tracks whether a ship address is required', ->
expect(Order.requireShipAddress()).toEqual true
Order.order.shipping_method_id = 25
expect(Order.requireShipAddress()).toEqual false
it 'Gets the current shipping price', ->
expect(Order.shippingPrice()).toEqual 0.0
Order.order.shipping_method_id = 25
expect(Order.shippingPrice()).toEqual 13
it 'Gets the current payment method', ->
expect(Order.paymentMethod()).toEqual null
Order.order.payment_method_id = 99
expect(Order.paymentMethod()).toEqual {test: "foo", method_type: "gateway"}
it "Posts the Order to the server", ->
$httpBackend.expectPUT("/checkout", {order: Order.preprocess()}).respond 200, {path: "test"}
Order.submit()
$httpBackend.flush()
it "sends flash messages to the flash service", ->
spyOn(FlashLoaderMock, "loadFlash") # Stubbing out writes to window.location
$httpBackend.expectPUT("/checkout").respond 400, {flash: {error: "frogs"}}
Order.submit()
$httpBackend.flush()
expect(FlashLoaderMock.loadFlash).toHaveBeenCalledWith {error: "frogs"}
it "puts errors into the scope", ->
$httpBackend.expectPUT("/checkout").respond 400, {errors: {error: "frogs"}}
Order.submit()
$httpBackend.flush()
expect(Order.errors).toEqual {error: "frogs"}
describe "data preprocessing", ->
beforeEach ->
Order.order.payment_method_id = 99
Order.secrets =
card_number: "1234567890123456"
card_month: "10"
card_year: "2015"
card_verification_value: "123"
it "munges the order attributes to add _attributes as Rails needs", ->
expect(Order.preprocess().bill_address_attributes).not.toBe(undefined)
expect(Order.preprocess().bill_address).toBe(undefined)
expect(Order.preprocess().ship_address_attributes).not.toBe(undefined)
expect(Order.preprocess().ship_address).toBe(undefined)
it "munges the order attributes to clone ship address from bill address", ->
Order.ship_address_same_as_billing = false
expect(Order.preprocess().ship_address_attributes).toEqual(orderData.ship_address)
Order.ship_address_same_as_billing = true
expect(Order.preprocess().ship_address_attributes).toEqual(orderData.bill_address)
it "creates attributes for card fields", ->
source_attributes = Order.preprocess().payments_attributes[0].source_attributes
expect(source_attributes).toBeDefined()
expect(source_attributes.number).toBe Order.secrets.card_number
expect(source_attributes.month).toBe Order.secrets.card_month
expect(source_attributes.year).toBe Order.secrets.card_year
expect(source_attributes.verification_value).toBe Order.secrets.card_verification_value
expect(source_attributes.first_name).toBe Order.order.bill_address.firstname
expect(source_attributes.last_name).toBe Order.order.bill_address.lastname
it "does not create attributes for card fields when no card is supplied", ->
Order.order.payment_method_id = 123
source_attributes = Order.preprocess().payments_attributes[0].source_attributes
expect(source_attributes).not.toBeDefined()

View File

@@ -7,7 +7,8 @@ describe 'Product service', ->
test: "cats"
supplier:
id: 9
price: 11
variants: []
beforeEach ->
module 'Darkswarm'
module ($provide)->
@@ -29,3 +30,15 @@ describe 'Product service', ->
$httpBackend.expectGET("/shop/products").respond([{supplier : {id: 9}}])
$httpBackend.flush()
expect(Product.products[0].supplier).toBe Enterprises.enterprises_by_id["9"]
describe "determining the price to display for a product", ->
it "displays the product price when the product does not have variants", ->
$httpBackend.expectGET("/shop/products").respond([product])
$httpBackend.flush()
expect(Product.products[0].price).toEqual 11.00
it "displays the minimum variant price when the product has variants", ->
product.variants = [{price: 22}, {price: 33}]
$httpBackend.expectGET("/shop/products").respond([product])
$httpBackend.flush()
expect(Product.products[0].price).toEqual 22