amazon-orders 3.2.5__tar.gz → 3.2.7__tar.gz
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.
- {amazon_orders-3.2.5 → amazon_orders-3.2.7}/CHANGELOG.md +18 -1
- {amazon_orders-3.2.5/amazon_orders.egg-info → amazon_orders-3.2.7}/PKG-INFO +1 -1
- {amazon_orders-3.2.5 → amazon_orders-3.2.7/amazon_orders.egg-info}/PKG-INFO +1 -1
- {amazon_orders-3.2.5 → amazon_orders-3.2.7}/amazonorders/__init__.py +1 -1
- {amazon_orders-3.2.5 → amazon_orders-3.2.7}/amazonorders/entity/order.py +2 -2
- {amazon_orders-3.2.5 → amazon_orders-3.2.7}/amazonorders/entity/transaction.py +1 -1
- {amazon_orders-3.2.5 → amazon_orders-3.2.7}/amazonorders/orders.py +0 -5
- {amazon_orders-3.2.5 → amazon_orders-3.2.7}/amazonorders/selectors.py +6 -6
- {amazon_orders-3.2.5 → amazon_orders-3.2.7}/amazonorders/transactions.py +1 -1
- {amazon_orders-3.2.5 → amazon_orders-3.2.7}/tests/test_cli.py +3 -2
- {amazon_orders-3.2.5 → amazon_orders-3.2.7}/tests/test_orders.py +27 -15
- {amazon_orders-3.2.5 → amazon_orders-3.2.7}/tests/test_session.py +30 -26
- amazon_orders-3.2.7/tests/test_transactions.py +131 -0
- {amazon_orders-3.2.5 → amazon_orders-3.2.7}/tests/test_util.py +1 -1
- amazon_orders-3.2.5/tests/test_transactions.py +0 -166
- {amazon_orders-3.2.5 → amazon_orders-3.2.7}/LICENSE +0 -0
- {amazon_orders-3.2.5 → amazon_orders-3.2.7}/MANIFEST.in +0 -0
- {amazon_orders-3.2.5 → amazon_orders-3.2.7}/README.md +0 -0
- {amazon_orders-3.2.5 → amazon_orders-3.2.7}/amazon_orders.egg-info/SOURCES.txt +0 -0
- {amazon_orders-3.2.5 → amazon_orders-3.2.7}/amazon_orders.egg-info/dependency_links.txt +0 -0
- {amazon_orders-3.2.5 → amazon_orders-3.2.7}/amazon_orders.egg-info/entry_points.txt +0 -0
- {amazon_orders-3.2.5 → amazon_orders-3.2.7}/amazon_orders.egg-info/requires.txt +0 -0
- {amazon_orders-3.2.5 → amazon_orders-3.2.7}/amazon_orders.egg-info/top_level.txt +0 -0
- {amazon_orders-3.2.5 → amazon_orders-3.2.7}/amazonorders/banner.txt +0 -0
- {amazon_orders-3.2.5 → amazon_orders-3.2.7}/amazonorders/cli.py +0 -0
- {amazon_orders-3.2.5 → amazon_orders-3.2.7}/amazonorders/conf.py +0 -0
- {amazon_orders-3.2.5 → amazon_orders-3.2.7}/amazonorders/constants.py +0 -0
- {amazon_orders-3.2.5 → amazon_orders-3.2.7}/amazonorders/entity/__init__.py +0 -0
- {amazon_orders-3.2.5 → amazon_orders-3.2.7}/amazonorders/entity/item.py +0 -0
- {amazon_orders-3.2.5 → amazon_orders-3.2.7}/amazonorders/entity/parsable.py +0 -0
- {amazon_orders-3.2.5 → amazon_orders-3.2.7}/amazonorders/entity/recipient.py +0 -0
- {amazon_orders-3.2.5 → amazon_orders-3.2.7}/amazonorders/entity/seller.py +0 -0
- {amazon_orders-3.2.5 → amazon_orders-3.2.7}/amazonorders/entity/shipment.py +0 -0
- {amazon_orders-3.2.5 → amazon_orders-3.2.7}/amazonorders/exception.py +0 -0
- {amazon_orders-3.2.5 → amazon_orders-3.2.7}/amazonorders/forms.py +0 -0
- {amazon_orders-3.2.5 → amazon_orders-3.2.7}/amazonorders/session.py +0 -0
- {amazon_orders-3.2.5 → amazon_orders-3.2.7}/amazonorders/util.py +0 -0
- {amazon_orders-3.2.5 → amazon_orders-3.2.7}/pyproject.toml +0 -0
- {amazon_orders-3.2.5 → amazon_orders-3.2.7}/setup.cfg +0 -0
- {amazon_orders-3.2.5 → amazon_orders-3.2.7}/tests/test_conf.py +0 -0
- {amazon_orders-3.2.5 → amazon_orders-3.2.7}/tests/testcase.py +0 -0
|
@@ -4,7 +4,24 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
-
## [Unreleased](https://github.com/alexdlaird/amazon-orders/compare/3.2.
|
|
7
|
+
## [Unreleased](https://github.com/alexdlaird/amazon-orders/compare/3.2.7...HEAD)
|
|
8
|
+
|
|
9
|
+
## [3.2.7](https://github.com/alexdlaird/amazon-orders/compare/3.2.6...3.2.7) - 2025-02-17
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- Fixes for parsing Amazon Fresh and Whole Foods Market orders, so they no longer need to be skipped (but their Items and Shipments will still be empty).
|
|
14
|
+
|
|
15
|
+
## [3.2.6](https://github.com/alexdlaird/amazon-orders/compare/3.2.5...3.2.6) - 2025-02-17
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
|
|
19
|
+
- Add generic integration tests for Transactions, now in weekly run.
|
|
20
|
+
- Other test improvements.
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
|
|
24
|
+
- Broken parsing when Transaction is pending.
|
|
8
25
|
|
|
9
26
|
## [3.2.5](https://github.com/alexdlaird/amazon-orders/compare/3.2.4...3.2.5) - 2025-02-12
|
|
10
27
|
|
|
@@ -92,7 +92,7 @@ class Order(Parsable):
|
|
|
92
92
|
return f"Order #{self.order_number}: {self.items}"
|
|
93
93
|
|
|
94
94
|
def _parse_shipments(self) -> List[Shipment]:
|
|
95
|
-
if not self.parsed:
|
|
95
|
+
if not self.parsed or len(util.select(self.parsed, self.config.selectors.ORDER_SKIP_ITEMS)) > 0:
|
|
96
96
|
return []
|
|
97
97
|
|
|
98
98
|
shipments: List[Shipment] = [self.config.shipment_cls(x, self.config)
|
|
@@ -102,7 +102,7 @@ class Order(Parsable):
|
|
|
102
102
|
return shipments
|
|
103
103
|
|
|
104
104
|
def _parse_items(self) -> List[Item]:
|
|
105
|
-
if not self.parsed:
|
|
105
|
+
if not self.parsed or len(util.select(self.parsed, self.config.selectors.ORDER_SKIP_ITEMS)) > 0:
|
|
106
106
|
return []
|
|
107
107
|
|
|
108
108
|
items: List[Item] = [self.config.item_cls(x, self.config)
|
|
@@ -73,11 +73,6 @@ class AmazonOrders:
|
|
|
73
73
|
response_parsed = self.amazon_session.last_response_parsed
|
|
74
74
|
|
|
75
75
|
for order_tag in util.select(response_parsed, self.config.selectors.ORDER_HISTORY_ENTITY_SELECTOR):
|
|
76
|
-
# First check if this Order is known to be of a type that we do not currently have a way to support
|
|
77
|
-
# parsing, meaning it should be skipped
|
|
78
|
-
if util.select(order_tag, self.config.selectors.ORDER_HISTORY_BRAND_SELECTOR):
|
|
79
|
-
continue
|
|
80
|
-
|
|
81
76
|
order: Order = self.config.order_cls(order_tag, self.config)
|
|
82
77
|
|
|
83
78
|
if full_details:
|
|
@@ -55,12 +55,12 @@ class Selectors:
|
|
|
55
55
|
SHIPMENT_ENTITY_SELECTOR = ["[data-component='orderCard'] [data-component='shipments'] .a-box",
|
|
56
56
|
"div.shipment",
|
|
57
57
|
"div.delivery-box"]
|
|
58
|
-
# Selectors defined here
|
|
59
|
-
#
|
|
60
|
-
|
|
61
|
-
# Amazon Fresh
|
|
58
|
+
# Selectors defined here mean we don't have a reliable way to parse all details in an Order, so Items and
|
|
59
|
+
# Shipments will be skipped
|
|
60
|
+
ORDER_SKIP_ITEMS = [
|
|
61
|
+
# Identifies an Amazon Fresh order
|
|
62
62
|
".brand-info-box .brand-logo img",
|
|
63
|
-
# Whole Foods Market
|
|
63
|
+
# Identifies a Whole Foods Market order
|
|
64
64
|
"a.yohtmlc-order-details-link[href^='/wholefoodsmarket']"
|
|
65
65
|
]
|
|
66
66
|
|
|
@@ -160,7 +160,7 @@ class Selectors:
|
|
|
160
160
|
FIELD_TRANSACTION_GRAND_TOTAL_SELECTOR = [
|
|
161
161
|
"div.apx-transactions-line-item-component-container > div:nth-child(1) span.a-size-base-plus"]
|
|
162
162
|
FIELD_TRANSACTION_ORDER_NUMBER_SELECTOR = [
|
|
163
|
-
"div.apx-transactions-line-item-component-container
|
|
163
|
+
"div.apx-transactions-line-item-component-container div .a-span12 a"]
|
|
164
164
|
FIELD_TRANSACTION_ORDER_LINK_SELECTOR = [
|
|
165
165
|
"div.apx-transactions-line-item-component-container > div:nth-child(2) a.a-link-normal"]
|
|
166
166
|
FIELD_TRANSACTION_SELLER_NAME_SELECTOR = [
|
|
@@ -65,7 +65,7 @@ class TestCli(UnitTestCase):
|
|
|
65
65
|
# GIVEN
|
|
66
66
|
order_id = "112-2961628-4757846"
|
|
67
67
|
self.given_login_responses_success()
|
|
68
|
-
with open(os.path.join(self.RESOURCES_DIR, "order-details-112-2961628-4757846.html"), "r",
|
|
68
|
+
with open(os.path.join(self.RESOURCES_DIR, "orders", "order-details-112-2961628-4757846.html"), "r",
|
|
69
69
|
encoding="utf-8") as f:
|
|
70
70
|
resp1 = responses.add(
|
|
71
71
|
responses.GET,
|
|
@@ -92,7 +92,8 @@ class TestCli(UnitTestCase):
|
|
|
92
92
|
mock_get_today.date.today.return_value = datetime.date(2024, 10, 11)
|
|
93
93
|
days = 1
|
|
94
94
|
self.given_login_responses_success()
|
|
95
|
-
with open(os.path.join(self.RESOURCES_DIR, "get-transactions.html"),
|
|
95
|
+
with open(os.path.join(self.RESOURCES_DIR, "transactions", "get-transactions-snippet.html"),
|
|
96
|
+
"r", encoding="utf-8") as f:
|
|
96
97
|
resp = responses.add(
|
|
97
98
|
responses.GET,
|
|
98
99
|
f"{self.test_config.constants.TRANSACTION_HISTORY_LANDING_URL}",
|
|
@@ -85,7 +85,7 @@ class TestOrders(UnitTestCase):
|
|
|
85
85
|
year = 2024
|
|
86
86
|
start_index = 0
|
|
87
87
|
resp1 = self.given_order_history_landing_exists()
|
|
88
|
-
with open(os.path.join(self.RESOURCES_DIR, "order-history-egift.html"), "r",
|
|
88
|
+
with open(os.path.join(self.RESOURCES_DIR, "orders", "order-history-egift.html"), "r",
|
|
89
89
|
encoding="utf-8") as f:
|
|
90
90
|
resp2 = responses.add(
|
|
91
91
|
responses.GET,
|
|
@@ -98,7 +98,7 @@ class TestOrders(UnitTestCase):
|
|
|
98
98
|
orders = self.amazon_orders.get_order_history(year=year, start_index=start_index)
|
|
99
99
|
|
|
100
100
|
# THEN
|
|
101
|
-
self.assertEqual(
|
|
101
|
+
self.assertEqual(10, len(orders))
|
|
102
102
|
self.assertEqual(1, resp1.call_count)
|
|
103
103
|
self.assertEqual(1, resp2.call_count)
|
|
104
104
|
order = orders[5]
|
|
@@ -119,7 +119,7 @@ class TestOrders(UnitTestCase):
|
|
|
119
119
|
year = 2010
|
|
120
120
|
resp1 = self.given_order_history_landing_exists()
|
|
121
121
|
resp2 = self.given_order_history_exists(year, 0)
|
|
122
|
-
with open(os.path.join(self.RESOURCES_DIR, f"order-history-{year}-10.html"), "r",
|
|
122
|
+
with open(os.path.join(self.RESOURCES_DIR, "orders", f"order-history-{year}-10.html"), "r",
|
|
123
123
|
encoding="utf-8") as f:
|
|
124
124
|
resp3 = responses.add(
|
|
125
125
|
responses.GET,
|
|
@@ -139,13 +139,13 @@ class TestOrders(UnitTestCase):
|
|
|
139
139
|
self.assertEqual(1, resp3.call_count)
|
|
140
140
|
|
|
141
141
|
@responses.activate
|
|
142
|
-
def
|
|
142
|
+
def test_get_order_history_fresh(self):
|
|
143
143
|
# GIVEN
|
|
144
144
|
self.amazon_session.is_authenticated = True
|
|
145
145
|
year = 2024
|
|
146
146
|
start_index = 0
|
|
147
147
|
resp1 = self.given_order_history_landing_exists()
|
|
148
|
-
with open(os.path.join(self.RESOURCES_DIR, "order-history-fresh.html"), "r",
|
|
148
|
+
with open(os.path.join(self.RESOURCES_DIR, "orders", "order-history-fresh.html"), "r",
|
|
149
149
|
encoding="utf-8") as f:
|
|
150
150
|
resp2 = responses.add(
|
|
151
151
|
responses.GET,
|
|
@@ -158,18 +158,24 @@ class TestOrders(UnitTestCase):
|
|
|
158
158
|
orders = self.amazon_orders.get_order_history(year=year, start_index=start_index)
|
|
159
159
|
|
|
160
160
|
# THEN
|
|
161
|
-
self.assertEqual(
|
|
161
|
+
self.assertEqual(10, len(orders))
|
|
162
162
|
self.assertEqual(1, resp1.call_count)
|
|
163
163
|
self.assertEqual(1, resp2.call_count)
|
|
164
|
+
order = orders[4]
|
|
165
|
+
self.assertEqual("111-2072777-8279433", order.order_number)
|
|
166
|
+
self.assertEqual(80.27, order.grand_total)
|
|
167
|
+
self.assertIsNotNone(order.order_details_link)
|
|
168
|
+
self.assertEqual(date(2025, 1, 3), order.order_placed_date)
|
|
169
|
+
self.assertEqual(0, len(order.items))
|
|
164
170
|
|
|
165
171
|
@responses.activate
|
|
166
|
-
def
|
|
172
|
+
def test_get_order_history_wholefoods(self):
|
|
167
173
|
# GIVEN
|
|
168
174
|
self.amazon_session.is_authenticated = True
|
|
169
175
|
year = 2024
|
|
170
176
|
start_index = 0
|
|
171
177
|
resp1 = self.given_order_history_landing_exists()
|
|
172
|
-
with open(os.path.join(self.RESOURCES_DIR, "order-history-wholefoods.html"), "r",
|
|
178
|
+
with open(os.path.join(self.RESOURCES_DIR, "orders", "order-history-wholefoods.html"), "r",
|
|
173
179
|
encoding="utf-8") as f:
|
|
174
180
|
resp2 = responses.add(
|
|
175
181
|
responses.GET,
|
|
@@ -182,9 +188,15 @@ class TestOrders(UnitTestCase):
|
|
|
182
188
|
orders = self.amazon_orders.get_order_history(year=year, start_index=start_index)
|
|
183
189
|
|
|
184
190
|
# THEN
|
|
185
|
-
self.assertEqual(
|
|
191
|
+
self.assertEqual(10, len(orders))
|
|
186
192
|
self.assertEqual(1, resp1.call_count)
|
|
187
193
|
self.assertEqual(1, resp2.call_count)
|
|
194
|
+
order = orders[7]
|
|
195
|
+
self.assertEqual("113-6307059-7336242", order.order_number)
|
|
196
|
+
self.assertEqual(62.92, order.grand_total)
|
|
197
|
+
self.assertIsNotNone(order.order_details_link)
|
|
198
|
+
self.assertEqual(date(2024, 12, 12), order.order_placed_date)
|
|
199
|
+
self.assertEqual(0, len(order.items))
|
|
188
200
|
|
|
189
201
|
@responses.activate
|
|
190
202
|
def test_get_order_history_full_details(self):
|
|
@@ -291,7 +303,7 @@ class TestOrders(UnitTestCase):
|
|
|
291
303
|
# GIVEN
|
|
292
304
|
self.amazon_session.is_authenticated = True
|
|
293
305
|
order_id = "112-9685975-5907428"
|
|
294
|
-
with open(os.path.join(self.RESOURCES_DIR, f"order-details-{order_id}.html"), "r",
|
|
306
|
+
with open(os.path.join(self.RESOURCES_DIR, "orders", f"order-details-{order_id}.html"), "r",
|
|
295
307
|
encoding="utf-8") as f:
|
|
296
308
|
resp1 = responses.add(
|
|
297
309
|
responses.GET,
|
|
@@ -312,7 +324,7 @@ class TestOrders(UnitTestCase):
|
|
|
312
324
|
# GIVEN
|
|
313
325
|
self.amazon_session.is_authenticated = True
|
|
314
326
|
order_id = "112-5939971-8962610"
|
|
315
|
-
with open(os.path.join(self.RESOURCES_DIR, f"order-details-{order_id}.html"), "r",
|
|
327
|
+
with open(os.path.join(self.RESOURCES_DIR, "orders", f"order-details-{order_id}.html"), "r",
|
|
316
328
|
encoding="utf-8") as f:
|
|
317
329
|
resp1 = responses.add(
|
|
318
330
|
responses.GET,
|
|
@@ -333,7 +345,7 @@ class TestOrders(UnitTestCase):
|
|
|
333
345
|
# GIVEN
|
|
334
346
|
self.amazon_session.is_authenticated = True
|
|
335
347
|
order_id = "112-4482432-2955442"
|
|
336
|
-
with open(os.path.join(self.RESOURCES_DIR, f"order-details-{order_id}.html"), "r",
|
|
348
|
+
with open(os.path.join(self.RESOURCES_DIR, "orders", f"order-details-{order_id}.html"), "r",
|
|
337
349
|
encoding="utf-8") as f:
|
|
338
350
|
resp1 = responses.add(
|
|
339
351
|
responses.GET,
|
|
@@ -354,7 +366,7 @@ class TestOrders(UnitTestCase):
|
|
|
354
366
|
# GIVEN
|
|
355
367
|
self.amazon_session.is_authenticated = True
|
|
356
368
|
order_id = "112-9087159-1657009"
|
|
357
|
-
with open(os.path.join(self.RESOURCES_DIR, f"order-details-{order_id}.html"), "r",
|
|
369
|
+
with open(os.path.join(self.RESOURCES_DIR, "orders", f"order-details-{order_id}.html"), "r",
|
|
358
370
|
encoding="utf-8") as f:
|
|
359
371
|
resp1 = responses.add(
|
|
360
372
|
responses.GET,
|
|
@@ -375,7 +387,7 @@ class TestOrders(UnitTestCase):
|
|
|
375
387
|
# GIVEN
|
|
376
388
|
self.amazon_session.is_authenticated = True
|
|
377
389
|
order_id = "114-8722141-6545058"
|
|
378
|
-
with open(os.path.join(self.RESOURCES_DIR, f"order-details-{order_id}.html"), "r",
|
|
390
|
+
with open(os.path.join(self.RESOURCES_DIR, "orders", f"order-details-{order_id}.html"), "r",
|
|
379
391
|
encoding="utf-8") as f:
|
|
380
392
|
resp1 = responses.add(
|
|
381
393
|
responses.GET,
|
|
@@ -396,7 +408,7 @@ class TestOrders(UnitTestCase):
|
|
|
396
408
|
# GIVEN
|
|
397
409
|
self.amazon_session.is_authenticated = True
|
|
398
410
|
order_id = "111-6778632-7354601"
|
|
399
|
-
with open(os.path.join(self.RESOURCES_DIR, f"order-details-{order_id}.html"), "r",
|
|
411
|
+
with open(os.path.join(self.RESOURCES_DIR, "orders", f"order-details-{order_id}.html"), "r",
|
|
400
412
|
encoding="utf-8") as f:
|
|
401
413
|
resp1 = responses.add(
|
|
402
414
|
responses.GET,
|
|
@@ -37,14 +37,15 @@ class TestSession(UnitTestCase):
|
|
|
37
37
|
@responses.activate
|
|
38
38
|
def test_login_invalid_username(self):
|
|
39
39
|
# GIVEN
|
|
40
|
-
with open(os.path.join(self.RESOURCES_DIR, "signin.html"), "r", encoding="utf-8") as f:
|
|
40
|
+
with open(os.path.join(self.RESOURCES_DIR, "auth", "signin.html"), "r", encoding="utf-8") as f:
|
|
41
41
|
resp1 = responses.add(
|
|
42
42
|
responses.GET,
|
|
43
43
|
f"{self.test_config.constants.BASE_URL}/gp/sign-in.html",
|
|
44
44
|
body=f.read(),
|
|
45
45
|
status=200,
|
|
46
46
|
)
|
|
47
|
-
with open(os.path.join(self.RESOURCES_DIR, "post-signin-invalid-email.html"), "r",
|
|
47
|
+
with open(os.path.join(self.RESOURCES_DIR, "auth", "post-signin-invalid-email.html"), "r",
|
|
48
|
+
encoding="utf-8") as f:
|
|
48
49
|
resp2 = responses.add(
|
|
49
50
|
responses.POST,
|
|
50
51
|
self.test_config.constants.SIGN_IN_REDIRECT_URL,
|
|
@@ -64,14 +65,15 @@ class TestSession(UnitTestCase):
|
|
|
64
65
|
@responses.activate
|
|
65
66
|
def test_login_invalid_password(self):
|
|
66
67
|
# GIVEN
|
|
67
|
-
with open(os.path.join(self.RESOURCES_DIR, "signin.html"), "r", encoding="utf-8") as f:
|
|
68
|
+
with open(os.path.join(self.RESOURCES_DIR, "auth", "signin.html"), "r", encoding="utf-8") as f:
|
|
68
69
|
resp1 = responses.add(
|
|
69
70
|
responses.GET,
|
|
70
71
|
f"{self.test_config.constants.BASE_URL}/gp/sign-in.html",
|
|
71
72
|
body=f.read(),
|
|
72
73
|
status=200,
|
|
73
74
|
)
|
|
74
|
-
with open(os.path.join(self.RESOURCES_DIR, "post-signin-invalid-password.html"), "r",
|
|
75
|
+
with open(os.path.join(self.RESOURCES_DIR, "auth", "post-signin-invalid-password.html"), "r",
|
|
76
|
+
encoding="utf-8") as f:
|
|
75
77
|
resp2 = responses.add(
|
|
76
78
|
responses.POST,
|
|
77
79
|
self.test_config.constants.SIGN_IN_REDIRECT_URL,
|
|
@@ -92,21 +94,21 @@ class TestSession(UnitTestCase):
|
|
|
92
94
|
@patch("builtins.input")
|
|
93
95
|
def test_mfa(self, input_mock):
|
|
94
96
|
# GIVEN
|
|
95
|
-
with open(os.path.join(self.RESOURCES_DIR, "signin.html"), "r", encoding="utf-8") as f:
|
|
97
|
+
with open(os.path.join(self.RESOURCES_DIR, "auth", "signin.html"), "r", encoding="utf-8") as f:
|
|
96
98
|
resp1 = responses.add(
|
|
97
99
|
responses.GET,
|
|
98
100
|
f"{self.test_config.constants.BASE_URL}/gp/sign-in.html",
|
|
99
101
|
body=f.read(),
|
|
100
102
|
status=200,
|
|
101
103
|
)
|
|
102
|
-
with open(os.path.join(self.RESOURCES_DIR, "post-signin-mfa.html"), "r", encoding="utf-8") as f:
|
|
104
|
+
with open(os.path.join(self.RESOURCES_DIR, "auth", "post-signin-mfa.html"), "r", encoding="utf-8") as f:
|
|
103
105
|
resp2 = responses.add(
|
|
104
106
|
responses.POST,
|
|
105
107
|
self.test_config.constants.SIGN_IN_REDIRECT_URL,
|
|
106
108
|
body=f.read(),
|
|
107
109
|
status=200,
|
|
108
110
|
)
|
|
109
|
-
with open(os.path.join(self.RESOURCES_DIR, "order-history-2018-0.html"), "r", encoding="utf-8") as f:
|
|
111
|
+
with open(os.path.join(self.RESOURCES_DIR, "orders", "order-history-2018-0.html"), "r", encoding="utf-8") as f:
|
|
110
112
|
resp3 = responses.add(
|
|
111
113
|
responses.POST,
|
|
112
114
|
self.test_config.constants.SIGN_IN_REDIRECT_URL,
|
|
@@ -128,28 +130,28 @@ class TestSession(UnitTestCase):
|
|
|
128
130
|
@patch("builtins.input")
|
|
129
131
|
def test_new_otp(self, input_mock):
|
|
130
132
|
# GIVEN
|
|
131
|
-
with open(os.path.join(self.RESOURCES_DIR, "signin.html"), "r", encoding="utf-8") as f:
|
|
133
|
+
with open(os.path.join(self.RESOURCES_DIR, "auth", "signin.html"), "r", encoding="utf-8") as f:
|
|
132
134
|
resp1 = responses.add(
|
|
133
135
|
responses.GET,
|
|
134
136
|
f"{self.test_config.constants.BASE_URL}/gp/sign-in.html",
|
|
135
137
|
body=f.read(),
|
|
136
138
|
status=200,
|
|
137
139
|
)
|
|
138
|
-
with open(os.path.join(self.RESOURCES_DIR, "post-signin-new-otp.html"), "r", encoding="utf-8") as f:
|
|
140
|
+
with open(os.path.join(self.RESOURCES_DIR, "auth", "post-signin-new-otp.html"), "r", encoding="utf-8") as f:
|
|
139
141
|
resp2 = responses.add(
|
|
140
142
|
responses.POST,
|
|
141
143
|
self.test_config.constants.SIGN_IN_REDIRECT_URL,
|
|
142
144
|
body=f.read(),
|
|
143
145
|
status=200,
|
|
144
146
|
)
|
|
145
|
-
with open(os.path.join(self.RESOURCES_DIR, "post-signin-mfa.html"), "r", encoding="utf-8") as f:
|
|
147
|
+
with open(os.path.join(self.RESOURCES_DIR, "auth", "post-signin-mfa.html"), "r", encoding="utf-8") as f:
|
|
146
148
|
resp3 = responses.add(
|
|
147
149
|
responses.POST,
|
|
148
150
|
self.test_config.constants.SIGN_IN_REDIRECT_URL,
|
|
149
151
|
body=f.read(),
|
|
150
152
|
status=200,
|
|
151
153
|
)
|
|
152
|
-
with open(os.path.join(self.RESOURCES_DIR, "order-history-2018-0.html"), "r", encoding="utf-8") as f:
|
|
154
|
+
with open(os.path.join(self.RESOURCES_DIR, "orders", "order-history-2018-0.html"), "r", encoding="utf-8") as f:
|
|
153
155
|
resp4 = responses.add(
|
|
154
156
|
responses.POST,
|
|
155
157
|
self.test_config.constants.SIGN_IN_REDIRECT_URL,
|
|
@@ -171,7 +173,7 @@ class TestSession(UnitTestCase):
|
|
|
171
173
|
@responses.activate
|
|
172
174
|
def test_captcha_1(self):
|
|
173
175
|
# GIVEN
|
|
174
|
-
with open(os.path.join(self.RESOURCES_DIR, "signin.html"), "r", encoding="utf-8") as f:
|
|
176
|
+
with open(os.path.join(self.RESOURCES_DIR, "auth", "signin.html"), "r", encoding="utf-8") as f:
|
|
175
177
|
resp1 = responses.add(
|
|
176
178
|
responses.GET,
|
|
177
179
|
f"{self.test_config.constants.BASE_URL}/gp/sign-in.html",
|
|
@@ -184,14 +186,14 @@ class TestSession(UnitTestCase):
|
|
|
184
186
|
status=302,
|
|
185
187
|
headers={"Location": f"{self.test_config.constants.BASE_URL}/ap/cvf/request"}
|
|
186
188
|
)
|
|
187
|
-
with open(os.path.join(self.RESOURCES_DIR, "post-signin-captcha-1.html"), "r", encoding="utf-8") as f:
|
|
189
|
+
with open(os.path.join(self.RESOURCES_DIR, "auth", "post-signin-captcha-1.html"), "r", encoding="utf-8") as f:
|
|
188
190
|
resp3 = responses.add(
|
|
189
191
|
responses.GET,
|
|
190
192
|
f"{self.test_config.constants.BASE_URL}/ap/cvf/request",
|
|
191
193
|
body=f.read(),
|
|
192
194
|
status=200
|
|
193
195
|
)
|
|
194
|
-
with open(os.path.join(self.RESOURCES_DIR, "captcha_easy.jpg"), "rb") as f:
|
|
196
|
+
with open(os.path.join(self.RESOURCES_DIR, "auth", "captcha_easy.jpg"), "rb") as f:
|
|
195
197
|
resp4 = responses.add(
|
|
196
198
|
responses.GET,
|
|
197
199
|
"https://opfcaptcha-prod.s3.amazonaws.com/d32ff4fa043d4f969a1693adfb5d663a.jpg",
|
|
@@ -199,7 +201,7 @@ class TestSession(UnitTestCase):
|
|
|
199
201
|
headers={"Content-Type": "image/jpeg"},
|
|
200
202
|
status=200,
|
|
201
203
|
)
|
|
202
|
-
with open(os.path.join(self.RESOURCES_DIR, "order-history-2018-0.html"), "r", encoding="utf-8") as f:
|
|
204
|
+
with open(os.path.join(self.RESOURCES_DIR, "orders", "order-history-2018-0.html"), "r", encoding="utf-8") as f:
|
|
203
205
|
request_data = {
|
|
204
206
|
"clientContext": "132-7968344-2156059",
|
|
205
207
|
"cvf_captcha_captcha_action": "verifyCaptcha",
|
|
@@ -242,21 +244,21 @@ class TestSession(UnitTestCase):
|
|
|
242
244
|
@responses.activate
|
|
243
245
|
def test_captcha_2(self):
|
|
244
246
|
# GIVEN
|
|
245
|
-
with open(os.path.join(self.RESOURCES_DIR, "signin.html"), "r", encoding="utf-8") as f:
|
|
247
|
+
with open(os.path.join(self.RESOURCES_DIR, "auth", "signin.html"), "r", encoding="utf-8") as f:
|
|
246
248
|
resp1 = responses.add(
|
|
247
249
|
responses.GET,
|
|
248
250
|
f"{self.test_config.constants.BASE_URL}/gp/sign-in.html",
|
|
249
251
|
body=f.read(),
|
|
250
252
|
status=200,
|
|
251
253
|
)
|
|
252
|
-
with open(os.path.join(self.RESOURCES_DIR, "post-signin-captcha-2.html"), "r", encoding="utf-8") as f:
|
|
254
|
+
with open(os.path.join(self.RESOURCES_DIR, "auth", "post-signin-captcha-2.html"), "r", encoding="utf-8") as f:
|
|
253
255
|
resp2 = responses.add(
|
|
254
256
|
responses.POST,
|
|
255
257
|
self.test_config.constants.SIGN_IN_REDIRECT_URL,
|
|
256
258
|
body=f.read(),
|
|
257
259
|
status=200,
|
|
258
260
|
)
|
|
259
|
-
with open(os.path.join(self.RESOURCES_DIR, "captcha_easy.jpg"), "rb") as f:
|
|
261
|
+
with open(os.path.join(self.RESOURCES_DIR, "auth", "captcha_easy.jpg"), "rb") as f:
|
|
260
262
|
resp3 = responses.add(
|
|
261
263
|
responses.GET,
|
|
262
264
|
"https://images-na.ssl-images-amazon.com/captcha/ddwwidnf/Captcha_gmwackhtzu.jpg",
|
|
@@ -264,7 +266,7 @@ class TestSession(UnitTestCase):
|
|
|
264
266
|
headers={"Content-Type": "image/jpeg"},
|
|
265
267
|
status=200,
|
|
266
268
|
)
|
|
267
|
-
with open(os.path.join(self.RESOURCES_DIR, "order-history-2018-0.html"), "r", encoding="utf-8") as f:
|
|
269
|
+
with open(os.path.join(self.RESOURCES_DIR, "orders", "order-history-2018-0.html"), "r", encoding="utf-8") as f:
|
|
268
270
|
resp4 = responses.add(
|
|
269
271
|
responses.GET,
|
|
270
272
|
f"{self.test_config.constants.BASE_URL}/errors/validateCaptcha",
|
|
@@ -293,7 +295,7 @@ class TestSession(UnitTestCase):
|
|
|
293
295
|
@patch("PIL.Image.Image.show")
|
|
294
296
|
def test_captcha_1_hard(self, show_mock, input_mock):
|
|
295
297
|
# GIVEN
|
|
296
|
-
with open(os.path.join(self.RESOURCES_DIR, "signin.html"), "r", encoding="utf-8") as f:
|
|
298
|
+
with open(os.path.join(self.RESOURCES_DIR, "auth", "signin.html"), "r", encoding="utf-8") as f:
|
|
297
299
|
resp1 = responses.add(
|
|
298
300
|
responses.GET,
|
|
299
301
|
f"{self.test_config.constants.BASE_URL}/gp/sign-in.html",
|
|
@@ -306,14 +308,14 @@ class TestSession(UnitTestCase):
|
|
|
306
308
|
status=302,
|
|
307
309
|
headers={"Location": f"{self.test_config.constants.BASE_URL}/ap/cvf/request"}
|
|
308
310
|
)
|
|
309
|
-
with open(os.path.join(self.RESOURCES_DIR, "post-signin-captcha-1.html"), "r", encoding="utf-8") as f:
|
|
311
|
+
with open(os.path.join(self.RESOURCES_DIR, "auth", "post-signin-captcha-1.html"), "r", encoding="utf-8") as f:
|
|
310
312
|
resp3 = responses.add(
|
|
311
313
|
responses.GET,
|
|
312
314
|
f"{self.test_config.constants.BASE_URL}/ap/cvf/request",
|
|
313
315
|
body=f.read(),
|
|
314
316
|
status=200
|
|
315
317
|
)
|
|
316
|
-
with open(os.path.join(self.RESOURCES_DIR, "captcha_hard.jpg"), "rb") as f:
|
|
318
|
+
with open(os.path.join(self.RESOURCES_DIR, "auth", "captcha_hard.jpg"), "rb") as f:
|
|
317
319
|
resp4 = responses.add(
|
|
318
320
|
responses.GET,
|
|
319
321
|
"https://opfcaptcha-prod.s3.amazonaws.com/d32ff4fa043d4f969a1693adfb5d663a.jpg",
|
|
@@ -321,7 +323,7 @@ class TestSession(UnitTestCase):
|
|
|
321
323
|
headers={"Content-Type": "image/jpeg"},
|
|
322
324
|
status=200,
|
|
323
325
|
)
|
|
324
|
-
with open(os.path.join(self.RESOURCES_DIR, "order-history-2018-0.html"), "r", encoding="utf-8") as f:
|
|
326
|
+
with open(os.path.join(self.RESOURCES_DIR, "orders", "order-history-2018-0.html"), "r", encoding="utf-8") as f:
|
|
325
327
|
resp5 = responses.add(
|
|
326
328
|
responses.POST,
|
|
327
329
|
f"{self.test_config.constants.BASE_URL}/ap/cvf/verify",
|
|
@@ -345,21 +347,23 @@ class TestSession(UnitTestCase):
|
|
|
345
347
|
@patch("builtins.input")
|
|
346
348
|
def test_captcha_otp(self, input_mock):
|
|
347
349
|
# GIVEN
|
|
348
|
-
with open(os.path.join(self.RESOURCES_DIR, "signin.html"), "r", encoding="utf-8") as f:
|
|
350
|
+
with open(os.path.join(self.RESOURCES_DIR, "auth", "signin.html"), "r", encoding="utf-8") as f:
|
|
349
351
|
resp1 = responses.add(
|
|
350
352
|
responses.GET,
|
|
351
353
|
f"{self.test_config.constants.BASE_URL}/gp/sign-in.html",
|
|
352
354
|
body=f.read(),
|
|
353
355
|
status=200,
|
|
354
356
|
)
|
|
355
|
-
with open(os.path.join(self.RESOURCES_DIR, "post-signin-captcha-otp.html"),
|
|
357
|
+
with open(os.path.join(self.RESOURCES_DIR, "auth", "post-signin-captcha-otp.html"),
|
|
358
|
+
"r", encoding="utf-8") as f:
|
|
356
359
|
resp2 = responses.add(
|
|
357
360
|
responses.POST,
|
|
358
361
|
self.test_config.constants.SIGN_IN_REDIRECT_URL,
|
|
359
362
|
body=f.read(),
|
|
360
363
|
status=200,
|
|
361
364
|
)
|
|
362
|
-
with open(os.path.join(self.RESOURCES_DIR, "order-history-2018-0.html"),
|
|
365
|
+
with open(os.path.join(self.RESOURCES_DIR, "orders", "order-history-2018-0.html"),
|
|
366
|
+
"r", encoding="utf-8") as f:
|
|
363
367
|
resp3 = responses.add(
|
|
364
368
|
responses.POST,
|
|
365
369
|
f"{self.test_config.constants.BASE_URL}/ap/cvf/approval/verifyOtp",
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
__copyright__ = "Copyright (c) 2024 Alex Laird"
|
|
2
|
+
__license__ = "MIT"
|
|
3
|
+
|
|
4
|
+
import datetime
|
|
5
|
+
import os
|
|
6
|
+
from unittest.mock import Mock, patch
|
|
7
|
+
|
|
8
|
+
import responses
|
|
9
|
+
from bs4 import BeautifulSoup
|
|
10
|
+
|
|
11
|
+
from amazonorders.session import AmazonSession
|
|
12
|
+
from amazonorders.transactions import AmazonTransactions, _parse_transaction_form_tag
|
|
13
|
+
from tests.unittestcase import UnitTestCase
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class TestOrders(UnitTestCase):
|
|
17
|
+
temp_order_history_file_path = os.path.join(
|
|
18
|
+
os.path.abspath(os.path.dirname(__file__)), "output", "temp-order-history.html"
|
|
19
|
+
)
|
|
20
|
+
temp_order_details_file_path = os.path.join(
|
|
21
|
+
os.path.abspath(os.path.dirname(__file__)), "output", "temp-order-details.html"
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
def setUp(self):
|
|
25
|
+
super().setUp()
|
|
26
|
+
|
|
27
|
+
self.amazon_session = AmazonSession(
|
|
28
|
+
"some-username", "some-password", config=self.test_config
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
self.amazon_transactions = AmazonTransactions(self.amazon_session)
|
|
32
|
+
|
|
33
|
+
@responses.activate
|
|
34
|
+
@patch("amazonorders.transactions.datetime", wraps=datetime)
|
|
35
|
+
def test_get_transactions(self, mock_get_today: Mock):
|
|
36
|
+
# GIVEN
|
|
37
|
+
mock_get_today.date.today.return_value = datetime.date(2024, 10, 11)
|
|
38
|
+
days = 1
|
|
39
|
+
self.amazon_session.is_authenticated = True
|
|
40
|
+
with open(os.path.join(self.RESOURCES_DIR, "transactions", "get-transactions-snippet.html"),
|
|
41
|
+
"r",
|
|
42
|
+
encoding="utf-8") as f:
|
|
43
|
+
responses.add(
|
|
44
|
+
responses.GET,
|
|
45
|
+
f"{self.test_config.constants.TRANSACTION_HISTORY_LANDING_URL}",
|
|
46
|
+
body=f.read(),
|
|
47
|
+
status=200,
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
# WHEN
|
|
51
|
+
transactions = self.amazon_transactions.get_transactions(days=days)
|
|
52
|
+
|
|
53
|
+
# THEN
|
|
54
|
+
self.assertEqual(1, len(transactions))
|
|
55
|
+
transaction = transactions[0]
|
|
56
|
+
self.assertEqual(transaction.completed_date, datetime.date(2024, 10, 11))
|
|
57
|
+
self.assertEqual(transaction.payment_method, "Visa ****1234")
|
|
58
|
+
self.assertEqual(transaction.grand_total, -45.19)
|
|
59
|
+
self.assertFalse(transaction.is_refund)
|
|
60
|
+
self.assertEqual(transaction.order_number, "123-4567890-1234567")
|
|
61
|
+
self.assertEqual(transaction.order_details_link,
|
|
62
|
+
"https://www.amazon.ca/gp/css/summary/edit.html?orderID=123-4567890-1234567")
|
|
63
|
+
self.assertEqual(transaction.seller, "AMZN Mktp CA")
|
|
64
|
+
|
|
65
|
+
@responses.activate
|
|
66
|
+
@patch("amazonorders.transactions.datetime", wraps=datetime)
|
|
67
|
+
def test_get_transactions_with_pending(self, mock_get_today: Mock):
|
|
68
|
+
# GIVEN
|
|
69
|
+
mock_get_today.date.today.return_value = datetime.date(2025, 2, 13)
|
|
70
|
+
days = 30
|
|
71
|
+
self.amazon_session.is_authenticated = True
|
|
72
|
+
with open(os.path.join(self.RESOURCES_DIR, "transactions", "transactions-in-progress.html"),
|
|
73
|
+
"r",
|
|
74
|
+
encoding="utf-8") as f:
|
|
75
|
+
responses.add(
|
|
76
|
+
responses.GET,
|
|
77
|
+
f"{self.test_config.constants.TRANSACTION_HISTORY_LANDING_URL}",
|
|
78
|
+
body=f.read(),
|
|
79
|
+
status=200,
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
# WHEN
|
|
83
|
+
transactions = self.amazon_transactions.get_transactions(days=days)
|
|
84
|
+
|
|
85
|
+
# THEN
|
|
86
|
+
self.assertEqual(20, len(transactions))
|
|
87
|
+
transaction = transactions[0]
|
|
88
|
+
self.assertEqual(transaction.completed_date, datetime.date(2025, 2, 12))
|
|
89
|
+
self.assertEqual(transaction.payment_method, "Prime Visa ****1111")
|
|
90
|
+
self.assertEqual(transaction.grand_total, -26.29)
|
|
91
|
+
self.assertFalse(transaction.is_refund)
|
|
92
|
+
self.assertEqual(transaction.order_number, "234-8832881-7100260")
|
|
93
|
+
self.assertEqual(transaction.order_details_link,
|
|
94
|
+
"https://www.amazon.com/gp/your-account/order-details?orderID=234-8832881-7100260")
|
|
95
|
+
self.assertEqual(transaction.seller, None)
|
|
96
|
+
transaction = transactions[1]
|
|
97
|
+
self.assertEqual(transaction.completed_date, datetime.date(2025, 2, 7))
|
|
98
|
+
self.assertEqual(transaction.payment_method, "Prime Visa ****1111")
|
|
99
|
+
self.assertEqual(transaction.grand_total, 43.94)
|
|
100
|
+
self.assertTrue(transaction.is_refund)
|
|
101
|
+
self.assertEqual(transaction.order_number, "234-3017692-4601031")
|
|
102
|
+
self.assertEqual(transaction.order_details_link,
|
|
103
|
+
"https://www.amazon.com/gp/css/summary/edit.html?orderID=234-3017692-4601031")
|
|
104
|
+
self.assertEqual(transaction.seller, "AMZN Mktp US")
|
|
105
|
+
|
|
106
|
+
def test_parse_transaction_form_tag(self):
|
|
107
|
+
# GIVEN
|
|
108
|
+
with open(os.path.join(self.RESOURCES_DIR, "transactions", "transaction-form-tag.html"),
|
|
109
|
+
"r",
|
|
110
|
+
encoding="utf-8") as f:
|
|
111
|
+
parsed = BeautifulSoup(f.read(), self.test_config.bs4_parser)
|
|
112
|
+
form_tag = parsed.select_one("form")
|
|
113
|
+
|
|
114
|
+
# WHEN
|
|
115
|
+
transactions, next_page_url, next_page_data = _parse_transaction_form_tag(
|
|
116
|
+
form_tag, self.test_config
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
# THEN
|
|
120
|
+
self.assertEqual(len(transactions), 2)
|
|
121
|
+
self.assertEqual(
|
|
122
|
+
next_page_url, "https://www.amazon.com:443/cpe/yourpayments/transactions"
|
|
123
|
+
)
|
|
124
|
+
self.assertEqual(
|
|
125
|
+
next_page_data,
|
|
126
|
+
{
|
|
127
|
+
"ppw-widgetState": "the-ppw-widgetState",
|
|
128
|
+
"ie": "UTF-8",
|
|
129
|
+
'ppw-widgetEvent:DefaultNextPageNavigationEvent:{"nextPageKey":"key"}': "",
|
|
130
|
+
},
|
|
131
|
+
)
|
|
@@ -1,166 +0,0 @@
|
|
|
1
|
-
__copyright__ = "Copyright (c) 2024 Jeff Sawatzky"
|
|
2
|
-
__license__ = "MIT"
|
|
3
|
-
|
|
4
|
-
import datetime
|
|
5
|
-
import os
|
|
6
|
-
from unittest.mock import Mock, patch
|
|
7
|
-
|
|
8
|
-
import responses
|
|
9
|
-
from bs4 import BeautifulSoup
|
|
10
|
-
|
|
11
|
-
from amazonorders.session import AmazonSession
|
|
12
|
-
from amazonorders.transactions import AmazonTransactions, _parse_transaction_form_tag
|
|
13
|
-
from tests.unittestcase import UnitTestCase
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
class TestOrders(UnitTestCase):
|
|
17
|
-
temp_order_history_file_path = os.path.join(
|
|
18
|
-
os.path.abspath(os.path.dirname(__file__)), "output", "temp-order-history.html"
|
|
19
|
-
)
|
|
20
|
-
temp_order_details_file_path = os.path.join(
|
|
21
|
-
os.path.abspath(os.path.dirname(__file__)), "output", "temp-order-details.html"
|
|
22
|
-
)
|
|
23
|
-
|
|
24
|
-
def setUp(self):
|
|
25
|
-
super().setUp()
|
|
26
|
-
|
|
27
|
-
self.amazon_session = AmazonSession(
|
|
28
|
-
"some-username", "some-password", config=self.test_config
|
|
29
|
-
)
|
|
30
|
-
|
|
31
|
-
self.amazon_transactions = AmazonTransactions(self.amazon_session)
|
|
32
|
-
|
|
33
|
-
@responses.activate
|
|
34
|
-
@patch("amazonorders.transactions.datetime", wraps=datetime)
|
|
35
|
-
def test_transactions_command(self, mock_get_today: Mock):
|
|
36
|
-
# GIVEN
|
|
37
|
-
mock_get_today.date.today.return_value = datetime.date(2024, 10, 11)
|
|
38
|
-
days = 1
|
|
39
|
-
self.amazon_session.is_authenticated = True
|
|
40
|
-
with open(
|
|
41
|
-
os.path.join(self.RESOURCES_DIR, "get-transactions.html"),
|
|
42
|
-
"r",
|
|
43
|
-
encoding="utf-8",
|
|
44
|
-
) as f:
|
|
45
|
-
responses.add(
|
|
46
|
-
responses.GET,
|
|
47
|
-
f"{self.test_config.constants.TRANSACTION_HISTORY_LANDING_URL}",
|
|
48
|
-
body=f.read(),
|
|
49
|
-
status=200,
|
|
50
|
-
)
|
|
51
|
-
|
|
52
|
-
# WHEN
|
|
53
|
-
transactions = self.amazon_transactions.get_transactions(days=days)
|
|
54
|
-
|
|
55
|
-
# THEN
|
|
56
|
-
self.assertEqual(1, len(transactions))
|
|
57
|
-
|
|
58
|
-
def test_parse_transaction_form_tag(self):
|
|
59
|
-
# GIVEN
|
|
60
|
-
parsed = BeautifulSoup(TEST_PARSE_TRANSACTION_FORM_TAG_HTML, self.test_config.bs4_parser)
|
|
61
|
-
form_tag = parsed.select_one("form")
|
|
62
|
-
|
|
63
|
-
# WHEN
|
|
64
|
-
transactions, next_page_url, next_page_data = _parse_transaction_form_tag(
|
|
65
|
-
form_tag, self.test_config
|
|
66
|
-
)
|
|
67
|
-
|
|
68
|
-
# THEN
|
|
69
|
-
self.assertEqual(len(transactions), 2)
|
|
70
|
-
self.assertEqual(
|
|
71
|
-
next_page_url, "https://www.amazon.com:443/cpe/yourpayments/transactions"
|
|
72
|
-
)
|
|
73
|
-
self.assertEqual(
|
|
74
|
-
next_page_data,
|
|
75
|
-
{
|
|
76
|
-
"ppw-widgetState": "the-ppw-widgetState",
|
|
77
|
-
"ie": "UTF-8",
|
|
78
|
-
'ppw-widgetEvent:DefaultNextPageNavigationEvent:{"nextPageKey":"key"}': "",
|
|
79
|
-
},
|
|
80
|
-
)
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
TEST_PARSE_TRANSACTION_FORM_TAG_HTML = """
|
|
84
|
-
<form action="https://www.amazon.com:443/cpe/yourpayments/transactions" class="a-spacing-none" method="post"><input
|
|
85
|
-
name="ppw-widgetState" type="hidden" value="the-ppw-widgetState" /><input name="ie" type="hidden"
|
|
86
|
-
value="UTF-8" />
|
|
87
|
-
<div class="a-box-group a-spacing-base">
|
|
88
|
-
<div class="a-box a-spacing-none a-box-title apx-transactions-sleeve-header-container">
|
|
89
|
-
<div class="a-box-inner a-padding-base"><span class="a-size-base a-text-bold">Completed</span></div>
|
|
90
|
-
</div>
|
|
91
|
-
<div class="a-box a-spacing-base">
|
|
92
|
-
<div class="a-box-inner a-padding-none">
|
|
93
|
-
<div class="a-section a-spacing-base a-padding-base apx-transaction-date-container pmts-portal-component pmts-portal-components-pp-kXMaEm-3"
|
|
94
|
-
data-pmts-component-id="pp-kXMaEm-3"><span>October 11, 2024</span></div>
|
|
95
|
-
<div class="a-section a-spacing-base pmts-portal-component pmts-portal-components-pp-kXMaEm-3"
|
|
96
|
-
data-pmts-component-id="pp-kXMaEm-3">
|
|
97
|
-
<div class="a-section a-spacing-base apx-transactions-line-item-component-container">
|
|
98
|
-
<div class="a-row pmts-portal-component pmts-portal-components-pp-kXMaEm-4"
|
|
99
|
-
data-pmts-component-id="pp-kXMaEm-4">
|
|
100
|
-
<div class="a-column a-span9"><span class="a-size-base a-text-bold">Visa ****1234</span>
|
|
101
|
-
</div>
|
|
102
|
-
<div class="a-column a-span3 a-text-right a-span-last"><span
|
|
103
|
-
class="a-size-base-plus a-text-bold">-CA$45.19</span></div>
|
|
104
|
-
</div>
|
|
105
|
-
<div class="a-section a-spacing-none a-spacing-top-mini pmts-portal-component pmts-portal-components-pp-kXMaEm-4"
|
|
106
|
-
data-pmts-component-id="pp-kXMaEm-4">
|
|
107
|
-
<div class="a-row">
|
|
108
|
-
<div class="a-column a-span12"><a class="a-link-normal"
|
|
109
|
-
href="https://www.amazon.ca/gp/css/summary/edit.html?orderID=123-4567890-1234567"
|
|
110
|
-
id="pp-kXMaEm-50">Order #123-4567890-1234567</a></div>
|
|
111
|
-
</div>
|
|
112
|
-
</div>
|
|
113
|
-
<div class="a-section a-spacing-none a-spacing-top-mini pmts-portal-component pmts-portal-components-pp-kXMaEm-4"
|
|
114
|
-
data-pmts-component-id="pp-kXMaEm-4">
|
|
115
|
-
<div class="a-row">
|
|
116
|
-
<div class="a-column a-span12"><span class="a-size-base">AMZN Mktp CA</span></div>
|
|
117
|
-
</div>
|
|
118
|
-
</div>
|
|
119
|
-
</div>
|
|
120
|
-
</div>
|
|
121
|
-
<div class="a-section a-spacing-base a-padding-base apx-transaction-date-container pmts-portal-component pmts-portal-components-pp-kXMaEm-8"
|
|
122
|
-
data-pmts-component-id="pp-kXMaEm-8"><span>October 9, 2024</span></div>
|
|
123
|
-
<div class="a-section a-spacing-base pmts-portal-component pmts-portal-components-pp-kXMaEm-8"
|
|
124
|
-
data-pmts-component-id="pp-kXMaEm-8">
|
|
125
|
-
<div class="a-section a-spacing-base apx-transactions-line-item-component-container">
|
|
126
|
-
<div class="a-row pmts-portal-component pmts-portal-components-pp-kXMaEm-9"
|
|
127
|
-
data-pmts-component-id="pp-kXMaEm-9">
|
|
128
|
-
<div class="a-column a-span9"><span class="a-size-base a-text-bold">Mastercard
|
|
129
|
-
****1234</span></div>
|
|
130
|
-
<div class="a-column a-span3 a-text-right a-span-last"><span
|
|
131
|
-
class="a-size-base-plus a-text-bold">-CA$28.79</span></div>
|
|
132
|
-
</div>
|
|
133
|
-
<div class="a-section a-spacing-none a-spacing-top-mini pmts-portal-component pmts-portal-components-pp-kXMaEm-9"
|
|
134
|
-
data-pmts-component-id="pp-kXMaEm-9">
|
|
135
|
-
<div class="a-row">
|
|
136
|
-
<div class="a-column a-span12"><a class="a-link-normal"
|
|
137
|
-
href="https://www.amazon.ca/gp/css/summary/edit.html?orderID=123-4567890-1234567"
|
|
138
|
-
id="pp-kXMaEm-52">Order #123-4567890-1234567</a></div>
|
|
139
|
-
</div>
|
|
140
|
-
</div>
|
|
141
|
-
<div class="a-section a-spacing-none a-spacing-top-mini pmts-portal-component pmts-portal-components-pp-kXMaEm-9"
|
|
142
|
-
data-pmts-component-id="pp-kXMaEm-9">
|
|
143
|
-
<div class="a-row">
|
|
144
|
-
<div class="a-column a-span12"><span class="a-size-base">Amazon.ca</span></div>
|
|
145
|
-
</div>
|
|
146
|
-
</div>
|
|
147
|
-
</div>
|
|
148
|
-
</div>
|
|
149
|
-
</div>
|
|
150
|
-
</div>
|
|
151
|
-
</div>
|
|
152
|
-
<div class="a-row a-spacing-top-extra-large">
|
|
153
|
-
<div class="a-column a-span2 a-text-center"><span class="a-button a-button-span12 a-button-base"><span
|
|
154
|
-
class="a-button-inner"><input class="a-button-input"
|
|
155
|
-
name='ppw-widgetEvent:DefaultPreviousPageNavigationEvent:{"previousPageKey":"key"}'
|
|
156
|
-
type="submit" /><span aria-hidden="true" class="a-button-text">Previous
|
|
157
|
-
Page</span></span></span></div>
|
|
158
|
-
<div class="a-column a-span2 a-text-center a-span-last"><span
|
|
159
|
-
class="a-button a-button-span12 a-button-base"><span class="a-button-inner"><input
|
|
160
|
-
class="a-button-input"
|
|
161
|
-
name='ppw-widgetEvent:DefaultNextPageNavigationEvent:{"nextPageKey":"key"}'
|
|
162
|
-
type="submit" /><span aria-hidden="true" class="a-button-text">Next Page</span></span></span>
|
|
163
|
-
</div>
|
|
164
|
-
</div>
|
|
165
|
-
</form>
|
|
166
|
-
""" # noqa
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|