diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/ConfirmEmail.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/ConfirmEmail.php new file mode 100644 index 00000000000..4d6287c73c2 --- /dev/null +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/ConfirmEmail.php @@ -0,0 +1,81 @@ +emailValidator->isValid($args['input']['email'])) { + throw new GraphQlInputException(__('Email is invalid')); + } + try { + $customer = $this->accountManagement->activate($args['input']['email'], $args['input']['confirmation_key']); + } catch (InvalidTransitionException | InputMismatchException $e) { + throw new GraphQlInputException(__($e->getRawMessage())); + } catch (StateException) { + throw new GraphQlInputException(__('This confirmation key is invalid or has expired.')); + } catch (\Exception) { + throw new GraphQlInputException(__('There was an error confirming the account')); + } + return ['customer' => $this->extractCustomerData->execute($customer)]; + } +} diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/ConfirmationStatus.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/ConfirmationStatus.php new file mode 100644 index 00000000000..70ef16bc5cb --- /dev/null +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/ConfirmationStatus.php @@ -0,0 +1,58 @@ +accountManagement->getConfirmationStatus($context->getUserId()); + } catch (LocalizedException $e) { + throw new GraphQlInputException(__($e->getMessage()), $e); + } + return strtoupper($confirmationStatus); + } +} diff --git a/app/code/Magento/CustomerGraphQl/etc/graphql/di.xml b/app/code/Magento/CustomerGraphQl/etc/graphql/di.xml index 0636098cd6d..305e9cd12d6 100644 --- a/app/code/Magento/CustomerGraphQl/etc/graphql/di.xml +++ b/app/code/Magento/CustomerGraphQl/etc/graphql/di.xml @@ -37,6 +37,7 @@ customer/password/required_character_classes_number customer/password/minimum_password_length customer/password/autocomplete_on_storefront + customer/create_account/confirm diff --git a/app/code/Magento/CustomerGraphQl/etc/schema.graphqls b/app/code/Magento/CustomerGraphQl/etc/schema.graphqls index fd148e12e70..848d0a9be8d 100644 --- a/app/code/Magento/CustomerGraphQl/etc/schema.graphqls +++ b/app/code/Magento/CustomerGraphQl/etc/schema.graphqls @@ -5,6 +5,7 @@ type StoreConfig { required_character_classes_number : String @doc(description: "The number of different character classes (lowercase, uppercase, digits, special characters) required in a password.") minimum_password_length : String @doc(description: "The minimum number of characters required for a valid password.") autocomplete_on_storefront : Boolean @doc(description: "Indicates whether to enable autocomplete on login and forgot password forms.") + create_account_confirmation: Boolean @doc(description: "Indicates if the new accounts need confirmation.") } type Query { @@ -29,6 +30,12 @@ type Mutation { requestPasswordResetEmail(email: String! @doc(description: "The customer's email address.")): Boolean @resolver(class: "\\Magento\\CustomerGraphQl\\Model\\Resolver\\RequestPasswordResetEmail") @doc(description: "Request an email with a reset password token for the registered customer identified by the specified email.") resetPassword(email: String! @doc(description: "The customer's email address."), resetPasswordToken: String! @doc(description: "A runtime token generated by the `requestPasswordResetEmail` mutation."), newPassword: String! @doc(description: "The customer's new password.")): Boolean @resolver(class: "\\Magento\\CustomerGraphQl\\Model\\Resolver\\ResetPassword") @doc(description: "Reset a customer's password using the reset password token that the customer received in an email after requesting it using `requestPasswordResetEmail`.") updateCustomerEmail(email: String! @doc(description: "The customer's email address."), password: String! @doc(description: "The customer's password.")): CustomerOutput @resolver(class: "\\Magento\\CustomerGraphQl\\Model\\Resolver\\UpdateCustomerEmail") @doc(description: "Change the email address for the logged-in customer.") + confirmEmail(input: ConfirmEmailInput! @doc(description: "An input object to identify the customer to confirm the email.")): CustomerOutput @resolver(class: "\\Magento\\CustomerGraphQl\\Model\\Resolver\\ConfirmEmail") @doc(description: "Confirms the email address for a customer.") +} + +input ConfirmEmailInput @doc(description: "Contains details about a customer email address to confirm.") { + email: String! @doc(description: "The email address to be confirmed.") + confirmation_key: String! @doc(description: "The key to confirm the email address.") } input CustomerAddressInput @doc(description: "Contains details about a billing or shipping address."){ @@ -140,6 +147,7 @@ type Customer @doc(description: "Defines the customer name, addresses, and other addresses: [CustomerAddress] @doc(description: "An array containing the customer's shipping and billing addresses.") @resolver(class: "\\Magento\\CustomerGraphQl\\Model\\Resolver\\CustomerAddresses") gender: Int @doc(description: "The customer's gender (Male - 1, Female - 2).") custom_attributes(attributeCodes: [ID!]): [AttributeValueInterface] @doc(description: "Customer's custom attributes.") @resolver(class: "Magento\\CustomerGraphQl\\Model\\Resolver\\CustomAttributeFilter") + confirmation_status: ConfirmationStatusEnum! @doc(description: "The customer's confirmation status.") @resolver(class: "Magento\\CustomerGraphQl\\Model\\Resolver\\ConfirmationStatus") } type CustomerAddress @doc(description: "Contains detailed information about a customer's billing or shipping address."){ @@ -467,3 +475,8 @@ enum ValidationRuleEnum @doc(description: "List of validation rule names applied MAX_IMAGE_HEIGHT MAX_IMAGE_WIDTH } + +enum ConfirmationStatusEnum @doc(description: "List of account confirmation statuses.") { + ACCOUNT_CONFIRMED @doc(description: "Account confirmed") + ACCOUNT_CONFIRMATION_NOT_REQUIRED @doc(description: "Account confirmation not required") +} diff --git a/app/code/Magento/IntegrationGraphQl/README.md b/app/code/Magento/IntegrationGraphQl/README.md new file mode 100644 index 00000000000..d6414fad7f7 --- /dev/null +++ b/app/code/Magento/IntegrationGraphQl/README.md @@ -0,0 +1,22 @@ +# Magento_IntegrationGraphQl module + +This module provides GraphQl resolvers for Integartion module. + +## Installation + +Before installing this module, note that the Magento_IntegrationGraphQl is dependent on the following modules: + +- `Magento_GraphQl` +- `Magento_Integration` + +For information about a module installation in Magento 2, see [Enable or disable modules](https://experienceleague.adobe.com/docs/commerce-operations/installation-guide/tutorials/manage-modules.html). + +## Extensibility + +Extension developers can interact with the Magento_IntegrationGraphQl module. For more information about the Magento extension mechanism, see [Magento plugins](https://developer.adobe.com/commerce/php/development/components/plugins/). + +[The Magento dependency injection mechanism](https://developer.adobe.com/commerce/php/development/components/dependency-injection/) enables you to override the functionality of the Magento_IntegrationGraphQl module. + +## Additional information + +You can get more information about [GraphQl In Magento 2](https://developer.adobe.com/commerce/webapi/graphql/). diff --git a/app/code/Magento/IntegrationGraphQl/composer.json b/app/code/Magento/IntegrationGraphQl/composer.json new file mode 100644 index 00000000000..bb26e5f2af1 --- /dev/null +++ b/app/code/Magento/IntegrationGraphQl/composer.json @@ -0,0 +1,21 @@ +{ + "name": "magento/module-integration-graph-ql", + "description": "N/A", + "type": "magento2-module", + "require": { + "php": "~8.1.0||~8.2.0||~8.3.0", + "magento/framework": "*" + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "autoload": { + "files": [ + "registration.php" + ], + "psr-4": { + "Magento\\IntegrationGraphQl\\": "" + } + } +} diff --git a/app/code/Magento/IntegrationGraphQl/etc/graphql/di.xml b/app/code/Magento/IntegrationGraphQl/etc/graphql/di.xml new file mode 100644 index 00000000000..9e0541e944c --- /dev/null +++ b/app/code/Magento/IntegrationGraphQl/etc/graphql/di.xml @@ -0,0 +1,27 @@ + + + + + + + oauth/access_token_lifetime/customer + + + + diff --git a/app/code/Magento/IntegrationGraphQl/etc/module.xml b/app/code/Magento/IntegrationGraphQl/etc/module.xml new file mode 100644 index 00000000000..ca4c956afc4 --- /dev/null +++ b/app/code/Magento/IntegrationGraphQl/etc/module.xml @@ -0,0 +1,22 @@ + + + + + + diff --git a/app/code/Magento/IntegrationGraphQl/etc/schema.graphqls b/app/code/Magento/IntegrationGraphQl/etc/schema.graphqls new file mode 100644 index 00000000000..1241f54dca1 --- /dev/null +++ b/app/code/Magento/IntegrationGraphQl/etc/schema.graphqls @@ -0,0 +1,18 @@ +# ADOBE CONFIDENTIAL +# ___________________ +# +# Copyright 2024 Adobe +# All Rights Reserved. +# +# NOTICE: All information contained herein is, and remains +# the property of Adobe and its suppliers, if any. The intellectual +# and technical concepts contained herein are proprietary to Adobe +# and its suppliers and are protected by all applicable intellectual +# property laws, including trade secret and copyright laws. +# Dissemination of this information or reproduction of this material +# is strictly forbidden unless prior written permission is obtained +# from Adobe. + +type StoreConfig { + customer_access_token_lifetime: Float @doc(description: "Customer access token lifetime.") +} diff --git a/app/code/Magento/IntegrationGraphQl/registration.php b/app/code/Magento/IntegrationGraphQl/registration.php new file mode 100644 index 00000000000..bb35a050bf7 --- /dev/null +++ b/app/code/Magento/IntegrationGraphQl/registration.php @@ -0,0 +1,19 @@ +checkout/options/guest_checkout checkout/options/onepage_checkout_enabled checkout/options/max_items_display_count + checkout/cart_link/use_qty + checkout/sidebar/display + checkout/sidebar/max_items_display_count + checkout/cart/delete_quote_after diff --git a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls index 9f44a24b197..53da2d87511 100644 --- a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls +++ b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls @@ -260,7 +260,7 @@ type ApplyCouponToCartOutput @doc(description: "Contains details about the cart type PlaceOrderOutput @doc(description: "Contains the results of the request to place an order.") { order: Order @deprecated(reason: "Use `orderV2` instead.") @doc(description: "The ID of the order.") - orderV2: CustomerOrder! @doc(description: "Full order information.") + orderV2: CustomerOrder @doc(description: "Full order information.") errors: [PlaceOrderError!]! @doc(description:"An array of place order errors.") } @@ -511,6 +511,10 @@ type StoreConfig { is_guest_checkout_enabled: Boolean @doc(description: "Extended Config Data - checkout/options/guest_checkout") is_one_page_checkout_enabled: Boolean @doc(description: "Extended Config Data - checkout/options/onepage_checkout_enabled") max_items_in_order_summary: Int @doc(description: "Extended Config Data - checkout/options/max_items_display_count") + cart_summary_display_quantity: Int @doc(description: "Extended Config Data - checkout/cart_link/use_qty") + minicart_display: Boolean @doc(description: "Extended Config Data - checkout/sidebar/display") + minicart_max_items: Int @doc(description: "Extended Config Data - checkout/sidebar/count") + cart_expires_in_days: Int @doc(description: "Extended Config Data - checkout/cart/delete_quote_after") } input EstimateTotalsInput { diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormItemsSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormItemsSection.xml index 0497bdbeb26..ef359ce11ea 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormItemsSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormItemsSection.xml @@ -36,6 +36,8 @@ + + diff --git a/app/code/Magento/SalesGraphQl/Model/Formatter/Order.php b/app/code/Magento/SalesGraphQl/Model/Formatter/Order.php index ec41c15e0fc..a1ad36d4dfb 100644 --- a/app/code/Magento/SalesGraphQl/Model/Formatter/Order.php +++ b/app/code/Magento/SalesGraphQl/Model/Formatter/Order.php @@ -17,26 +17,14 @@ */ class Order { - /** - * @var OrderAddress - */ - private $orderAddress; - - /** - * @var OrderPayments - */ - private $orderPayments; - /** * @param OrderAddress $orderAddress * @param OrderPayments $orderPayments */ public function __construct( - OrderAddress $orderAddress, - OrderPayments $orderPayments + private readonly OrderAddress $orderAddress, + private readonly OrderPayments $orderPayments ) { - $this->orderAddress = $orderAddress; - $this->orderPayments = $orderPayments; } /** @@ -57,6 +45,7 @@ public function format(OrderInterface $orderModel): array 'order_date' => $orderModel->getCreatedAt(), 'order_number' => $orderModel->getIncrementId(), 'status' => $orderModel->getStatusLabel(), + 'email' => $orderModel->getCustomerEmail(), 'shipping_method' => $orderModel->getShippingDescription(), 'shipping_address' => $this->orderAddress->getOrderShippingAddress($orderModel), 'billing_address' => $this->orderAddress->getOrderBillingAddress($orderModel), diff --git a/app/code/Magento/SalesGraphQl/etc/schema.graphqls b/app/code/Magento/SalesGraphQl/etc/schema.graphqls index b1d1739694d..1e2991d7fed 100644 --- a/app/code/Magento/SalesGraphQl/etc/schema.graphqls +++ b/app/code/Magento/SalesGraphQl/etc/schema.graphqls @@ -74,6 +74,7 @@ type CustomerOrder @doc(description: "Contains details about each of the custome grand_total: Float @deprecated(reason: "Use the `totals.grand_total` field instead.") token: String! @doc(description: "The token that can be used to retrieve the order using order query.") @resolver(class: "Magento\\SalesGraphQl\\Model\\Resolver\\Token") applied_coupons: [AppliedCoupon!]! @doc(description: "Coupons applied to the order.") + email: String @doc(description: "Order customer email.") } type OrderAddress @doc(description: "Contains detailed information about an order's billing and shipping addresses."){ diff --git a/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleData.xml b/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleData.xml index a7f2da506e8..f76a1d49358 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleData.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Data/SalesRuleData.xml @@ -604,4 +604,39 @@ Percent of product price discount 10 + + + SimpleSalesRule + Sales Rule Description + + 1 + + + 1 + + 10 + true + false + true + 1 + by_fixed + 10 + 10 + 0 + false + 0 + false + SPECIFIC_COUPON + false + 10 + 1 + + + + false + + + 3 + true + diff --git a/app/code/Magento/TaxGraphQl/Model/Resolver/DisplayWrapping.php b/app/code/Magento/TaxGraphQl/Model/Resolver/DisplayWrapping.php new file mode 100644 index 00000000000..bc85bcd6249 --- /dev/null +++ b/app/code/Magento/TaxGraphQl/Model/Resolver/DisplayWrapping.php @@ -0,0 +1,54 @@ +enumLookup->getEnumValueFromField( + 'TaxWrappingEnum', + $value['shopping_cart_display_tax_gift_wrapping'] + ); + } + return null; + } +} diff --git a/app/code/Magento/TaxGraphQl/etc/graphql/di.xml b/app/code/Magento/TaxGraphQl/etc/graphql/di.xml index a0316135e46..601a81b7476 100644 --- a/app/code/Magento/TaxGraphQl/etc/graphql/di.xml +++ b/app/code/Magento/TaxGraphQl/etc/graphql/di.xml @@ -16,7 +16,8 @@ * ************************************************************************ */ --> - + @@ -26,7 +27,21 @@ tax/cart_display/grandtotal tax/cart_display/full_summary tax/cart_display/zero_tax + tax/cart_display/gift_wrapping + + + + + + + + 1 + 2 + 3 + + + diff --git a/app/code/Magento/TaxGraphQl/etc/schema.graphqls b/app/code/Magento/TaxGraphQl/etc/schema.graphqls index e6234b83539..2696098dbaa 100644 --- a/app/code/Magento/TaxGraphQl/etc/schema.graphqls +++ b/app/code/Magento/TaxGraphQl/etc/schema.graphqls @@ -12,4 +12,11 @@ type StoreConfig { shopping_cart_display_grand_total: Boolean @doc(description: "Extended Config Data - tax/cart_display/grandtotal") shopping_cart_display_full_summary: Boolean @doc(description: "Extended Config Data - tax/cart_display/full_summary") shopping_cart_display_zero_tax: Boolean @doc(description: "Extended Config Data - tax/cart_display/zero_tax") + shopping_cart_display_tax_gift_wrapping: TaxWrappingEnum @doc(description: "Extended Config Data - tax/cart_display/gift_wrapping") @resolver(class: "Magento\\TaxGraphQl\\Model\\Resolver\\DisplayWrapping") +} + +enum TaxWrappingEnum { + DISPLAY_EXCLUDING_TAX + DISPLAY_INCLUDING_TAX + DISPLAY_TYPE_BOTH } diff --git a/composer.json b/composer.json index c8a0fe02966..444e7146a38 100644 --- a/composer.json +++ b/composer.json @@ -206,6 +206,7 @@ "magento/module-indexer": "*", "magento/module-instant-purchase": "*", "magento/module-integration": "*", + "magento/module-integration-graph-ql": "*", "magento/module-layered-navigation": "*", "magento/module-login-as-customer": "*", "magento/module-login-as-customer-admin-ui": "*", diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/ConfirmEmailTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/ConfirmEmailTest.php new file mode 100644 index 00000000000..4b6fa08cd27 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/ConfirmEmailTest.php @@ -0,0 +1,160 @@ + 'customer@example.com', + 'confirmation' => 'abcde', + ], + 'customer' + ) +] +class ConfirmEmailTest extends GraphQlAbstract +{ + private const QUERY = <<graphQlMutation( + sprintf( + self::QUERY, + 'customer@example.com', + 'abcde', + ), + [], + '', + $this->getCustomerAuthHeaders('customer@example.com', self::PASSWORD) + ); + + $this->assertEquals( + [ + 'confirmEmail' => [ + 'customer' => [ + 'email' => 'customer@example.com' + ] + ] + ], + $response + ); + + $this->expectException(\Exception::class); + $this->expectExceptionMessage('The account is already active.'); + + $this->graphQlMutation( + sprintf( + self::QUERY, + 'customer@example.com', + 'abcde', + ), + [], + '', + $this->getCustomerAuthHeaders('customer@example.com', self::PASSWORD) + ); + } + + /** + * @return void + * @throws AuthenticationException + */ + public function testConfirmEmailWrongEmail() + { + $this->expectException(\Exception::class); + $this->expectExceptionMessage('Email is invalid'); + + $this->graphQlMutation( + sprintf( + self::QUERY, + 'bad-email', + 'abcde', + ), + [], + '', + $this->getCustomerAuthHeaders('customer@example.com', self::PASSWORD) + ); + } + + /** + * @return void + * @throws AuthenticationException + */ + public function testConfirmEmailWrongConfirmation() + { + $this->expectException(\Exception::class); + $this->expectExceptionMessage('The confirmation token is invalid. Verify the token and try again.'); + + $this->graphQlMutation( + sprintf( + self::QUERY, + 'customer@example.com', + 'wrong-confirmation', + ), + [], + '', + $this->getCustomerAuthHeaders('customer@example.com', self::PASSWORD) + ); + } + + /** + * @param string $email + * @param string $password + * @return array + * @throws AuthenticationException + */ + private function getCustomerAuthHeaders(string $email, string $password): array + { + $customerToken = Bootstrap::getObjectManager()->get( + CustomerTokenServiceInterface::class + )->createCustomerAccessToken( + $email, + $password + ); + return ['Authorization' => 'Bearer ' . $customerToken]; + } +} diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/ConfirmationStatusTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/ConfirmationStatusTest.php new file mode 100644 index 00000000000..e2e05392917 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/ConfirmationStatusTest.php @@ -0,0 +1,188 @@ +customerTokenService = Bootstrap::getObjectManager()->get(CustomerTokenServiceInterface::class); + $this->customerRepository = Bootstrap::getObjectManager()->get(CustomerRepositoryInterface::class); + } + + #[ + Config('customer/create_account/confirm', 0), + DataFixture( + Customer::class, + [ + 'email' => 'customer@example.com', + ], + 'customer' + ) + ] + public function testGetConfirmationStatusConfirmationNotRequiredTest() + { + $customer = DataFixtureStorageManager::getStorage()->get('customer'); + $query = <<graphQlQuery( + $query, + [], + '', + $this->getHeaderMap($customer->getEmail(), 'password') + ); + $this->assertEquals( + strtoupper(AccountManagementInterface::ACCOUNT_CONFIRMATION_NOT_REQUIRED), + $response['customer']['confirmation_status'] + ); + } + + #[ + Config('customer/create_account/confirm', 1), + DataFixture( + Customer::class, + [ + 'email' => 'customer@example.com', + ], + 'customer' + ) + ] + public function testGetConfirmationStatusConfirmedTest() + { + $customer = DataFixtureStorageManager::getStorage()->get('customer'); + $query = <<graphQlQuery( + $query, + [], + '', + $this->getHeaderMap($customer->getEmail(), 'password') + ); + $this->assertEquals( + strtoupper(AccountManagementInterface::ACCOUNT_CONFIRMED), + $response['customer']['confirmation_status'] + ); + } + + #[ + Config('customer/create_account/confirm', 1), + DataFixture( + Customer::class, + [ + 'email' => 'another@example.com', + ], + 'customer' + ) + ] + public function testGetConfirmationStatusConfirmationRequiredTest() + { + $this->expectExceptionMessage("This account isn't confirmed. Verify and try again."); + /** @var CustomerInterface $customer */ + $customer = DataFixtureStorageManager::getStorage()->get('customer'); + $headersMap = $this->getHeaderMap($customer->getEmail(), 'password'); + $customer->setConfirmation(AccountManagementInterface::ACCOUNT_CONFIRMATION_REQUIRED); + $this->customerRepository->save($this->customerRepository->get($customer->getEmail())); + $query = <<graphQlQuery( + $query, + [], + '', + $headersMap + ); + } + + #[ + DataFixture( + Customer::class, + [ + 'email' => 'test@example.com', + ], + 'customer' + ) + ] + public function testGetConfirmationStatusIfUserIsNotAuthorizedTest() + { + $this->expectException(\Exception::class); + $this->expectExceptionMessage('The current customer isn\'t authorized.'); + + $query = <<graphQlQuery($query); + } + + /** + * @param string $email + * @param string $password + * + * @return array + * @throws AuthenticationException + */ + private function getHeaderMap(string $email, string $password): array + { + $customerToken = $this->customerTokenService->createCustomerAccessToken($email, $password); + return ['Authorization' => 'Bearer ' . $customerToken]; + } +} diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/StoreConfigTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/StoreConfigTest.php index 473105c2c46..99b9e95e8d1 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/StoreConfigTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Customer/StoreConfigTest.php @@ -8,6 +8,7 @@ namespace Magento\GraphQl\Customer; use Exception; +use Magento\TestFramework\Fixture\Config; use Magento\TestFramework\TestCase\GraphQlAbstract; /** @@ -35,4 +36,38 @@ public function testReturnTypeAutocompleteOnStorefrontConfig() self::assertArrayHasKey('autocomplete_on_storefront', $response['storeConfig']); self::assertTrue($response['storeConfig']['autocomplete_on_storefront']); } + + #[ + Config('customer/create_account/confirm', 1) + ] + public function testCreateAccountConfirmationEnabledStorefrontConfig() + { + $query = <<graphQlQuery($query); + self::assertArrayHasKey('create_account_confirmation', $response['storeConfig']); + self::assertTrue($response['storeConfig']['create_account_confirmation']); + } + + #[ + Config('customer/create_account/confirm', 0) + ] + public function testCreateAccountConfirmationDisabledStorefrontConfig() + { + $query = <<graphQlQuery($query); + self::assertArrayHasKey('create_account_confirmation', $response['storeConfig']); + self::assertFalse($response['storeConfig']['create_account_confirmation']); + } } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/IntegrationGraphQl/CustomerAccessTokenLifetimeResolverTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/IntegrationGraphQl/CustomerAccessTokenLifetimeResolverTest.php new file mode 100644 index 00000000000..10bc66fc4a4 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/IntegrationGraphQl/CustomerAccessTokenLifetimeResolverTest.php @@ -0,0 +1,85 @@ +assertEquals( + $this->graphQlQuery($this->getQuery()), + [ + 'storeConfig' => [ + "customer_access_token_lifetime" => 2 + ] + ] + ); + } + + #[ + Config('oauth/access_token_lifetime/customer', 2.5) + ] + public function testGetCustomerAccessTokenLifetimeAsFloat() + { + $this->assertEquals( + $this->graphQlQuery($this->getQuery()), + [ + 'storeConfig' => [ + "customer_access_token_lifetime" => 2.5 + ] + ] + ); + } + + #[ + Config('oauth/access_token_lifetime/customer', null) + ] + public function testGetCustomerAccessTokenLifetimeNull() + { + $this->assertEquals( + $this->graphQlQuery($this->getQuery()), + [ + 'storeConfig' => [ + "customer_access_token_lifetime" => null + ] + ] + ); + } + + /** + * @return string + */ + private function getQuery(): string + { + return <<orderFactory->create(); @@ -147,7 +149,9 @@ public function testPlaceOrderWithAutoGroup() self::assertArrayHasKey('placeOrder', $response); self::assertArrayHasKey('order_number', $response['placeOrder']['order']); + self::assertArrayHasKey('number', $response['placeOrder']['orderV2']); self::assertEquals($reservedOrderId, $response['placeOrder']['order']['order_number']); + self::assertEquals($reservedOrderId, $response['placeOrder']['orderV2']['number']); self::assertEmpty(count($response['placeOrder']['errors'])); $orderIncrementId = $response['placeOrder']['order']['order_number']; $order = $this->orderFactory->create(); @@ -458,7 +462,9 @@ public function testPlaceOrderWithGiftMessage() self::assertArrayHasKey('placeOrder', $response); self::assertArrayHasKey('order_number', $response['placeOrder']['order']); + self::assertArrayHasKey('number', $response['placeOrder']['orderV2']); self::assertEquals($reservedOrderId, $response['placeOrder']['order']['order_number']); + self::assertEquals($reservedOrderId, $response['placeOrder']['orderV2']['number']); $orderIncrementId = $response['placeOrder']['order']['order_number']; $order = $this->orderFactory->create(); $order->loadByIncrementId($orderIncrementId); @@ -477,6 +483,9 @@ private function getQuery(string $maskedQuoteId): string order { order_number } + orderV2 { + number + } errors { message code diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/StoreConfigResolverTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/StoreConfigResolverTest.php index 0dbf16957ff..9e8bf05b3be 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/StoreConfigResolverTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/StoreConfigResolverTest.php @@ -18,11 +18,28 @@ class StoreConfigResolverTest extends GraphQlAbstract { private const MAX_ITEMS_TO_DISPLAY = 5; + private const CART_SUMMARY_DISPLAY_TOTAL = 1; + private const MINICART_MAX_ITEMS = 10; + private const CART_EXPIRES_IN_DAYS = 5; #[ ConfigFixture(Data::XML_PATH_GUEST_CHECKOUT, true, ScopeInterface::SCOPE_STORE, 'default'), ConfigFixture('checkout/options/onepage_checkout_enabled', true, ScopeInterface::SCOPE_STORE, 'default'), - ConfigFixture('checkout/options/max_items_display_count', self::MAX_ITEMS_TO_DISPLAY) + ConfigFixture('checkout/options/max_items_display_count', self::MAX_ITEMS_TO_DISPLAY), + ConfigFixture('checkout/cart_link/use_qty', 1, ScopeInterface::SCOPE_STORE, 'default'), + ConfigFixture('checkout/sidebar/display', true, ScopeInterface::SCOPE_STORE, 'default'), + ConfigFixture( + 'checkout/sidebar/max_items_display_count', + self::MINICART_MAX_ITEMS, + ScopeInterface::SCOPE_STORE, + 'default' + ), + ConfigFixture( + 'checkout/cart/delete_quote_after', + self::CART_EXPIRES_IN_DAYS, + ScopeInterface::SCOPE_STORE, + 'default' + ), ] public function testGetStoreConfig(): void { @@ -32,7 +49,11 @@ public function testGetStoreConfig(): void storeConfig { is_guest_checkout_enabled, is_one_page_checkout_enabled, - max_items_in_order_summary + max_items_in_order_summary, + cart_summary_display_quantity, + minicart_display, + minicart_max_items, + cart_expires_in_days } } QUERY; @@ -52,5 +73,9 @@ private function validateStoreConfig( $this->assertTrue($responseConfig['is_guest_checkout_enabled']); $this->assertTrue($responseConfig['is_one_page_checkout_enabled']); $this->assertEquals(self::MAX_ITEMS_TO_DISPLAY, $responseConfig['max_items_in_order_summary']); + $this->assertEquals(self::CART_SUMMARY_DISPLAY_TOTAL, $responseConfig['cart_summary_display_quantity']); + $this->assertTrue($responseConfig['minicart_display']); + $this->assertEquals(self::MINICART_MAX_ITEMS, $responseConfig['minicart_max_items']); + $this->assertEquals(self::CART_EXPIRES_IN_DAYS, $responseConfig['cart_expires_in_days']); } } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/GuestOrderByTokenTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/GuestOrderByTokenTest.php index f25c54d57a1..959d21b3e5d 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/GuestOrderByTokenTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/GuestOrderByTokenTest.php @@ -48,6 +48,7 @@ class GuestOrderByTokenTest extends GraphQlAbstract token: "%token" }) { number + email billing_address { firstname lastname @@ -63,6 +64,7 @@ class GuestOrderByTokenTest extends GraphQlAbstract }) { orderV2 { number + email billing_address { firstname lastname @@ -97,6 +99,7 @@ public function testGuestOrder(): void $this->assertNotEmpty($placeOrderResponse['placeOrder']['orderV2']['number']); $this->assertNotEmpty($placeOrderResponse['placeOrder']['orderV2']['token']); + $this->assertNotEmpty($placeOrderResponse['placeOrder']['orderV2']['email']); $this->assertNotEmpty($placeOrderResponse['placeOrder']['orderV2']['billing_address']['firstname']); $this->assertNotEmpty($placeOrderResponse['placeOrder']['orderV2']['billing_address']['lastname']); @@ -111,6 +114,7 @@ public function testGuestOrder(): void [ 'guestOrderByToken' => [ 'number' => $placeOrderResponse['placeOrder']['orderV2']['number'], + 'email' => $placeOrderResponse['placeOrder']['orderV2']['email'], 'billing_address' => [ 'firstname' => $placeOrderResponse['placeOrder']['orderV2']['billing_address']['firstname'], 'lastname' => $placeOrderResponse['placeOrder']['orderV2']['billing_address']['lastname'] diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/GuestOrderTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/GuestOrderTest.php index fe8a57d19a2..856defb108f 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/GuestOrderTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Sales/GuestOrderTest.php @@ -47,6 +47,7 @@ class GuestOrderTest extends GraphQlAbstract postcode: "%postcode" }) { number + email billing_address { firstname lastname @@ -83,6 +84,7 @@ public function testGuestOrder(): void [ 'guestOrder' => [ 'number' => $order->getIncrementId(), + 'email' => $order->getBillingAddress()->getEmail(), 'billing_address' => [ 'firstname' => $order->getBillingAddress()->getFirstname(), 'lastname' => $order->getBillingAddress()->getLastname() diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Tax/StoreConfigResolverTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Tax/StoreConfigResolverTest.php index 9b8f2123198..0f2af31f786 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Tax/StoreConfigResolverTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Tax/StoreConfigResolverTest.php @@ -25,6 +25,7 @@ class StoreConfigResolverTest extends GraphQlAbstract ConfigFixture(Config::XML_PATH_DISPLAY_CART_GRANDTOTAL, 1, ScopeInterface::SCOPE_STORE, 'default'), ConfigFixture(Config::XML_PATH_DISPLAY_CART_FULL_SUMMARY, 1, ScopeInterface::SCOPE_STORE, 'default'), ConfigFixture(Config::XML_PATH_DISPLAY_CART_ZERO_TAX, 1, ScopeInterface::SCOPE_STORE, 'default'), + ConfigFixture('tax/cart_display/gift_wrapping', 3, ScopeInterface::SCOPE_STORE, 'default'), ] public function testGetStoreConfig(): void { @@ -38,6 +39,7 @@ public function testGetStoreConfig(): void shopping_cart_display_grand_total, shopping_cart_display_full_summary, shopping_cart_display_zero_tax, + shopping_cart_display_tax_gift_wrapping, } } QUERY; @@ -60,5 +62,6 @@ private function validateStoreConfig( $this->assertEquals(1, $responseConfig['shopping_cart_display_grand_total']); $this->assertEquals(1, $responseConfig['shopping_cart_display_full_summary']); $this->assertEquals(1, $responseConfig['shopping_cart_display_zero_tax']); + $this->assertEquals('DISPLAY_TYPE_BOTH', $responseConfig['shopping_cart_display_tax_gift_wrapping']); } }