amazon-orders 3.2.4__tar.gz → 3.2.6__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.4 → amazon_orders-3.2.6}/CHANGELOG.md +18 -1
- {amazon_orders-3.2.4/amazon_orders.egg-info → amazon_orders-3.2.6}/PKG-INFO +1 -1
- {amazon_orders-3.2.4 → amazon_orders-3.2.6/amazon_orders.egg-info}/PKG-INFO +1 -1
- {amazon_orders-3.2.4 → amazon_orders-3.2.6}/amazonorders/__init__.py +1 -1
- {amazon_orders-3.2.4 → amazon_orders-3.2.6}/amazonorders/entity/transaction.py +1 -1
- {amazon_orders-3.2.4 → amazon_orders-3.2.6}/amazonorders/selectors.py +5 -3
- {amazon_orders-3.2.4 → amazon_orders-3.2.6}/amazonorders/transactions.py +1 -1
- {amazon_orders-3.2.4 → amazon_orders-3.2.6}/tests/test_cli.py +3 -2
- {amazon_orders-3.2.4 → amazon_orders-3.2.6}/tests/test_orders.py +44 -9
- {amazon_orders-3.2.4 → amazon_orders-3.2.6}/tests/test_session.py +30 -26
- amazon_orders-3.2.6/tests/test_transactions.py +137 -0
- {amazon_orders-3.2.4 → amazon_orders-3.2.6}/tests/test_util.py +1 -1
- amazon_orders-3.2.4/tests/test_transactions.py +0 -166
- {amazon_orders-3.2.4 → amazon_orders-3.2.6}/LICENSE +0 -0
- {amazon_orders-3.2.4 → amazon_orders-3.2.6}/MANIFEST.in +0 -0
- {amazon_orders-3.2.4 → amazon_orders-3.2.6}/README.md +0 -0
- {amazon_orders-3.2.4 → amazon_orders-3.2.6}/amazon_orders.egg-info/SOURCES.txt +0 -0
- {amazon_orders-3.2.4 → amazon_orders-3.2.6}/amazon_orders.egg-info/dependency_links.txt +0 -0
- {amazon_orders-3.2.4 → amazon_orders-3.2.6}/amazon_orders.egg-info/entry_points.txt +0 -0
- {amazon_orders-3.2.4 → amazon_orders-3.2.6}/amazon_orders.egg-info/requires.txt +0 -0
- {amazon_orders-3.2.4 → amazon_orders-3.2.6}/amazon_orders.egg-info/top_level.txt +0 -0
- {amazon_orders-3.2.4 → amazon_orders-3.2.6}/amazonorders/banner.txt +0 -0
- {amazon_orders-3.2.4 → amazon_orders-3.2.6}/amazonorders/cli.py +0 -0
- {amazon_orders-3.2.4 → amazon_orders-3.2.6}/amazonorders/conf.py +0 -0
- {amazon_orders-3.2.4 → amazon_orders-3.2.6}/amazonorders/constants.py +0 -0
- {amazon_orders-3.2.4 → amazon_orders-3.2.6}/amazonorders/entity/__init__.py +0 -0
- {amazon_orders-3.2.4 → amazon_orders-3.2.6}/amazonorders/entity/item.py +0 -0
- {amazon_orders-3.2.4 → amazon_orders-3.2.6}/amazonorders/entity/order.py +0 -0
- {amazon_orders-3.2.4 → amazon_orders-3.2.6}/amazonorders/entity/parsable.py +0 -0
- {amazon_orders-3.2.4 → amazon_orders-3.2.6}/amazonorders/entity/recipient.py +0 -0
- {amazon_orders-3.2.4 → amazon_orders-3.2.6}/amazonorders/entity/seller.py +0 -0
- {amazon_orders-3.2.4 → amazon_orders-3.2.6}/amazonorders/entity/shipment.py +0 -0
- {amazon_orders-3.2.4 → amazon_orders-3.2.6}/amazonorders/exception.py +0 -0
- {amazon_orders-3.2.4 → amazon_orders-3.2.6}/amazonorders/forms.py +0 -0
- {amazon_orders-3.2.4 → amazon_orders-3.2.6}/amazonorders/orders.py +0 -0
- {amazon_orders-3.2.4 → amazon_orders-3.2.6}/amazonorders/session.py +0 -0
- {amazon_orders-3.2.4 → amazon_orders-3.2.6}/amazonorders/util.py +0 -0
- {amazon_orders-3.2.4 → amazon_orders-3.2.6}/pyproject.toml +0 -0
- {amazon_orders-3.2.4 → amazon_orders-3.2.6}/setup.cfg +0 -0
- {amazon_orders-3.2.4 → amazon_orders-3.2.6}/tests/test_conf.py +0 -0
- {amazon_orders-3.2.4 → amazon_orders-3.2.6}/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.6...HEAD)
|
|
8
|
+
|
|
9
|
+
## [3.2.6](https://github.com/alexdlaird/amazon-orders/compare/3.2.5...3.2.6) - 2025-02-17
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- Add generic integration tests for Transactions, now in weekly run.
|
|
14
|
+
- Other test improvements.
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
|
|
18
|
+
- Broken parsing when Transaction is pending.
|
|
19
|
+
|
|
20
|
+
## [3.2.5](https://github.com/alexdlaird/amazon-orders/compare/3.2.4...3.2.5) - 2025-02-12
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
|
|
24
|
+
- Parsing errors on gift cards totals and broken item links due to changes in Amazon.com DOM.
|
|
8
25
|
|
|
9
26
|
## [3.2.4](https://github.com/alexdlaird/amazon-orders/compare/3.2.3...3.2.4) - 2025-02-11
|
|
10
27
|
|
|
@@ -76,7 +76,8 @@ class Selectors:
|
|
|
76
76
|
".yohtmlc-item a", ".yohtmlc-product-title"]
|
|
77
77
|
FIELD_ITEM_LINK_SELECTOR = ["[data-component='itemTitle'] a",
|
|
78
78
|
".yohtmlc-item a",
|
|
79
|
-
"a:has(> .yohtmlc-product-title)"
|
|
79
|
+
"a:has(> .yohtmlc-product-title)",
|
|
80
|
+
".yohtmlc-product-title a"]
|
|
80
81
|
FIELD_ITEM_TAG_ITERATOR_SELECTOR = [".yohtmlc-item div"]
|
|
81
82
|
FIELD_ITEM_PRICE_SELECTOR = ["[data-component='unitPrice'] .a-text-price :not(.a-offscreen)",
|
|
82
83
|
".yohtmlc-item .a-color-price"]
|
|
@@ -93,7 +94,8 @@ class Selectors:
|
|
|
93
94
|
"bdi[dir='ltr']",
|
|
94
95
|
"span[dir='ltr']"]
|
|
95
96
|
FIELD_ORDER_GRAND_TOTAL_SELECTOR = ["div.yohtmlc-order-total span.value",
|
|
96
|
-
"div.order-header div.a-column.a-span2"
|
|
97
|
+
"div.order-header div.a-column.a-span2",
|
|
98
|
+
"div.order-header div.a-col-left .a-span9"]
|
|
97
99
|
FIELD_ORDER_PLACED_DATE_SELECTOR = ["[data-component='briefOrderInfo'] div.a-column",
|
|
98
100
|
"span.order-date-invoice-item",
|
|
99
101
|
"div.a-span3"]
|
|
@@ -158,7 +160,7 @@ class Selectors:
|
|
|
158
160
|
FIELD_TRANSACTION_GRAND_TOTAL_SELECTOR = [
|
|
159
161
|
"div.apx-transactions-line-item-component-container > div:nth-child(1) span.a-size-base-plus"]
|
|
160
162
|
FIELD_TRANSACTION_ORDER_NUMBER_SELECTOR = [
|
|
161
|
-
"div.apx-transactions-line-item-component-container
|
|
163
|
+
"div.apx-transactions-line-item-component-container div .a-span12 a"]
|
|
162
164
|
FIELD_TRANSACTION_ORDER_LINK_SELECTOR = [
|
|
163
165
|
"div.apx-transactions-line-item-component-container > div:nth-child(2) a.a-link-normal"]
|
|
164
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.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}",
|
|
@@ -3,6 +3,7 @@ __license__ = "MIT"
|
|
|
3
3
|
|
|
4
4
|
import os
|
|
5
5
|
import unittest
|
|
6
|
+
from datetime import date
|
|
6
7
|
|
|
7
8
|
import responses
|
|
8
9
|
|
|
@@ -77,6 +78,40 @@ class TestOrders(UnitTestCase):
|
|
|
77
78
|
self.assertEqual(1, resp1.call_count)
|
|
78
79
|
self.assertEqual(1, resp2.call_count)
|
|
79
80
|
|
|
81
|
+
@responses.activate
|
|
82
|
+
def test_get_order_history_2025_gift_card(self):
|
|
83
|
+
# GIVEN
|
|
84
|
+
self.amazon_session.is_authenticated = True
|
|
85
|
+
year = 2024
|
|
86
|
+
start_index = 0
|
|
87
|
+
resp1 = self.given_order_history_landing_exists()
|
|
88
|
+
with open(os.path.join(self.RESOURCES_DIR, "orders", "order-history-egift.html"), "r",
|
|
89
|
+
encoding="utf-8") as f:
|
|
90
|
+
resp2 = responses.add(
|
|
91
|
+
responses.GET,
|
|
92
|
+
self.test_config.constants.ORDER_HISTORY_URL,
|
|
93
|
+
body=f.read(),
|
|
94
|
+
status=200,
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
# WHEN
|
|
98
|
+
orders = self.amazon_orders.get_order_history(year=year, start_index=start_index)
|
|
99
|
+
|
|
100
|
+
# THEN
|
|
101
|
+
self.assertEqual(9, len(orders))
|
|
102
|
+
self.assertEqual(1, resp1.call_count)
|
|
103
|
+
self.assertEqual(1, resp2.call_count)
|
|
104
|
+
order = orders[5]
|
|
105
|
+
self.assertEqual("112-8022032-9113020", order.order_number)
|
|
106
|
+
self.assertEqual(150.00, order.grand_total)
|
|
107
|
+
self.assertIsNotNone(order.order_details_link)
|
|
108
|
+
self.assertEqual(date(2024, 10, 28), order.order_placed_date)
|
|
109
|
+
self.assertEqual(1, len(order.items))
|
|
110
|
+
self.assertEqual("Amazon eGift Card - Birthday Candles (Animated)",
|
|
111
|
+
order.items[0].title)
|
|
112
|
+
self.assertIsNotNone(order.items[0].link)
|
|
113
|
+
self.assertIsNotNone(order.items[0].image_link)
|
|
114
|
+
|
|
80
115
|
@responses.activate
|
|
81
116
|
def test_get_order_history_paginated(self):
|
|
82
117
|
# GIVEN
|
|
@@ -84,7 +119,7 @@ class TestOrders(UnitTestCase):
|
|
|
84
119
|
year = 2010
|
|
85
120
|
resp1 = self.given_order_history_landing_exists()
|
|
86
121
|
resp2 = self.given_order_history_exists(year, 0)
|
|
87
|
-
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",
|
|
88
123
|
encoding="utf-8") as f:
|
|
89
124
|
resp3 = responses.add(
|
|
90
125
|
responses.GET,
|
|
@@ -110,7 +145,7 @@ class TestOrders(UnitTestCase):
|
|
|
110
145
|
year = 2024
|
|
111
146
|
start_index = 0
|
|
112
147
|
resp1 = self.given_order_history_landing_exists()
|
|
113
|
-
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",
|
|
114
149
|
encoding="utf-8") as f:
|
|
115
150
|
resp2 = responses.add(
|
|
116
151
|
responses.GET,
|
|
@@ -134,7 +169,7 @@ class TestOrders(UnitTestCase):
|
|
|
134
169
|
year = 2024
|
|
135
170
|
start_index = 0
|
|
136
171
|
resp1 = self.given_order_history_landing_exists()
|
|
137
|
-
with open(os.path.join(self.RESOURCES_DIR, "order-history-wholefoods.html"), "r",
|
|
172
|
+
with open(os.path.join(self.RESOURCES_DIR, "orders", "order-history-wholefoods.html"), "r",
|
|
138
173
|
encoding="utf-8") as f:
|
|
139
174
|
resp2 = responses.add(
|
|
140
175
|
responses.GET,
|
|
@@ -256,7 +291,7 @@ class TestOrders(UnitTestCase):
|
|
|
256
291
|
# GIVEN
|
|
257
292
|
self.amazon_session.is_authenticated = True
|
|
258
293
|
order_id = "112-9685975-5907428"
|
|
259
|
-
with open(os.path.join(self.RESOURCES_DIR, f"order-details-{order_id}.html"), "r",
|
|
294
|
+
with open(os.path.join(self.RESOURCES_DIR, "orders", f"order-details-{order_id}.html"), "r",
|
|
260
295
|
encoding="utf-8") as f:
|
|
261
296
|
resp1 = responses.add(
|
|
262
297
|
responses.GET,
|
|
@@ -277,7 +312,7 @@ class TestOrders(UnitTestCase):
|
|
|
277
312
|
# GIVEN
|
|
278
313
|
self.amazon_session.is_authenticated = True
|
|
279
314
|
order_id = "112-5939971-8962610"
|
|
280
|
-
with open(os.path.join(self.RESOURCES_DIR, f"order-details-{order_id}.html"), "r",
|
|
315
|
+
with open(os.path.join(self.RESOURCES_DIR, "orders", f"order-details-{order_id}.html"), "r",
|
|
281
316
|
encoding="utf-8") as f:
|
|
282
317
|
resp1 = responses.add(
|
|
283
318
|
responses.GET,
|
|
@@ -298,7 +333,7 @@ class TestOrders(UnitTestCase):
|
|
|
298
333
|
# GIVEN
|
|
299
334
|
self.amazon_session.is_authenticated = True
|
|
300
335
|
order_id = "112-4482432-2955442"
|
|
301
|
-
with open(os.path.join(self.RESOURCES_DIR, f"order-details-{order_id}.html"), "r",
|
|
336
|
+
with open(os.path.join(self.RESOURCES_DIR, "orders", f"order-details-{order_id}.html"), "r",
|
|
302
337
|
encoding="utf-8") as f:
|
|
303
338
|
resp1 = responses.add(
|
|
304
339
|
responses.GET,
|
|
@@ -319,7 +354,7 @@ class TestOrders(UnitTestCase):
|
|
|
319
354
|
# GIVEN
|
|
320
355
|
self.amazon_session.is_authenticated = True
|
|
321
356
|
order_id = "112-9087159-1657009"
|
|
322
|
-
with open(os.path.join(self.RESOURCES_DIR, f"order-details-{order_id}.html"), "r",
|
|
357
|
+
with open(os.path.join(self.RESOURCES_DIR, "orders", f"order-details-{order_id}.html"), "r",
|
|
323
358
|
encoding="utf-8") as f:
|
|
324
359
|
resp1 = responses.add(
|
|
325
360
|
responses.GET,
|
|
@@ -340,7 +375,7 @@ class TestOrders(UnitTestCase):
|
|
|
340
375
|
# GIVEN
|
|
341
376
|
self.amazon_session.is_authenticated = True
|
|
342
377
|
order_id = "114-8722141-6545058"
|
|
343
|
-
with open(os.path.join(self.RESOURCES_DIR, f"order-details-{order_id}.html"), "r",
|
|
378
|
+
with open(os.path.join(self.RESOURCES_DIR, "orders", f"order-details-{order_id}.html"), "r",
|
|
344
379
|
encoding="utf-8") as f:
|
|
345
380
|
resp1 = responses.add(
|
|
346
381
|
responses.GET,
|
|
@@ -361,7 +396,7 @@ class TestOrders(UnitTestCase):
|
|
|
361
396
|
# GIVEN
|
|
362
397
|
self.amazon_session.is_authenticated = True
|
|
363
398
|
order_id = "111-6778632-7354601"
|
|
364
|
-
with open(os.path.join(self.RESOURCES_DIR, f"order-details-{order_id}.html"), "r",
|
|
399
|
+
with open(os.path.join(self.RESOURCES_DIR, "orders", f"order-details-{order_id}.html"), "r",
|
|
365
400
|
encoding="utf-8") as f:
|
|
366
401
|
resp1 = responses.add(
|
|
367
402
|
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,137 @@
|
|
|
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(
|
|
41
|
+
os.path.join(self.RESOURCES_DIR, "transactions", "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
|
+
transaction = transactions[0]
|
|
58
|
+
self.assertEqual(transaction.completed_date, datetime.date(2024, 10, 11))
|
|
59
|
+
self.assertEqual(transaction.payment_method, "Visa ****1234")
|
|
60
|
+
self.assertEqual(transaction.grand_total, -45.19)
|
|
61
|
+
self.assertFalse(transaction.is_refund)
|
|
62
|
+
self.assertEqual(transaction.order_number, "123-4567890-1234567")
|
|
63
|
+
self.assertEqual(transaction.order_details_link,
|
|
64
|
+
"https://www.amazon.ca/gp/css/summary/edit.html?orderID=123-4567890-1234567")
|
|
65
|
+
self.assertEqual(transaction.seller, "AMZN Mktp CA")
|
|
66
|
+
|
|
67
|
+
@responses.activate
|
|
68
|
+
@patch("amazonorders.transactions.datetime", wraps=datetime)
|
|
69
|
+
def test_get_transactions_with_pending(self, mock_get_today: Mock):
|
|
70
|
+
# GIVEN
|
|
71
|
+
mock_get_today.date.today.return_value = datetime.date(2025, 2, 13)
|
|
72
|
+
days = 30
|
|
73
|
+
self.amazon_session.is_authenticated = True
|
|
74
|
+
with open(
|
|
75
|
+
os.path.join(self.RESOURCES_DIR, "transactions", "transactions-in-progress.html"),
|
|
76
|
+
"r",
|
|
77
|
+
encoding="utf-8",
|
|
78
|
+
) as f:
|
|
79
|
+
responses.add(
|
|
80
|
+
responses.GET,
|
|
81
|
+
f"{self.test_config.constants.TRANSACTION_HISTORY_LANDING_URL}",
|
|
82
|
+
body=f.read(),
|
|
83
|
+
status=200,
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
# WHEN
|
|
87
|
+
transactions = self.amazon_transactions.get_transactions(days=days)
|
|
88
|
+
|
|
89
|
+
# THEN
|
|
90
|
+
self.assertEqual(20, len(transactions))
|
|
91
|
+
transaction = transactions[0]
|
|
92
|
+
self.assertEqual(transaction.completed_date, datetime.date(2025, 2, 12))
|
|
93
|
+
self.assertEqual(transaction.payment_method, "Prime Visa ****1111")
|
|
94
|
+
self.assertEqual(transaction.grand_total, -26.29)
|
|
95
|
+
self.assertFalse(transaction.is_refund)
|
|
96
|
+
self.assertEqual(transaction.order_number, "234-8832881-7100260")
|
|
97
|
+
self.assertEqual(transaction.order_details_link,
|
|
98
|
+
"https://www.amazon.com/gp/your-account/order-details?orderID=234-8832881-7100260")
|
|
99
|
+
self.assertEqual(transaction.seller, None)
|
|
100
|
+
transaction = transactions[1]
|
|
101
|
+
self.assertEqual(transaction.completed_date, datetime.date(2025, 2, 7))
|
|
102
|
+
self.assertEqual(transaction.payment_method, "Prime Visa ****1111")
|
|
103
|
+
self.assertEqual(transaction.grand_total, 43.94)
|
|
104
|
+
self.assertTrue(transaction.is_refund)
|
|
105
|
+
self.assertEqual(transaction.order_number, "234-3017692-4601031")
|
|
106
|
+
self.assertEqual(transaction.order_details_link,
|
|
107
|
+
"https://www.amazon.com/gp/css/summary/edit.html?orderID=234-3017692-4601031")
|
|
108
|
+
self.assertEqual(transaction.seller, "AMZN Mktp US")
|
|
109
|
+
|
|
110
|
+
def test_parse_transaction_form_tag(self):
|
|
111
|
+
# GIVEN
|
|
112
|
+
with open(
|
|
113
|
+
os.path.join(self.RESOURCES_DIR, "transactions", "transaction-form-tag.html"),
|
|
114
|
+
"r",
|
|
115
|
+
encoding="utf-8",
|
|
116
|
+
) as f:
|
|
117
|
+
parsed = BeautifulSoup(f.read(), self.test_config.bs4_parser)
|
|
118
|
+
form_tag = parsed.select_one("form")
|
|
119
|
+
|
|
120
|
+
# WHEN
|
|
121
|
+
transactions, next_page_url, next_page_data = _parse_transaction_form_tag(
|
|
122
|
+
form_tag, self.test_config
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
# THEN
|
|
126
|
+
self.assertEqual(len(transactions), 2)
|
|
127
|
+
self.assertEqual(
|
|
128
|
+
next_page_url, "https://www.amazon.com:443/cpe/yourpayments/transactions"
|
|
129
|
+
)
|
|
130
|
+
self.assertEqual(
|
|
131
|
+
next_page_data,
|
|
132
|
+
{
|
|
133
|
+
"ppw-widgetState": "the-ppw-widgetState",
|
|
134
|
+
"ie": "UTF-8",
|
|
135
|
+
'ppw-widgetEvent:DefaultNextPageNavigationEvent:{"nextPageKey":"key"}': "",
|
|
136
|
+
},
|
|
137
|
+
)
|
|
@@ -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
|
|
File without changes
|
|
File without changes
|