Skip to content

Commit 47babf0

Browse files
authored
LYNX-786: Gift options should not persist on empty cart
1 parent c670850 commit 47babf0

File tree

2 files changed

+345
-2
lines changed

2 files changed

+345
-2
lines changed

app/code/Magento/Quote/Model/Quote.php

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?php
22
/**
3-
* Copyright © Magento, Inc. All rights reserved.
4-
* See COPYING.txt for license details.
3+
* Copyright 2025 Adobe
4+
* All Rights Reserved.
55
*/
66
namespace Magento\Quote\Model;
77

@@ -1563,6 +1563,12 @@ public function removeItem($itemId)
15631563
$item = $this->getItemById($itemId);
15641564

15651565
if ($item) {
1566+
1567+
// Remove gift message from cart
1568+
if ((int)$this->getItemsCount() === 1) {
1569+
$this->setGiftMessageId(0);
1570+
}
1571+
15661572
$item->setQuote($this);
15671573
$item->isDeleted(true);
15681574
if ($item->getHasChildren()) {
@@ -1596,6 +1602,10 @@ public function removeAllItems()
15961602
$item->isDeleted(true);
15971603
}
15981604
}
1605+
1606+
// Remove gift message from cart
1607+
$this->setGiftMessageId(0);
1608+
15991609
return $this;
16001610
}
16011611

Lines changed: 333 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,333 @@
1+
<?php
2+
/**
3+
* Copyright 2025 Adobe
4+
* All Rights Reserved.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\GraphQl\GiftMessage;
9+
10+
use Magento\Catalog\Test\Fixture\Product as ProductFixture;
11+
use Magento\Customer\Test\Fixture\Customer as CustomerFixture;
12+
use Magento\Framework\App\ResourceConnection;
13+
use Magento\Framework\Exception\AuthenticationException;
14+
use Magento\GiftMessage\Test\Fixture\GiftMessage;
15+
use Magento\Integration\Api\CustomerTokenServiceInterface;
16+
use Magento\Quote\Test\Fixture\AddProductToCart as AddProductToCartFixture;
17+
use Magento\Quote\Test\Fixture\CustomerCart as CustomerCartFixture;
18+
use Magento\Quote\Test\Fixture\QuoteIdMask as QuoteMaskFixture;
19+
use Magento\TestFramework\Fixture\Config;
20+
use Magento\TestFramework\Fixture\DataFixture;
21+
use Magento\TestFramework\Fixture\DataFixtureStorage;
22+
use Magento\TestFramework\Fixture\DataFixtureStorageManager;
23+
use Magento\TestFramework\Helper\Bootstrap;
24+
use Magento\TestFramework\TestCase\GraphQlAbstract;
25+
26+
class RemoveGiftMessageFromCartTest extends GraphQlAbstract
27+
{
28+
/**
29+
* @var DataFixtureStorage
30+
*/
31+
private $fixtures;
32+
33+
/**
34+
* @var ResourceConnection
35+
*/
36+
private $resourceConnection;
37+
38+
/**
39+
* @var CustomerTokenServiceInterface
40+
*/
41+
private $customerTokenService;
42+
43+
/**
44+
* @inheritdoc
45+
*/
46+
protected function setUp(): void
47+
{
48+
$this->fixtures = Bootstrap::getObjectManager()->get(DataFixtureStorageManager::class)->getStorage();
49+
$this->resourceConnection = Bootstrap::getObjectManager()->get(ResourceConnection::class);
50+
$this->customerTokenService = Bootstrap::getObjectManager()->get(CustomerTokenServiceInterface::class);
51+
}
52+
53+
#[
54+
Config('sales/gift_options/allow_order', true),
55+
Config('sales/gift_options/allow_items', true),
56+
DataFixture(CustomerFixture::class, as: 'customer'),
57+
DataFixture(
58+
ProductFixture::class,
59+
['type_id' => 'simple', 'weight' => 10, 'gift_message_available' => 2],
60+
as: 'product'
61+
),
62+
DataFixture(GiftMessage::class, as: 'message'),
63+
DataFixture(
64+
CustomerCartFixture::class,
65+
[
66+
'customer_id' => '$customer.id$'
67+
],
68+
as: 'quote'
69+
),
70+
DataFixture(
71+
AddProductToCartFixture::class,
72+
[
73+
'cart_id' => '$quote.id$',
74+
'product_id' => '$product.id$'
75+
]
76+
),
77+
DataFixture(QuoteMaskFixture::class, ['cart_id' => '$quote.id$'], 'quoteIdMask')
78+
]
79+
public function testCartQueryWithGiftMessageAfterRemovingCartItem(): void
80+
{
81+
$this->setGiftMessageOnCart();
82+
83+
$maskedQuoteId = $this->fixtures->get('quoteIdMask')->getMaskedId();
84+
$customerAuthHeaders = $this->getCustomerAuthHeaders($this->fixtures->get('customer')->getEmail());
85+
86+
// Remove item from cart
87+
$this->graphQlMutation(
88+
$this->removeItemFromCartMutation($maskedQuoteId),
89+
[],
90+
'',
91+
$customerAuthHeaders
92+
);
93+
94+
self::assertEquals(
95+
[
96+
'cart' => [
97+
'gift_message' => null
98+
]
99+
],
100+
$this->graphQlQuery(
101+
$this->getCartGraphQlQuery($maskedQuoteId),
102+
[],
103+
'',
104+
$customerAuthHeaders
105+
)
106+
);
107+
}
108+
109+
#[
110+
Config('sales/gift_options/allow_order', true),
111+
Config('sales/gift_options/allow_items', true),
112+
DataFixture(CustomerFixture::class, as: 'customer'),
113+
DataFixture(
114+
ProductFixture::class,
115+
['type_id' => 'simple', 'weight' => 10, 'gift_message_available' => 2],
116+
as: 'product'
117+
),
118+
DataFixture(
119+
ProductFixture::class,
120+
['type_id' => 'simple', 'weight' => 10, 'gift_message_available' => 2],
121+
as: 'product2'
122+
),
123+
DataFixture(GiftMessage::class, as: 'message'),
124+
DataFixture(
125+
CustomerCartFixture::class,
126+
[
127+
'customer_id' => '$customer.id$'
128+
],
129+
as: 'quote'
130+
),
131+
DataFixture(
132+
AddProductToCartFixture::class,
133+
[
134+
'cart_id' => '$quote.id$',
135+
'product_id' => '$product.id$'
136+
]
137+
),
138+
DataFixture(
139+
AddProductToCartFixture::class,
140+
[
141+
'cart_id' => '$quote.id$',
142+
'product_id' => '$product2.id$'
143+
]
144+
),
145+
DataFixture(QuoteMaskFixture::class, ['cart_id' => '$quote.id$'], 'quoteIdMask')
146+
]
147+
public function testGiftMessagePersistenceAfterRemovingOneCartItem(): void
148+
{
149+
$this->setGiftMessageOnCart();
150+
$maskedQuoteId = $this->fixtures->get('quoteIdMask')->getMaskedId();
151+
$customerAuthHeaders = $this->getCustomerAuthHeaders($this->fixtures->get('customer')->getEmail());
152+
153+
// Remove item from cart
154+
$this->graphQlMutation(
155+
$this->removeItemFromCartMutation($maskedQuoteId),
156+
[],
157+
'',
158+
$customerAuthHeaders
159+
);
160+
161+
self::assertEquals(
162+
[
163+
'cart' => [
164+
'gift_message' => [
165+
'from' => 'Romeo',
166+
'to' => 'Mercutio',
167+
'message' => 'Fixture Test message.'
168+
]
169+
]
170+
],
171+
$this->graphQlQuery(
172+
$this->getCartGraphQlQuery($maskedQuoteId),
173+
[],
174+
'',
175+
$customerAuthHeaders
176+
)
177+
);
178+
}
179+
180+
#[
181+
Config('sales/gift_options/allow_order', true),
182+
Config('sales/gift_options/allow_items', true),
183+
DataFixture(CustomerFixture::class, as: 'customer'),
184+
DataFixture(
185+
ProductFixture::class,
186+
['type_id' => 'simple', 'weight' => 10, 'gift_message_available' => 2],
187+
as: 'product'
188+
),
189+
DataFixture(GiftMessage::class, as: 'message'),
190+
DataFixture(
191+
CustomerCartFixture::class,
192+
[
193+
'customer_id' => '$customer.id$'
194+
],
195+
as: 'quote'
196+
),
197+
DataFixture(
198+
AddProductToCartFixture::class,
199+
[
200+
'cart_id' => '$quote.id$',
201+
'product_id' => '$product.id$'
202+
]
203+
),
204+
DataFixture(QuoteMaskFixture::class, ['cart_id' => '$quote.id$'], 'quoteIdMask')
205+
]
206+
public function testCartQueryWithGiftMessageAfterClearCart(): void
207+
{
208+
$this->setGiftMessageOnCart();
209+
// Clear cart
210+
$this->clearCart();
211+
212+
self::assertEquals(
213+
[
214+
'cart' => [
215+
'gift_message' => null
216+
]
217+
],
218+
$this->graphQlQuery(
219+
$this->getCartGraphQlQuery($this->fixtures->get('quoteIdMask')->getMaskedId()),
220+
[],
221+
'',
222+
$this->getCustomerAuthHeaders($this->fixtures->get('customer')->getEmail())
223+
)
224+
);
225+
}
226+
227+
/**
228+
* Get cart query with gift message
229+
*
230+
* @param string $cartId
231+
* @return string
232+
*/
233+
private function getCartGraphQlQuery(string $cartId): string
234+
{
235+
return <<<QUERY
236+
{
237+
cart(cart_id: "{$cartId}") {
238+
gift_message {
239+
from
240+
to
241+
message
242+
}
243+
}
244+
}
245+
QUERY;
246+
}
247+
248+
/**
249+
* Remove item from cart mutation
250+
*
251+
* @param string $cartId
252+
* @return string
253+
*/
254+
private function removeItemFromCartMutation(string $cartId): string
255+
{
256+
$itemId = $this->getItemId(
257+
(int)$this->fixtures->get('quote')->getId(),
258+
(int)$this->fixtures->get('product')->getId()
259+
);
260+
261+
return <<<MUTATION
262+
mutation removeItemFromCart {
263+
removeItemFromCart(
264+
input: {
265+
cart_id: "{$cartId}"
266+
cart_item_id: "{$itemId}"
267+
}
268+
) {
269+
cart {
270+
id
271+
}
272+
}
273+
}
274+
MUTATION;
275+
}
276+
277+
/**
278+
* Get item id from quote_id and product_id
279+
*
280+
* @param int $cartId
281+
* @param int $productId
282+
* @return int
283+
*/
284+
private function getItemId(int $cartId, int $productId): int
285+
{
286+
$connection = $this->resourceConnection->getConnection();
287+
return (int)$connection->fetchOne(
288+
$connection->select()
289+
->from($this->resourceConnection->getTableName('quote_item'))
290+
->reset('columns')
291+
->columns('item_id')
292+
->where('quote_id = ?', $cartId)
293+
->where('product_id = ?', $productId)
294+
);
295+
}
296+
297+
/**
298+
* Set gift message on cart
299+
*
300+
* @return void
301+
*/
302+
public function setGiftMessageOnCart(): void
303+
{
304+
$quote = $this->fixtures->get('quote');
305+
$quote->setGiftMessageId($this->fixtures->get('message')->getId());
306+
$quote->save();
307+
}
308+
309+
/**
310+
* Remove all items form cart
311+
*
312+
* @return void
313+
*/
314+
public function clearCart(): void
315+
{
316+
$quote = $this->fixtures->get('quote');
317+
$quote->removeAllItems();
318+
$quote->save();
319+
}
320+
321+
/**
322+
* Returns the header with customer token for GQL Mutation
323+
*
324+
* @param string $email
325+
* @return array
326+
* @throws AuthenticationException
327+
*/
328+
private function getCustomerAuthHeaders(string $email): array
329+
{
330+
$customerToken = $this->customerTokenService->createCustomerAccessToken($email, 'password');
331+
return ['Authorization' => 'Bearer ' . $customerToken];
332+
}
333+
}

0 commit comments

Comments
 (0)