braintree 2.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.rdoc +3 -3
- data/lib/braintree.rb +1 -0
- data/lib/braintree/advanced_search.rb +70 -8
- data/lib/braintree/credit_card.rb +43 -4
- data/lib/braintree/error_codes.rb +7 -0
- data/lib/braintree/subscription.rb +22 -4
- data/lib/braintree/transaction.rb +34 -17
- data/lib/braintree/transaction_search.rb +63 -0
- data/lib/braintree/transparent_redirect.rb +1 -1
- data/lib/braintree/util.rb +3 -2
- data/lib/braintree/version.rb +1 -1
- data/lib/braintree/xml/generator.rb +17 -3
- data/spec/integration/braintree/credit_card_spec.rb +47 -0
- data/spec/integration/braintree/subscription_spec.rb +39 -8
- data/spec/integration/braintree/transaction_search_spec.rb +734 -0
- data/spec/integration/braintree/transaction_spec.rb +138 -97
- data/spec/spec_helper.rb +4 -0
- data/spec/unit/braintree/credit_card_spec.rb +46 -4
- data/spec/unit/braintree/subscription_spec.rb +22 -3
- data/spec/unit/braintree/transaction_search_spec.rb +24 -0
- data/spec/unit/braintree/transaction_spec.rb +13 -2
- data/spec/unit/braintree/util_spec.rb +7 -1
- data/spec/unit/braintree/xml_spec.rb +22 -0
- metadata +16 -24
@@ -199,6 +199,51 @@ describe Braintree::Transaction do
|
|
199
199
|
result.success?.should == false
|
200
200
|
result.errors.for(:transaction).on(:base)[0].code.should == Braintree::ErrorCodes::Transaction::PaymentMethodDoesNotBelongToCustomer
|
201
201
|
end
|
202
|
+
|
203
|
+
context "new credit card for existing customer" do
|
204
|
+
it "allows a new credit card to be used for an existing customer" do
|
205
|
+
customer = Braintree::Customer.create!(
|
206
|
+
:credit_card => {
|
207
|
+
:number => Braintree::Test::CreditCardNumbers::Visa,
|
208
|
+
:expiration_date => "05/2010"
|
209
|
+
}
|
210
|
+
)
|
211
|
+
result = Braintree::Transaction.create(
|
212
|
+
:type => "sale",
|
213
|
+
:amount => Braintree::Test::TransactionAmounts::Authorize,
|
214
|
+
:customer_id => customer.id,
|
215
|
+
:credit_card => {
|
216
|
+
:number => Braintree::Test::CreditCardNumbers::Visa,
|
217
|
+
:expiration_date => "12/12"
|
218
|
+
}
|
219
|
+
)
|
220
|
+
result.success?.should == true
|
221
|
+
result.transaction.credit_card_details.masked_number.should == "401288******1881"
|
222
|
+
result.transaction.vault_credit_card.should be_nil
|
223
|
+
end
|
224
|
+
|
225
|
+
it "allows a new credit card to be used and stored in the vault" do
|
226
|
+
customer = Braintree::Customer.create!(
|
227
|
+
:credit_card => {
|
228
|
+
:number => Braintree::Test::CreditCardNumbers::Visa,
|
229
|
+
:expiration_date => "05/2010"
|
230
|
+
}
|
231
|
+
)
|
232
|
+
result = Braintree::Transaction.create(
|
233
|
+
:type => "sale",
|
234
|
+
:amount => Braintree::Test::TransactionAmounts::Authorize,
|
235
|
+
:customer_id => customer.id,
|
236
|
+
:credit_card => {
|
237
|
+
:number => Braintree::Test::CreditCardNumbers::Visa,
|
238
|
+
:expiration_date => "12/12",
|
239
|
+
},
|
240
|
+
:options => { :store_in_vault => true }
|
241
|
+
)
|
242
|
+
result.success?.should == true
|
243
|
+
result.transaction.credit_card_details.masked_number.should == "401288******1881"
|
244
|
+
result.transaction.vault_credit_card.masked_number.should == "401288******1881"
|
245
|
+
end
|
246
|
+
end
|
202
247
|
end
|
203
248
|
|
204
249
|
describe "self.create!" do
|
@@ -301,8 +346,8 @@ describe Braintree::Transaction do
|
|
301
346
|
transaction.amount.should == BigDecimal.new("100.00")
|
302
347
|
transaction.order_id.should == "123"
|
303
348
|
transaction.processor_response_code.should == "1000"
|
304
|
-
transaction.created_at.between?(Time.now -
|
305
|
-
transaction.updated_at.between?(Time.now -
|
349
|
+
transaction.created_at.between?(Time.now - 60, Time.now).should == true
|
350
|
+
transaction.updated_at.between?(Time.now - 60, Time.now).should == true
|
306
351
|
transaction.credit_card_details.bin.should == "510510"
|
307
352
|
transaction.credit_card_details.cardholder_name.should == "The Cardholder"
|
308
353
|
transaction.credit_card_details.last_4.should == "5100"
|
@@ -339,6 +384,31 @@ describe Braintree::Transaction do
|
|
339
384
|
transaction.shipping_details.country_name.should == "United States of America"
|
340
385
|
end
|
341
386
|
|
387
|
+
it "allows merchant account to be specified" do
|
388
|
+
result = Braintree::Transaction.sale(
|
389
|
+
:amount => Braintree::Test::TransactionAmounts::Authorize,
|
390
|
+
:merchant_account_id => SpecHelper::NonDefaultMerchantAccountId,
|
391
|
+
:credit_card => {
|
392
|
+
:number => Braintree::Test::CreditCardNumbers::Visa,
|
393
|
+
:expiration_date => "05/2009"
|
394
|
+
}
|
395
|
+
)
|
396
|
+
result.success?.should == true
|
397
|
+
result.transaction.merchant_account_id.should == SpecHelper::NonDefaultMerchantAccountId
|
398
|
+
end
|
399
|
+
|
400
|
+
it "uses default merchant account when it is not specified" do
|
401
|
+
result = Braintree::Transaction.sale(
|
402
|
+
:amount => Braintree::Test::TransactionAmounts::Authorize,
|
403
|
+
:credit_card => {
|
404
|
+
:number => Braintree::Test::CreditCardNumbers::Visa,
|
405
|
+
:expiration_date => "05/2009"
|
406
|
+
}
|
407
|
+
)
|
408
|
+
result.success?.should == true
|
409
|
+
result.transaction.merchant_account_id.should == SpecHelper::DefaultMerchantAccountId
|
410
|
+
end
|
411
|
+
|
342
412
|
it "can store customer and credit card in the vault" do
|
343
413
|
result = Braintree::Transaction.sale(
|
344
414
|
:amount => "100",
|
@@ -660,6 +730,31 @@ describe Braintree::Transaction do
|
|
660
730
|
result.params.should == {:transaction => {:type => 'credit', :amount => nil, :credit_card => {:expiration_date => "05/2009"}}}
|
661
731
|
result.errors.for(:transaction).on(:amount)[0].code.should == Braintree::ErrorCodes::Transaction::AmountIsRequired
|
662
732
|
end
|
733
|
+
|
734
|
+
it "allows merchant account to be specified" do
|
735
|
+
result = Braintree::Transaction.credit(
|
736
|
+
:amount => Braintree::Test::TransactionAmounts::Authorize,
|
737
|
+
:merchant_account_id => SpecHelper::NonDefaultMerchantAccountId,
|
738
|
+
:credit_card => {
|
739
|
+
:number => Braintree::Test::CreditCardNumbers::Visa,
|
740
|
+
:expiration_date => "05/2009"
|
741
|
+
}
|
742
|
+
)
|
743
|
+
result.success?.should == true
|
744
|
+
result.transaction.merchant_account_id.should == SpecHelper::NonDefaultMerchantAccountId
|
745
|
+
end
|
746
|
+
|
747
|
+
it "uses default merchant account when it is not specified" do
|
748
|
+
result = Braintree::Transaction.credit(
|
749
|
+
:amount => Braintree::Test::TransactionAmounts::Authorize,
|
750
|
+
:credit_card => {
|
751
|
+
:number => Braintree::Test::CreditCardNumbers::Visa,
|
752
|
+
:expiration_date => "05/2009"
|
753
|
+
}
|
754
|
+
)
|
755
|
+
result.success?.should == true
|
756
|
+
result.transaction.merchant_account_id.should == SpecHelper::DefaultMerchantAccountId
|
757
|
+
end
|
663
758
|
end
|
664
759
|
|
665
760
|
describe "self.credit!" do
|
@@ -721,6 +816,30 @@ describe Braintree::Transaction do
|
|
721
816
|
transaction.credit_card_details.expiration_date.should == "05/2009"
|
722
817
|
end
|
723
818
|
|
819
|
+
it "raises an error with a message if given invalid params" do
|
820
|
+
params = {
|
821
|
+
:transaction => {
|
822
|
+
:bad => "value",
|
823
|
+
:amount => Braintree::Test::TransactionAmounts::Authorize,
|
824
|
+
:credit_card => {
|
825
|
+
:number => Braintree::Test::CreditCardNumbers::Visa,
|
826
|
+
:expiration_date => "05/2009"
|
827
|
+
}
|
828
|
+
}
|
829
|
+
}
|
830
|
+
tr_data_params = {
|
831
|
+
:transaction => {
|
832
|
+
:type => "sale"
|
833
|
+
}
|
834
|
+
}
|
835
|
+
tr_data = Braintree::TransparentRedirect.transaction_data({:redirect_url => "http://example.com"}.merge(tr_data_params))
|
836
|
+
query_string_response = SpecHelper.simulate_form_post_for_tr(Braintree::Transaction.create_transaction_url, tr_data, params)
|
837
|
+
|
838
|
+
expect do
|
839
|
+
Braintree::Transaction.create_from_transparent_redirect(query_string_response)
|
840
|
+
end.to raise_error(Braintree::AuthorizationError, "Invalid params: transaction[bad]")
|
841
|
+
end
|
842
|
+
|
724
843
|
it "can put any param in tr_data" do
|
725
844
|
params = {
|
726
845
|
|
@@ -924,6 +1043,23 @@ describe Braintree::Transaction do
|
|
924
1043
|
end
|
925
1044
|
|
926
1045
|
describe "refund" do
|
1046
|
+
context "partial refunds" do
|
1047
|
+
it "allows partial refunds" do
|
1048
|
+
transaction = create_transaction_to_refund
|
1049
|
+
result = transaction.refund(transaction.amount / 2)
|
1050
|
+
result.success?.should == true
|
1051
|
+
result.new_transaction.type.should == "credit"
|
1052
|
+
end
|
1053
|
+
|
1054
|
+
it "does not all multiple refunds" do
|
1055
|
+
transaction = create_transaction_to_refund
|
1056
|
+
transaction.refund(transaction.amount / 2)
|
1057
|
+
result = transaction.refund(BigDecimal.new("1"))
|
1058
|
+
result.success?.should == false
|
1059
|
+
result.errors.for(:transaction).on(:base)[0].code.should == Braintree::ErrorCodes::Transaction::HasAlreadyBeenRefunded
|
1060
|
+
end
|
1061
|
+
end
|
1062
|
+
|
927
1063
|
it "returns a successful result if successful" do
|
928
1064
|
transaction = create_transaction_to_refund
|
929
1065
|
transaction.status.should == Braintree::Transaction::Status::Settled
|
@@ -1044,101 +1180,6 @@ describe Braintree::Transaction do
|
|
1044
1180
|
end
|
1045
1181
|
end
|
1046
1182
|
|
1047
|
-
describe "search" do
|
1048
|
-
describe "advanced" do
|
1049
|
-
it "pretty much works in one big fell swoop/hash" do
|
1050
|
-
result = Braintree::Transaction.create(
|
1051
|
-
:type => "sale",
|
1052
|
-
:amount => Braintree::Test::TransactionAmounts::Authorize,
|
1053
|
-
:credit_card => {
|
1054
|
-
:number => Braintree::Test::CreditCardNumbers::Visa,
|
1055
|
-
:expiration_date => "05/2009"
|
1056
|
-
},
|
1057
|
-
:order_id => "123",
|
1058
|
-
:customer => {
|
1059
|
-
:company => "Apple",
|
1060
|
-
:fax => "312-555-1234",
|
1061
|
-
:first_name => "Steve",
|
1062
|
-
:last_name => "Jobs",
|
1063
|
-
:phone => "614-555-1234",
|
1064
|
-
:website => "http://www.apple.com",
|
1065
|
-
},
|
1066
|
-
:billing => {
|
1067
|
-
:country_name => "United States of America",
|
1068
|
-
:extended_address => "Apt 1F",
|
1069
|
-
:locality => "Chicago",
|
1070
|
-
:postal_code => "60622",
|
1071
|
-
:region => "Illinois",
|
1072
|
-
:street_address => "1234 W North Ave",
|
1073
|
-
},
|
1074
|
-
:shipping => {
|
1075
|
-
:country_name => "United States of America",
|
1076
|
-
:extended_address => "Apt 123",
|
1077
|
-
:locality => "Bartlett",
|
1078
|
-
:postal_code => "60004",
|
1079
|
-
:region => "Illinois",
|
1080
|
-
:street_address => "123 Main St",
|
1081
|
-
}
|
1082
|
-
)
|
1083
|
-
result.success?.should == true
|
1084
|
-
expected_transaction = result.transaction
|
1085
|
-
search_criteria = {
|
1086
|
-
:billing_country_name => {:is => "United States of America"},
|
1087
|
-
:billing_extended_address => {:is => "Apt 1F"},
|
1088
|
-
:billing_locality => {:is => "Chicago"},
|
1089
|
-
:billing_postal_code => {:is => "60622"},
|
1090
|
-
:billing_region => {:is => "Illinois"},
|
1091
|
-
:billing_street_address => {:is => "1234 W North Ave"},
|
1092
|
-
:credit_card_number => {:is => Braintree::Test::CreditCardNumbers::Visa},
|
1093
|
-
:customer_company => {:is => "Apple"},
|
1094
|
-
:customer_fax => {:is => "312-555-1234"},
|
1095
|
-
:customer_first_name => {:is => "Steve"},
|
1096
|
-
:customer_last_name => {:is => "Jobs"},
|
1097
|
-
:customer_phone => {:is => "614-555-1234"},
|
1098
|
-
:customer_website => {:is => "http://www.apple.com"},
|
1099
|
-
:expiration_date => {:is => "05/2009"},
|
1100
|
-
:order_id => {:is => "123"},
|
1101
|
-
:shipping_country_name => {:is => "United States of America"},
|
1102
|
-
:shipping_extended_address => {:is => "Apt 123"},
|
1103
|
-
:shipping_locality => {:is => "Bartlett"},
|
1104
|
-
:shipping_postal_code => {:is => "60004"},
|
1105
|
-
:shipping_region => {:is => "Illinois"},
|
1106
|
-
:shipping_street_address => {:is => "123 Main St"},
|
1107
|
-
}
|
1108
|
-
search_results = Braintree::Transaction.search(search_criteria)
|
1109
|
-
search_results.should include(expected_transaction)
|
1110
|
-
end
|
1111
|
-
|
1112
|
-
it "properly parses the xml if only one transaction is found" do
|
1113
|
-
transaction = Braintree::Transaction.create!(
|
1114
|
-
:type => "sale",
|
1115
|
-
:amount => Braintree::Test::TransactionAmounts::Authorize,
|
1116
|
-
:credit_card => {
|
1117
|
-
:number => Braintree::Test::CreditCardNumbers::Visa,
|
1118
|
-
:expiration_date => "05/2009"
|
1119
|
-
}
|
1120
|
-
)
|
1121
|
-
search_results = Braintree::Transaction.search(:transaction_id => {:is => transaction.id})
|
1122
|
-
search_results.first.should == transaction
|
1123
|
-
end
|
1124
|
-
end
|
1125
|
-
|
1126
|
-
describe "basic" do
|
1127
|
-
it "returns transactions matching the given search terms" do
|
1128
|
-
transactions = Braintree::Transaction.search "1111"
|
1129
|
-
transactions._approximate_size.should > 0
|
1130
|
-
end
|
1131
|
-
|
1132
|
-
it "can iterate over the entire collection" do
|
1133
|
-
transactions = Braintree::Transaction.search "411111"
|
1134
|
-
transactions._approximate_size.should > 100
|
1135
|
-
|
1136
|
-
transaction_ids = transactions.map {|t| t.id }.uniq.compact
|
1137
|
-
transaction_ids.size.should == transactions._approximate_size
|
1138
|
-
end
|
1139
|
-
end
|
1140
|
-
end
|
1141
|
-
|
1142
1183
|
describe "status_history" do
|
1143
1184
|
it "returns an array of StatusDetail" do
|
1144
1185
|
transaction = Braintree::Transaction.sale!(
|
data/spec/spec_helper.rb
CHANGED
@@ -17,6 +17,10 @@ unless defined?(SPEC_HELPER_LOADED)
|
|
17
17
|
Braintree::Configuration.logger.level = Logger::INFO
|
18
18
|
|
19
19
|
module SpecHelper
|
20
|
+
|
21
|
+
DefaultMerchantAccountId = "sandbox_credit_card"
|
22
|
+
NonDefaultMerchantAccountId = "sandbox_credit_card_non_default"
|
23
|
+
|
20
24
|
def self.stub_time_dot_now(desired_time)
|
21
25
|
Time.class_eval do
|
22
26
|
class << self
|
@@ -10,14 +10,56 @@ describe Braintree::CreditCard do
|
|
10
10
|
end
|
11
11
|
|
12
12
|
describe "self.create_signature" do
|
13
|
-
it "should
|
14
|
-
Braintree::CreditCard._create_signature.should
|
13
|
+
it "should be what we expect" do
|
14
|
+
Braintree::CreditCard._create_signature.should == [
|
15
|
+
:cardholder_name,
|
16
|
+
:cvv,
|
17
|
+
:expiration_date,
|
18
|
+
:expiration_month,
|
19
|
+
:expiration_year,
|
20
|
+
:number,
|
21
|
+
:token,
|
22
|
+
{:options => [:make_default, :verify_card]},
|
23
|
+
{:billing_address => [
|
24
|
+
:company,
|
25
|
+
:country_name,
|
26
|
+
:extended_address,
|
27
|
+
:first_name,
|
28
|
+
:last_name,
|
29
|
+
:locality,
|
30
|
+
:postal_code,
|
31
|
+
:region,
|
32
|
+
:street_address
|
33
|
+
]},
|
34
|
+
:customer_id
|
35
|
+
]
|
15
36
|
end
|
16
37
|
end
|
17
38
|
|
18
39
|
describe "self.update_signature" do
|
19
|
-
it "should
|
20
|
-
Braintree::CreditCard._update_signature.
|
40
|
+
it "should be what we expect" do
|
41
|
+
Braintree::CreditCard._update_signature.should == [
|
42
|
+
:cardholder_name,
|
43
|
+
:cvv,
|
44
|
+
:expiration_date,
|
45
|
+
:expiration_month,
|
46
|
+
:expiration_year,
|
47
|
+
:number,
|
48
|
+
:token,
|
49
|
+
{:options => [:make_default, :verify_card]},
|
50
|
+
{:billing_address => [
|
51
|
+
:company,
|
52
|
+
:country_name,
|
53
|
+
:extended_address,
|
54
|
+
:first_name,
|
55
|
+
:last_name,
|
56
|
+
:locality,
|
57
|
+
:postal_code,
|
58
|
+
:region,
|
59
|
+
:street_address,
|
60
|
+
{:options => [:update_existing]}
|
61
|
+
]}
|
62
|
+
]
|
21
63
|
end
|
22
64
|
end
|
23
65
|
|
@@ -3,13 +3,13 @@ require File.dirname(__FILE__) + "/../spec_helper"
|
|
3
3
|
describe Braintree::Subscription do
|
4
4
|
context "price" do
|
5
5
|
it "accepts price as either a String or a BigDecimal" do
|
6
|
-
Braintree::Subscription.
|
7
|
-
Braintree::Subscription.
|
6
|
+
Braintree::Subscription._new(:price => "12.34", :transactions => []).price.should == BigDecimal.new("12.34")
|
7
|
+
Braintree::Subscription._new(:price => BigDecimal.new("12.34"), :transactions => []).price.should == BigDecimal.new("12.34")
|
8
8
|
end
|
9
9
|
|
10
10
|
it "blows up if price is not a string or BigDecimal" do
|
11
11
|
expect {
|
12
|
-
Braintree::Subscription.
|
12
|
+
Braintree::Subscription._new(:price => 12.34, :transactions => [])
|
13
13
|
}.to raise_error(/Argument must be a String or BigDecimal/)
|
14
14
|
end
|
15
15
|
end
|
@@ -23,4 +23,23 @@ describe Braintree::Subscription do
|
|
23
23
|
end.should raise_error(ArgumentError)
|
24
24
|
end
|
25
25
|
end
|
26
|
+
|
27
|
+
describe "==" do
|
28
|
+
it "returns true for subscriptions with the same id" do
|
29
|
+
subscription1 = Braintree::Subscription._new(:id => "123", :transactions => [])
|
30
|
+
subscription2 = Braintree::Subscription._new(:id => "123", :transactions => [])
|
31
|
+
subscription1.should == subscription2
|
32
|
+
end
|
33
|
+
|
34
|
+
it "returns false for subscriptions with different ids" do
|
35
|
+
subscription1 = Braintree::Subscription._new(:id => "123", :transactions => [])
|
36
|
+
subscription2 = Braintree::Subscription._new(:id => "not_123", :transactions => [])
|
37
|
+
subscription1.should_not == subscription2
|
38
|
+
end
|
39
|
+
|
40
|
+
it "returns false if not comparing to a subscription" do
|
41
|
+
subscription = Braintree::Subscription._new(:id => "123", :transactions => [])
|
42
|
+
subscription.should_not == "not a subscription"
|
43
|
+
end
|
44
|
+
end
|
26
45
|
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/../spec_helper"
|
2
|
+
|
3
|
+
describe Braintree::TransactionSearch do
|
4
|
+
it "overrides previous 'is' with new 'is' for the same field" do
|
5
|
+
search = Braintree::TransactionSearch.new
|
6
|
+
search.billing_company.is "one"
|
7
|
+
search.billing_company.is "two"
|
8
|
+
search.to_hash.should == {:billing_company => {:is => "two"}}
|
9
|
+
end
|
10
|
+
|
11
|
+
it "overrides previous 'in' with new 'in' for the same field" do
|
12
|
+
search = Braintree::TransactionSearch.new
|
13
|
+
search.status.in Braintree::Transaction::Status::Authorized
|
14
|
+
search.status.in Braintree::Transaction::Status::SubmittedForSettlement
|
15
|
+
search.to_hash.should == {:status => [Braintree::Transaction::Status::SubmittedForSettlement]}
|
16
|
+
end
|
17
|
+
|
18
|
+
it "raises if the operator 'is' is left off" do
|
19
|
+
search = Braintree::TransactionSearch.new
|
20
|
+
expect do
|
21
|
+
search.billing_company "one"
|
22
|
+
end.to raise_error(RuntimeError, "An operator is required")
|
23
|
+
end
|
24
|
+
end
|
@@ -127,7 +127,7 @@ describe Braintree::Transaction do
|
|
127
127
|
end
|
128
128
|
|
129
129
|
describe "==" do
|
130
|
-
it "returns true
|
130
|
+
it "returns true for transactions with the same id" do
|
131
131
|
first = Braintree::Transaction._new(:id => 123)
|
132
132
|
second = Braintree::Transaction._new(:id => 123)
|
133
133
|
|
@@ -135,13 +135,24 @@ describe Braintree::Transaction do
|
|
135
135
|
second.should == first
|
136
136
|
end
|
137
137
|
|
138
|
-
it "returns false
|
138
|
+
it "returns false for transactions with different ids" do
|
139
139
|
first = Braintree::Transaction._new(:id => 123)
|
140
140
|
second = Braintree::Transaction._new(:id => 124)
|
141
141
|
|
142
142
|
first.should_not == second
|
143
143
|
second.should_not == first
|
144
144
|
end
|
145
|
+
|
146
|
+
it "returns false when comparing to nil" do
|
147
|
+
Braintree::Transaction._new({}).should_not == nil
|
148
|
+
end
|
149
|
+
|
150
|
+
it "returns false when comparing to non-transactions" do
|
151
|
+
same_id_different_object = Object.new
|
152
|
+
def same_id_different_object.id; 123; end
|
153
|
+
transaction = Braintree::Transaction._new(:id => 123)
|
154
|
+
transaction.should_not == same_id_different_object
|
155
|
+
end
|
145
156
|
end
|
146
157
|
|
147
158
|
describe "new" do
|