3
3
* Copyright 2025 Adobe
4
4
* All Rights Reserved.
5
5
*/
6
+
6
7
declare (strict_types=1 );
7
8
8
- namespace Magento \Checkout \Test \ Integration ;
9
+ namespace Magento \Checkout \Helper ;
9
10
10
11
use Magento \Checkout \Helper \Data ;
11
12
use Magento \Framework \ObjectManagerInterface ;
21
22
/**
22
23
* Integration test for sending the "payment failed" email on virtual product order failure.
23
24
*
25
+ * Ensures that when a virtual product order fails during payment, the appropriate failure email is
26
+ * sent and does not include any shipping address or method information.
27
+ *
24
28
* @magentoAppIsolation enabled
25
29
* @magentoDbIsolation enabled
26
- * @magentoApiDataFixture Magento/Checkout/_files/quote.php
27
30
* @magentoDataFixture Magento/Checkout/_files/quote_with_virtual_product_and_address.php
28
31
*/
29
- class VirtualProductFailedPaymentEmailTest extends TestCase
32
+ class DataTest extends TestCase
30
33
{
31
- /** @var ObjectManagerInterface */
34
+ /**
35
+ * Magento Object Manager
36
+ *
37
+ * @var ObjectManagerInterface
38
+ */
32
39
private ObjectManagerInterface $ objectManager ;
33
40
34
- /** @var QuoteManagement */
41
+ /**
42
+ * Quote management service
43
+ *
44
+ * @var QuoteManagement
45
+ */
35
46
private QuoteManagement $ quoteManagement ;
36
47
37
- /** @var QuoteFactory */
48
+ /**
49
+ * Quote factory
50
+ *
51
+ * @var QuoteFactory
52
+ */
38
53
private QuoteFactory $ quoteFactory ;
39
54
40
- /** @var Data */
55
+ /**
56
+ * Checkout data helper
57
+ *
58
+ * @var Data
59
+ */
41
60
private Data $ checkoutHelper ;
42
61
43
- /** @var OrderRepositoryInterface */
62
+ /**
63
+ * Order repository
64
+ *
65
+ * @var OrderRepositoryInterface
66
+ */
44
67
private OrderRepositoryInterface $ orderRepository ;
45
68
69
+ /**
70
+ * Set up required Magento services for the test
71
+ *
72
+ * @return void
73
+ */
46
74
protected function setUp (): void
47
75
{
76
+ parent ::setUp ();
77
+
48
78
$ this ->objectManager = Bootstrap::getObjectManager ();
49
79
$ this ->quoteManagement = $ this ->objectManager ->get (QuoteManagement::class);
50
80
$ this ->quoteFactory = $ this ->objectManager ->get (QuoteFactory::class);
51
81
$ this ->checkoutHelper = $ this ->objectManager ->get (Data::class);
52
82
$ this ->orderRepository = $ this ->objectManager ->get (OrderRepositoryInterface::class);
53
83
}
54
84
85
+ /**
86
+ * Test the sending of the "payment failed" email for a virtual product order.
87
+ *
88
+ * Asserts that:
89
+ * - The email is sent
90
+ * - The email does not contain shipping address or shipping method
91
+ *
92
+ * @return void
93
+ */
55
94
public function testSendPaymentFailedEmail (): void
56
95
{
57
96
[$ order , $ quote ] = $ this ->prepareOrderFromFixtureQuote ();
@@ -66,15 +105,16 @@ public function testSendPaymentFailedEmail(): void
66
105
67
106
/** @var TransportBuilderMock $transportBuilder */
68
107
$ transportBuilder = $ this ->objectManager ->get (TransportBuilderMock::class);
108
+
69
109
/** @var \Magento\Framework\Mail\EmailMessageInterface $message */
70
110
$ message = $ transportBuilder ->getSentMessage ();
71
111
$ this ->assertNotNull ($ message , 'Expected a payment failed email to be sent. ' );
72
112
73
- // Compatibility with Magento 2.4.6+ and 2.4.7+
74
113
$ emailBody = $ message ->getBody ();
75
114
$ emailContent = method_exists ($ emailBody , 'bodyToString ' )
76
115
? quoted_printable_decode ($ emailBody ->bodyToString ())
77
116
: $ emailBody ->getParts ()[0 ]->getRawContent ();
117
+
78
118
$ this ->assertStringNotContainsString (
79
119
'Shipping Address ' ,
80
120
$ emailContent ,
@@ -86,26 +126,46 @@ public function testSendPaymentFailedEmail(): void
86
126
'Shipping method should not appear in the payment failed email for virtual product. '
87
127
);
88
128
}
129
+
130
+ /**
131
+ * Prepare an order from a quote fixture containing a virtual product.
132
+ *
133
+ * Loads a predefined quote and submits it to create an order.
134
+ *
135
+ * @return array{0: Order, 1: Quote}
136
+ */
89
137
private function prepareOrderFromFixtureQuote (): array
90
138
{
91
- /** @var \Magento\Quote\Model\ Quote $quote */
139
+ /** @var Quote $quote */
92
140
$ quote = $ this ->objectManager ->create (Quote::class)
93
141
->load ('test_order_with_virtual_product ' , 'reserved_order_id ' );
142
+
94
143
$ this ->assertTrue ((bool )$ quote ->getId (), 'Quote was not loaded from fixture. ' );
95
144
$ this ->assertNotEmpty ($ quote ->getAllItems (), 'Quote from fixture is empty. ' );
145
+
96
146
$ quote ->getPayment ()->setMethod ('checkmo ' );
97
147
98
148
$ order = $ this ->quoteManagement ->submit ($ quote );
99
149
$ this ->assertNotNull ($ order ->getId (), 'Order was not created. ' );
100
150
$ this ->assertNotEmpty ($ order ->getIncrementId (), 'Order increment ID is missing. ' );
151
+
101
152
return [$ order , $ quote ];
102
153
}
154
+
155
+ /**
156
+ * Simulate a payment failure by cancelling the order and adding a history comment.
157
+ *
158
+ * @param Order $order
159
+ * @return void
160
+ */
103
161
private function simulatePaymentFailure (Order $ order ): void
104
162
{
105
163
$ order ->setStatus (Order::STATE_CANCELED )
106
164
->setState (Order::STATE_CANCELED )
107
165
->addCommentToStatusHistory (__ ('Simulated: Payment failure due to gateway timeout. ' ));
166
+
108
167
$ this ->orderRepository ->save ($ order );
168
+
109
169
$ this ->assertSame (
110
170
Order::STATE_CANCELED ,
111
171
$ order ->getState (),
0 commit comments