amazon-associates 0.6.3
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/.gitignore +3 -0
- data/CHANGELOG +29 -0
- data/LICENSE +21 -0
- data/README.rdoc +84 -0
- data/Rakefile +62 -0
- data/VERSION.yml +5 -0
- data/amazon-associates.gemspec +118 -0
- data/lib/amazon-associates.rb +90 -0
- data/lib/amazon-associates/caching/filesystem_cache.rb +121 -0
- data/lib/amazon-associates/errors.rb +23 -0
- data/lib/amazon-associates/extensions/core.rb +31 -0
- data/lib/amazon-associates/extensions/hpricot.rb +115 -0
- data/lib/amazon-associates/request.rb +143 -0
- data/lib/amazon-associates/requests/browse_node.rb +10 -0
- data/lib/amazon-associates/requests/cart.rb +81 -0
- data/lib/amazon-associates/requests/item.rb +13 -0
- data/lib/amazon-associates/responses/browse_node_lookup_response.rb +10 -0
- data/lib/amazon-associates/responses/cart_responses.rb +26 -0
- data/lib/amazon-associates/responses/item_lookup_response.rb +16 -0
- data/lib/amazon-associates/responses/item_search_response.rb +20 -0
- data/lib/amazon-associates/responses/response.rb +27 -0
- data/lib/amazon-associates/responses/similarity_lookup_response.rb +9 -0
- data/lib/amazon-associates/types/api_result.rb +8 -0
- data/lib/amazon-associates/types/browse_node.rb +48 -0
- data/lib/amazon-associates/types/cart.rb +87 -0
- data/lib/amazon-associates/types/customer_review.rb +15 -0
- data/lib/amazon-associates/types/editorial_review.rb +8 -0
- data/lib/amazon-associates/types/error.rb +8 -0
- data/lib/amazon-associates/types/image.rb +37 -0
- data/lib/amazon-associates/types/image_set.rb +11 -0
- data/lib/amazon-associates/types/item.rb +156 -0
- data/lib/amazon-associates/types/listmania_list.rb +9 -0
- data/lib/amazon-associates/types/measurement.rb +47 -0
- data/lib/amazon-associates/types/offer.rb +10 -0
- data/lib/amazon-associates/types/ordinal.rb +24 -0
- data/lib/amazon-associates/types/price.rb +29 -0
- data/lib/amazon-associates/types/requests.rb +50 -0
- data/spec/requests/browse_node_lookup_spec.rb +41 -0
- data/spec/requests/item_search_spec.rb +27 -0
- data/spec/spec_helper.rb +8 -0
- data/spec/types/cart_spec.rb +294 -0
- data/spec/types/item_spec.rb +55 -0
- data/spec/types/measurement_spec.rb +43 -0
- data/test/amazon/browse_node_test.rb +34 -0
- data/test/amazon/cache_test.rb +33 -0
- data/test/amazon/caching/filesystem_cache_test.rb +198 -0
- data/test/amazon/item_test.rb +397 -0
- data/test/test_helper.rb +9 -0
- data/test/utilities/filesystem_test_helper.rb +35 -0
- metadata +216 -0
@@ -0,0 +1,47 @@
|
|
1
|
+
module Amazon
|
2
|
+
module Associates
|
3
|
+
class Measurement < ApiResult
|
4
|
+
include Comparable
|
5
|
+
|
6
|
+
xml_reader :value, :from => :content, :as => Float
|
7
|
+
xml_reader :units, :from => :attr
|
8
|
+
|
9
|
+
def initialize(value = nil, units = 'pixels')
|
10
|
+
@value = value && Float(value)
|
11
|
+
@units = units.to_s
|
12
|
+
normalize_hundredths
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_s
|
16
|
+
value = @value.whole? ? @value.to_i : @value
|
17
|
+
#singularize here to avoid comparison problems
|
18
|
+
units = @value == 1 ? @units.singularize : @units
|
19
|
+
[value, units].join(' ')
|
20
|
+
end
|
21
|
+
alias_attribute :inspect, :to_s
|
22
|
+
|
23
|
+
def to_i
|
24
|
+
@value.round
|
25
|
+
end
|
26
|
+
|
27
|
+
def <=>(other)
|
28
|
+
return nil unless @units == other.units
|
29
|
+
|
30
|
+
@value <=> other.value
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
def after_parse
|
35
|
+
@units ||= 'pixels'
|
36
|
+
normalize_hundredths
|
37
|
+
end
|
38
|
+
|
39
|
+
def normalize_hundredths
|
40
|
+
if @units.try(:starts_with?, 'hundredths-')
|
41
|
+
@value /= 100.0
|
42
|
+
@units = @units.split('hundredths-')[1]
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
module Amazon
|
2
|
+
module Associates
|
3
|
+
class Offer < ApiResult
|
4
|
+
xml_reader :listing_id, :from => 'OfferListingId'
|
5
|
+
xml_reader :price, :as => Price
|
6
|
+
xml_reader :availability
|
7
|
+
xml_reader :is_eligible_for_super_saver_shipping?, :in => 'xmlns:OfferListing'
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Amazon
|
2
|
+
module Associates
|
3
|
+
class Ordinal < ApiResult
|
4
|
+
include Comparable
|
5
|
+
|
6
|
+
xml_reader :value, :from => :content do |val|
|
7
|
+
val.to_i
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize(value = nil)
|
11
|
+
@value = value && value.to_i
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_s
|
15
|
+
@value.ordinalize
|
16
|
+
end
|
17
|
+
alias_attribute :inspect, :to_s
|
18
|
+
|
19
|
+
def ==(other)
|
20
|
+
@value == other
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Amazon
|
2
|
+
module Associates
|
3
|
+
class Price < ApiResult
|
4
|
+
include Comparable
|
5
|
+
attr_reader :cents, :currency
|
6
|
+
|
7
|
+
xml_reader :to_s, :from => 'FormattedPrice'
|
8
|
+
xml_reader :currency, :from => 'CurrencyCode'
|
9
|
+
xml_reader :cents, :from => 'Amount', :as => Integer
|
10
|
+
|
11
|
+
def initialize(str = nil, cents = nil, currency = nil)
|
12
|
+
@to_s = str.to_s
|
13
|
+
@cents = Integer(cents)
|
14
|
+
@currency = currency.to_s
|
15
|
+
end
|
16
|
+
|
17
|
+
def <=>(other)
|
18
|
+
return nil if @currency.nil? or @cents.nil?
|
19
|
+
return nil if @currency != other.currency
|
20
|
+
|
21
|
+
@cents <=> other.cents
|
22
|
+
end
|
23
|
+
|
24
|
+
def inspect
|
25
|
+
"#{to_s} #{currency}"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Amazon
|
2
|
+
module Associates
|
3
|
+
class Request < ApiResult
|
4
|
+
xml_reader :valid?, :from => 'IsValid', :required => true
|
5
|
+
xml_reader :errors, :as => [Error] do |errors|
|
6
|
+
errors.collect do |error|
|
7
|
+
if error.code && !IGNORE_ERRORS.include?(error.code)
|
8
|
+
if exception = ERROR[error.code]
|
9
|
+
exception.new("#{error.message} (#{@url})")
|
10
|
+
else
|
11
|
+
RuntimeError.new("#{error.code}: #{error.message} (#{@url})")
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def ==(other)
|
18
|
+
(instance_variables.sort == other.instance_variables.sort) && instance_variables.all? do |v|
|
19
|
+
instance_variable_get(v) == other.instance_variable_get(v)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class ItemSearchRequest < ApiResult
|
25
|
+
xml_name 'ItemSearchRequest'
|
26
|
+
|
27
|
+
xml_reader :current_page, :from => 'ItemPage', :as => Integer, :else => 1
|
28
|
+
end
|
29
|
+
|
30
|
+
class ItemLookupRequest < ApiResult
|
31
|
+
end
|
32
|
+
|
33
|
+
class BrowseNodeLookupRequest < ApiResult
|
34
|
+
xml_name 'BrowseNodeLookupRequest'
|
35
|
+
|
36
|
+
xml_reader :response_groups, :as => []
|
37
|
+
end
|
38
|
+
|
39
|
+
class CartRequest < ApiResult
|
40
|
+
end
|
41
|
+
|
42
|
+
class OperationRequest < ApiResult
|
43
|
+
xml_name 'OperationRequest'
|
44
|
+
|
45
|
+
xml_reader :request_id
|
46
|
+
xml_reader :arguments, :as => {:key => '@Name', :value => '@Value'}
|
47
|
+
xml_reader :request_processing_time
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
module Amazon
|
4
|
+
module Associates
|
5
|
+
describe ".browse_node_lookup" do
|
6
|
+
context "with 'TopSellers' response group" do
|
7
|
+
before(:all) do
|
8
|
+
@response = Amazon::Associates.browse_node_lookup("5", :response_group => "TopSellers")
|
9
|
+
end
|
10
|
+
it_should_behave_like "Amazon Associates response"
|
11
|
+
|
12
|
+
it "should work" do
|
13
|
+
@response.browse_nodes.first.id.should == '5'
|
14
|
+
@response.request_query.response_groups.first.should == "TopSellers"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
context "with 'BrowseNodeInfo' response group" do
|
19
|
+
before(:all) do
|
20
|
+
@response = Amazon::Associates.browse_node_lookup("5", :response_group => "BrowseNodeInfo")
|
21
|
+
end
|
22
|
+
it_should_behave_like "Amazon Associates response"
|
23
|
+
|
24
|
+
it "should work" do
|
25
|
+
@response.request_query.response_groups.first.should == "BrowseNodeInfo"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context "with 'NewReleases' response group" do
|
30
|
+
before(:all) do
|
31
|
+
@response = Amazon::Associates.browse_node_lookup("5", :response_group => "NewReleases")
|
32
|
+
end
|
33
|
+
it_should_behave_like "Amazon Associates response"
|
34
|
+
|
35
|
+
it "should work" do
|
36
|
+
@response.request_query.response_groups.first.should == "NewReleases"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
module Amazon
|
4
|
+
module Associates
|
5
|
+
describe ".item_search" do
|
6
|
+
context "when omitting required parameters" do
|
7
|
+
it "should fail" do
|
8
|
+
proc { Amazon::Associates.item_search(nil) }.should raise_error(Amazon::Associates::RequiredParameterMissing)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
context "when the country is not recognized" do
|
13
|
+
it "should fail" do
|
14
|
+
proc { Amazon::Associates.item_search('ruby', :country => :asfdkjjk) }.should raise_error(Amazon::Associates::RequestError)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
context "on valid request" do
|
19
|
+
before(:all) do
|
20
|
+
@response = Amazon::Associates.item_search("ruby", :item_page => 2)
|
21
|
+
end
|
22
|
+
|
23
|
+
it_should_behave_like "Amazon Associates response"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,294 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
module Amazon
|
4
|
+
module Associates
|
5
|
+
describe Cart do
|
6
|
+
before(:all) do
|
7
|
+
@potter = Amazon::Associates::item_lookup("0545010225").item
|
8
|
+
@batman = Amazon::Associates::item_search("batman").items.first
|
9
|
+
@joker = Amazon::Associates::item_search("joker").items.first
|
10
|
+
|
11
|
+
@existing_items = [@potter, @batman]
|
12
|
+
@existing_cart = Cart.create(@potter => 1, @batman => 3)
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "a cart", :shared => true do
|
16
|
+
it "should have a valid purchase_url" do
|
17
|
+
@cart.purchase_url.should_not be_blank
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should have a valid hmac" do
|
21
|
+
@cart.hmac.should_not be_blank
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should have a valid id" do
|
25
|
+
@cart.id.should_not be_blank
|
26
|
+
end
|
27
|
+
|
28
|
+
describe "#items" do
|
29
|
+
subject { @cart.items }
|
30
|
+
|
31
|
+
it { should be_an_instance_of(Array) }
|
32
|
+
it { should be_frozen }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "a valid cart", :shared => true do
|
37
|
+
it_should_behave_like "a cart"
|
38
|
+
|
39
|
+
it "should be in a valid state" do
|
40
|
+
@cart.changed?.should be_false
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe "a modified cart", :shared => true do
|
45
|
+
it_should_behave_like "a cart"
|
46
|
+
|
47
|
+
it "should be in a valid state" do
|
48
|
+
@cart.changed?.should be_true
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe ".create" do
|
53
|
+
context "items passed in hash form" do
|
54
|
+
before(:all) do
|
55
|
+
@cart = Cart.create(@potter => 1, @batman => 3)
|
56
|
+
end
|
57
|
+
|
58
|
+
it_should_behave_like "a valid cart"
|
59
|
+
|
60
|
+
it "should create a cart with those items in the quantities provided" do
|
61
|
+
@cart.items.index_by(&:quantity).should == {1 => @potter, 3 => @batman}
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
context "items passed in array form" do
|
66
|
+
context "with actual items" do
|
67
|
+
before(:all) do
|
68
|
+
@cart = Cart.create([@potter, @batman])
|
69
|
+
end
|
70
|
+
|
71
|
+
it_should_behave_like "a valid cart"
|
72
|
+
|
73
|
+
it "should create a chart with those items, implicitly 1 each" do
|
74
|
+
@cart.items.should =~ [@potter, @batman]
|
75
|
+
@cart.items.map(&:quantity).should == [1, 1]
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
context "with hashes containing item asins" do
|
80
|
+
before(:all) do
|
81
|
+
@items = { { :asin => "0974514055" } => 2, { :asin => "0672328844" } => 3 }
|
82
|
+
@cart = Cart.create(@items)
|
83
|
+
end
|
84
|
+
|
85
|
+
it_should_behave_like "a valid cart"
|
86
|
+
|
87
|
+
describe "#items" do
|
88
|
+
it "should match the passed items asins" do
|
89
|
+
@cart.items.map(&:asin).should =~ @items.keys.map {|item| item[:asin] }
|
90
|
+
end
|
91
|
+
|
92
|
+
it "should match the passed items quantities" do
|
93
|
+
@cart.items.map(&:quantity).should == @items.values
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
context "with hashes containing offer_listing_ids" do
|
99
|
+
before(:all) do
|
100
|
+
@items = { { :offer_listing_id => "MCK%2FnCXIges8tpX%2B222nOYEqeZ4AzbrFyiHuP6pFf45N3vZHTm8hFTytRF%2FLRONNkVmt182%2BmeX72n%2BbtUcGEtpLN92Oy9Y7"} => 2 }
|
101
|
+
@cart = Cart.create(@items)
|
102
|
+
end
|
103
|
+
|
104
|
+
it_should_behave_like "a valid cart"
|
105
|
+
|
106
|
+
describe "#items" do
|
107
|
+
it "should match the passed items asins" do
|
108
|
+
@cart.items.size.should == @items.size
|
109
|
+
end
|
110
|
+
|
111
|
+
it "should not have the offer_listing_id reflected immediately in the cart item" do
|
112
|
+
@cart.items.first.cart_item_id.should_not == @items.keys.first[:offer_listing_id]
|
113
|
+
end
|
114
|
+
|
115
|
+
it "should match the passed items quantities" do
|
116
|
+
@cart.items.map(&:quantity).should == @items.values
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
describe ".get" do
|
124
|
+
describe "gotten cart", :shared => true do
|
125
|
+
subject { @cart }
|
126
|
+
|
127
|
+
it { should == @existing_cart }
|
128
|
+
|
129
|
+
it_should_behave_like "a valid cart"
|
130
|
+
|
131
|
+
it "should have the same items as the original cart" do
|
132
|
+
@cart.items.should == @existing_cart.items
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
context "with an existing cart" do
|
137
|
+
before(:all) do
|
138
|
+
@cart = Cart.get(@existing_cart)
|
139
|
+
end
|
140
|
+
|
141
|
+
it_should_behave_like "gotten cart"
|
142
|
+
end
|
143
|
+
|
144
|
+
context "with an id and hmac" do
|
145
|
+
before(:all) do
|
146
|
+
@cart = Cart.get(:id => @existing_cart.id, :hmac => @existing_cart.hmac)
|
147
|
+
end
|
148
|
+
|
149
|
+
it_should_behave_like "gotten cart"
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
it "should have the capacity to remove existing items (short of #clear)"
|
154
|
+
# # Test cart_modify
|
155
|
+
# def test_cart_modify
|
156
|
+
# resp = Amazon::Associates.cart_get(:id => @cart_id, :hmac => @hmac)
|
157
|
+
# cart_item_id = resp.cart.items.first.cart_item_id
|
158
|
+
# resp = Amazon::Associates.cart_modify(:id => @cart_id, :hmac => @hmac,
|
159
|
+
# :items => [{:cart_item_id => cart_item_id, :quantity => 2}])
|
160
|
+
# item = resp.cart.items.first
|
161
|
+
#
|
162
|
+
# assert resp.request.valid?
|
163
|
+
# assert_equal 2, item.quantity
|
164
|
+
# assert_not_nil resp.cart.purchase_url
|
165
|
+
# end
|
166
|
+
#
|
167
|
+
|
168
|
+
describe "#add" do
|
169
|
+
context "adding a new item, on save" do
|
170
|
+
before(:all) do
|
171
|
+
@number_added = 3
|
172
|
+
@item_added = @joker
|
173
|
+
@existing_cart.should_not include(@item_added)
|
174
|
+
@existing_cart.add(@item_added, @number_added)
|
175
|
+
@cart = @existing_cart
|
176
|
+
end
|
177
|
+
|
178
|
+
context "before save" do
|
179
|
+
it_should_behave_like "a modified cart"
|
180
|
+
|
181
|
+
it "should be unchanged" do
|
182
|
+
@existing_cart.should have(2).items
|
183
|
+
@existing_cart.quantity.should == 4
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
context "on save" do
|
188
|
+
before(:all) do
|
189
|
+
@existing_cart.should have(2).items
|
190
|
+
lambda { @existing_cart.save }.should change(@existing_cart, :quantity).by(@number_added)
|
191
|
+
@existing_cart.should have(3).items
|
192
|
+
end
|
193
|
+
|
194
|
+
it_should_behave_like "a valid cart"
|
195
|
+
|
196
|
+
it "should include the old items" do
|
197
|
+
@existing_cart.items.should include(*@existing_items)
|
198
|
+
end
|
199
|
+
|
200
|
+
it "should include the new item" do
|
201
|
+
@existing_cart.items.should include(@item_added)
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
context "adding an existing item", :shared => true do
|
207
|
+
context "before save" do
|
208
|
+
it_should_behave_like "a modified cart"
|
209
|
+
|
210
|
+
it "should be unchanged" do
|
211
|
+
@existing_cart.should have(2).items
|
212
|
+
@existing_cart.quantity.should == 4
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
context "on save" do
|
217
|
+
before(:all) do
|
218
|
+
lambda { @existing_cart.save }.should change {
|
219
|
+
@existing_cart.items.find {|item| item == @item_added }.quantity }.by(@number_added)
|
220
|
+
|
221
|
+
@cart = @existing_cart
|
222
|
+
end
|
223
|
+
|
224
|
+
it_should_behave_like "a valid cart"
|
225
|
+
|
226
|
+
it "should add the item in the quantity requested" do
|
227
|
+
@existing_cart.should have(2).items
|
228
|
+
@existing_cart.items.should =~ @existing_items
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
context "adding an existing item, on save, via add" do
|
234
|
+
before(:all) do
|
235
|
+
@number_added = 2
|
236
|
+
@item_added = @existing_items.first
|
237
|
+
@cart = @existing_cart
|
238
|
+
@cart.add(@item_added, @number_added)
|
239
|
+
end
|
240
|
+
|
241
|
+
it_should_behave_like "adding an existing item"
|
242
|
+
end
|
243
|
+
|
244
|
+
context "adding an existing item, on save, via add" do
|
245
|
+
before(:all) do
|
246
|
+
@number_added = 1
|
247
|
+
@item_added = @existing_items.first
|
248
|
+
@cart = @existing_cart
|
249
|
+
@cart << @item_added
|
250
|
+
end
|
251
|
+
|
252
|
+
it_should_behave_like "adding an existing item"
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
describe "#clear" do
|
257
|
+
before(:all) do
|
258
|
+
@existing_quantity = @existing_cart.quantity
|
259
|
+
@existing_quantity.should == 4
|
260
|
+
@existing_cart.empty?.should be_false
|
261
|
+
@existing_cart.clear
|
262
|
+
@cart = @existing_cart
|
263
|
+
end
|
264
|
+
|
265
|
+
context "before #save" do
|
266
|
+
it_should_behave_like "a modified cart"
|
267
|
+
|
268
|
+
it "should have no effect" do
|
269
|
+
@cart.empty?.should be_false
|
270
|
+
@cart.items.should =~ @existing_items
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
context "after #save" do
|
275
|
+
before(:all) do
|
276
|
+
@cart.should have(2).items
|
277
|
+
|
278
|
+
lambda { @cart.save }.should change {
|
279
|
+
@cart.quantity }.by(- @existing_quantity)
|
280
|
+
end
|
281
|
+
|
282
|
+
it_should_behave_like "a valid cart"
|
283
|
+
|
284
|
+
it "should remove all items" do
|
285
|
+
@cart.items.should == []
|
286
|
+
@cart.items.should be_empty
|
287
|
+
@cart.should be_empty
|
288
|
+
@cart.quantity.should == 0
|
289
|
+
end
|
290
|
+
end
|
291
|
+
end
|
292
|
+
end
|
293
|
+
end
|
294
|
+
end
|