amazon_seller_central 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +42 -5
- data/VERSION +1 -1
- data/amazon_seller_central.gemspec +22 -4
- data/lib/amazon_seller_central/feedback_page.rb +1 -13
- data/lib/amazon_seller_central/inventory.rb +23 -0
- data/lib/amazon_seller_central/inventory_page.rb +126 -0
- data/lib/amazon_seller_central/listing.rb +42 -0
- data/lib/amazon_seller_central/listing_set.rb +13 -0
- data/lib/amazon_seller_central/mechanizer.rb +10 -5
- data/lib/amazon_seller_central/page.rb +26 -0
- data/lib/amazon_seller_central.rb +5 -0
- data/spec/lib/feedback_page_spec.rb +5 -25
- data/spec/lib/inventory_page_spec.rb +120 -0
- data/spec/lib/inventory_spec.rb +26 -0
- data/spec/lib/listing_set_spec.rb +39 -0
- data/spec/lib/listing_spec.rb +78 -0
- data/spec/lib/mechanizer_spec.rb +24 -19
- data/spec/spec_helper.rb +3 -0
- data/spec/support/page_body_regexen.rb +7 -0
- data/spec/support/page_examples.rb +23 -0
- data/spec/support/sample_pages/Feedback Manager.html +1 -1
- data/spec/support/sample_pages/another_listings_page.html +12299 -0
- data/spec/support/sample_pages/listings_last_page.html +3345 -0
- data/spec/support/sample_pages/listings_page_1.html +12937 -0
- data/spec/support/sample_pages/listings_page_2.html +12970 -0
- data/spec/support/sample_pages/update_inventory_result_from_last_page.html +3345 -0
- data/spec/support/sample_pages/update_inventory_result_from_page_1.html +12939 -0
- data/spec/support/sample_pages/update_inventory_result_from_page_2.html +11274 -0
- data/spec/support/sample_pages.rb +25 -12
- metadata +82 -64
data/README.md
CHANGED
@@ -1,7 +1,8 @@
|
|
1
|
-
#
|
1
|
+
# amazon\_seller\_central
|
2
2
|
|
3
3
|
This gem is intended to wrap Amazon's SellerCentral pages with a Ruby
|
4
|
-
API. Currently this gem supports accessing buyer feedback
|
4
|
+
API. Currently this gem supports accessing buyer feedback, accessing
|
5
|
+
current inventory listings, and simple updates to those listings.
|
5
6
|
|
6
7
|
## Setup
|
7
8
|
|
@@ -36,14 +37,50 @@ the block form:
|
|
36
37
|
feedback.rating # an integer, 1 - 5
|
37
38
|
end
|
38
39
|
|
39
|
-
##
|
40
|
+
## Inventory Access
|
41
|
+
|
42
|
+
You can access current inventory in much the same way as feedback:
|
43
|
+
|
44
|
+
AmazonSellerCentral::Inventory.each_page do |page|
|
45
|
+
listings = page.listings
|
46
|
+
listing = listings.first
|
47
|
+
|
48
|
+
listing.sku # <Your sku for this listing>
|
49
|
+
listing.asin # An ASIN, e.g. B003962DXE
|
50
|
+
listing.product_name # The name of the product
|
51
|
+
listing.created_at # a Time object representing when you created this listing
|
52
|
+
listing.quantity # an integer
|
53
|
+
listing.condition # "New" or "Used - Very Good" or whichever
|
54
|
+
listing.price_cents # an integer of cents. listing.price is also available to get dollars
|
55
|
+
listing.status # "Open", "Closed (Out of Stock)", or "Incomplete"
|
56
|
+
end
|
57
|
+
|
58
|
+
## Updating Inventory
|
59
|
+
|
60
|
+
Currently this gem supports updating a listing you've already created
|
61
|
+
via the web interface or some other API. Future versions may include
|
62
|
+
creating listing entries from scratch.
|
63
|
+
|
64
|
+
To update a listing, you use `ListingPage#apply_listings`, like so:
|
65
|
+
|
66
|
+
# alternately get a page inside the 'each_page' method above
|
67
|
+
page = AmazonSellerCentral::Inventory.load_first_page
|
68
|
+
# Updating a listing for my sku, "PR6902-2":
|
69
|
+
listing = page.listings.find("PR6902-2")
|
70
|
+
listing.quantity = 10
|
71
|
+
listing.price_cents = 1599
|
72
|
+
# Note the array syntax here allows you to update multiple listings
|
73
|
+
# on this page at the same time.
|
74
|
+
page.apply_listings([listing])
|
75
|
+
|
76
|
+
## TODO
|
40
77
|
|
41
78
|
* Access to more sections of Amazon SellerCentral.
|
42
79
|
* Rework Mechanize access to allow parallel / thread-safe usage
|
43
80
|
* **Note**: This library is likely not thread-safe. (It's untested)
|
44
81
|
|
45
|
-
## Contributing to
|
46
|
-
|
82
|
+
## Contributing to amazon\_seller\_central
|
83
|
+
|
47
84
|
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
|
48
85
|
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
|
49
86
|
* Fork the project
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.2.0
|
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{amazon_seller_central}
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.2.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["optoro"]
|
12
|
-
s.date = %q{2011-
|
12
|
+
s.date = %q{2011-09-01}
|
13
13
|
s.description = %q{This gem is intended to wrap Amazon's SellerCentral pages with a Ruby API. Currently this gem supports accessing buyer feedback only.}
|
14
14
|
s.email = %q{dev@optoro.com}
|
15
15
|
s.extra_rdoc_files = [
|
@@ -31,12 +31,23 @@ Gem::Specification.new do |s|
|
|
31
31
|
"lib/amazon_seller_central/configuration.rb",
|
32
32
|
"lib/amazon_seller_central/feedback.rb",
|
33
33
|
"lib/amazon_seller_central/feedback_page.rb",
|
34
|
+
"lib/amazon_seller_central/inventory.rb",
|
35
|
+
"lib/amazon_seller_central/inventory_page.rb",
|
36
|
+
"lib/amazon_seller_central/listing.rb",
|
37
|
+
"lib/amazon_seller_central/listing_set.rb",
|
34
38
|
"lib/amazon_seller_central/mechanizer.rb",
|
39
|
+
"lib/amazon_seller_central/page.rb",
|
35
40
|
"spec/amazon_seller_central_spec.rb",
|
36
41
|
"spec/lib/feedback_page_spec.rb",
|
37
42
|
"spec/lib/feedback_spec.rb",
|
43
|
+
"spec/lib/inventory_page_spec.rb",
|
44
|
+
"spec/lib/inventory_spec.rb",
|
45
|
+
"spec/lib/listing_set_spec.rb",
|
46
|
+
"spec/lib/listing_spec.rb",
|
38
47
|
"spec/lib/mechanizer_spec.rb",
|
39
48
|
"spec/spec_helper.rb",
|
49
|
+
"spec/support/page_body_regexen.rb",
|
50
|
+
"spec/support/page_examples.rb",
|
40
51
|
"spec/support/sample_pages.rb",
|
41
52
|
"spec/support/sample_pages/Feedback Manager.html",
|
42
53
|
"spec/support/sample_pages/Feedback Page 1.html",
|
@@ -44,12 +55,19 @@ Gem::Specification.new do |s|
|
|
44
55
|
"spec/support/sample_pages/Feedback Page Last.html",
|
45
56
|
"spec/support/sample_pages/Seller Central Homepage.html",
|
46
57
|
"spec/support/sample_pages/Seller Central Redirect.html",
|
47
|
-
"spec/support/sample_pages/Seller Central.html"
|
58
|
+
"spec/support/sample_pages/Seller Central.html",
|
59
|
+
"spec/support/sample_pages/another_listings_page.html",
|
60
|
+
"spec/support/sample_pages/listings_last_page.html",
|
61
|
+
"spec/support/sample_pages/listings_page_1.html",
|
62
|
+
"spec/support/sample_pages/listings_page_2.html",
|
63
|
+
"spec/support/sample_pages/update_inventory_result_from_last_page.html",
|
64
|
+
"spec/support/sample_pages/update_inventory_result_from_page_1.html",
|
65
|
+
"spec/support/sample_pages/update_inventory_result_from_page_2.html"
|
48
66
|
]
|
49
67
|
s.homepage = %q{http://github.com/optoro/amazon_seller_central}
|
50
68
|
s.licenses = ["MIT"]
|
51
69
|
s.require_paths = ["lib"]
|
52
|
-
s.rubygems_version = %q{1.5.
|
70
|
+
s.rubygems_version = %q{1.5.0}
|
53
71
|
s.summary = %q{Ruby API to access Amazon's SellerCentral}
|
54
72
|
|
55
73
|
if s.respond_to? :specification_version then
|
@@ -1,20 +1,11 @@
|
|
1
1
|
module AmazonSellerCentral
|
2
|
-
class FeedbackPage
|
2
|
+
class FeedbackPage < Page
|
3
3
|
attr_accessor :body
|
4
4
|
|
5
|
-
def initialize(options={})
|
6
|
-
@page = options.delete(:page)
|
7
|
-
@body = @page ? @page.body : ""
|
8
|
-
end
|
9
|
-
|
10
5
|
def has_next?
|
11
6
|
@has_next ||= @page.search('a').map(&:text).grep(/Next/).count > 0
|
12
7
|
end
|
13
8
|
|
14
|
-
def last_page?
|
15
|
-
!has_next?
|
16
|
-
end
|
17
|
-
|
18
9
|
def next_page
|
19
10
|
@next_page ||= begin
|
20
11
|
raise NoNextPageAvailableError unless has_next?
|
@@ -54,9 +45,6 @@ module AmazonSellerCentral
|
|
54
45
|
|
55
46
|
extend ClassMethods
|
56
47
|
|
57
|
-
class NoNextPageAvailableError < StandardError
|
58
|
-
end
|
59
|
-
|
60
48
|
private
|
61
49
|
def feedback_row_to_object(row)
|
62
50
|
data = row.search('.//td').map(&:text)
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module AmazonSellerCentral
|
2
|
+
class Inventory
|
3
|
+
module ClassMethods
|
4
|
+
def load_first_page
|
5
|
+
AmazonSellerCentral.mechanizer.login_to_seller_central
|
6
|
+
manage_inventory = AmazonSellerCentral.mechanizer.follow_link_with(:text => "Manage Inventory")
|
7
|
+
InventoryPage.new( :page => manage_inventory, :page_no => 1, :uri_base => manage_inventory.uri.to_s )
|
8
|
+
end
|
9
|
+
|
10
|
+
def load_all_pages
|
11
|
+
pages = [load_first_page]
|
12
|
+
while pages.last.has_next?
|
13
|
+
pages << pages.last.next_page
|
14
|
+
yield pages.last if block_given?
|
15
|
+
end
|
16
|
+
pages
|
17
|
+
end
|
18
|
+
alias each_page load_all_pages
|
19
|
+
end
|
20
|
+
|
21
|
+
extend ClassMethods
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
module AmazonSellerCentral
|
2
|
+
class InventoryPage < Page
|
3
|
+
def initialize(options={})
|
4
|
+
@listings_applied = false
|
5
|
+
@page_no = options.delete(:page_no)
|
6
|
+
@uri_base = options.delete(:uri_base)
|
7
|
+
super
|
8
|
+
end
|
9
|
+
|
10
|
+
def has_next?
|
11
|
+
@has_next ||= @page.search(".//div[@id='nextPage']").any?
|
12
|
+
end
|
13
|
+
|
14
|
+
def next_page
|
15
|
+
@next_page ||= begin
|
16
|
+
raise NoNextPageAvailableError unless has_next?
|
17
|
+
|
18
|
+
next_page = AmazonSellerCentral.mechanizer.agent.get("#{@uri_base}&searchPageOffset=#{@page_no + 1}")
|
19
|
+
InventoryPage.new(
|
20
|
+
:page => next_page,
|
21
|
+
:page_no => (@page_no + 1),
|
22
|
+
:uri_base => @uri_base
|
23
|
+
)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def listings(rescan=false)
|
28
|
+
@listings = nil if rescan
|
29
|
+
@listings ||= begin
|
30
|
+
set = ListingSet.new
|
31
|
+
# being more specific here breaks on some pages
|
32
|
+
@page.parser.css('tr').select{|r| r['id'] =~ /^sku-/ && r.css('td').size == 12 }.each do |row|
|
33
|
+
set << listing_row_to_object(row)
|
34
|
+
end
|
35
|
+
set
|
36
|
+
end
|
37
|
+
end
|
38
|
+
alias :parse :listings
|
39
|
+
|
40
|
+
def apply_listings(new_listings)
|
41
|
+
if @listings_applied
|
42
|
+
raise UnsupportedModification.new("Can't apply listing data twice from the same page object. Refetch this page before attempting to update listings")
|
43
|
+
end
|
44
|
+
|
45
|
+
form = @page.form_with(:name => 'itemSummaryForm')
|
46
|
+
new_listings.each do |l|
|
47
|
+
if listings(true).find(l.sku).price.nil? && l.price != nil
|
48
|
+
raise UnsupportedModification.new("Can't set price for #{l.asin} to $#{l.price}, the listing is not yet complete and this library doesn't yet support completing listings")
|
49
|
+
end
|
50
|
+
|
51
|
+
form["inv|#{l.sku}|#{l.asin}"] = l.quantity
|
52
|
+
form["price|#{l.sku}|#{l.asin}"] = l.price
|
53
|
+
end
|
54
|
+
form['formOperation'] = 'saveChanges'
|
55
|
+
r = form.submit
|
56
|
+
@listings_applied = true
|
57
|
+
true
|
58
|
+
end
|
59
|
+
|
60
|
+
class UnsupportedModification < StandardError; end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
# 0 - hidden input of sku
|
65
|
+
# 1 - checkbox itemOffer
|
66
|
+
# 2 - actions
|
67
|
+
# 3 - sku
|
68
|
+
# 4 - asin
|
69
|
+
# 5 - product name
|
70
|
+
# 6 - date created
|
71
|
+
# 7 - qty
|
72
|
+
# 8 - condition
|
73
|
+
# 9 - your price
|
74
|
+
# 10 - low price
|
75
|
+
# 11 - status
|
76
|
+
def listing_row_to_object(row)
|
77
|
+
l = Listing.new
|
78
|
+
row.css('td').each_with_index do |td, i|
|
79
|
+
|
80
|
+
txt = td.text.strip # yes, slightly slower to do this here, but I type less.
|
81
|
+
|
82
|
+
case i
|
83
|
+
when 3
|
84
|
+
l.sku = txt
|
85
|
+
when 4
|
86
|
+
l.asin = txt
|
87
|
+
when 5
|
88
|
+
l.product_name = txt
|
89
|
+
when 6
|
90
|
+
l.created_at = Time.parse(txt)
|
91
|
+
when 7
|
92
|
+
l.quantity = td.css('input').first['value'].to_i
|
93
|
+
when 8
|
94
|
+
l.condition = txt
|
95
|
+
when 9
|
96
|
+
l.price = get_price(td)
|
97
|
+
when 10
|
98
|
+
l.low_price = get_low_price(td)
|
99
|
+
when 11
|
100
|
+
l.status = txt
|
101
|
+
end
|
102
|
+
end
|
103
|
+
l
|
104
|
+
end
|
105
|
+
|
106
|
+
def get_price(td)
|
107
|
+
if td.css('input').size == 0 # incomplete listing
|
108
|
+
nil
|
109
|
+
else
|
110
|
+
td.css('input').first['value'].to_f
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
# Turns out you can't get this without a subrequest
|
115
|
+
def get_low_price(td)
|
116
|
+
nil
|
117
|
+
# if td.css('a').size == 0 # no listing complete
|
118
|
+
# nil
|
119
|
+
# elsif td.css('a div').size == 1
|
120
|
+
# true
|
121
|
+
# else
|
122
|
+
# td.text.gsub(/[^\d.]/,'').to_f
|
123
|
+
# end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module AmazonSellerCentral
|
2
|
+
# It turns out you can't get low_price without executing a sub-request, so
|
3
|
+
# I'm leaving it alone for now.
|
4
|
+
class Listing
|
5
|
+
module ClassMethods
|
6
|
+
def is_asin?(value)
|
7
|
+
value.to_s =~ /^B.{9}$/
|
8
|
+
end
|
9
|
+
end
|
10
|
+
extend ClassMethods
|
11
|
+
|
12
|
+
attr_accessor :sku, :asin, :product_name, :created_at, :quantity, :condition, :price_cents, :low_price_cents, :status
|
13
|
+
|
14
|
+
alias :qty :quantity
|
15
|
+
alias :qty= :quantity=
|
16
|
+
|
17
|
+
alias :product :product_name
|
18
|
+
alias :product= :product_name=
|
19
|
+
|
20
|
+
def price
|
21
|
+
@price_cents.kind_of?(Numeric) ? @price_cents / 100.0 : @price_cents
|
22
|
+
end
|
23
|
+
def price=(price)
|
24
|
+
@price_cents = price.kind_of?(Numeric) ? (price * 100).round : price
|
25
|
+
end
|
26
|
+
|
27
|
+
alias :your_price :price
|
28
|
+
alias :your_price= :price=
|
29
|
+
|
30
|
+
def low_price
|
31
|
+
@low_price_cents.kind_of?(Numeric) ? @low_price_cents / 100.0 : @low_price_cents
|
32
|
+
end
|
33
|
+
def low_price=(low_price)
|
34
|
+
@low_price_cents = low_price.kind_of?(Numeric) ? (low_price * 100).round : low_price
|
35
|
+
end
|
36
|
+
|
37
|
+
def save
|
38
|
+
puts "Sorry, you want to send this listing as part of an array to the InventoryPage it came from, with InventoryPage#apply_listings"
|
39
|
+
false
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -5,7 +5,7 @@ module AmazonSellerCentral
|
|
5
5
|
include Singleton
|
6
6
|
MASQUERADE_AGENTS = ['Mac Safari', 'Mac FireFox', 'Linux Firefox']
|
7
7
|
|
8
|
-
attr_reader :agent
|
8
|
+
attr_reader :agent
|
9
9
|
|
10
10
|
def login_email
|
11
11
|
AmazonSellerCentral.configuration.login_email
|
@@ -19,25 +19,30 @@ module AmazonSellerCentral
|
|
19
19
|
@agent ||= Mechanize.new {|ag| ag.user_agent_alias = MASQUERADE_AGENTS[rand(MASQUERADE_AGENTS.size)] }
|
20
20
|
end
|
21
21
|
|
22
|
+
def last_page
|
23
|
+
agent.current_page
|
24
|
+
end
|
25
|
+
|
22
26
|
def login_to_seller_central
|
23
27
|
page = agent.get('https://sellercentral.amazon.com/')
|
24
28
|
form = page.form_with(:name => 'signin')
|
25
29
|
|
26
|
-
form.email
|
30
|
+
form.email = login_email
|
27
31
|
form.password = login_password
|
28
|
-
|
32
|
+
form.submit
|
33
|
+
last_page.body =~ /Welcome! You are signed in as/
|
29
34
|
end
|
30
35
|
|
31
36
|
def follow_link_with(options)
|
32
37
|
raise AgentResetError unless last_page
|
33
38
|
link = last_page.link_with(options)
|
34
39
|
raise LinkNotFoundError unless link
|
35
|
-
|
40
|
+
agent.click(link)
|
36
41
|
end
|
37
42
|
|
38
43
|
def reset!
|
39
44
|
@agent = nil
|
40
|
-
|
45
|
+
#@last_page = nil
|
41
46
|
end
|
42
47
|
|
43
48
|
class LinkNotFoundError < StandardError; end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module AmazonSellerCentral
|
2
|
+
class Page
|
3
|
+
attr_accessor :body
|
4
|
+
|
5
|
+
def initialize(options={})
|
6
|
+
@page = options.delete(:page)
|
7
|
+
@body = @page ? @page.body : ""
|
8
|
+
end
|
9
|
+
|
10
|
+
def has_next?
|
11
|
+
false
|
12
|
+
end
|
13
|
+
|
14
|
+
def last_page?
|
15
|
+
!has_next?
|
16
|
+
end
|
17
|
+
|
18
|
+
def next_page
|
19
|
+
raise NoNextPageAvailableError.new("Unimplemented, override Page#next_page")
|
20
|
+
end
|
21
|
+
|
22
|
+
class NoNextPageAvailableError < StandardError
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
@@ -1,7 +1,12 @@
|
|
1
1
|
require 'amazon_seller_central/configuration'
|
2
2
|
require 'amazon_seller_central/mechanizer'
|
3
|
+
require 'amazon_seller_central/listing_set'
|
4
|
+
require 'amazon_seller_central/page'
|
3
5
|
require 'amazon_seller_central/feedback'
|
4
6
|
require 'amazon_seller_central/feedback_page'
|
7
|
+
require 'amazon_seller_central/listing'
|
8
|
+
require 'amazon_seller_central/inventory'
|
9
|
+
require 'amazon_seller_central/inventory_page'
|
5
10
|
|
6
11
|
module AmazonSellerCentral
|
7
12
|
def self.configuration
|
@@ -3,36 +3,16 @@ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
|
3
3
|
describe "FeedbackPage" do
|
4
4
|
before :all do
|
5
5
|
#AmazonSellerCentral.mechanizer.reset!
|
6
|
-
@first_page_test_regex =
|
7
|
-
@second_page_test_regex =
|
8
|
-
@last_page_test_regex =
|
6
|
+
@first_page_test_regex = FEEDBACK_FIRST_PAGE_TEST_REGEX
|
7
|
+
@second_page_test_regex = FEEDBACK_SECOND_PAGE_TEST_REGEX
|
8
|
+
@last_page_test_regex = FEEDBACK_LAST_PAGE_TEST_REGEX
|
9
9
|
|
10
10
|
@first_page = AmazonSellerCentral::FeedbackPage.load_first_page
|
11
11
|
@second_page = @first_page.next_page
|
12
12
|
@last_page = @second_page.next_page
|
13
13
|
end
|
14
14
|
|
15
|
-
|
16
|
-
@first_page.has_next?.should be_true
|
17
|
-
@second_page.has_next?.should be_true
|
18
|
-
@last_page.has_next?.should be_false
|
19
|
-
end
|
20
|
-
|
21
|
-
it "returns the next page when there is one" do
|
22
|
-
@first_page.next_page.body.should =~ @second_page_test_regex
|
23
|
-
end
|
24
|
-
|
25
|
-
it "raises an exception when asked for a next page and there is none" do
|
26
|
-
lambda {
|
27
|
-
@last_page.next_page
|
28
|
-
}.should raise_exception(AmazonSellerCentral::FeedbackPage::NoNextPageAvailableError)
|
29
|
-
end
|
30
|
-
|
31
|
-
it "knows if it is the last page" do
|
32
|
-
@first_page.last_page?.should be_false
|
33
|
-
@second_page.last_page?.should be_false
|
34
|
-
@last_page.last_page?.should be_true
|
35
|
-
end
|
15
|
+
it_should_behave_like "all pages"
|
36
16
|
|
37
17
|
it "transforms itself into a collection of Feedback objects" do
|
38
18
|
feedback = @first_page.feedbacks
|
@@ -40,7 +20,7 @@ describe "FeedbackPage" do
|
|
40
20
|
feedback.last.comments.should == "quick delivery. product arrived in perfect condition. good experience."
|
41
21
|
end
|
42
22
|
|
43
|
-
describe "
|
23
|
+
describe "ClassMethods" do
|
44
24
|
it "loads the first page of feedback data" do
|
45
25
|
AmazonSellerCentral.mechanizer.reset!
|
46
26
|
page = AmazonSellerCentral::FeedbackPage.load_first_page
|
@@ -0,0 +1,120 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe "InventoryPage" do
|
4
|
+
before :all do
|
5
|
+
@first_page_test_regex = INVENTORY_FIRST_PAGE_TEST_REGEX
|
6
|
+
@second_page_test_regex = INVENTORY_SECOND_PAGE_TEST_REGEX
|
7
|
+
@last_page_test_regex = INVENTORY_LAST_PAGE_TEST_REGEX
|
8
|
+
end
|
9
|
+
|
10
|
+
before :each do
|
11
|
+
mock_seller_central_page_results!
|
12
|
+
@first_page = AmazonSellerCentral::Inventory.load_first_page
|
13
|
+
@second_page = @first_page.next_page
|
14
|
+
@last_page = @second_page.next_page
|
15
|
+
end
|
16
|
+
|
17
|
+
it_should_behave_like "all pages"
|
18
|
+
|
19
|
+
it "returns a ListingSet for listings" do
|
20
|
+
listings = @first_page.listings
|
21
|
+
listings.should be_kind_of(AmazonSellerCentral::ListingSet)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "transforms itself into a set of Listing objects" do
|
25
|
+
listings = @first_page.listings
|
26
|
+
listings.size.should == 250
|
27
|
+
|
28
|
+
listings[27].sku.should == "PR30122-11"
|
29
|
+
listings[27].asin.should == "B0019MU9C2"
|
30
|
+
listings[27].product_name.should == "Battery Brain Platinum"
|
31
|
+
listings[27].created_at.should == Time.parse("2011-08-31 00:36:22")
|
32
|
+
listings[27].quantity.should == 0
|
33
|
+
listings[27].condition.should == "New"
|
34
|
+
listings[27].price_cents.should == nil
|
35
|
+
listings[27].low_price.should == nil
|
36
|
+
listings[27].low_price_cents.should == nil
|
37
|
+
listings[27].status.should == "Incomplete"
|
38
|
+
|
39
|
+
listings[1].sku.should == "PR48458-3"
|
40
|
+
listings[1].asin.should == "B004O0TRDI"
|
41
|
+
listings[1].product_name.should == "Onkyo HT-S3400 5.1 Channel Home Theater Receiver/Speaker Package"
|
42
|
+
listings[1].created_at.should == Time.parse("2011-08-31 18:14:49")
|
43
|
+
listings[1].quantity.should == 1
|
44
|
+
listings[1].condition.should == "Used - Good"
|
45
|
+
listings[1].price_cents.should == 22559
|
46
|
+
# listings[1].low_price.should == true
|
47
|
+
# listings[1].low_price_cents.should == true
|
48
|
+
listings[1].status.should == "Open"
|
49
|
+
|
50
|
+
listings[10].sku.should == "PR27880-11"
|
51
|
+
listings[10].asin.should == "B003962DXE"
|
52
|
+
listings[10].product_name.should == "Panasonic Lumix DMC-FH20K 14.1 MP Digital Camera with 8x Optical Image Stabilized Zoom and 2.7-Inch LCD (Black)"
|
53
|
+
listings[10].created_at.should == Time.parse("2011-08-31 10:40:13")
|
54
|
+
listings[10].quantity.should == 1
|
55
|
+
listings[10].condition.should == "New"
|
56
|
+
listings[10].price_cents.should == 10899
|
57
|
+
#listings[10].low_price_cents.should == 10250
|
58
|
+
listings[10].status.should == "Open"
|
59
|
+
end
|
60
|
+
|
61
|
+
it "loads listings appropriately for another sample page" do
|
62
|
+
# This sample page was only parsing 25 results before
|
63
|
+
FakeWeb.register_uri(:get, 'https://sellercentral.amazon.com/gp/ezdpc-gui/inventory-status/status.html/ref=ag_invmgr_mmap_home', :response => mock_pages[:another_listings_page])
|
64
|
+
listings = AmazonSellerCentral::Inventory.load_first_page.listings
|
65
|
+
listings.size.should == 250
|
66
|
+
|
67
|
+
listings[1].sku.should == "PR6902-2"
|
68
|
+
listings[1].asin.should == "B000Q82PIQ"
|
69
|
+
listings[1].product_name.should == "Western Digital 500 GB Caviar Blue SATA 3 Gb/s 7200 RPM 16 MB Cache Bulk/OEM Desktop Hard Drive - WD5000AAKS"
|
70
|
+
listings[1].created_at.should == Time.parse("2011-08-31 22:42:13")
|
71
|
+
listings[1].quantity.should == 1
|
72
|
+
listings[1].condition.should == "Used - Very Good"
|
73
|
+
listings[1].price_cents.should == 3819
|
74
|
+
# listings[1].low_price.should == true
|
75
|
+
listings[1].status.should == "Open"
|
76
|
+
end
|
77
|
+
|
78
|
+
it "accepts a set of Listing objects to apply updates to the page" do
|
79
|
+
listings = @first_page.listings
|
80
|
+
l = listings[0]
|
81
|
+
# l.quantity = 0
|
82
|
+
# l.price = 225.59
|
83
|
+
l.quantity = 2
|
84
|
+
l.price = 220.99
|
85
|
+
|
86
|
+
listings = [l]
|
87
|
+
|
88
|
+
@first_page.apply_listings(listings).should be_true
|
89
|
+
|
90
|
+
(AmazonSellerCentral.mechanizer.last_page.parser.css('div#msg_saveSuccess')[0]['style'] !~ /display: none/).should be_true
|
91
|
+
|
92
|
+
FakeWeb.register_uri(:get, 'https://sellercentral.amazon.com/gp/ezdpc-gui/inventory-status/status.html/ref=ag_invmgr_mmap_home', :response => mock_pages[:update_inventory_result_from_page_1])
|
93
|
+
listing = AmazonSellerCentral::Inventory.load_first_page.listings[0]
|
94
|
+
listing.sku.should == l.sku
|
95
|
+
listing.quantity.should == l.quantity
|
96
|
+
listing.price.should == l.price
|
97
|
+
end
|
98
|
+
|
99
|
+
it "raises an unsupported modification error when trying to set the price on an incomplete listing" do
|
100
|
+
listings = @first_page.listings
|
101
|
+
l = listings[27]
|
102
|
+
l.price = 24.26
|
103
|
+
lambda {
|
104
|
+
@first_page.apply_listings([l])
|
105
|
+
}.should raise_exception(AmazonSellerCentral::InventoryPage::UnsupportedModification)
|
106
|
+
end
|
107
|
+
|
108
|
+
it "raises an unsupported modification error when trying to apply listings twice" do
|
109
|
+
listings = @first_page.listings
|
110
|
+
l = listings[0]
|
111
|
+
l.price = 24.26
|
112
|
+
@first_page.apply_listings([l])
|
113
|
+
|
114
|
+
l.price = 26.26
|
115
|
+
lambda {
|
116
|
+
@first_page.apply_listings([l])
|
117
|
+
}.should raise_exception(AmazonSellerCentral::InventoryPage::UnsupportedModification)
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe "Inventory" do
|
4
|
+
it "loads the first page of inventory data" do
|
5
|
+
AmazonSellerCentral.mechanizer.reset!
|
6
|
+
page = AmazonSellerCentral::Inventory.load_first_page
|
7
|
+
page.should be_kind_of(AmazonSellerCentral::InventoryPage)
|
8
|
+
page.body.should =~ INVENTORY_FIRST_PAGE_TEST_REGEX
|
9
|
+
end
|
10
|
+
|
11
|
+
it "loads all inventory pages" do
|
12
|
+
AmazonSellerCentral.mechanizer.reset!
|
13
|
+
pages = AmazonSellerCentral::Inventory.load_all_pages
|
14
|
+
pages.size.should == 3
|
15
|
+
pages.first.body.should =~ INVENTORY_FIRST_PAGE_TEST_REGEX
|
16
|
+
pages.last.body.should =~ INVENTORY_LAST_PAGE_TEST_REGEX
|
17
|
+
end
|
18
|
+
|
19
|
+
it "passes a page to a block when given" do
|
20
|
+
last_seen = nil
|
21
|
+
AmazonSellerCentral::Inventory.each_page do |page|
|
22
|
+
last_seen = page
|
23
|
+
end
|
24
|
+
last_seen.body.should =~ INVENTORY_LAST_PAGE_TEST_REGEX
|
25
|
+
end
|
26
|
+
end
|