amazon_seller_central 0.1.1 → 0.2.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.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
|