Skip to content

Commit 89016aa

Browse files
committed
MC-32201: Reorder functionality (test coverage)
1 parent 91dfa0c commit 89016aa

File tree

5 files changed

+440
-1
lines changed

5 files changed

+440
-1
lines changed

app/code/Magento/Sales/Model/Reorder/Reorder.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,8 @@ private function prepareOutput(CartInterface $cart): Data\ReorderOutput
242242
{
243243
$output = new Data\ReorderOutput($cart, $this->errors);
244244
$this->errors = [];
245+
// we already show user errors, do not expose it to cart level
246+
$cart->setHasError(false);
245247
return $output;
246248
}
247249

Lines changed: 335 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,335 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\GraphQl\Sales;
9+
10+
use Magento\Framework\Exception\NoSuchEntityException;
11+
use Magento\Integration\Api\CustomerTokenServiceInterface;
12+
use Magento\Quote\Api\CartRepositoryInterface;
13+
use Magento\TestFramework\Helper\Bootstrap;
14+
use Magento\TestFramework\TestCase\GraphQlAbstract;
15+
16+
/**
17+
* Test Reorder with and without products overlay in shopping cart.
18+
*/
19+
class ReorderConfigurableWithVariationsTest extends GraphQlAbstract
20+
{
21+
/**
22+
* Customer Id
23+
*/
24+
private const CUSTOMER_ID = 1;
25+
26+
/**
27+
* Order Number
28+
*/
29+
private const ORDER_NUMBER = '100000001';
30+
31+
/**
32+
* Customer email
33+
*/
34+
private const CUSTOMER_EMAIL = 'customer@example.com';
35+
36+
/**
37+
* @var CustomerTokenServiceInterface
38+
*/
39+
private $customerTokenService;
40+
41+
/**
42+
* @var CartRepositoryInterface
43+
*/
44+
private $cartRepository;
45+
46+
/**
47+
* @inheritDoc
48+
*/
49+
protected function setUp()
50+
{
51+
parent::setUp();
52+
$this->customerTokenService = Bootstrap::getObjectManager()->get(CustomerTokenServiceInterface::class);
53+
54+
// be sure previous tests didn't left customer quote
55+
/** @var CartRepositoryInterface $cartRepository */
56+
$this->cartRepository = Bootstrap::getObjectManager()->get(CartRepositoryInterface::class);
57+
try {
58+
$quote = $this->cartRepository->getForCustomer(self::CUSTOMER_ID);
59+
$this->cartRepository->delete($quote);
60+
} catch (NoSuchEntityException $e) {
61+
}
62+
}
63+
64+
/**
65+
* @magentoApiDataFixture Magento/Sales/_files/order_with_two_configurable_variations.php
66+
*/
67+
public function testVariations()
68+
{
69+
/** @var \Magento\Catalog\Api\ProductRepositoryInterface $repository */
70+
$productRepository = Bootstrap::getObjectManager()
71+
->create(\Magento\Catalog\Api\ProductRepositoryInterface::class);
72+
$productSku = 'simple_20';
73+
/** @var \Magento\Catalog\Api\Data\ProductInterface $product */
74+
$product = $productRepository->get($productSku);
75+
76+
$this->assertValidVariations();
77+
$this->assertWithOutOfStockVariation($productRepository, $product);
78+
$this->assertWithDeletedVariation($productRepository, $product);
79+
}
80+
81+
/**
82+
* Assert 2 variations of configurable product.
83+
*
84+
* @return void
85+
* @throws NoSuchEntityException
86+
*/
87+
private function assertValidVariations(): void
88+
{
89+
$response = $this->makeReorderForDefaultCustomer(self::ORDER_NUMBER);
90+
91+
$expectedResponse = [
92+
'userInputErrors' => [],
93+
'cart' => [
94+
'email' => 'customer@example.com',
95+
'total_quantity' => 2,
96+
'items' => [
97+
[
98+
'quantity' => 1,
99+
'product' => [
100+
'sku' => 'configurable',
101+
],
102+
'configurable_options' => [
103+
[
104+
'option_label' => 'Test Configurable',
105+
'value_label' => 'Option 1',
106+
]
107+
],
108+
],
109+
[
110+
'quantity' => 1,
111+
'product' => [
112+
'sku' => 'configurable',
113+
],
114+
'configurable_options' => [
115+
[
116+
'option_label' => 'Test Configurable',
117+
'value_label' => 'Option 2',
118+
],
119+
],
120+
],
121+
],
122+
],
123+
];
124+
$this->assertResponseFields($response['reorderItems'] ?? [], $expectedResponse);
125+
$this->cartRepository->delete($this->cartRepository->getForCustomer(self::CUSTOMER_ID));
126+
}
127+
128+
/**
129+
* Assert reorder with out of stock variation.
130+
*
131+
* @param \Magento\Catalog\Api\ProductRepositoryInterface $productRepository
132+
* @param \Magento\Catalog\Api\Data\ProductInterface $product
133+
* @return void
134+
* @throws NoSuchEntityException
135+
* @throws \Magento\Framework\Exception\CouldNotSaveException
136+
* @throws \Magento\Framework\Exception\InputException
137+
* @throws \Magento\Framework\Exception\StateException *@throws NoSuchEntityException
138+
*/
139+
private function assertWithOutOfStockVariation(
140+
\Magento\Catalog\Api\ProductRepositoryInterface $productRepository,
141+
\Magento\Catalog\Api\Data\ProductInterface $product
142+
): void {
143+
// make product available in stock but disable and make reorder
144+
$product->setStockData(
145+
[
146+
'use_config_manage_stock' => 1,
147+
'qty' => 0,
148+
'is_qty_decimal' => 0,
149+
'is_in_stock' => 0,
150+
]
151+
);
152+
$productRepository->save($product);
153+
$response = $this->makeReorderForDefaultCustomer(self::ORDER_NUMBER);
154+
$this->assetProductNotSalable($response);
155+
$this->cartRepository->delete($this->cartRepository->getForCustomer(self::CUSTOMER_ID));
156+
}
157+
158+
/**
159+
* Assert reorder with "out of stock" variation.
160+
*
161+
* @param \Magento\Catalog\Api\ProductRepositoryInterface $productRepository
162+
* @param \Magento\Catalog\Api\Data\ProductInterface $product
163+
* @return void
164+
* @throws \Magento\Framework\Exception\StateException
165+
* @throws NoSuchEntityException
166+
*/
167+
private function assertWithDeletedVariation(
168+
\Magento\Catalog\Api\ProductRepositoryInterface $productRepository,
169+
\Magento\Catalog\Api\Data\ProductInterface $product
170+
): void {
171+
// delete a product and make reorder
172+
/** @var \Magento\Framework\Registry $registry */
173+
$registry = Bootstrap::getObjectManager()
174+
->get(\Magento\Framework\Registry::class);
175+
$registry->unregister('isSecureArea');
176+
$registry->register('isSecureArea', true);
177+
$productRepository->delete($product);
178+
$registry->unregister('isSecureArea');
179+
$registry->register('isSecureArea', false);
180+
181+
$response = $this->makeReorderForDefaultCustomer(self::ORDER_NUMBER);
182+
$this->assetProductUndefined($response);
183+
$this->cartRepository->delete($this->cartRepository->getForCustomer(self::CUSTOMER_ID));
184+
}
185+
186+
/**
187+
* Assert that variation is not salable.
188+
*
189+
* @param array $response
190+
* @return void
191+
*/
192+
private function assetProductNotSalable(array $response)
193+
{
194+
$expectedResponse = [
195+
'userInputErrors' => [
196+
[
197+
'path' => [
198+
'orderNumber',
199+
],
200+
'code' => 'NOT_SALABLE',
201+
'message' => 'Could not add the product with SKU "configurable" to the shopping cart:' .
202+
' This product is out of stock.',
203+
],
204+
],
205+
'cart' => [
206+
'email' => 'customer@example.com',
207+
'total_quantity' => 1,
208+
'items' => [
209+
[
210+
'quantity' => 1,
211+
'product' => [
212+
'sku' => 'configurable',
213+
],
214+
'configurable_options' => [
215+
[
216+
'option_label' => 'Test Configurable',
217+
'value_label' => 'Option 1',
218+
],
219+
],
220+
],
221+
],
222+
],
223+
];
224+
$this->assertResponseFields($response['reorderItems'] ?? [], $expectedResponse);
225+
}
226+
227+
/**
228+
* Assert condition that variation is undefined.
229+
*
230+
* @param array $response
231+
* @return void
232+
*/
233+
private function assetProductUndefined(array $response): void
234+
{
235+
$expectedResponse = [
236+
'userInputErrors' => [
237+
[
238+
'path' => [
239+
'orderNumber',
240+
],
241+
'code' => 'UNDEFINED',
242+
'message' => 'Could not add the product with SKU "configurable" to the shopping cart: ' .
243+
'You need to choose options for your item.',
244+
],
245+
],
246+
'cart' => [
247+
'email' => 'customer@example.com',
248+
'total_quantity' => 1,
249+
'items' => [
250+
[
251+
'quantity' => 1,
252+
'product' => [
253+
'sku' => 'configurable',
254+
],
255+
'configurable_options' => [
256+
[
257+
'option_label' => 'Test Configurable',
258+
'value_label' => 'Option 1',
259+
],
260+
],
261+
],
262+
],
263+
],
264+
];
265+
$this->assertResponseFields($response['reorderItems'] ?? [], $expectedResponse);
266+
}
267+
268+
269+
/**
270+
* @param string $email
271+
* @param string $password
272+
* @return array
273+
* @throws \Magento\Framework\Exception\AuthenticationException
274+
*/
275+
private function getCustomerAuthHeaders(string $email, string $password): array
276+
{
277+
$customerToken = $this->customerTokenService->createCustomerAccessToken($email, $password);
278+
return ['Authorization' => 'Bearer ' . $customerToken];
279+
}
280+
281+
/**
282+
* Execute GraphQL Mutation for default customer (make reorder)
283+
*
284+
* @param string $orderId
285+
* @return array|bool|float|int|string
286+
* @throws \Exception
287+
*/
288+
private function makeReorderForDefaultCustomer(string $orderId = self::ORDER_NUMBER)
289+
{
290+
$query = $this->getQuery($orderId);
291+
$currentPassword = 'password';
292+
return $this->graphQlMutation(
293+
$query,
294+
[],
295+
'',
296+
$this->getCustomerAuthHeaders(self::CUSTOMER_EMAIL, $currentPassword)
297+
);
298+
}
299+
300+
/**
301+
* @param string $orderNumber
302+
* @return string
303+
*/
304+
protected function getQuery($orderNumber): string
305+
{
306+
return
307+
<<<MUTATION
308+
mutation {
309+
reorderItems(orderNumber: "{$orderNumber}") {
310+
userInputErrors {
311+
path
312+
code
313+
message
314+
}
315+
cart {
316+
email
317+
total_quantity
318+
items {
319+
quantity
320+
product {
321+
sku
322+
}
323+
... on ConfigurableCartItem {
324+
configurable_options {
325+
option_label
326+
value_label
327+
}
328+
}
329+
}
330+
}
331+
}
332+
}
333+
MUTATION;
334+
}
335+
}

0 commit comments

Comments
 (0)