diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 92a51eb84a4ab..fe6fc3f3e3e8f 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -1,18 +1,23 @@ # Contributing to Magento 2 code Contributions to the Magento 2 codebase are done using the fork & pull model. -This contribution model has contributors maintaining their own copy of the forked codebase (which can easily be synced with the main copy). The forked repository is then used to submit a request to the base repository to “pull” a set of changes. For more information on pull requests please refer to [GitHub Help](https://help.github.com/articles/about-pull-requests/). +This contribution model has contributors maintaining their own fork of the Magento 2 repository. +The forked repository is then used to submit a request to the base repository to “pull” a set of changes. +For more information on pull requests please refer to [GitHub Help](https://help.github.com/articles/about-pull-requests/). Contributions can take the form of new components or features, changes to existing features, tests, documentation (such as developer guides, user guides, examples, or specifications), bug fixes or optimizations. -The Magento 2 development team will review all issues and contributions submitted by the community of developers in the first in, first out order. During the review we might require clarifications from the contributor. If there is no response from the contributor within two weeks, the pull request will be closed. +The Magento 2 development team or community maintainers will review all issues and contributions submitted by the community of developers in the first in, first out order. +During the review we might require clarifications from the contributor. +If there is no response from the contributor within two weeks, the pull request will be closed. +For more detialed information on contribution please read our [beginners guide](https://github.com/magento/magento2/wiki/Getting-Started). ## Contribution requirements -1. Contributions must adhere to the [Magento coding standards](https://devdocs.magento.com/guides/v2.2/coding-standards/bk-coding-standards.html). +1. Contributions must adhere to the [Magento coding standards](https://devdocs.magento.com/guides/v2.3/coding-standards/bk-coding-standards.html). 2. Pull requests (PRs) must be accompanied by a meaningful description of their purpose. Comprehensive descriptions increase the chances of a pull request being merged quickly and without additional clarification requests. -3. Commits must be accompanied by meaningful commit messages. Please see the [Magento Pull Request Template](https://github.com/magento/magento2/blob/2.2-develop/.github/PULL_REQUEST_TEMPLATE.md) for more information. +3. Commits must be accompanied by meaningful commit messages. Please see the [Magento Pull Request Template](https://github.com/magento/magento2/blob/2.3-develop/.github/PULL_REQUEST_TEMPLATE.md) for more information. 4. PRs which include bug fixes must be accompanied with a step-by-step description of how to reproduce the bug. 3. PRs which include new logic or new features must be submitted along with: * Unit/integration test coverage @@ -22,15 +27,22 @@ The Magento 2 development team will review all issues and contributions submitte ## Contribution process -If you are a new GitHub user, we recommend that you create your own [free github account](https://github.com/signup/free). This will allow you to collaborate with the Magento 2 development team, fork the Magento 2 project and send pull requests. +If you are a new GitHub user, we recommend that you create your own [free github account](https://github.com/signup/free). +This will allow you to collaborate with the Magento 2 development team, fork the Magento 2 project and send pull requests. 1. Search current [listed issues](https://github.com/magento/magento2/issues) (open or closed) for similar proposals of intended contribution before starting work on a new contribution. 2. Review the [Contributor License Agreement](https://magento.com/legaldocuments/mca) if this is your first time contributing. 3. Create and test your work. -4. Fork the Magento 2 repository according to the [Fork A Repository instructions](https://devdocs.magento.com/guides/v2.2/contributor-guide/contributing.html#fork) and when you are ready to send us a pull request – follow the [Create A Pull Request instructions](https://devdocs.magento.com/guides/v2.2/contributor-guide/contributing.html#pull_request). +4. Fork the Magento 2 repository according to the [Fork A Repository instructions](https://devdocs.magento.com/guides/v2.3/contributor-guide/contributing.html#fork) and when you are ready to send us a pull request – follow the [Create A Pull Request instructions](https://devdocs.magento.com/guides/v2.3/contributor-guide/contributing.html#pull_request). 5. Once your contribution is received the Magento 2 development team will review the contribution and collaborate with you as needed. ## Code of Conduct Please note that this project is released with a Contributor Code of Conduct. We expect you to agree to its terms when participating in this project. The full text is available in the repository [Wiki](https://github.com/magento/magento2/wiki/Magento-Code-of-Conduct). + +## Connecting with Community! + +If you have any questions, join us in [#beginners](https://magentocommeng.slack.com/messages/CH8BGFX9D) Slack chat. If you are not on our slack, [click here](http://tinyurl.com/engcom-slack) to join. + +Need to find a project? Check out the [Slack Channels](https://github.com/magento/magento2/wiki/Slack-Channels) (with listed project info) and the [Magento Community Portal](https://opensource.magento.com/). diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index f191bd9aaba67..11da06ee704c6 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -21,7 +21,6 @@ There could be 1 or more issues linked here and it will help us find some more information about the reasoning behind this change. --> 1. magento/magento2#: Issue title -2. ... ### Manual testing scenarios (*) + ### Contribution checklist (*) - [ ] Pull request has a meaningful description of its purpose - [ ] All commits are accompanied by meaningful commit messages - [ ] All new or changed code is covered with unit/integration tests (if applicable) - - [ ] All automated tests passed successfully (all builds on Travis CI are green) + - [ ] All automated tests passed successfully (all builds are green) diff --git a/.travis.yml b/.travis.yml.sample similarity index 100% rename from .travis.yml rename to .travis.yml.sample diff --git a/README.md b/README.md index ecd457a4f1aef..73154c18d891d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,3 @@ -[![Build Status](https://travis-ci.org/magento/magento2.svg?branch=2.3-develop)](https://travis-ci.org/magento/magento2) [![Open Source Helpers](https://www.codetriage.com/magento/magento2/badges/users.svg)](https://www.codetriage.com/magento/magento2) [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/magento/magento2?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) [![Crowdin](https://d322cqt584bo4o.cloudfront.net/magento-2/localized.svg)](https://crowdin.com/project/magento-2) diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000000000..2b06199e5f95a --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,10 @@ +# Reporting Security Issues + +Magento values the contributions of the security research community, and we look forward to working with you to minimize risk to Magento merchants. + +## Where should I report security issues? + +We strongly encourage you to report all security issues privately via our [bug bounty program](https://hackerone.com/magento). Please provide us with relevant technical details and repro steps to expedite our investigation. If you prefer not to use HackerOne, email us directly at `psirt@adobe.com` with details and repro steps. + +## Learning More About Security +To learn more about securing a Magento store, please visit the [Security Center](https://magento.com/security). diff --git a/app/code/Magento/AdminNotification/view/adminhtml/templates/system/messages/popup.phtml b/app/code/Magento/AdminNotification/view/adminhtml/templates/system/messages/popup.phtml index 0448daaf17644..37548e1599004 100644 --- a/app/code/Magento/AdminNotification/view/adminhtml/templates/system/messages/popup.phtml +++ b/app/code/Magento/AdminNotification/view/adminhtml/templates/system/messages/popup.phtml @@ -27,4 +27,4 @@ } } } - \ No newline at end of file + diff --git a/app/code/Magento/AdvancedSearch/view/adminhtml/templates/system/config/testconnection.phtml b/app/code/Magento/AdvancedSearch/view/adminhtml/templates/system/config/testconnection.phtml index ae202cbfaf442..71697d2fd0bc2 100644 --- a/app/code/Magento/AdvancedSearch/view/adminhtml/templates/system/config/testconnection.phtml +++ b/app/code/Magento/AdvancedSearch/view/adminhtml/templates/system/config/testconnection.phtml @@ -6,10 +6,10 @@ // @codingStandardsIgnoreFile ?> diff --git a/app/code/Magento/AdvancedSearch/view/adminhtml/web/js/testconnection.js b/app/code/Magento/AdvancedSearch/view/adminhtml/web/js/testconnection.js index e28f1b4d07d94..ba08b8ecd291b 100644 --- a/app/code/Magento/AdvancedSearch/view/adminhtml/web/js/testconnection.js +++ b/app/code/Magento/AdvancedSearch/view/adminhtml/web/js/testconnection.js @@ -40,7 +40,8 @@ define([ element = $('#' + this.options.elementId), self = this, params = {}, - msg = ''; + msg = '', + fieldToCheck = this.options.fieldToCheck || 'success'; element.removeClass('success').addClass('fail'); $.each($.parseJSON(this.options.fieldMapping), function (key, el) { @@ -49,9 +50,10 @@ define([ $.ajax({ url: this.options.url, showLoader: true, - data: params + data: params, + headers: this.options.headers || {} }).done(function (response) { - if (response.success) { + if (response[fieldToCheck]) { element.removeClass('fail').addClass('success'); result = self.options.successText; } else { diff --git a/app/code/Magento/AdvancedSearch/view/frontend/templates/search_data.phtml b/app/code/Magento/AdvancedSearch/view/frontend/templates/search_data.phtml index 6e660555053a1..053670ca19dac 100644 --- a/app/code/Magento/AdvancedSearch/view/frontend/templates/search_data.phtml +++ b/app/code/Magento/AdvancedSearch/view/frontend/templates/search_data.phtml @@ -13,13 +13,13 @@ $data = $block->getItems(); if (count($data)):?>
-
getTitle()) ?>
+
escapeHtml(__($block->getTitle())) ?>
- escapeHtml($additionalInfo->getQueryText()) ?> isShowResultsCount()): ?> - getResultsCount() ?> + getResultsCount() ?>
diff --git a/app/code/Magento/AmqpStore/LICENSE.txt b/app/code/Magento/AmqpStore/LICENSE.txt new file mode 100644 index 0000000000000..49525fd99da9c --- /dev/null +++ b/app/code/Magento/AmqpStore/LICENSE.txt @@ -0,0 +1,48 @@ + +Open Software License ("OSL") v. 3.0 + +This Open Software License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work: + +Licensed under the Open Software License version 3.0 + + 1. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following: + + 1. to reproduce the Original Work in copies, either alone or as part of a collective work; + + 2. to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work; + + 3. to distribute or communicate copies of the Original Work and Derivative Works to the public, with the proviso that copies of Original Work or Derivative Works that You distribute or communicate shall be licensed under this Open Software License; + + 4. to perform the Original Work publicly; and + + 5. to display the Original Work publicly. + + 2. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works. + + 3. Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work. + + 4. Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license. + + 5. External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c). + + 6. Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work. + + 7. Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer. + + 8. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation. + + 9. Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including 'fair use' or 'fair dealing'). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c). + + 10. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware. + + 11. Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License. + + 12. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License. + + 13. Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. + + 14. Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + + 15. Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You. + + 16. Modification of This License. This License is Copyright (C) 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Open Software License" or "OSL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under " or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process. \ No newline at end of file diff --git a/app/code/Magento/AmqpStore/LICENSE_AFL.txt b/app/code/Magento/AmqpStore/LICENSE_AFL.txt new file mode 100644 index 0000000000000..f39d641b18a19 --- /dev/null +++ b/app/code/Magento/AmqpStore/LICENSE_AFL.txt @@ -0,0 +1,48 @@ + +Academic Free License ("AFL") v. 3.0 + +This Academic Free License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work: + +Licensed under the Academic Free License version 3.0 + + 1. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following: + + 1. to reproduce the Original Work in copies, either alone or as part of a collective work; + + 2. to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work; + + 3. to distribute or communicate copies of the Original Work and Derivative Works to the public, under any license of your choice that does not contradict the terms and conditions, including Licensor's reserved rights and remedies, in this Academic Free License; + + 4. to perform the Original Work publicly; and + + 5. to display the Original Work publicly. + + 2. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works. + + 3. Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work. + + 4. Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license. + + 5. External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c). + + 6. Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work. + + 7. Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer. + + 8. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation. + + 9. Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including "fair use" or "fair dealing"). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c). + + 10. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware. + + 11. Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License. + + 12. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License. + + 13. Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. + + 14. Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + + 15. Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You. + + 16. Modification of This License. This License is Copyright © 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Academic Free License" or "AFL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under " or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process. diff --git a/app/code/Magento/AmqpStore/Plugin/AsynchronousOperations/MassConsumerEnvelopeCallback.php b/app/code/Magento/AmqpStore/Plugin/AsynchronousOperations/MassConsumerEnvelopeCallback.php new file mode 100644 index 0000000000000..e05004cea78cd --- /dev/null +++ b/app/code/Magento/AmqpStore/Plugin/AsynchronousOperations/MassConsumerEnvelopeCallback.php @@ -0,0 +1,101 @@ +storeManager = $storeManager; + $this->envelopeFactory = $envelopeFactory; + $this->logger = $logger; + } + + /** + * Check if amqpProperties['application_headers'] have 'store_id' and use it to setCurrentStore + * Restore original store value in consumer process after execution. + * Reject queue messages because of wrong store_id. + * + * @param SubjectMassConsumerEnvelopeCallback $subject + * @param callable $proceed + * @param EnvelopeInterface $message + * @return void + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function aroundExecute( + SubjectMassConsumerEnvelopeCallback $subject, + callable $proceed, + EnvelopeInterface $message + ) { + $amqpProperties = $message->getProperties(); + if (isset($amqpProperties['application_headers'])) { + $headers = $amqpProperties['application_headers']; + if ($headers instanceof AMQPTable) { + $headers = $headers->getNativeData(); + } + if (isset($headers['store_id'])) { + $storeId = $headers['store_id']; + try { + $currentStoreId = $this->storeManager->getStore()->getId(); + } catch (NoSuchEntityException $e) { + $this->logger->error( + sprintf( + "Can't set currentStoreId during processing queue. Message rejected. Error %s.", + $e->getMessage() + ) + ); + $subject->getQueue()->reject($message, false, $e->getMessage()); + return; + } + if (isset($storeId) && $storeId !== $currentStoreId) { + $this->storeManager->setCurrentStore($storeId); + } + } + } + $proceed($message); + if (isset($storeId, $currentStoreId) && $storeId !== $currentStoreId) { + $this->storeManager->setCurrentStore($currentStoreId);//restore original store value + } + } +} diff --git a/app/code/Magento/AmqpStore/Plugin/Framework/Amqp/Bulk/Exchange.php b/app/code/Magento/AmqpStore/Plugin/Framework/Amqp/Bulk/Exchange.php new file mode 100644 index 0000000000000..c5db1f5300c29 --- /dev/null +++ b/app/code/Magento/AmqpStore/Plugin/Framework/Amqp/Bulk/Exchange.php @@ -0,0 +1,117 @@ +storeManager = $storeManager; + $this->envelopeFactory = $envelopeFactory; + $this->logger = $logger; + } + + /** + * Set current store_id in amqpProperties['application_headers'] + * so consumer may check store_id and execute operation in correct store scope. + * Prevent publishing inconsistent messages because of store_id not defined or wrong. + * + * @param SubjectExchange $subject + * @param string $topic + * @param EnvelopeInterface[] $envelopes + * @return array + * @throws AMQPInvalidArgumentException + * @throws \LogicException + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function beforeEnqueue(SubjectExchange $subject, $topic, array $envelopes) + { + try { + $storeId = $this->storeManager->getStore()->getId(); + } catch (NoSuchEntityException $e) { + $errorMessage = sprintf( + "Can't get current storeId and inject to amqp message. Error %s.", + $e->getMessage() + ); + $this->logger->error($errorMessage); + throw new \LogicException($errorMessage); + } + + $updatedEnvelopes = []; + foreach ($envelopes as $envelope) { + $properties = $envelope->getProperties(); + if (!isset($properties)) { + $properties = []; + } + if (isset($properties['application_headers'])) { + $headers = $properties['application_headers']; + if ($headers instanceof AMQPTable) { + try { + $headers->set('store_id', $storeId); + // phpcs:ignore Magento2.Exceptions.ThrowCatch + } catch (AMQPInvalidArgumentException $ea) { + $errorMessage = sprintf("Can't set storeId to amqp message. Error %s.", $ea->getMessage()); + $this->logger->error($errorMessage); + throw new AMQPInvalidArgumentException($errorMessage); + } + $properties['application_headers'] = $headers; + } + } else { + $properties['application_headers'] = new AMQPTable(['store_id' => $storeId]); + } + $updatedEnvelopes[] = $this->envelopeFactory->create( + [ + 'body' => $envelope->getBody(), + 'properties' => $properties + ] + ); + } + if (!empty($updatedEnvelopes)) { + $envelopes = $updatedEnvelopes; + } + return [$topic, $envelopes]; + } +} diff --git a/app/code/Magento/AmqpStore/README.md b/app/code/Magento/AmqpStore/README.md new file mode 100644 index 0000000000000..0f84c8ff3276e --- /dev/null +++ b/app/code/Magento/AmqpStore/README.md @@ -0,0 +1,3 @@ +# Amqp Store + +**AmqpStore** provides ability to specify store before publish messages with Amqp. diff --git a/app/code/Magento/AmqpStore/composer.json b/app/code/Magento/AmqpStore/composer.json new file mode 100644 index 0000000000000..bd11527bbb36e --- /dev/null +++ b/app/code/Magento/AmqpStore/composer.json @@ -0,0 +1,30 @@ +{ + "name": "magento/module-amqp-store", + "description": "N/A", + "config": { + "sort-packages": true + }, + "require": { + "magento/framework": "*", + "magento/framework-amqp": "*", + "magento/module-store": "*", + "php": "~7.1.3||~7.2.0" + }, + "suggest": { + "magento/module-asynchronous-operations": "*", + "magento/framework-message-queue": "*" + }, + "type": "magento2-module", + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "autoload": { + "files": [ + "registration.php" + ], + "psr-4": { + "Magento\\AmqpStore\\": "" + } + } +} diff --git a/app/code/Magento/AmqpStore/etc/di.xml b/app/code/Magento/AmqpStore/etc/di.xml new file mode 100644 index 0000000000000..3bbbebd249535 --- /dev/null +++ b/app/code/Magento/AmqpStore/etc/di.xml @@ -0,0 +1,15 @@ + + + + + + + + + + diff --git a/app/code/Magento/AmqpStore/etc/module.xml b/app/code/Magento/AmqpStore/etc/module.xml new file mode 100644 index 0000000000000..085b97b5e2f96 --- /dev/null +++ b/app/code/Magento/AmqpStore/etc/module.xml @@ -0,0 +1,14 @@ + + + + + + + + + diff --git a/app/code/Magento/AmqpStore/registration.php b/app/code/Magento/AmqpStore/registration.php new file mode 100644 index 0000000000000..4922879bfbf16 --- /dev/null +++ b/app/code/Magento/AmqpStore/registration.php @@ -0,0 +1,9 @@ +invoker = $invoker; - $this->resource = $resource; - $this->messageController = $messageController; $this->configuration = $configuration; - $this->operationProcessor = $operationProcessorFactory->create([ - 'configuration' => $configuration - ]); - $this->logger = $logger; + $this->massConsumerEnvelopeCallback = $massConsumerEnvelopeCallback; $this->registry = $registry ?? \Magento\Framework\App\ObjectManager::getInstance() ->get(Registry::class); } @@ -122,38 +89,14 @@ public function process($maxNumberOfMessages = null) */ private function getTransactionCallback(QueueInterface $queue) { - return function (EnvelopeInterface $message) use ($queue) { - /** @var LockInterface $lock */ - $lock = null; - try { - $topicName = $message->getProperties()['topic_name']; - $lock = $this->messageController->lock($message, $this->configuration->getConsumerName()); - - $allowedTopics = $this->configuration->getTopicNames(); - if (in_array($topicName, $allowedTopics)) { - $this->operationProcessor->process($message->getBody()); - } else { - $queue->reject($message); - return; - } - $queue->acknowledge($message); - } catch (MessageLockException $exception) { - $queue->acknowledge($message); - } catch (ConnectionLostException $e) { - if ($lock) { - $this->resource->getConnection() - ->delete($this->resource->getTableName('queue_lock'), ['id = ?' => $lock->getId()]); - } - } catch (NotFoundException $e) { - $queue->acknowledge($message); - $this->logger->warning($e->getMessage()); - } catch (\Exception $e) { - $queue->reject($message, false, $e->getMessage()); - if ($lock) { - $this->resource->getConnection() - ->delete($this->resource->getTableName('queue_lock'), ['id = ?' => $lock->getId()]); - } - } + $callbackInstance = $this->massConsumerEnvelopeCallback->create( + [ + 'configuration' => $this->configuration, + 'queue' => $queue, + ] + ); + return function (EnvelopeInterface $message) use ($callbackInstance) { + $callbackInstance->execute($message); }; } } diff --git a/app/code/Magento/AsynchronousOperations/Model/MassConsumerEnvelopeCallback.php b/app/code/Magento/AsynchronousOperations/Model/MassConsumerEnvelopeCallback.php new file mode 100644 index 0000000000000..42437292e6f00 --- /dev/null +++ b/app/code/Magento/AsynchronousOperations/Model/MassConsumerEnvelopeCallback.php @@ -0,0 +1,137 @@ +resource = $resource; + $this->messageController = $messageController; + $this->configuration = $configuration; + $this->operationProcessor = $operationProcessorFactory->create( + [ + 'configuration' => $configuration + ] + ); + $this->logger = $logger; + $this->queue = $queue; + } + + /** + * Get transaction callback. This handles the case of async. + * + * @param EnvelopeInterface $message + * @return void + */ + public function execute(EnvelopeInterface $message) + { + $queue = $this->queue; + /** @var LockInterface $lock */ + $lock = null; + try { + $topicName = $message->getProperties()['topic_name']; + $lock = $this->messageController->lock($message, $this->configuration->getConsumerName()); + + $allowedTopics = $this->configuration->getTopicNames(); + if (in_array($topicName, $allowedTopics)) { + $this->operationProcessor->process($message->getBody()); + } else { + $queue->reject($message); + return; + } + $queue->acknowledge($message); + } catch (MessageLockException $exception) { + $queue->acknowledge($message); + } catch (ConnectionLostException $e) { + if ($lock) { + $this->resource->getConnection() + ->delete($this->resource->getTableName('queue_lock'), ['id = ?' => $lock->getId()]); + } + } catch (NotFoundException $e) { + $queue->acknowledge($message); + $this->logger->warning($e->getMessage()); + } catch (\Exception $e) { + $queue->reject($message, false, $e->getMessage()); + if ($lock) { + $this->resource->getConnection() + ->delete($this->resource->getTableName('queue_lock'), ['id = ?' => $lock->getId()]); + } + } + } + + /** + * Get message queue. + * + * @return QueueInterface + */ + public function getQueue() + { + return $this->queue; + } +} diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/Test/FullCaptureAuthorizenetAcceptjsTest.xml b/app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/Test/FullCaptureAuthorizenetAcceptjsTest.xml index 42a78291436ed..6aa6792e0e0d4 100644 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/Test/FullCaptureAuthorizenetAcceptjsTest.xml +++ b/app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/Test/FullCaptureAuthorizenetAcceptjsTest.xml @@ -54,6 +54,7 @@ + diff --git a/app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/Test/GuestCheckoutVirtualProductAuthorizenetAcceptjsTest.xml b/app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/Test/GuestCheckoutVirtualProductAuthorizenetAcceptjsTest.xml index 95c2436905212..6f71bd180766f 100644 --- a/app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/Test/GuestCheckoutVirtualProductAuthorizenetAcceptjsTest.xml +++ b/app/code/Magento/AuthorizenetAcceptjs/Test/Mftf/Test/GuestCheckoutVirtualProductAuthorizenetAcceptjsTest.xml @@ -55,8 +55,10 @@ + + diff --git a/app/code/Magento/AuthorizenetGraphQl/Model/AuthorizenetDataProvider.php b/app/code/Magento/AuthorizenetGraphQl/Model/AuthorizenetDataProvider.php new file mode 100644 index 0000000000000..1f8baa266f32f --- /dev/null +++ b/app/code/Magento/AuthorizenetGraphQl/Model/AuthorizenetDataProvider.php @@ -0,0 +1,62 @@ +arrayManager = $arrayManager; + } + + /** + * Return additional data + * + * @param array $args + * @return array + */ + public function getData(array $args): array + { + $additionalData = $this->arrayManager->get(static::PATH_ADDITIONAL_DATA, $args) ?? []; + foreach ($additionalData as $key => $value) { + $additionalData[$this->snakeCaseToCamelCase($key)] = $value; + unset($additionalData[$key]); + } + return $additionalData; + } + + /** + * Converts an input string from snake_case to camelCase. + * + * @param string $input + * @return string + */ + private function snakeCaseToCamelCase($input) + { + return lcfirst(str_replace('_', '', ucwords($input, '_'))); + } +} diff --git a/app/code/Magento/AuthorizenetGraphQl/README.md b/app/code/Magento/AuthorizenetGraphQl/README.md new file mode 100644 index 0000000000000..8b920e569341f --- /dev/null +++ b/app/code/Magento/AuthorizenetGraphQl/README.md @@ -0,0 +1,3 @@ +# AuthorizenetGraphQl + + **AuthorizenetGraphQl** defines the data types needed to pass payment information data from the client to Magento. diff --git a/app/code/Magento/AuthorizenetGraphQl/composer.json b/app/code/Magento/AuthorizenetGraphQl/composer.json new file mode 100644 index 0000000000000..6adf11ff72b5a --- /dev/null +++ b/app/code/Magento/AuthorizenetGraphQl/composer.json @@ -0,0 +1,25 @@ +{ + "name": "magento/module-authorizenet-graph-ql", + "description": "N/A", + "type": "magento2-module", + "require": { + "php": "~7.1.3||~7.2.0", + "magento/framework": "*", + "magento/module-quote-graph-ql": "*" + }, + "suggest": { + "magento/module-graph-ql": "*" + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "autoload": { + "files": [ + "registration.php" + ], + "psr-4": { + "Magento\\AuthorizenetGraphQl\\": "" + } + } +} diff --git a/app/code/Magento/AuthorizenetGraphQl/etc/graphql/di.xml b/app/code/Magento/AuthorizenetGraphQl/etc/graphql/di.xml new file mode 100644 index 0000000000000..e8ea45091c044 --- /dev/null +++ b/app/code/Magento/AuthorizenetGraphQl/etc/graphql/di.xml @@ -0,0 +1,16 @@ + + + + + + + Magento\AuthorizenetGraphQl\Model\AuthorizenetDataProvider + + + + diff --git a/app/code/Magento/AuthorizenetGraphQl/etc/module.xml b/app/code/Magento/AuthorizenetGraphQl/etc/module.xml new file mode 100644 index 0000000000000..85a780a881975 --- /dev/null +++ b/app/code/Magento/AuthorizenetGraphQl/etc/module.xml @@ -0,0 +1,10 @@ + + + + + diff --git a/app/code/Magento/AuthorizenetGraphQl/etc/schema.graphqls b/app/code/Magento/AuthorizenetGraphQl/etc/schema.graphqls new file mode 100644 index 0000000000000..1d724bbde3c5d --- /dev/null +++ b/app/code/Magento/AuthorizenetGraphQl/etc/schema.graphqls @@ -0,0 +1,12 @@ +# Copyright © Magento, Inc. All rights reserved. +# See COPYING.txt for license details. + +input PaymentMethodAdditionalDataInput { + authorizenet_acceptjs: AuthorizenetInput @doc(description: "Defines the required attributes for Authorize.Net payments") +} + +input AuthorizenetInput { + opaque_data_descriptor: String! @doc(description: "Authorize.Net's description of the transaction request") + opaque_data_value: String! @doc(description: "The nonce returned by Authorize.Net") + cc_last_4: Int! @doc(description: "The last four digits of the credit or debit card") +} \ No newline at end of file diff --git a/app/code/Magento/AuthorizenetGraphQl/registration.php b/app/code/Magento/AuthorizenetGraphQl/registration.php new file mode 100644 index 0000000000000..2e50f9fe92aaa --- /dev/null +++ b/app/code/Magento/AuthorizenetGraphQl/registration.php @@ -0,0 +1,10 @@ +_menu; } + /** + * Set scope entity + * + * @param mixed $scopeId + * @return \Magento\Framework\UrlInterface + */ + public function setScope($scopeId) + { + parent::setScope($scopeId); + $this->_scope = $this->_scopeResolver->getScope($scopeId); + return $this; + } + /** * Set custom auth session * @@ -402,13 +416,13 @@ public function getAreaFrontName() } /** - * Retrieve action path. - * Add backend area front name as a prefix to action path + * Retrieve action path, add backend area front name as a prefix to action path * * @return string */ protected function _getActionPath() { + $path = parent::_getActionPath(); if ($path) { if ($this->getAreaFrontName()) { @@ -448,8 +462,7 @@ protected function _getConfigCacheId($path) } /** - * Get config data by path - * Use only global config values for backend + * Get config data by path, use only global config values for backend * * @param string $path * @return null|string diff --git a/app/code/Magento/Backend/Test/Mftf/ActionGroup/AssertAdminPageIsNot404ActionGroup.xml b/app/code/Magento/Backend/Test/Mftf/ActionGroup/AssertAdminPageIsNot404ActionGroup.xml new file mode 100644 index 0000000000000..eaf7c7cd8a6ab --- /dev/null +++ b/app/code/Magento/Backend/Test/Mftf/ActionGroup/AssertAdminPageIsNot404ActionGroup.xml @@ -0,0 +1,14 @@ + + + + + + + + diff --git a/app/code/Magento/Backend/Test/Mftf/ActionGroup/AssertMessageInAdminPanelActionGroup.xml b/app/code/Magento/Backend/Test/Mftf/ActionGroup/AssertMessageInAdminPanelActionGroup.xml new file mode 100644 index 0000000000000..23823ea085acd --- /dev/null +++ b/app/code/Magento/Backend/Test/Mftf/ActionGroup/AssertMessageInAdminPanelActionGroup.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + diff --git a/app/code/Magento/Backend/Test/Mftf/Section/AdminHeaderSection.xml b/app/code/Magento/Backend/Test/Mftf/Section/AdminHeaderSection.xml index 5b517c7be8a79..6475d0a3c8166 100644 --- a/app/code/Magento/Backend/Test/Mftf/Section/AdminHeaderSection.xml +++ b/app/code/Magento/Backend/Test/Mftf/Section/AdminHeaderSection.xml @@ -11,5 +11,7 @@
+ +
diff --git a/app/code/Magento/Backend/Test/Mftf/Section/AdminMessagesSection.xml b/app/code/Magento/Backend/Test/Mftf/Section/AdminMessagesSection.xml index 88e740d689cdd..be3ef92acf0ac 100644 --- a/app/code/Magento/Backend/Test/Mftf/Section/AdminMessagesSection.xml +++ b/app/code/Magento/Backend/Test/Mftf/Section/AdminMessagesSection.xml @@ -13,5 +13,6 @@ + diff --git a/app/code/Magento/Backend/Test/Mftf/Section/AdminSlideOutDialogSection.xml b/app/code/Magento/Backend/Test/Mftf/Section/AdminSlideOutDialogSection.xml index a01e025ba3dca..2f799721a8cef 100644 --- a/app/code/Magento/Backend/Test/Mftf/Section/AdminSlideOutDialogSection.xml +++ b/app/code/Magento/Backend/Test/Mftf/Section/AdminSlideOutDialogSection.xml @@ -8,7 +8,7 @@
- + diff --git a/app/code/Magento/Backend/Test/Mftf/Test/AdminDashboardWithChartsChart.xml b/app/code/Magento/Backend/Test/Mftf/Test/AdminDashboardWithChartsChart.xml index 55cb5a71505a5..c3ef66b399481 100644 --- a/app/code/Magento/Backend/Test/Mftf/Test/AdminDashboardWithChartsChart.xml +++ b/app/code/Magento/Backend/Test/Mftf/Test/AdminDashboardWithChartsChart.xml @@ -11,15 +11,16 @@ - - <description value="Google chart on Magento dashboard page is not broken"/> + <stories value="Google Charts on Magento dashboard"/> + <title value="Admin should see Google chart on Magento dashboard"/> + <description value="Google chart on Magento dashboard page is displaying properly"/> <severity value="MAJOR"/> <testCaseId value="MAGETWO-98934"/> <useCaseId value="MAGETWO-98584"/> <group value="backend"/> </annotations> <before> - <magentoCLI command="config:set admin/dashboard/enable_charts 1" stepKey="setEnableCharts" /> + <magentoCLI command="config:set admin/dashboard/enable_charts 1" stepKey="setEnableCharts"/> <createData entity="SimpleProduct2" stepKey="createProduct"> <field key="price">150</field> </createData> @@ -33,7 +34,7 @@ <comment userInput="Reset admin order filter" stepKey="resetAdminOrderFilter"/> <actionGroup ref="AdminOrdersGridClearFiltersActionGroup" stepKey="clearOrderFilters"/> <waitForLoadingMaskToDisappear stepKey="waitForLoadingOrderGrid"/> - <magentoCLI command="config:set admin/dashboard/enable_charts 0" stepKey="setDisableChartsAsDefault" /> + <magentoCLI command="config:set admin/dashboard/enable_charts 0" stepKey="setDisableChartsAsDefault"/> <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> <actionGroup ref="logout" stepKey="logout"/> @@ -47,7 +48,7 @@ <!-- Login as customer --> <comment userInput="Login as customer" stepKey="loginAsCustomer"/> <actionGroup ref="LoginToStorefrontActionGroup" stepKey="customerLogin"> - <argument name="Customer" value="$$createCustomer$$" /> + <argument name="Customer" value="$$createCustomer$$"/> </actionGroup> <!-- Add Product to Shopping Cart--> <comment userInput="Add product to the shopping cart" stepKey="addProductToCart"/> diff --git a/app/code/Magento/Backend/Test/Mftf/Test/AdminLoginAfterJSMinificationTest.xml b/app/code/Magento/Backend/Test/Mftf/Test/AdminLoginAfterJSMinificationTest.xml new file mode 100644 index 0000000000000..38749dfd792ca --- /dev/null +++ b/app/code/Magento/Backend/Test/Mftf/Test/AdminLoginAfterJSMinificationTest.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminLoginAfterJSMinificationTest"> + <annotations> + <features value="Backend"/> + <stories value="Admin Panel JS minification"/> + <title value="Admin panel should be accessible with JS minification enabled"/> + <description value="Admin panel should be accessible with JS minification enabled"/> + <testCaseId value="MC-14104"/> + <severity value="MAJOR"/> + <group value="backend"/> + <group value="mtf_migrated"/> + <skip> + <issueId value="MC-17140"/> + </skip> + </annotations> + <before> + <magentoCLI command="config:set {{MinifyJavaScriptFilesEnableConfigData.path}} {{MinifyJavaScriptFilesEnableConfigData.value}}" stepKey="enableJsMinification"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <magentoCLI command="config:set {{MinifyJavaScriptFilesDisableConfigData.path}} {{MinifyJavaScriptFilesDisableConfigData.value}}" stepKey="disableJsMinification"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <actionGroup ref="AssertAdminSuccessLoginActionGroup" stepKey="loggedInSuccessfully"/> + <actionGroup ref="AssertAdminPageIsNot404ActionGroup" stepKey="dontSee404Page"/> + </test> +</tests> diff --git a/app/code/Magento/Backend/Test/Mftf/Test/AdminUserLoginWithStoreCodeInUrlTest.xml b/app/code/Magento/Backend/Test/Mftf/Test/AdminUserLoginWithStoreCodeInUrlTest.xml index 5485dcaea33ee..df5ca6d037813 100644 --- a/app/code/Magento/Backend/Test/Mftf/Test/AdminUserLoginWithStoreCodeInUrlTest.xml +++ b/app/code/Magento/Backend/Test/Mftf/Test/AdminUserLoginWithStoreCodeInUrlTest.xml @@ -11,9 +11,11 @@ <test name="AdminUserLoginWithStoreCodeInUrlTest"> <annotations> <features value="Backend"/> + <stories value="Admin Panel URL with Store Code"/> <title value="Admin panel should be accessible with Add Store Code to URL setting enabled"/> <description value="Admin panel should be accessible with Add Store Code to URL setting enabled"/> - <testCaseId value="MC-14279" /> + <testCaseId value="MC-14279"/> + <severity value="CRITICAL"/> <group value="backend"/> <group value="mtf_migrated"/> </annotations> diff --git a/app/code/Magento/Backend/Test/Unit/Block/Widget/Grid/Column/Filter/TextTest.php b/app/code/Magento/Backend/Test/Unit/Block/Widget/Grid/Column/Filter/TextTest.php index 1bf23649e2ea8..5aa8cf7a0c579 100644 --- a/app/code/Magento/Backend/Test/Unit/Block/Widget/Grid/Column/Filter/TextTest.php +++ b/app/code/Magento/Backend/Test/Unit/Block/Widget/Grid/Column/Filter/TextTest.php @@ -8,6 +8,9 @@ use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +/** + * Unit test for \Magento\Backend\Block\Widget\Grid\Column\Filter\Text + */ class TextTest extends \PHPUnit\Framework\TestCase { /** @var \Magento\Backend\Block\Widget\Grid\Column\Filter\Text*/ @@ -31,7 +34,10 @@ protected function setUp() ->setMethods(['getEscaper']) ->disableOriginalConstructor() ->getMock(); - $this->escaper = $this->createPartialMock(\Magento\Framework\Escaper::class, ['escapeHtml']); + $this->escaper = $this->createPartialMock( + \Magento\Framework\Escaper::class, + ['escapeHtml', 'escapeHtmlAttr'] + ); $this->helper = $this->createMock(\Magento\Framework\DB\Helper::class); $this->context->expects($this->once())->method('getEscaper')->willReturn($this->escaper); @@ -60,6 +66,13 @@ public function testGetHtml() $this->block->setColumn($column); $this->escaper->expects($this->any())->method('escapeHtml')->willReturn('escapedHtml'); + $this->escaper->expects($this->once()) + ->method('escapeHtmlAttr') + ->willReturnCallback( + function ($string) { + return $string; + } + ); $column->expects($this->any())->method('getId')->willReturn('id'); $column->expects($this->once())->method('getHtmlId')->willReturn('htmlId'); diff --git a/app/code/Magento/Braintree/Block/Paypal/Button.php b/app/code/Magento/Braintree/Block/Paypal/Button.php index efd9e473699c3..fe829cf9f1fdd 100644 --- a/app/code/Magento/Braintree/Block/Paypal/Button.php +++ b/app/code/Magento/Braintree/Block/Paypal/Button.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Braintree\Block\Paypal; use Magento\Braintree\Gateway\Config\PayPal\Config; @@ -49,8 +51,6 @@ class Button extends Template implements ShortcutInterface private $payment; /** - * Constructor - * * @param Context $context * @param ResolverInterface $localeResolver * @param Session $checkoutSession @@ -98,6 +98,8 @@ public function getAlias() } /** + * Returns container id. + * * @return string */ public function getContainerId() @@ -106,6 +108,8 @@ public function getContainerId() } /** + * Returns locale. + * * @return string */ public function getLocale() @@ -114,6 +118,8 @@ public function getLocale() } /** + * Returns currency. + * * @return string */ public function getCurrency() @@ -122,6 +128,8 @@ public function getCurrency() } /** + * Returns amount. + * * @return float */ public function getAmount() @@ -130,6 +138,8 @@ public function getAmount() } /** + * Returns if is active. + * * @return bool */ public function isActive() @@ -139,6 +149,8 @@ public function isActive() } /** + * Returns merchant name. + * * @return string */ public function getMerchantName() @@ -147,6 +159,8 @@ public function getMerchantName() } /** + * Returns client token. + * * @return string|null */ public function getClientToken() @@ -155,10 +169,22 @@ public function getClientToken() } /** + * Returns action success. + * * @return string */ public function getActionSuccess() { return $this->getUrl(ConfigProvider::CODE . '/paypal/review', ['_secure' => true]); } + + /** + * Gets environment value. + * + * @return string + */ + public function getEnvironment(): string + { + return $this->configProvider->getConfig()['payment'][ConfigProvider::CODE]['environment']; + } } diff --git a/app/code/Magento/Braintree/Gateway/Config/Config.php b/app/code/Magento/Braintree/Gateway/Config/Config.php index 2089a9646ae94..905b802061aa6 100644 --- a/app/code/Magento/Braintree/Gateway/Config/Config.php +++ b/app/code/Magento/Braintree/Gateway/Config/Config.php @@ -30,7 +30,6 @@ class Config extends \Magento\Payment\Gateway\Config\Config const KEY_VERIFY_SPECIFIC = 'verify_specific_countries'; const VALUE_3DSECURE_ALL = 0; const CODE_3DSECURE = 'three_d_secure'; - const KEY_KOUNT_MERCHANT_ID = 'kount_id'; const FRAUD_PROTECTION = 'fraudprotection'; /** @@ -173,6 +172,7 @@ public function get3DSecureSpecificCountries($storeId = null) /** * Gets value of configured environment. + * * Possible values: production or sandbox. * * @param int|null $storeId @@ -183,17 +183,6 @@ public function getEnvironment($storeId = null) return $this->getValue(Config::KEY_ENVIRONMENT, $storeId); } - /** - * Gets Kount merchant ID. - * - * @param int|null $storeId - * @return string - */ - public function getKountMerchantId($storeId = null) - { - return $this->getValue(Config::KEY_KOUNT_MERCHANT_ID, $storeId); - } - /** * Gets merchant ID. * @@ -217,6 +206,8 @@ public function getMerchantAccountId($storeId = null) } /** + * Returns SDK url. + * * @return string */ public function getSdkUrl() @@ -224,6 +215,16 @@ public function getSdkUrl() return $this->getValue(Config::KEY_SDK_URL); } + /** + * Gets Hosted Fields SDK Url + * + * @return string + */ + public function getHostedFieldsSdkUrl(): string + { + return $this->getValue('hosted_fields_sdk_url'); + } + /** * Checks if fraud protection is enabled. * diff --git a/app/code/Magento/Braintree/Gateway/Request/VaultThreeDSecureDataBuilder.php b/app/code/Magento/Braintree/Gateway/Request/VaultThreeDSecureDataBuilder.php new file mode 100644 index 0000000000000..5441067b9d813 --- /dev/null +++ b/app/code/Magento/Braintree/Gateway/Request/VaultThreeDSecureDataBuilder.php @@ -0,0 +1,55 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Braintree\Gateway\Request; + +use Magento\Braintree\Gateway\SubjectReader; +use Magento\Payment\Gateway\Request\BuilderInterface; + +/** + * Since we can't validate 3Dsecure for sequence multishipping orders based on vault tokens, + * we skip 3D secure verification for vault transactions. + * For common vault transaction original 3d secure verification builder is called. + */ +class VaultThreeDSecureDataBuilder implements BuilderInterface +{ + /** + * @var ThreeDSecureDataBuilder + */ + private $threeDSecureDataBuilder; + + /** + * @var SubjectReader + */ + private $subjectReader; + + /** + * Constructor + * + * @param ThreeDSecureDataBuilder $threeDSecureDataBuilder + * @param SubjectReader $subjectReader + */ + public function __construct( + ThreeDSecureDataBuilder $threeDSecureDataBuilder, + SubjectReader $subjectReader + ) { + $this->threeDSecureDataBuilder = $threeDSecureDataBuilder; + $this->subjectReader = $subjectReader; + } + + /** + * @inheritdoc + */ + public function build(array $buildSubject) + { + $paymentDO = $this->subjectReader->readPayment($buildSubject); + $payment = $paymentDO->getPayment(); + if ($payment->getAdditionalInformation('is_multishipping')) { + return []; + } + + return $this->threeDSecureDataBuilder->build($buildSubject); + } +} diff --git a/app/code/Magento/Braintree/Model/Multishipping/PlaceOrder.php b/app/code/Magento/Braintree/Model/Multishipping/PlaceOrder.php index a6c1b088400a7..a95d7a922f9bd 100644 --- a/app/code/Magento/Braintree/Model/Multishipping/PlaceOrder.php +++ b/app/code/Magento/Braintree/Model/Multishipping/PlaceOrder.php @@ -8,6 +8,7 @@ namespace Magento\Braintree\Model\Multishipping; use Magento\Braintree\Gateway\Command\GetPaymentNonceCommand; +use Magento\Braintree\Gateway\Config\Config; use Magento\Braintree\Model\Ui\ConfigProvider; use Magento\Braintree\Observer\DataAssignObserver; use Magento\Braintree\Model\Ui\PayPal\ConfigProvider as PaypalConfigProvider; @@ -118,6 +119,10 @@ private function setVaultPayment(OrderPaymentInterface $orderPayment, PaymentTok PaymentTokenInterface::CUSTOMER_ID, $customerId ); + $orderPayment->setAdditionalInformation( + 'is_multishipping', + 1 + ); } /** diff --git a/app/code/Magento/Braintree/Model/Paypal/Helper/QuoteUpdater.php b/app/code/Magento/Braintree/Model/Paypal/Helper/QuoteUpdater.php index ae2b1b1423640..197b398380f74 100644 --- a/app/code/Magento/Braintree/Model/Paypal/Helper/QuoteUpdater.php +++ b/app/code/Magento/Braintree/Model/Paypal/Helper/QuoteUpdater.php @@ -173,14 +173,14 @@ private function updateBillingAddress(Quote $quote, array $details) */ private function updateAddressData(Address $address, array $addressData) { - $extendedAddress = isset($addressData['extendedAddress']) - ? $addressData['extendedAddress'] + $extendedAddress = isset($addressData['line2']) + ? $addressData['line2'] : null; - $address->setStreet([$addressData['streetAddress'], $extendedAddress]); - $address->setCity($addressData['locality']); - $address->setRegionCode($addressData['region']); - $address->setCountryId($addressData['countryCodeAlpha2']); + $address->setStreet([$addressData['line1'], $extendedAddress]); + $address->setCity($addressData['city']); + $address->setRegionCode($addressData['state']); + $address->setCountryId($addressData['countryCode']); $address->setPostcode($addressData['postalCode']); // PayPal's address supposes not saving against customer account diff --git a/app/code/Magento/Braintree/Model/Ui/ConfigProvider.php b/app/code/Magento/Braintree/Model/Ui/ConfigProvider.php index 928769498a035..ab23037b4e98e 100644 --- a/app/code/Magento/Braintree/Model/Ui/ConfigProvider.php +++ b/app/code/Magento/Braintree/Model/Ui/ConfigProvider.php @@ -13,6 +13,8 @@ /** * Class ConfigProvider + * + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) */ class ConfigProvider implements ConfigProviderInterface { @@ -72,11 +74,11 @@ public function getConfig() 'clientToken' => $this->getClientToken(), 'ccTypesMapper' => $this->config->getCcTypesMapper(), 'sdkUrl' => $this->config->getSdkUrl(), + 'hostedFieldsSdkUrl' => $this->config->getHostedFieldsSdkUrl(), 'countrySpecificCardTypes' => $this->config->getCountrySpecificCardTypeConfig($storeId), 'availableCardTypes' => $this->config->getAvailableCardTypes($storeId), 'useCvv' => $this->config->isCvvEnabled($storeId), 'environment' => $this->config->getEnvironment($storeId), - 'kountMerchantId' => $this->config->getKountMerchantId($storeId), 'hasFraudProtection' => $this->config->hasFraudProtection($storeId), 'merchantId' => $this->config->getMerchantId($storeId), 'ccVaultCode' => self::CC_VAULT_CODE, @@ -92,6 +94,7 @@ public function getConfig() /** * Generate a new client token if necessary + * * @return string */ public function getClientToken() diff --git a/app/code/Magento/Braintree/Test/Mftf/Test/BraintreeCreditCardOnCheckoutTest.xml b/app/code/Magento/Braintree/Test/Mftf/Test/BraintreeCreditCardOnCheckoutTest.xml index a781841e0a77b..ac653638b53a0 100644 --- a/app/code/Magento/Braintree/Test/Mftf/Test/BraintreeCreditCardOnCheckoutTest.xml +++ b/app/code/Magento/Braintree/Test/Mftf/Test/BraintreeCreditCardOnCheckoutTest.xml @@ -49,6 +49,7 @@ <waitForPageLoad stepKey="waitForPageLoad"/> <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addProductToCart"/> <waitForPageLoad stepKey="waitForPageLoad1"/> + <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessage"/> <!--Proceed to checkout--> <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="GoToCheckoutFromMinicartActionGroup"/> @@ -74,6 +75,7 @@ <waitForPageLoad stepKey="waitForPageLoad6"/> <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addProductToCart1"/> <waitForPageLoad stepKey="waitForPageLoad7"/> + <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessage2"/> <!--Proceed to checkout--> <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="GoToCheckoutFromMinicartActionGroup1"/> <click selector="{{CheckoutPaymentSection.addressAction('New Address')}}" stepKey="clickOnNewAddress"/> diff --git a/app/code/Magento/Braintree/Test/Unit/Model/Paypal/Helper/QuoteUpdaterTest.php b/app/code/Magento/Braintree/Test/Unit/Model/Paypal/Helper/QuoteUpdaterTest.php index c2678d1c78437..ec716732b114e 100644 --- a/app/code/Magento/Braintree/Test/Unit/Model/Paypal/Helper/QuoteUpdaterTest.php +++ b/app/code/Magento/Braintree/Test/Unit/Model/Paypal/Helper/QuoteUpdaterTest.php @@ -159,21 +159,21 @@ private function getDetails(): array 'phone' => '312-123-4567', 'countryCode' => 'US', 'shippingAddress' => [ - 'streetAddress' => '123 Division Street', - 'extendedAddress' => 'Apt. #1', - 'locality' => 'Chicago', - 'region' => 'IL', + 'line1' => '123 Division Street', + 'line2' => 'Apt. #1', + 'city' => 'Chicago', + 'state' => 'IL', 'postalCode' => '60618', - 'countryCodeAlpha2' => 'US', + 'countryCode' => 'US', 'recipientName' => 'Jane Smith', ], 'billingAddress' => [ - 'streetAddress' => '123 Billing Street', - 'extendedAddress' => 'Apt. #1', - 'locality' => 'Chicago', - 'region' => 'IL', + 'line1' => '123 Billing Street', + 'line2' => 'Apt. #1', + 'city' => 'Chicago', + 'state' => 'IL', 'postalCode' => '60618', - 'countryCodeAlpha2' => 'US', + 'countryCode' => 'US', ], ]; } @@ -206,13 +206,13 @@ private function updateShippingAddressStep(array $details): void private function updateAddressDataStep(MockObject $address, array $addressData): void { $address->method('setStreet') - ->with([$addressData['streetAddress'], $addressData['extendedAddress']]); + ->with([$addressData['line1'], $addressData['line2']]); $address->method('setCity') - ->with($addressData['locality']); + ->with($addressData['city']); $address->method('setRegionCode') - ->with($addressData['region']); + ->with($addressData['state']); $address->method('setCountryId') - ->with($addressData['countryCodeAlpha2']); + ->with($addressData['countryCode']); $address->method('setPostcode') ->with($addressData['postalCode']); } diff --git a/app/code/Magento/Braintree/Test/Unit/Model/Ui/ConfigProviderTest.php b/app/code/Magento/Braintree/Test/Unit/Model/Ui/ConfigProviderTest.php index 24bc4eae960be..55bc2cb195d6e 100644 --- a/app/code/Magento/Braintree/Test/Unit/Model/Ui/ConfigProviderTest.php +++ b/app/code/Magento/Braintree/Test/Unit/Model/Ui/ConfigProviderTest.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Braintree\Test\Unit\Model\Ui; use Magento\Braintree\Gateway\Config\Config; @@ -124,6 +126,7 @@ public function getConfigDataProvider() 'isActive' => true, 'getCcTypesMapper' => ['visa' => 'VI', 'american-express'=> 'AE'], 'getSdkUrl' => self::SDK_URL, + 'getHostedFieldsSdkUrl' => 'https://sdk.com/test.js', 'getCountrySpecificCardTypeConfig' => [ 'GB' => ['VI', 'AE'], 'US' => ['DI', 'JCB'] @@ -134,7 +137,6 @@ public function getConfigDataProvider() 'getThresholdAmount' => 20, 'get3DSecureSpecificCountries' => ['GB', 'US', 'CA'], 'getEnvironment' => 'test-environment', - 'getKountMerchantId' => 'test-kount-merchant-id', 'getMerchantId' => 'test-merchant-id', 'hasFraudProtection' => true, ], @@ -145,6 +147,7 @@ public function getConfigDataProvider() 'clientToken' => self::CLIENT_TOKEN, 'ccTypesMapper' => ['visa' => 'VI', 'american-express' => 'AE'], 'sdkUrl' => self::SDK_URL, + 'hostedFieldsSdkUrl' => 'https://sdk.com/test.js', 'countrySpecificCardTypes' =>[ 'GB' => ['VI', 'AE'], 'US' => ['DI', 'JCB'] @@ -152,7 +155,6 @@ public function getConfigDataProvider() 'availableCardTypes' => ['AE', 'VI', 'MC', 'DI', 'JCB'], 'useCvv' => true, 'environment' => 'test-environment', - 'kountMerchantId' => 'test-kount-merchant-id', 'merchantId' => 'test-merchant-id', 'hasFraudProtection' => true, 'ccVaultCode' => ConfigProvider::CC_VAULT_CODE diff --git a/app/code/Magento/Braintree/etc/adminhtml/system.xml b/app/code/Magento/Braintree/etc/adminhtml/system.xml index 67c47f8ea9dc3..bd4346e095c6d 100644 --- a/app/code/Magento/Braintree/etc/adminhtml/system.xml +++ b/app/code/Magento/Braintree/etc/adminhtml/system.xml @@ -95,14 +95,6 @@ <comment>Be sure to Enable Advanced Fraud Protection in Your Braintree Account in Settings/Processing Section</comment> <config_path>payment/braintree/fraudprotection</config_path> </field> - <field id="kount_id" translate="label comment" sortOrder="35" showInDefault="1" showInWebsite="1" showInStore="0"> - <label>Kount Merchant ID</label> - <comment><![CDATA[Used for direct fraud tool integration. Make sure you also contact <a href="mailto:accounts@braintreepayments.com">accounts@braintreepayments.com</a> to setup your Kount account.]]></comment> - <depends> - <field id="fraudprotection">1</field> - </depends> - <config_path>payment/braintree/kount_id</config_path> - </field> <field id="debug" translate="label" type="select" sortOrder="40" showInDefault="1" showInWebsite="1" showInStore="0"> <label>Debug</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> diff --git a/app/code/Magento/Braintree/etc/config.xml b/app/code/Magento/Braintree/etc/config.xml index fe4cfab9c0e30..522d32302168e 100644 --- a/app/code/Magento/Braintree/etc/config.xml +++ b/app/code/Magento/Braintree/etc/config.xml @@ -34,7 +34,8 @@ <order_status>processing</order_status> <environment>sandbox</environment> <allowspecific>0</allowspecific> - <sdk_url><![CDATA[https://js.braintreegateway.com/js/braintree-2.32.0.min.js]]></sdk_url> + <sdk_url><![CDATA[https://js.braintreegateway.com/web/3.44.1/js/client.min.js]]></sdk_url> + <hosted_fields_sdk_url><![CDATA[https://js.braintreegateway.com/web/3.44.1/js/hosted-fields.min.js]]></hosted_fields_sdk_url> <public_key backend_model="Magento\Config\Model\Config\Backend\Encrypted" /> <private_key backend_model="Magento\Config\Model\Config\Backend\Encrypted" /> <masked_fields>cvv,number</masked_fields> diff --git a/app/code/Magento/Braintree/etc/di.xml b/app/code/Magento/Braintree/etc/di.xml index b81513caf17a2..6f8b7d1d6c368 100644 --- a/app/code/Magento/Braintree/etc/di.xml +++ b/app/code/Magento/Braintree/etc/di.xml @@ -288,7 +288,7 @@ <item name="payment" xsi:type="string">Magento\Braintree\Gateway\Request\PaymentDataBuilder</item> <item name="channel" xsi:type="string">Magento\Braintree\Gateway\Request\ChannelDataBuilder</item> <item name="address" xsi:type="string">Magento\Braintree\Gateway\Request\AddressDataBuilder</item> - <item name="3dsecure" xsi:type="string">Magento\Braintree\Gateway\Request\ThreeDSecureDataBuilder</item> + <item name="3dsecure" xsi:type="string">Magento\Braintree\Gateway\Request\VaultThreeDSecureDataBuilder</item> <item name="device_data" xsi:type="string">Magento\Braintree\Gateway\Request\KountPaymentDataBuilder</item> <item name="dynamic_descriptor" xsi:type="string">Magento\Braintree\Gateway\Request\DescriptorDataBuilder</item> <item name="store" xsi:type="string">Magento\Braintree\Gateway\Request\StoreConfigBuilder</item> @@ -614,7 +614,6 @@ <item name="payment/braintree/merchant_id" xsi:type="string">1</item> <item name="payment/braintree/private_key" xsi:type="string">1</item> <item name="payment/braintree/merchant_account_id" xsi:type="string">1</item> - <item name="payment/braintree/kount_id" xsi:type="string">1</item> <item name="payment/braintree_paypal/merchant_name_override" xsi:type="string">1</item> <item name="payment/braintree/descriptor_phone" xsi:type="string">1</item> <item name="payment/braintree/descriptor_url" xsi:type="string">1</item> diff --git a/app/code/Magento/Braintree/view/adminhtml/web/js/braintree.js b/app/code/Magento/Braintree/view/adminhtml/web/js/braintree.js index ab01565d7f1e5..0359c16283d50 100644 --- a/app/code/Magento/Braintree/view/adminhtml/web/js/braintree.js +++ b/app/code/Magento/Braintree/view/adminhtml/web/js/braintree.js @@ -22,9 +22,16 @@ define([ container: 'payment_form_braintree', active: false, scriptLoaded: false, - braintree: null, + braintreeClient: null, + braintreeHostedFields: null, + hostedFieldsInstance: null, selectedCardType: null, - checkout: null, + selectorsMapper: { + 'expirationMonth': 'expirationMonth', + 'expirationYear': 'expirationYear', + 'number': 'cc_number', + 'cvv': 'cc_cid' + }, imports: { onActiveChange: 'active' } @@ -108,9 +115,10 @@ define([ state = self.scriptLoaded; $('body').trigger('processStart'); - require([this.sdkUrl], function (braintree) { + require([this.sdkUrl, this.hostedFieldsSdkUrl], function (client, hostedFields) { state(true); - self.braintree = braintree; + self.braintreeClient = client; + self.braintreeHostedFields = hostedFields; self.initBraintree(); $('body').trigger('processStop'); }); @@ -146,46 +154,26 @@ define([ _initBraintree: function () { var self = this; - this.disableEventListeners(); - - if (self.checkout) { - self.checkout.teardown(function () { - self.checkout = null; - }); - } - - self.braintree.setup(self.clientToken, 'custom', { - id: self.selector, - hostedFields: self.getHostedFields(), - - /** - * Triggered when sdk was loaded - */ - onReady: function (checkout) { - self.checkout = checkout; - $('body').trigger('processStop'); + self.disableEventListeners(); + + self.braintreeClient.create({ + authorization: self.clientToken + }) + .then(function (clientInstance) { + return self.braintreeHostedFields.create({ + client: clientInstance, + fields: self.getHostedFields() + }); + }) + .then(function (hostedFieldsInstance) { + self.hostedFieldsInstance = hostedFieldsInstance; self.enableEventListeners(); - }, - - /** - * Callback for success response - * @param {Object} response - */ - onPaymentMethodReceived: function (response) { - if (self.validateCardType()) { - self.setPaymentDetails(response.nonce); - self.placeOrder(); - } - }, - - /** - * Error callback - * @param {Object} response - */ - onError: function (response) { - self.error(response.message); - } - }); + self.fieldEventHandler(hostedFieldsInstance); + $('body').trigger('processStop'); + }) + .catch(function () { + self.error($t('Braintree can\'t be initialized.')); + }); }, /** @@ -205,14 +193,6 @@ define([ expirationYear: { selector: self.getSelector('cc_exp_year'), placeholder: $t('YY') - }, - - /** - * Triggered when hosted field is changed - * @param {Object} event - */ - onFieldEvent: function (event) { - return self.fieldEventHandler(event); } }; @@ -227,36 +207,49 @@ define([ /** * Function to handle hosted fields events - * @param {Object} event - * @returns {Boolean} + * @param {Object} hostedFieldsInstance */ - fieldEventHandler: function (event) { + fieldEventHandler: function (hostedFieldsInstance) { var self = this, $cardType = $('#' + self.container).find('.icon-type'); - if (event.isEmpty === false) { - self.validateCardType(); - } + hostedFieldsInstance.on('empty', function (event) { + if (event.emittedBy === 'number') { + $cardType.attr('class', 'icon-type'); + self.selectedCardType(null); + } - if (event.type !== 'fieldStateChange') { + }); - return false; - } + hostedFieldsInstance.on('validityChange', function (event) { + var field = event.fields[event.emittedBy], + fieldKey = event.emittedBy; - // Handle a change in validation or card type - if (event.target.fieldKey === 'number') { - self.selectedCardType(null); - } + if (fieldKey === 'number') { + $cardType.addClass('icon-type-' + event.cards[0].type); + } + + if (fieldKey in self.selectorsMapper && field.isValid === false) { + self.addInvalidClass(self.selectorsMapper[fieldKey]); + } + }); - // remove previously set classes - $cardType.attr('class', 'icon-type'); + hostedFieldsInstance.on('blur', function (event) { + if (event.emittedBy === 'number') { + self.validateCardType(); + } + }); + + hostedFieldsInstance.on('cardTypeChange', function (event) { + if (event.cards.length !== 1) { + return; + } - if (event.card) { - $cardType.addClass('icon-type-' + event.card.type); + $cardType.addClass('icon-type-' + event.cards[0].type); self.selectedCardType( - validator.getMageCardType(event.card.type, self.getCcAvailableTypes()) + validator.getMageCardType(event.cards[0].type, self.getCcAvailableTypes()) ); - } + }); }, /** @@ -298,16 +291,31 @@ define([ * Trigger order submit */ submitOrder: function () { - this.$selector.validate().form(); - this.$selector.trigger('afterValidate.beforeSubmit'); + var self = this; + + self.$selector.validate().form(); + self.$selector.trigger('afterValidate.beforeSubmit'); $('body').trigger('processStop'); // validate parent form - if (this.$selector.validate().errorList.length) { + if (self.$selector.validate().errorList.length) { + return false; + } + + if (!self.validateCardType()) { return false; } - $('#' + this.container).find('[type="submit"]').trigger('click'); + self.hostedFieldsInstance.tokenize(function (err, payload) { + if (err) { + self.error($t('Some payment input fields are invalid.')); + + return false; + } + + self.setPaymentDetails(payload.nonce); + $('#' + self.container).find('[type="submit"]').trigger('click'); + }); }, /** @@ -337,12 +345,10 @@ define([ * @returns {Boolean} */ validateCardType: function () { - var $input = $(this.getSelector('cc_number')); - - $input.removeClass('braintree-hosted-fields-invalid'); + this.removeInvalidClass('cc_number'); if (!this.selectedCardType()) { - $input.addClass('braintree-hosted-fields-invalid'); + this.addInvalidClass('cc_number'); return false; } @@ -358,6 +364,28 @@ define([ */ getSelector: function (field) { return '#' + this.code + '_' + field; + }, + + /** + * Add invalid class to field. + * + * @param {String} field + * @returns void + * @private + */ + addInvalidClass: function (field) { + $(this.getSelector(field)).addClass('braintree-hosted-fields-invalid'); + }, + + /** + * Remove invalid class from field. + * + * @param {String} field + * @returns void + * @private + */ + removeInvalidClass: function (field) { + $(this.getSelector(field)).removeClass('braintree-hosted-fields-invalid'); } }); }); diff --git a/app/code/Magento/Braintree/view/frontend/requirejs-config.js b/app/code/Magento/Braintree/view/frontend/requirejs-config.js index 9fc38064677ef..e2f5fb03e58bf 100644 --- a/app/code/Magento/Braintree/view/frontend/requirejs-config.js +++ b/app/code/Magento/Braintree/view/frontend/requirejs-config.js @@ -6,7 +6,19 @@ var config = { map: { '*': { - braintree: 'https://js.braintreegateway.com/js/braintree-2.32.0.min.js' + braintreeClient: 'https://js.braintreegateway.com/web/3.44.1/js/client.min.js', + braintreeHostedFields: 'https://js.braintreegateway.com/web/3.44.1/js/hosted-fields.min.js', + braintreePayPal: 'https://js.braintreegateway.com/web/3.44.1/js/paypal-checkout.min.js', + braintree3DSecure: 'https://js.braintreegateway.com/web/3.44.1/js/three-d-secure.min.js', + braintreeDataCollector: 'https://js.braintreegateway.com/web/3.44.1/js/data-collector.min.js' + } + }, + paths: { + braintreePayPalCheckout: 'https://www.paypalobjects.com/api/checkout.min' + }, + shim: { + braintreePayPalCheckout: { + exports: 'paypal' } } }; diff --git a/app/code/Magento/Braintree/view/frontend/templates/multishipping/form.phtml b/app/code/Magento/Braintree/view/frontend/templates/multishipping/form.phtml index bf8aa8dd09c2c..fc3030b6a4b36 100644 --- a/app/code/Magento/Braintree/view/frontend/templates/multishipping/form.phtml +++ b/app/code/Magento/Braintree/view/frontend/templates/multishipping/form.phtml @@ -15,7 +15,7 @@ }; layout([ { - component: 'Magento_Braintree/js/view/payment/method-renderer/multishipping/hosted-fields', + component: 'Magento_Braintree/js/view/payment/method-renderer/multishipping/cc-form', name: 'payment_method_braintree', method: paymentMethodData.method, item: paymentMethodData diff --git a/app/code/Magento/Braintree/view/frontend/templates/paypal/button.phtml b/app/code/Magento/Braintree/view/frontend/templates/paypal/button.phtml index c1ef461ecae7c..e0a9e46bd7c5c 100644 --- a/app/code/Magento/Braintree/view/frontend/templates/paypal/button.phtml +++ b/app/code/Magento/Braintree/view/frontend/templates/paypal/button.phtml @@ -15,21 +15,18 @@ $config = [ 'id' => $id, 'clientToken' => $block->getClientToken(), 'displayName' => $block->getMerchantName(), - 'actionSuccess' => $block->getActionSuccess() + 'actionSuccess' => $block->getActionSuccess(), + 'environment' => $block->getEnvironment() ] ]; ?> -<div data-mage-init='<?= /* @noEscape */ json_encode($config) ?>' - class="paypal checkout paypal-logo braintree-paypal-logo<?= /* @noEscape */ $block->getContainerId() ?>-container"> - <button data-currency="<?= /* @noEscape */ $block->getCurrency() ?>" - data-locale="<?= /* @noEscape */ $block->getLocale() ?>" - data-amount="<?= /* @noEscape */ $block->getAmount() ?>" - id="<?= /* @noEscape */ $id ?>" - class="action-braintree-paypal-logo" disabled> - <img class="braintree-paypal-button-hidden" - src="https://checkout.paypal.com/pwpp/2.17.6/images/pay-with-paypal.png" - alt="<?= $block->escapeHtml(__('Pay with PayPal')) ?>" - title="<?= $block->escapeHtml(__('Pay with PayPal')) ?>"/> - </button> +<div data-mage-init='<?= /* @noEscape */ json_encode($config); ?>' + class="paypal checkout paypal-logo braintree-paypal-logo<?= /* @noEscape */ $block->getContainerId(); ?>-container"> + <div data-currency="<?= /* @noEscape */ $block->getCurrency(); ?>" + data-locale="<?= /* @noEscape */ $block->getLocale(); ?>" + data-amount="<?= /* @noEscape */ $block->getAmount(); ?>" + id="<?= /* @noEscape */ $id; ?>" + class="action-braintree-paypal-logo"> + </div> </div> diff --git a/app/code/Magento/Braintree/view/frontend/web/js/paypal/button.js b/app/code/Magento/Braintree/view/frontend/web/js/paypal/button.js index 3ac50fbcb47cc..aacd3016d7367 100644 --- a/app/code/Magento/Braintree/view/frontend/web/js/paypal/button.js +++ b/app/code/Magento/Braintree/view/frontend/web/js/paypal/button.js @@ -9,7 +9,9 @@ define( 'uiComponent', 'underscore', 'jquery', - 'braintree', + 'braintreeClient', + 'braintreePayPal', + 'braintreePayPalCheckout', 'Magento_Braintree/js/paypal/form-builder', 'domReady!' ], @@ -19,7 +21,9 @@ define( Component, _, $, - braintree, + braintreeClient, + braintreePayPal, + braintreePayPalCheckout, formBuilder ) { 'use strict'; @@ -27,58 +31,34 @@ define( return Component.extend({ defaults: { - - integrationName: 'braintreePaypal.currentIntegration', - - /** - * {String} - */ displayName: null, - - /** - * {String} - */ clientToken: null, - - /** - * {Object} - */ - clientConfig: { - - /** - * @param {Object} integration - */ - onReady: function (integration) { - resolver(function () { - registry.set(this.integrationName, integration); - $('#' + this.id).removeAttr('disabled'); - }, this); - }, - - /** - * @param {Object} payload - */ - onPaymentMethodReceived: function (payload) { - $('body').trigger('processStart'); - - formBuilder.build( - { - action: this.actionSuccess, - fields: { - result: JSON.stringify(payload) - } - } - ).submit(); - } - } + paypalCheckoutInstance: null }, /** * @returns {Object} */ initialize: function () { - this._super() - .initComponent(); + var self = this; + + self._super(); + + braintreeClient.create({ + authorization: self.clientToken + }) + .then(function (clientInstance) { + return braintreePayPal.create({ + client: clientInstance + }); + }) + .then(function (paypalCheckoutInstance) { + self.paypalCheckoutInstance = paypalCheckoutInstance; + + return self.paypalCheckoutInstance; + }); + + self.initComponent(); return this; }, @@ -87,64 +67,76 @@ define( * @returns {Object} */ initComponent: function () { - var currentIntegration = registry.get(this.integrationName), - $this = $('#' + this.id), - self = this, + var self = this, + selector = '#' + self.id, + $this = $(selector), data = { amount: $this.data('amount'), locale: $this.data('locale'), currency: $this.data('currency') + }; + + $this.html(''); + braintreePayPalCheckout.Button.render({ + env: self.environment, + style: { + color: 'blue', + shape: 'rect', + size: 'medium', + label: 'pay', + tagline: false + }, + + /** + * Payment setup + */ + payment: function () { + return self.paypalCheckoutInstance.createPayment(self.getClientConfig(data)); }, - initCallback = function () { - $this.attr('disabled', 'disabled'); - registry.remove(this.integrationName); - braintree.setup(this.clientToken, 'custom', this.getClientConfig(data)); - - $this.off('click') - .on('click', function (event) { - event.preventDefault(); - - registry.get(self.integrationName, function (integration) { - try { - integration.paypal.initAuthFlow(); - } catch (e) { - $this.attr('disabled', 'disabled'); + + /** + * Triggers on `onAuthorize` event + * + * @param {Object} response + */ + onAuthorize: function (response) { + return self.paypalCheckoutInstance.tokenizePayment(response) + .then(function (payload) { + $('body').trigger('processStart'); + + formBuilder.build( + { + action: self.actionSuccess, + fields: { + result: JSON.stringify(payload) + } } - }); + ).submit(); }); - }.bind(this); - - currentIntegration ? - currentIntegration.teardown(initCallback) : - initCallback(); + } + }, selector); return this; }, /** * @returns {Object} + * @private */ getClientConfig: function (data) { - this.clientConfig.paypal = { - singleUse: true, + var config = { + flow: 'checkout', amount: data.amount, currency: data.currency, locale: data.locale, - enableShippingAddress: true, - headless: true + enableShippingAddress: true }; if (this.displayName) { - this.clientConfig.paypal.displayName = this.displayName; + config.displayName = this.displayName; } - _.each(this.clientConfig, function (fn, name) { - if (typeof fn === 'function') { - this.clientConfig[name] = fn.bind(this); - } - }, this); - - return this.clientConfig; + return config; } }); } diff --git a/app/code/Magento/Braintree/view/frontend/web/js/view/payment/3d-secure.js b/app/code/Magento/Braintree/view/frontend/web/js/view/payment/3d-secure.js index e3b806bf21384..84fa8cf62720f 100644 --- a/app/code/Magento/Braintree/view/frontend/web/js/view/payment/3d-secure.js +++ b/app/code/Magento/Braintree/view/frontend/web/js/view/payment/3d-secure.js @@ -7,17 +7,57 @@ define([ 'jquery', + 'braintree3DSecure', 'Magento_Braintree/js/view/payment/adapter', 'Magento_Checkout/js/model/quote', - 'mage/translate' -], function ($, braintree, quote, $t) { + 'mage/translate', + 'Magento_Ui/js/modal/modal', + 'Magento_Checkout/js/model/full-screen-loader' +], function ( + $, + braintree3DSecure, + braintreeAdapter, + quote, + $t, + Modal, + fullScreenLoader +) { 'use strict'; return { config: null, + modal: null, + threeDSecureInstance: null, + state: null, /** - * Set 3d secure config + * Initializes component + */ + initialize: function () { + var self = this, + promise = $.Deferred(); + + self.state = $.Deferred(); + braintreeAdapter.getApiClient() + .then(function (clientInstance) { + return braintree3DSecure.create({ + client: clientInstance + }); + }) + .then(function (threeDSecureInstance) { + self.threeDSecureInstance = threeDSecureInstance; + promise.resolve(self.threeDSecureInstance); + }) + .catch(function (err) { + promise.reject(err); + }); + + return promise.promise(); + }, + + /** + * Sets 3D Secure config + * * @param {Object} config */ setConfig: function (config) { @@ -26,7 +66,8 @@ define([ }, /** - * Get code + * Gets code + * * @returns {String} */ getCode: function () { @@ -34,54 +75,114 @@ define([ }, /** - * Validate Braintree payment nonce + * Validates 3D Secure + * * @param {Object} context * @returns {Object} */ validate: function (context) { - var client = braintree.getApiClient(), - state = $.Deferred(), + var self = this, totalAmount = quote.totals()['base_grand_total'], - billingAddress = quote.billingAddress(); + billingAddress = quote.billingAddress(), + options = { + amount: totalAmount, + nonce: context.paymentPayload.nonce, + + /** + * Adds iframe to page + * @param {Object} err + * @param {Object} iframe + */ + addFrame: function (err, iframe) { + self.createModal($(iframe)); + fullScreenLoader.stopLoader(); + self.modal.openModal(); + }, + + /** + * Removes iframe from page + */ + removeFrame: function () { + self.modal.closeModal(); + } + }; if (!this.isAmountAvailable(totalAmount) || !this.isCountryAvailable(billingAddress.countryId)) { - state.resolve(); + self.state.resolve(); - return state.promise(); + return self.state.promise(); } - client.verify3DS({ - amount: totalAmount, - creditCard: context.paymentMethodNonce - }, function (error, response) { - var liability; + fullScreenLoader.startLoader(); + this.initialize() + .then(function () { + self.threeDSecureInstance.verifyCard(options, function (err, payload) { + if (err) { + self.state.reject(err.message); + + return; + } + + // `liabilityShifted` indicates that 3DS worked and authentication succeeded + // if `liabilityShifted` and `liabilityShiftPossible` are false - card is ineligible for 3DS + if (payload.liabilityShifted || !payload.liabilityShifted && !payload.liabilityShiftPossible) { + context.paymentPayload.nonce = payload.nonce; + self.state.resolve(); + } else { + self.state.reject($t('Please try again with another form of payment.')); + } + }); + }) + .fail(function () { + fullScreenLoader.stopLoader(); + self.state.reject($t('Please try again with another form of payment.')); + }); + + return self.state.promise(); + }, - if (error) { - state.reject(error.message); + /** + * Creates modal window + * + * @param {Object} $context + * @private + */ + createModal: function ($context) { + var self = this, + options = { + clickableOverlay: false, + buttons: [], + modalCloseBtnHandler: self.cancelFlow.bind(self), + keyEventHandlers: { + escapeKey: self.cancelFlow.bind(self) + } + }; - return; - } + // adjust iframe styles + $context.attr('width', '100%'); + self.modal = Modal(options, $context); + }, - liability = { - shifted: response.verificationDetails.liabilityShifted, - shiftPossible: response.verificationDetails.liabilityShiftPossible - }; + /** + * Cancels 3D Secure flow + * + * @private + */ + cancelFlow: function () { + var self = this; - if (liability.shifted || !liability.shifted && !liability.shiftPossible) { - context.paymentMethodNonce = response.nonce; - state.resolve(); - } else { - state.reject($t('Please try again with another form of payment.')); - } + self.threeDSecureInstance.cancelVerifyCard(function () { + self.modal.closeModal(); + self.state.reject(); }); - - return state.promise(); }, /** - * Check minimal amount for 3d secure activation + * Checks minimal amount for 3D Secure activation + * * @param {Number} amount * @returns {Boolean} + * @private */ isAmountAvailable: function (amount) { amount = parseFloat(amount); @@ -90,9 +191,11 @@ define([ }, /** - * Check if current country is available for 3d secure + * Checks if current country is available for 3D Secure + * * @param {String} countryId * @returns {Boolean} + * @private */ isCountryAvailable: function (countryId) { var key, diff --git a/app/code/Magento/Braintree/view/frontend/web/js/view/payment/adapter.js b/app/code/Magento/Braintree/view/frontend/web/js/view/payment/adapter.js index 185e347bc9fd1..9cd6aa688674e 100644 --- a/app/code/Magento/Braintree/view/frontend/web/js/view/payment/adapter.js +++ b/app/code/Magento/Braintree/view/frontend/web/js/view/payment/adapter.js @@ -6,81 +6,42 @@ /*global define*/ define([ 'jquery', - 'braintree', - 'Magento_Ui/js/model/messageList', - 'mage/translate' -], function ($, braintree, globalMessageList, $t) { + 'braintreeClient' +], function ($, braintreeClient) { 'use strict'; return { apiClient: null, - config: {}, checkout: null, + code: 'braintree', /** - * Get Braintree api client + * Returns Braintree API client * @returns {Object} */ getApiClient: function () { - if (!this.apiClient) { - this.apiClient = new braintree.api.Client({ - clientToken: this.getClientToken() - }); - } - - return this.apiClient; - }, - - /** - * Set configuration - * @param {Object} config - */ - setConfig: function (config) { - this.config = config; - }, - - /** - * Setup Braintree SDK - */ - setup: function () { - if (!this.getClientToken()) { - this.showError($t('Sorry, but something went wrong.')); - } - - braintree.setup(this.getClientToken(), 'custom', this.config); + return braintreeClient.create({ + authorization: this.getClientToken() + }); }, /** - * Get payment name + * Returns payment code + * * @returns {String} */ getCode: function () { - return 'braintree'; + return this.code; }, /** - * Get client token - * @returns {String|*} - */ - getClientToken: function () { - - return window.checkoutConfig.payment[this.getCode()].clientToken; - }, - - /** - * Show error message + * Returns client token * - * @param {String} errorMessage - */ - showError: function (errorMessage) { - globalMessageList.addErrorMessage({ - message: errorMessage - }); - }, - - /** - * May be triggered on Braintree SDK setup + * @returns {String} + * @private */ - onReady: function () {} + getClientToken: function () { + return window.checkoutConfig.payment[this.code].clientToken; + } }; }); diff --git a/app/code/Magento/Braintree/view/frontend/web/js/view/payment/braintree.js b/app/code/Magento/Braintree/view/frontend/web/js/view/payment/braintree.js index 2e1c65632e85f..132fcd6b3b06c 100644 --- a/app/code/Magento/Braintree/view/frontend/web/js/view/payment/braintree.js +++ b/app/code/Magento/Braintree/view/frontend/web/js/view/payment/braintree.js @@ -23,7 +23,7 @@ define( rendererList.push( { type: braintreeType, - component: 'Magento_Braintree/js/view/payment/method-renderer/hosted-fields' + component: 'Magento_Braintree/js/view/payment/method-renderer/cc-form' } ); } diff --git a/app/code/Magento/Braintree/view/frontend/web/js/view/payment/kount.js b/app/code/Magento/Braintree/view/frontend/web/js/view/payment/kount.js new file mode 100644 index 0000000000000..cd0d024387b8c --- /dev/null +++ b/app/code/Magento/Braintree/view/frontend/web/js/view/payment/kount.js @@ -0,0 +1,61 @@ +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +/*browser:true*/ +/*global define*/ + +define([ + 'jquery', + 'braintreeDataCollector', + 'Magento_Braintree/js/view/payment/adapter' +], function ( + $, + braintreeDataCollector, + braintreeAdapter +) { + 'use strict'; + + return { + paymentCode: 'braintree', + + /** + * Returns information about a customer's device on checkout page for passing to Kount for review. + * + * @returns {Object} + */ + getDeviceData: function () { + var state = $.Deferred(); + + if (this.hasFraudProtection()) { + braintreeAdapter.getApiClient() + .then(function (clientInstance) { + return braintreeDataCollector.create({ + client: clientInstance, + kount: true + }); + }) + .then(function (dataCollectorInstance) { + var deviceData = dataCollectorInstance.deviceData; + + state.resolve(deviceData); + }) + .catch(function (err) { + state.reject(err); + }); + } + + return state.promise(); + }, + + /** + * Returns setting value. + * + * @returns {Boolean} + * @private + */ + hasFraudProtection: function () { + return window.checkoutConfig.payment[this.paymentCode].hasFraudProtection; + } + }; +}); diff --git a/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/cc-form.js b/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/cc-form.js index 39bdf582c8cd7..ac97e4fa5eb58 100644 --- a/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/cc-form.js +++ b/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/cc-form.js @@ -9,90 +9,97 @@ define( 'underscore', 'jquery', 'Magento_Payment/js/view/payment/cc-form', - 'Magento_Checkout/js/model/quote', 'Magento_Braintree/js/view/payment/adapter', - 'mage/translate', + 'braintreeHostedFields', + 'Magento_Checkout/js/model/quote', 'Magento_Braintree/js/validator', + 'Magento_Ui/js/model/messageList', 'Magento_Braintree/js/view/payment/validator-handler', - 'Magento_Checkout/js/model/full-screen-loader' + 'Magento_Vault/js/view/payment/vault-enabler', + 'Magento_Braintree/js/view/payment/kount', + 'mage/translate', + 'prototype', + 'domReady!' ], function ( _, $, Component, + braintreeAdapter, + hostedFields, quote, - braintree, - $t, validator, + globalMessageList, validatorManager, - fullScreenLoader + VaultEnabler, + kount, + $t ) { 'use strict'; return Component.extend({ defaults: { + template: 'Magento_Braintree/payment/form', active: false, - braintreeClient: null, - braintreeDeviceData: null, - paymentMethodNonce: null, - lastBillingAddress: null, - ccCode: null, - ccMessageContainer: null, - validatorManager: validatorManager, code: 'braintree', - - /** - * Additional payment data - * - * {Object} - */ - additionalData: {}, - - /** - * Braintree client configuration - * - * {Object} - */ - clientConfig: { - - /** - * Triggers on payment nonce receive - * @param {Object} response - */ - onPaymentMethodReceived: function (response) { - this.beforePlaceOrder(response); - }, - - /** - * Device data initialization - * - * @param {Object} checkout - */ - onReady: function (checkout) { - braintree.checkout = checkout; - braintree.onReady(); - }, - - /** - * Triggers on any Braintree error - * @param {Object} response - */ - onError: function (response) { - braintree.showError($t('Payment ' + this.getTitle() + ' can\'t be initialized')); - this.isPlaceOrderActionAllowed(true); - throw response.message; - }, - - /** - * Triggers when customer click "Cancel" - */ - onCancelled: function () { - this.paymentMethodNonce = null; - } + lastBillingAddress: null, + hostedFieldsInstance: null, + selectorsMapper: { + 'expirationMonth': 'expirationMonth', + 'expirationYear': 'expirationYear', + 'number': 'cc_number', + 'cvv': 'cc_cid' }, - imports: { - onActiveChange: 'active' - } + paymentPayload: { + nonce: null + }, + additionalData: {} + }, + + /** + * @returns {exports.initialize} + */ + initialize: function () { + var self = this; + + self._super(); + self.vaultEnabler = new VaultEnabler(); + self.vaultEnabler.setPaymentCode(self.getVaultCode()); + + kount.getDeviceData() + .then(function (deviceData) { + self.additionalData['device_data'] = deviceData; + }); + + return self; + }, + + /** + * Init hosted fields. + * + * Is called after knockout finishes input fields bindings. + */ + initHostedFields: function () { + var self = this; + + braintreeAdapter.getApiClient() + .then(function (clientInstance) { + + return hostedFields.create({ + client: clientInstance, + fields: self.getFieldsConfiguration() + }); + }) + .then(function (hostedFieldsInstance) { + self.hostedFieldsInstance = hostedFieldsInstance; + self.isPlaceOrderActionAllowed(true); + self.initFormValidationEvents(hostedFieldsInstance); + + return self.hostedFieldsInstance; + }) + .catch(function () { + self.showError($t('Payment ' + self.getTitle() + ' can\'t be initialized')); + }); }, /** @@ -104,8 +111,6 @@ define( validator.setConfig(window.checkoutConfig.payment[this.getCode()]); this._super() .observe(['active']); - this.validatorManager.initialize(); - this.initClientConfig(); return this; }, @@ -133,225 +138,288 @@ define( }, /** - * Triggers when payment method change - * @param {Boolean} isActive + * Get data + * + * @returns {Object} */ - onActiveChange: function (isActive) { - if (!isActive) { - return; - } + getData: function () { + var data = { + 'method': this.getCode(), + 'additional_data': { + 'payment_method_nonce': this.paymentPayload.nonce + } + }; - this.restoreMessageContainer(); - this.restoreCode(); + data['additional_data'] = _.extend(data['additional_data'], this.additionalData); + this.vaultEnabler.visitAdditionalData(data); - /** - * Define onReady callback - */ - braintree.onReady = function () {}; - this.initBraintree(); + return data; }, /** - * Restore original message container for cc-form component + * Get list of available CC types + * + * @returns {Object} */ - restoreMessageContainer: function () { - this.messageContainer = this.ccMessageContainer; + getCcAvailableTypes: function () { + var availableTypes = validator.getAvailableCardTypes(), + billingAddress = quote.billingAddress(), + billingCountryId; + + this.lastBillingAddress = quote.shippingAddress(); + + if (!billingAddress) { + billingAddress = this.lastBillingAddress; + } + + billingCountryId = billingAddress.countryId; + + if (billingCountryId && validator.getCountrySpecificCardTypes(billingCountryId)) { + return validator.collectTypes( + availableTypes, + validator.getCountrySpecificCardTypes(billingCountryId) + ); + } + + return availableTypes; }, /** - * Restore original code for cc-form component + * @returns {Boolean} */ - restoreCode: function () { - this.code = this.ccCode; + isVaultEnabled: function () { + return this.vaultEnabler.isVaultEnabled(); }, - /** @inheritdoc */ - initChildren: function () { - this._super(); - this.ccMessageContainer = this.messageContainer; - this.ccCode = this.code; - - return this; + /** + * Returns vault code. + * + * @returns {String} + */ + getVaultCode: function () { + return window.checkoutConfig.payment[this.getCode()].ccVaultCode; }, /** - * Init config + * Action to place order + * @param {String} key */ - initClientConfig: function () { - // Advanced fraud tools settings - if (this.hasFraudProtection()) { - this.clientConfig = _.extend(this.clientConfig, this.kountConfig()); + placeOrder: function (key) { + var self = this; + + if (key) { + return self._super(); } + // place order on success validation + validatorManager.validate(self, function () { + return self.placeOrder('parent'); + }, function (err) { - _.each(this.clientConfig, function (fn, name) { - if (typeof fn === 'function') { - this.clientConfig[name] = fn.bind(this); + if (err) { + self.showError(err); } - }, this); + }); + + return false; }, /** - * Init Braintree configuration + * Returns state of place order button + * + * @returns {Boolean} */ - initBraintree: function () { - var intervalId = setInterval(function () { - // stop loader when frame will be loaded - if ($('#braintree-hosted-field-number').length) { - clearInterval(intervalId); - fullScreenLoader.stopLoader(); - } - }, 500); - - if (braintree.checkout) { - braintree.checkout.teardown(function () { - braintree.checkout = null; - }); - } - - fullScreenLoader.startLoader(); - braintree.setConfig(this.clientConfig); - braintree.setup(); + isButtonActive: function () { + return this.isActive() && this.isPlaceOrderActionAllowed(); }, /** - * @returns {Object} + * Trigger order placing */ - kountConfig: function () { - var config = { - dataCollector: { - kount: { - environment: this.getEnvironment() + placeOrderClick: function () { + var self = this; + + if (this.isFormValid(this.hostedFieldsInstance)) { + self.hostedFieldsInstance.tokenize(function (err, payload) { + if (err) { + self.showError($t('Some payment input fields are invalid.')); + + return; } - }, - - /** - * Device data initialization - * - * @param {Object} checkout - */ - onReady: function (checkout) { - braintree.checkout = checkout; - this.additionalData['device_data'] = checkout.deviceData; - braintree.onReady(); - } - }; - if (this.getKountMerchantId()) { - config.dataCollector.kount.merchantId = this.getKountMerchantId(); + self.setPaymentPayload(payload); + self.placeOrder(); + }); } - - return config; }, /** - * Get full selector name + * Validates credit card form. * - * @param {String} field - * @returns {String} + * @param {Object} hostedFieldsInstance + * @returns {Boolean} + * @private */ - getSelector: function (field) { - return '#' + this.getCode() + '_' + field; + isFormValid: function (hostedFieldsInstance) { + var self = this, + state = hostedFieldsInstance.getState(); + + return Object.keys(state.fields).every(function (fieldKey) { + if (fieldKey in self.selectorsMapper && state.fields[fieldKey].isValid === false) { + self.addInvalidClass(self.selectorsMapper[fieldKey]); + } + + return state.fields[fieldKey].isValid; + }); }, /** - * Get list of available CC types + * Init form validation events. * - * @returns {Object} + * @param {Object} hostedFieldsInstance + * @private */ - getCcAvailableTypes: function () { - var availableTypes = validator.getAvailableCardTypes(), - billingAddress = quote.billingAddress(), - billingCountryId; + initFormValidationEvents: function (hostedFieldsInstance) { + var self = this; - this.lastBillingAddress = quote.shippingAddress(); + hostedFieldsInstance.on('empty', function (event) { + if (event.emittedBy === 'number') { + self.selectedCardType(null); + } - if (!billingAddress) { - billingAddress = this.lastBillingAddress; - } + }); - billingCountryId = billingAddress.countryId; + hostedFieldsInstance.on('blur', function (event) { + if (event.emittedBy === 'number') { + self.validateCardType(); + } + }); - if (billingCountryId && validator.getCountrySpecificCardTypes(billingCountryId)) { - return validator.collectTypes( - availableTypes, validator.getCountrySpecificCardTypes(billingCountryId) - ); - } + hostedFieldsInstance.on('validityChange', function (event) { + var field = event.fields[event.emittedBy], + fieldKey = event.emittedBy; - return availableTypes; + if (fieldKey === 'number') { + self.isValidCardNumber = field.isValid; + } + + if (fieldKey in self.selectorsMapper && field.isValid === false) { + self.addInvalidClass(self.selectorsMapper[fieldKey]); + } + }); + + hostedFieldsInstance.on('cardTypeChange', function (event) { + if (event.cards.length === 1) { + self.selectedCardType( + validator.getMageCardType(event.cards[0].type, self.getCcAvailableTypes()) + ); + } + }); }, /** - * @returns {Boolean} + * Get full selector name + * + * @param {String} field + * @returns {String} + * @private */ - hasFraudProtection: function () { - return window.checkoutConfig.payment[this.getCode()].hasFraudProtection; + getSelector: function (field) { + return '#' + this.getCode() + '_' + field; }, /** - * @returns {String} + * Add invalid class to field. + * + * @param {String} field + * @returns void + * @private */ - getEnvironment: function () { - return window.checkoutConfig.payment[this.getCode()].environment; + addInvalidClass: function (field) { + $(this.getSelector(field)).addClass('braintree-hosted-fields-invalid'); }, /** - * @returns {String} + * Remove invalid class from field. + * + * @param {String} field + * @returns void + * @private */ - getKountMerchantId: function () { - return window.checkoutConfig.payment[this.getCode()].kountMerchantId; + removeInvalidClass: function (field) { + $(this.getSelector(field)).removeClass('braintree-hosted-fields-invalid'); }, /** - * Get data + * Get Braintree Hosted Fields * * @returns {Object} + * @private */ - getData: function () { - var data = { - 'method': this.getCode(), - 'additional_data': { - 'payment_method_nonce': this.paymentMethodNonce - } - }; + getFieldsConfiguration: function () { + var self = this, + fields = { + number: { + selector: self.getSelector('cc_number') + }, + expirationMonth: { + selector: self.getSelector('expirationMonth'), + placeholder: $t('MM') + }, + expirationYear: { + selector: self.getSelector('expirationYear'), + placeholder: $t('YY') + } + }; - data['additional_data'] = _.extend(data['additional_data'], this.additionalData); + if (self.hasVerification()) { + fields.cvv = { + selector: self.getSelector('cc_cid') + }; + } - return data; + return fields; }, /** - * Set payment nonce - * @param {String} paymentMethodNonce + * Validate current credit card type. + * + * @returns {Boolean} + * @private */ - setPaymentMethodNonce: function (paymentMethodNonce) { - this.paymentMethodNonce = paymentMethodNonce; + validateCardType: function () { + var cardFieldName = 'cc_number'; + + this.removeInvalidClass(cardFieldName); + + if (this.selectedCardType() === null || !this.isValidCardNumber) { + this.addInvalidClass(cardFieldName); + + return false; + } + + return true; }, /** - * Prepare data to place order - * @param {Object} data + * Sets payment payload + * + * @param {Object} paymentPayload + * @private */ - beforePlaceOrder: function (data) { - this.setPaymentMethodNonce(data.nonce); - this.placeOrder(); + setPaymentPayload: function (paymentPayload) { + this.paymentPayload = paymentPayload; }, /** - * Action to place order - * @param {String} key + * Show error message + * + * @param {String} errorMessage + * @private */ - placeOrder: function (key) { - var self = this; - - if (key) { - return self._super(); - } - // place order on success validation - self.validatorManager.validate(self, function () { - return self.placeOrder('parent'); + showError: function (errorMessage) { + globalMessageList.addErrorMessage({ + message: errorMessage }); - - return false; } }); } diff --git a/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/hosted-fields.js b/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/hosted-fields.js deleted file mode 100644 index 9e496e43b27c5..0000000000000 --- a/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/hosted-fields.js +++ /dev/null @@ -1,171 +0,0 @@ -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -/*browser:true*/ -/*global define*/ - -define([ - 'jquery', - 'Magento_Braintree/js/view/payment/method-renderer/cc-form', - 'Magento_Braintree/js/validator', - 'Magento_Vault/js/view/payment/vault-enabler', - 'mage/translate', - 'Magento_Checkout/js/model/payment/additional-validators' -], function ($, Component, validator, VaultEnabler, $t, additionalValidators) { - 'use strict'; - - return Component.extend({ - - defaults: { - template: 'Magento_Braintree/payment/form', - clientConfig: { - - /** - * {String} - */ - id: 'co-transparent-form-braintree' - }, - isValidCardNumber: false - }, - - /** - * @returns {exports.initialize} - */ - initialize: function () { - this._super(); - this.vaultEnabler = new VaultEnabler(); - this.vaultEnabler.setPaymentCode(this.getVaultCode()); - - return this; - }, - - /** - * Init config - */ - initClientConfig: function () { - this._super(); - - // Hosted fields settings - this.clientConfig.hostedFields = this.getHostedFields(); - }, - - /** - * @returns {Object} - */ - getData: function () { - var data = this._super(); - - this.vaultEnabler.visitAdditionalData(data); - - return data; - }, - - /** - * @returns {Boolean} - */ - isVaultEnabled: function () { - return this.vaultEnabler.isVaultEnabled(); - }, - - /** - * Get Braintree Hosted Fields - * @returns {Object} - */ - getHostedFields: function () { - var self = this, - fields = { - number: { - selector: self.getSelector('cc_number') - }, - expirationMonth: { - selector: self.getSelector('expirationMonth'), - placeholder: $t('MM') - }, - expirationYear: { - selector: self.getSelector('expirationYear'), - placeholder: $t('YY') - } - }; - - if (self.hasVerification()) { - fields.cvv = { - selector: self.getSelector('cc_cid') - }; - } - - /** - * Triggers on Hosted Field changes - * @param {Object} event - * @returns {Boolean} - */ - fields.onFieldEvent = function (event) { - if (event.isEmpty === false) { - self.validateCardType(); - } - - if (event.type !== 'fieldStateChange') { - return false; - } - - // Handle a change in validation or card type - if (event.target.fieldKey === 'number') { - self.selectedCardType(null); - } - - if (event.target.fieldKey === 'number' && event.card) { - self.isValidCardNumber = event.isValid; - self.selectedCardType( - validator.getMageCardType(event.card.type, self.getCcAvailableTypes()) - ); - } - }; - - return fields; - }, - - /** - * Validate current credit card type - * @returns {Boolean} - */ - validateCardType: function () { - var $selector = $(this.getSelector('cc_number')), - invalidClass = 'braintree-hosted-fields-invalid'; - - $selector.removeClass(invalidClass); - - if (this.selectedCardType() === null || !this.isValidCardNumber) { - $(this.getSelector('cc_number')).addClass(invalidClass); - - return false; - } - - return true; - }, - - /** - * Returns state of place order button - * @returns {Boolean} - */ - isButtonActive: function () { - return this.isActive() && this.isPlaceOrderActionAllowed(); - }, - - /** - * Trigger order placing - */ - placeOrderClick: function () { - if (this.validateCardType() && additionalValidators.validate()) { - this.isPlaceOrderActionAllowed(false); - $(this.getSelector('submit')).trigger('click'); - } - }, - - /** - * @returns {String} - */ - getVaultCode: function () { - return window.checkoutConfig.payment[this.getCode()].ccVaultCode; - } - }); -}); diff --git a/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/multishipping/hosted-fields.js b/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/multishipping/cc-form.js similarity index 66% rename from app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/multishipping/hosted-fields.js rename to app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/multishipping/cc-form.js index 1ceebc8e66282..dc816c035a23d 100644 --- a/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/multishipping/hosted-fields.js +++ b/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/multishipping/cc-form.js @@ -7,13 +7,14 @@ define([ 'jquery', - 'Magento_Braintree/js/view/payment/method-renderer/hosted-fields', + 'Magento_Braintree/js/view/payment/method-renderer/cc-form', 'Magento_Braintree/js/validator', 'Magento_Ui/js/model/messageList', 'mage/translate', 'Magento_Checkout/js/model/full-screen-loader', 'Magento_Checkout/js/action/set-payment-information', - 'Magento_Checkout/js/model/payment/additional-validators' + 'Magento_Checkout/js/model/payment/additional-validators', + 'Magento_Braintree/js/view/payment/validator-handler' ], function ( $, Component, @@ -22,7 +23,8 @@ define([ $t, fullScreenLoader, setPaymentInformationAction, - additionalValidators + additionalValidators, + validatorManager ) { 'use strict'; @@ -31,33 +33,13 @@ define([ template: 'Magento_Braintree/payment/multishipping/form' }, - /** - * Get list of available CC types - * - * @returns {Object} - */ - getCcAvailableTypes: function () { - var availableTypes = validator.getAvailableCardTypes(), - billingCountryId; - - billingCountryId = $('#multishipping_billing_country_id').val(); - - if (billingCountryId && validator.getCountrySpecificCardTypes(billingCountryId)) { - return validator.collectTypes( - availableTypes, validator.getCountrySpecificCardTypes(billingCountryId) - ); - } - - return availableTypes; - }, - /** * @override */ placeOrder: function () { var self = this; - this.validatorManager.validate(self, function () { + validatorManager.validate(self, function () { return self.setPaymentInformation(); }); }, @@ -67,9 +49,7 @@ define([ */ setPaymentInformation: function () { if (additionalValidators.validate()) { - fullScreenLoader.startLoader(); - $.when( setPaymentInformationAction( this.messageContainer, diff --git a/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/multishipping/paypal.js b/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/multishipping/paypal.js index 6702e58d1214b..0a9ec4fb6c6ee 100644 --- a/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/multishipping/paypal.js +++ b/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/multishipping/paypal.js @@ -10,32 +10,56 @@ define([ 'Magento_Braintree/js/view/payment/method-renderer/paypal', 'Magento_Checkout/js/action/set-payment-information', 'Magento_Checkout/js/model/payment/additional-validators', - 'Magento_Checkout/js/model/full-screen-loader', - 'mage/translate' + 'Magento_Checkout/js/model/full-screen-loader' ], function ( $, _, Component, setPaymentInformationAction, additionalValidators, - fullScreenLoader, - $t + fullScreenLoader ) { 'use strict'; return Component.extend({ defaults: { template: 'Magento_Braintree/payment/multishipping/paypal', - submitButtonSelector: '#payment-continue span' + submitButtonSelector: '#payment-continue span', + paypalButtonSelector: '[id="parent-payment-continue"]', + reviewButtonHtml: '' }, /** * @override */ - onActiveChange: function (isActive) { - this.updateSubmitButtonTitle(isActive); + initObservable: function () { + this.reviewButtonHtml = $(this.paypalButtonSelector).html(); + + return this._super(); + }, + /** + * Get configuration for PayPal. + * + * @returns {Object} + */ + getPayPalConfig: function () { + var config; + + config = this._super(); + config.flow = 'vault'; + config.enableShippingAddress = false; + config.shippingAddressEditable = false; + + return config; + }, + + /** + * @override + */ + onActiveChange: function (isActive) { this._super(isActive); + this.updateSubmitButton(isActive); }, /** @@ -44,7 +68,7 @@ define([ beforePlaceOrder: function (data) { this._super(data); - this.updateSubmitButtonTitle(true); + this.updateSubmitButton(true); }, /** @@ -87,38 +111,32 @@ define([ * @returns {Boolean} */ isPaymentMethodNonceReceived: function () { - return this.paymentMethodNonce !== null; + return this.paymentPayload.nonce !== null; }, /** - * Updates submit button title on multi-addresses checkout billing form. + * Updates submit button on multi-addresses checkout billing form. * * @param {Boolean} isActive */ - updateSubmitButtonTitle: function (isActive) { - var title = this.isPaymentMethodNonceReceived() || !isActive ? - $t('Go to Review Your Order') : $t('Continue to PayPal'); - - $(this.submitButtonSelector).html(title); + updateSubmitButton: function (isActive) { + if (this.isPaymentMethodNonceReceived() || !isActive) { + $(this.paypalButtonSelector).html(this.reviewButtonHtml); + } }, /** * @override */ placeOrder: function () { - if (!this.isPaymentMethodNonceReceived()) { - this.payWithPayPal(); - } else { - fullScreenLoader.startLoader(); - - $.when( - setPaymentInformationAction( - this.messageContainer, - this.getData() - ) - ).done(this.done.bind(this)) - .fail(this.fail.bind(this)); - } + fullScreenLoader.startLoader(); + $.when( + setPaymentInformationAction( + this.messageContainer, + this.getData() + ) + ).done(this.done.bind(this)) + .fail(this.fail.bind(this)); }, /** diff --git a/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/paypal.js b/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/paypal.js index eaebd8492b0a1..c46e65ffb8abd 100644 --- a/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/paypal.js +++ b/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/paypal.js @@ -9,22 +9,28 @@ define([ 'underscore', 'Magento_Checkout/js/view/payment/default', 'Magento_Braintree/js/view/payment/adapter', + 'braintreePayPal', + 'braintreePayPalCheckout', 'Magento_Checkout/js/model/quote', 'Magento_Checkout/js/model/full-screen-loader', 'Magento_Checkout/js/model/payment/additional-validators', 'Magento_Vault/js/view/payment/vault-enabler', 'Magento_Checkout/js/action/create-billing-address', + 'Magento_Braintree/js/view/payment/kount', 'mage/translate' ], function ( $, _, Component, - Braintree, + BraintreeAdapter, + BraintreePayPal, + BraintreePayPalCheckout, quote, fullScreenLoader, additionalValidators, VaultEnabler, createBillingAddress, + kount, $t ) { 'use strict'; @@ -34,10 +40,15 @@ define([ template: 'Magento_Braintree/payment/paypal', code: 'braintree_paypal', active: false, - paymentMethodNonce: null, grandTotalAmount: null, isReviewRequired: false, + paypalCheckoutInstance: null, customerEmail: null, + vaultEnabler: null, + paymentPayload: { + nonce: null + }, + paypalButtonSelector: '[data-container="paypal-button"]', /** * Additional payment data @@ -46,39 +57,42 @@ define([ */ additionalData: {}, - /** - * PayPal client configuration - * {Object} - */ - clientConfig: { - dataCollector: { - paypal: true - }, - - /** - * Triggers when widget is loaded - * @param {Object} checkout - */ - onReady: function (checkout) { - Braintree.checkout = checkout; - this.additionalData['device_data'] = checkout.deviceData; - this.enableButton(); - Braintree.onReady(); - }, - - /** - * Triggers on payment nonce receive - * @param {Object} response - */ - onPaymentMethodReceived: function (response) { - this.beforePlaceOrder(response); - } - }, imports: { onActiveChange: 'active' } }, + /** + * Initialize view. + * + * @return {exports} + */ + initialize: function () { + var self = this; + + self._super(); + + BraintreeAdapter.getApiClient().then(function (clientInstance) { + return BraintreePayPal.create({ + client: clientInstance + }); + }).then(function (paypalCheckoutInstance) { + self.paypalCheckoutInstance = paypalCheckoutInstance; + + return self.paypalCheckoutInstance; + }); + + kount.getDeviceData() + .then(function (deviceData) { + self.additionalData['device_data'] = deviceData; + }); + + // for each component initialization need update property + this.isReviewRequired(false); + + return self; + }, + /** * Set list of observable attributes * @returns {exports.initObservable} @@ -109,10 +123,6 @@ define([ } }); - // for each component initialization need update property - this.isReviewRequired(false); - this.initClientConfig(); - return this; }, @@ -161,24 +171,13 @@ define([ }, /** - * Init config - */ - initClientConfig: function () { - this.clientConfig = _.extend(this.clientConfig, this.getPayPalConfig()); - - _.each(this.clientConfig, function (fn, name) { - if (typeof fn === 'function') { - this.clientConfig[name] = fn.bind(this); - } - }, this); - }, - - /** - * Set payment nonce - * @param {String} paymentMethodNonce + * Sets payment payload + * + * @param {Object} paymentPayload + * @private */ - setPaymentMethodNonce: function (paymentMethodNonce) { - this.paymentMethodNonce = paymentMethodNonce; + setPaymentPayload: function (paymentPayload) { + this.paymentPayload = paymentPayload; }, /** @@ -205,21 +204,21 @@ define([ /** * Prepare data to place order - * @param {Object} data + * @param {Object} payload */ - beforePlaceOrder: function (data) { - this.setPaymentMethodNonce(data.nonce); + beforePlaceOrder: function (payload) { + this.setPaymentPayload(payload); if ((this.isRequiredBillingAddress() || quote.billingAddress() === null) && - typeof data.details.billingAddress !== 'undefined' + typeof payload.details.billingAddress !== 'undefined' ) { - this.setBillingAddress(data.details, data.details.billingAddress); + this.setBillingAddress(payload.details, payload.details.billingAddress); } if (this.isSkipOrderReview()) { this.placeOrder(); } else { - this.customerEmail(data.details.email); + this.customerEmail(payload.details.email); this.isReviewRequired(true); } }, @@ -228,18 +227,46 @@ define([ * Re-init PayPal Auth Flow */ reInitPayPal: function () { - if (Braintree.checkout) { - Braintree.checkout.teardown(function () { - Braintree.checkout = null; - }); - } + var self = this; - this.disableButton(); - this.clientConfig.paypal.amount = this.grandTotalAmount; - this.clientConfig.paypal.shippingAddressOverride = this.getShippingAddress(); + $(self.paypalButtonSelector).html(''); + + return BraintreePayPalCheckout.Button.render({ + env: this.getEnvironment(), + style: { + color: 'blue', + shape: 'rect', + size: 'medium', + label: 'pay', + tagline: false + }, + + /** + * Creates a PayPal payment + */ + payment: function () { + return self.paypalCheckoutInstance.createPayment( + self.getPayPalConfig() + ); + }, - Braintree.setConfig(this.clientConfig); - Braintree.setup(); + /** + * Tokenizes the authorize data + */ + onAuthorize: function (data) { + return self.paypalCheckoutInstance.tokenizePayment(data) + .then(function (payload) { + self.beforePlaceOrder(payload); + }); + }, + + /** + * Triggers on error + */ + onError: function () { + self.showError($t('Payment ' + self.getTitle() + ' can\'t be initialized')); + } + }, self.paypalButtonSelector); }, /** @@ -272,37 +299,22 @@ define([ */ getPayPalConfig: function () { var totals = quote.totals(), - config = {}, + config, isActiveVaultEnabler = this.isActiveVault(); - config.paypal = { - container: 'paypal-container', - singleUse: !isActiveVaultEnabler, - headless: true, + config = { + flow: !isActiveVaultEnabler ? 'checkout' : 'vault', amount: this.grandTotalAmount, currency: totals['base_currency_code'], locale: this.getLocale(), enableShippingAddress: true, - - /** - * Triggers on any Braintree error - */ - onError: function () { - this.paymentMethodNonce = null; - }, - - /** - * Triggers if browser doesn't support PayPal Checkout - */ - onUnsupported: function () { - this.paymentMethodNonce = null; - } + shippingAddressEditable: this.isAllowOverrideShippingAddress() }; - config.paypal.shippingAddressOverride = this.getShippingAddress(); + config.shippingAddressOverride = this.getShippingAddress(); if (this.getMerchantName()) { - config.paypal.displayName = this.getMerchantName(); + config.displayName = this.getMerchantName(); } return config; @@ -320,14 +332,13 @@ define([ } return { - recipientName: address.firstname + ' ' + address.lastname, - streetAddress: address.street[0], - locality: address.city, - countryCodeAlpha2: address.countryId, + line1: address.street[0], + city: address.city, + state: address.regionCode, postalCode: address.postcode, - region: address.regionCode, + countryCode: address.countryId, phone: address.telephone, - editable: this.isAllowOverrideShippingAddress() + recipientName: address.firstname + ' ' + address.lastname }; }, @@ -347,7 +358,7 @@ define([ var data = { 'method': this.getCode(), 'additional_data': { - 'payment_method_nonce': this.paymentMethodNonce + 'payment_method_nonce': this.paymentPayload.nonce } }; @@ -374,6 +385,13 @@ define([ return window.checkoutConfig.payment[this.getCode()].vaultCode; }, + /** + * @returns {String} + */ + getEnvironment: function () { + return window.checkoutConfig.payment[BraintreeAdapter.getCode()].environment; + }, + /** * Check if need to skip order review * @returns {Boolean} @@ -394,59 +412,7 @@ define([ * Re-init PayPal Auth flow to use Vault */ onVaultPaymentTokenEnablerChange: function () { - this.clientConfig.paypal.singleUse = !this.isActiveVault(); this.reInitPayPal(); - }, - - /** - * Disable submit button - */ - disableButton: function () { - // stop any previous shown loaders - fullScreenLoader.stopLoader(true); - fullScreenLoader.startLoader(); - $('[data-button="place"]').attr('disabled', 'disabled'); - }, - - /** - * Enable submit button - */ - enableButton: function () { - $('[data-button="place"]').removeAttr('disabled'); - fullScreenLoader.stopLoader(); - }, - - /** - * Triggers when customer click "Continue to PayPal" button - */ - payWithPayPal: function () { - if (!additionalValidators.validate()) { - return; - } - - try { - Braintree.checkout.paypal.initAuthFlow(); - } catch (e) { - this.messageContainer.addErrorMessage({ - message: $t('Payment ' + this.getTitle() + ' can\'t be initialized.') - }); - } - }, - - /** - * Get button title - * @returns {String} - */ - getButtonTitle: function () { - return this.isSkipOrderReview() ? 'Pay with PayPal' : 'Continue to PayPal'; - }, - - /** - * Get button id - * @returns {String} - */ - getButtonId: function () { - return this.getCode() + (this.isSkipOrderReview() ? '_pay_with' : '_continue_to'); } }); }); diff --git a/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/vault.js b/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/vault.js index 85e531706d62e..ad8ac02bfb8c6 100644 --- a/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/vault.js +++ b/app/code/Magento/Braintree/view/frontend/web/js/view/payment/method-renderer/vault.js @@ -51,15 +51,7 @@ define([ placeOrder: function () { var self = this; - /** - * Define onReady callback - */ - Braintree.onReady = function () { - self.getPaymentMethodNonce(); - }; - self.hostedFields(function (formComponent) { - formComponent.initBraintree(); - }); + self.getPaymentMethodNonce(); }, /** @@ -75,7 +67,7 @@ define([ .done(function (response) { fullScreenLoader.stopLoader(); self.hostedFields(function (formComponent) { - formComponent.setPaymentMethodNonce(response.paymentMethodNonce); + formComponent.paymentPayload.nonce = response.paymentMethodNonce; formComponent.additionalData['public_hash'] = self.publicHash; formComponent.code = self.code; formComponent.messageContainer = self.messageContainer; diff --git a/app/code/Magento/Braintree/view/frontend/web/js/view/payment/validator-handler.js b/app/code/Magento/Braintree/view/frontend/web/js/view/payment/validator-handler.js index fbe85c3b46027..992c241fad665 100644 --- a/app/code/Magento/Braintree/view/frontend/web/js/view/payment/validator-handler.js +++ b/app/code/Magento/Braintree/view/frontend/web/js/view/payment/validator-handler.js @@ -7,28 +7,26 @@ define([ 'jquery', - 'Magento_Ui/js/model/messageList', 'Magento_Braintree/js/view/payment/3d-secure' -], function ($, globalMessageList, verify3DSecure) { +], function ($, verify3DSecure) { 'use strict'; return { + initialized: false, validators: [], /** - * Get payment config - * @returns {Object} - */ - getConfig: function () { - return window.checkoutConfig.payment; - }, - - /** - * Init list of validators + * Inits list of validators */ initialize: function () { var config = this.getConfig(); + if (this.initialized) { + return; + } + + this.initialized = true; + if (config[verify3DSecure.getCode()].enabled) { verify3DSecure.setConfig(config[verify3DSecure.getCode()]); this.add(verify3DSecure); @@ -36,7 +34,17 @@ define([ }, /** - * Add new validator + * Gets payment config + * + * @returns {Object} + */ + getConfig: function () { + return window.checkoutConfig.payment; + }, + + /** + * Adds new validator + * * @param {Object} validator */ add: function (validator) { @@ -44,17 +52,21 @@ define([ }, /** - * Run pull of validators + * Runs pull of validators + * * @param {Object} context - * @param {Function} callback + * @param {Function} successCallback + * @param {Function} errorCallback */ - validate: function (context, callback) { + validate: function (context, successCallback, errorCallback) { var self = this, deferred; + self.initialize(); + // no available validators if (!self.validators.length) { - callback(); + successCallback(); return; } @@ -66,20 +78,10 @@ define([ $.when.apply($, deferred) .done(function () { - callback(); + successCallback(); }).fail(function (error) { - self.showError(error); + errorCallback(error); }); - }, - - /** - * Show error message - * @param {String} errorMessage - */ - showError: function (errorMessage) { - globalMessageList.addErrorMessage({ - message: errorMessage - }); } }; }); diff --git a/app/code/Magento/Braintree/view/frontend/web/template/payment/form.html b/app/code/Magento/Braintree/view/frontend/web/template/payment/form.html index 819b06ca75788..9bcb5dad8b636 100644 --- a/app/code/Magento/Braintree/view/frontend/web/template/payment/form.html +++ b/app/code/Magento/Braintree/view/frontend/web/template/payment/form.html @@ -87,7 +87,7 @@ <span><!-- ko i18n: 'Card Verification Number'--><!-- /ko --></span> </label> <div class="control _with-tooltip"> - <div data-bind="attr: {id: getCode() + '_cc_cid'}" class="hosted-control hosted-cid"></div> + <div data-bind="afterRender: initHostedFields, attr: {id: getCode() + '_cc_cid'}" class="hosted-control hosted-cid"></div> <div class="hosted-error"><!-- ko i18n: 'Please, enter valid Card Verification Number'--><!-- /ko --></div> <div class="field-tooltip toggle"> diff --git a/app/code/Magento/Braintree/view/frontend/web/template/payment/multishipping/form.html b/app/code/Magento/Braintree/view/frontend/web/template/payment/multishipping/form.html index 964e15df166d3..b72ef24b81b63 100644 --- a/app/code/Magento/Braintree/view/frontend/web/template/payment/multishipping/form.html +++ b/app/code/Magento/Braintree/view/frontend/web/template/payment/multishipping/form.html @@ -41,7 +41,7 @@ </div> </div> <div class="field number required"> - <label data-bind="attr: {for: getCode() + '_cc_number'}" class="label"> + <label data-bind="afterRender: initHostedFields, attr: {for: getCode() + '_cc_number'}" class="label"> <span><!-- ko i18n: 'Credit Card Number'--><!-- /ko --></span> </label> <div class="control"> diff --git a/app/code/Magento/Braintree/view/frontend/web/template/payment/multishipping/paypal.html b/app/code/Magento/Braintree/view/frontend/web/template/payment/multishipping/paypal.html index 722989e41f98f..fcd5320351938 100644 --- a/app/code/Magento/Braintree/view/frontend/web/template/payment/multishipping/paypal.html +++ b/app/code/Magento/Braintree/view/frontend/web/template/payment/multishipping/paypal.html @@ -20,6 +20,7 @@ <fieldset class="braintree-paypal-fieldset" data-bind='attr: {id: "payment_form_" + getCode()}'> <div id="paypal-container"></div> </fieldset> + <div data-container="paypal-button"></div> <div class="actions-toolbar braintree-paypal-actions" data-bind="visible: isReviewRequired()"> <div class="payment-method-item braintree-paypal-account"> <span class="payment-method-type">PayPal</span> diff --git a/app/code/Magento/Braintree/view/frontend/web/template/payment/paypal.html b/app/code/Magento/Braintree/view/frontend/web/template/payment/paypal.html index e1f6a1b4c25ce..0abf3483ac76c 100644 --- a/app/code/Magento/Braintree/view/frontend/web/template/payment/paypal.html +++ b/app/code/Magento/Braintree/view/frontend/web/template/payment/paypal.html @@ -65,15 +65,7 @@ </div> </div> <div class="actions-toolbar" data-bind="visible: !isReviewRequired()"> - <div class="primary"> - <button data-button="place" data-role="review-save" - type="submit" - data-bind="attr: {id: getButtonId(), title: $t(getButtonTitle())}, enable: (isActive()), click: payWithPayPal" - class="action primary checkout" - disabled> - <span translate="getButtonTitle()"></span> - </button> - </div> + <div data-container="paypal-button" class="primary"></div> </div> </div> </div> diff --git a/app/code/Magento/Bundle/Test/Mftf/ActionGroup/AdminOrderBundleProductActionGroup.xml b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/AdminOrderBundleProductActionGroup.xml new file mode 100644 index 0000000000000..d73d31c4498f8 --- /dev/null +++ b/app/code/Magento/Bundle/Test/Mftf/ActionGroup/AdminOrderBundleProductActionGroup.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminOrderConfigureBundleProduct"> + <arguments> + <argument name="productName" type="string" defaultValue="{{SimpleProduct.sku}}"/> + <argument name="productNumber" type="string" defaultValue="1"/> + <argument name="productQty" type="string" defaultValue="1"/> + </arguments> + <click selector="{{AdminOrderFormItemsOrderedSection.configureButtonBySku}}" stepKey="clickConfigure"/> + <waitForPageLoad stepKey="waitForConfigurePageLoad"/> + <checkOption selector="{{AdminOrderBundleProductSection.bundleProductCheckbox(productNumber)}}" stepKey="checkProduct"/> + <fillField selector="{{AdminOrderFormConfigureProductSection.quantity}}" userInput="{{productQty}}" stepKey="fillProductQty"/> + <click selector="{{AdminOrderFormConfigureProductSection.ok}}" stepKey="clickOk"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Bundle/Test/Mftf/Section/AdminOrderBundleProductSection.xml b/app/code/Magento/Bundle/Test/Mftf/Section/AdminOrderBundleProductSection.xml new file mode 100644 index 0000000000000..915b11ebdbbaa --- /dev/null +++ b/app/code/Magento/Bundle/Test/Mftf/Section/AdminOrderBundleProductSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> +<section name="AdminOrderBundleProductSection"> + <element name="bundleProductCheckbox" type="checkbox" selector="(//input[contains(@class, 'admin__control-checkbox') and contains(@class, 'bundle-option')])[{{productNumber}}]" parameterized="true"/> +</section> +</sections> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/AdminDeleteBundleDynamicProductTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/AdminDeleteBundleDynamicProductTest.xml index bc9a3dba9a5f1..a4e26256e9773 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/AdminDeleteBundleDynamicProductTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/AdminDeleteBundleDynamicProductTest.xml @@ -16,6 +16,9 @@ <severity value="CRITICAL"/> <testCaseId value="MC-11016"/> <group value="mtf_migrated"/> + <skip> + <issueId value="MC-16393"/> + </skip> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> diff --git a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAddBundleOptionsToCartTest.xml b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAddBundleOptionsToCartTest.xml index a1630128638d9..779f1370c4da4 100644 --- a/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAddBundleOptionsToCartTest.xml +++ b/app/code/Magento/Bundle/Test/Mftf/Test/StorefrontAddBundleOptionsToCartTest.xml @@ -17,6 +17,9 @@ <severity value="MAJOR"/> <testCaseId value="MAGETWO-95933"/> <group value="Bundle"/> + <skip> + <issueId value="MC-16684"/> + </skip> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="login"/> diff --git a/app/code/Magento/Bundle/etc/db_schema.xml b/app/code/Magento/Bundle/etc/db_schema.xml index 33738cd252d61..97e86e5c17359 100644 --- a/app/code/Magento/Bundle/etc/db_schema.xml +++ b/app/code/Magento/Bundle/etc/db_schema.xml @@ -97,7 +97,7 @@ comment="Website Id"/> <column xsi:type="smallint" name="selection_price_type" padding="5" unsigned="true" nullable="false" identity="false" default="0" comment="Selection Price Type"/> - <column xsi:type="decimal" name="selection_price_value" scale="4" precision="12" unsigned="false" + <column xsi:type="decimal" name="selection_price_value" scale="6" precision="20" unsigned="false" nullable="false" default="0" comment="Selection Price Value"/> <column xsi:type="int" name="parent_product_id" padding="10" unsigned="true" nullable="false" identity="false" comment="Parent Product Id"/> @@ -125,9 +125,9 @@ comment="Website Id"/> <column xsi:type="int" name="customer_group_id" padding="10" unsigned="true" nullable="false" identity="false" comment="Customer Group ID"/> - <column xsi:type="decimal" name="min_price" scale="4" precision="12" unsigned="false" nullable="false" + <column xsi:type="decimal" name="min_price" scale="6" precision="20" unsigned="false" nullable="false" comment="Min Price"/> - <column xsi:type="decimal" name="max_price" scale="4" precision="12" unsigned="false" nullable="false" + <column xsi:type="decimal" name="max_price" scale="6" precision="20" unsigned="false" nullable="false" comment="Max Price"/> <constraint xsi:type="primary" referenceId="PRIMARY"> <column name="entity_id"/> @@ -181,21 +181,21 @@ default="0" comment="Tax Class ID"/> <column xsi:type="smallint" name="price_type" padding="5" unsigned="true" nullable="false" identity="false" comment="Price Type"/> - <column xsi:type="decimal" name="special_price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="special_price" scale="6" precision="20" unsigned="false" nullable="true" comment="Special Price"/> - <column xsi:type="decimal" name="tier_percent" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="tier_percent" scale="6" precision="20" unsigned="false" nullable="true" comment="Tier Percent"/> - <column xsi:type="decimal" name="orig_price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="orig_price" scale="6" precision="20" unsigned="false" nullable="true" comment="Orig Price"/> - <column xsi:type="decimal" name="price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="price" scale="6" precision="20" unsigned="false" nullable="true" comment="Price"/> - <column xsi:type="decimal" name="min_price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="min_price" scale="6" precision="20" unsigned="false" nullable="true" comment="Min Price"/> - <column xsi:type="decimal" name="max_price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="max_price" scale="6" precision="20" unsigned="false" nullable="true" comment="Max Price"/> - <column xsi:type="decimal" name="tier_price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="tier_price" scale="6" precision="20" unsigned="false" nullable="true" comment="Tier Price"/> - <column xsi:type="decimal" name="base_tier" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="base_tier" scale="6" precision="20" unsigned="false" nullable="true" comment="Base Tier"/> <constraint xsi:type="primary" referenceId="PRIMARY"> <column name="entity_id"/> @@ -215,21 +215,21 @@ default="0" comment="Tax Class ID"/> <column xsi:type="smallint" name="price_type" padding="5" unsigned="true" nullable="false" identity="false" comment="Price Type"/> - <column xsi:type="decimal" name="special_price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="special_price" scale="6" precision="20" unsigned="false" nullable="true" comment="Special Price"/> - <column xsi:type="decimal" name="tier_percent" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="tier_percent" scale="6" precision="20" unsigned="false" nullable="true" comment="Tier Percent"/> - <column xsi:type="decimal" name="orig_price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="orig_price" scale="6" precision="20" unsigned="false" nullable="true" comment="Orig Price"/> - <column xsi:type="decimal" name="price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="price" scale="6" precision="20" unsigned="false" nullable="true" comment="Price"/> - <column xsi:type="decimal" name="min_price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="min_price" scale="6" precision="20" unsigned="false" nullable="true" comment="Min Price"/> - <column xsi:type="decimal" name="max_price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="max_price" scale="6" precision="20" unsigned="false" nullable="true" comment="Max Price"/> - <column xsi:type="decimal" name="tier_price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="tier_price" scale="6" precision="20" unsigned="false" nullable="true" comment="Tier Price"/> - <column xsi:type="decimal" name="base_tier" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="base_tier" scale="6" precision="20" unsigned="false" nullable="true" comment="Base Tier"/> <constraint xsi:type="primary" referenceId="PRIMARY"> <column name="entity_id"/> @@ -253,9 +253,9 @@ default="0" comment="Group Type"/> <column xsi:type="smallint" name="is_required" padding="5" unsigned="true" nullable="true" identity="false" default="0" comment="Is Required"/> - <column xsi:type="decimal" name="price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="price" scale="6" precision="20" unsigned="false" nullable="true" comment="Price"/> - <column xsi:type="decimal" name="tier_price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="tier_price" scale="6" precision="20" unsigned="false" nullable="true" comment="Tier Price"/> <constraint xsi:type="primary" referenceId="PRIMARY"> <column name="entity_id"/> @@ -281,9 +281,9 @@ default="0" comment="Group Type"/> <column xsi:type="smallint" name="is_required" padding="5" unsigned="true" nullable="true" identity="false" default="0" comment="Is Required"/> - <column xsi:type="decimal" name="price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="price" scale="6" precision="20" unsigned="false" nullable="true" comment="Price"/> - <column xsi:type="decimal" name="tier_price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="tier_price" scale="6" precision="20" unsigned="false" nullable="true" comment="Tier Price"/> <constraint xsi:type="primary" referenceId="PRIMARY"> <column name="entity_id"/> @@ -303,15 +303,15 @@ comment="Website ID"/> <column xsi:type="int" name="option_id" padding="10" unsigned="true" nullable="false" identity="false" default="0" comment="Option Id"/> - <column xsi:type="decimal" name="min_price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="min_price" scale="6" precision="20" unsigned="false" nullable="true" comment="Min Price"/> - <column xsi:type="decimal" name="alt_price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="alt_price" scale="6" precision="20" unsigned="false" nullable="true" comment="Alt Price"/> - <column xsi:type="decimal" name="max_price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="max_price" scale="6" precision="20" unsigned="false" nullable="true" comment="Max Price"/> - <column xsi:type="decimal" name="tier_price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="tier_price" scale="6" precision="20" unsigned="false" nullable="true" comment="Tier Price"/> - <column xsi:type="decimal" name="alt_tier_price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="alt_tier_price" scale="6" precision="20" unsigned="false" nullable="true" comment="Alt Tier Price"/> <constraint xsi:type="primary" referenceId="PRIMARY"> <column name="entity_id"/> @@ -330,15 +330,15 @@ comment="Website ID"/> <column xsi:type="int" name="option_id" padding="10" unsigned="true" nullable="false" identity="false" default="0" comment="Option Id"/> - <column xsi:type="decimal" name="min_price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="min_price" scale="6" precision="20" unsigned="false" nullable="true" comment="Min Price"/> - <column xsi:type="decimal" name="alt_price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="alt_price" scale="6" precision="20" unsigned="false" nullable="true" comment="Alt Price"/> - <column xsi:type="decimal" name="max_price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="max_price" scale="6" precision="20" unsigned="false" nullable="true" comment="Max Price"/> - <column xsi:type="decimal" name="tier_price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="tier_price" scale="6" precision="20" unsigned="false" nullable="true" comment="Tier Price"/> - <column xsi:type="decimal" name="alt_tier_price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="alt_tier_price" scale="6" precision="20" unsigned="false" nullable="true" comment="Alt Tier Price"/> <constraint xsi:type="primary" referenceId="PRIMARY"> <column name="entity_id"/> diff --git a/app/code/Magento/Bundle/view/adminhtml/templates/catalog/product/edit/tab/attributes/extend.phtml b/app/code/Magento/Bundle/view/adminhtml/templates/catalog/product/edit/tab/attributes/extend.phtml index a770ae864a74c..f028c7013df90 100644 --- a/app/code/Magento/Bundle/view/adminhtml/templates/catalog/product/edit/tab/attributes/extend.phtml +++ b/app/code/Magento/Bundle/view/adminhtml/templates/catalog/product/edit/tab/attributes/extend.phtml @@ -4,8 +4,6 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - /** @var $block \Magento\Bundle\Block\Adminhtml\Catalog\Product\Edit\Tab\Attributes\Extend */ $elementHtml = $block->getParentElementHtml(); @@ -20,8 +18,8 @@ $isElementReadonly = $block->getElement() ->getReadonly(); ?> -<?php if (!($attributeCode === 'price' && $block->getCanReadPrice() === false)): ?> - <div class="<?= /* @escapeNotVerified */ $attributeCode ?> "><?= /* @escapeNotVerified */ $elementHtml ?></div> +<?php if (!($attributeCode === 'price' && $block->getCanReadPrice() === false)) : ?> + <div class="<?= $block->escapeHtmlAttr($attributeCode) ?> "><?= /* @noEscape */ $elementHtml ?></div> <?php endif; ?> <?= $block->getExtendedElement($switchAttributeCode)->toHtml() ?> @@ -29,9 +27,9 @@ $isElementReadonly = $block->getElement() <?php if (!$isElementReadonly && $block->getDisableChild()) { ?> <script> require(['prototype'], function () { - function <?= /* @escapeNotVerified */ $switchAttributeCode ?>_change() { - var $attribute = $('<?= /* @escapeNotVerified */ $attributeCode ?>'); - if ($('<?= /* @escapeNotVerified */ $switchAttributeCode ?>').value == '<?= /* @escapeNotVerified */ $block::DYNAMIC ?>') { + function <?= /* @noEscape */ $switchAttributeCode ?>_change() { + var $attribute = $('<?= $block->escapeJs($attributeCode) ?>'); + if ($('<?= /* @noEscape */ $switchAttributeCode ?>').value == '<?= $block->escapeJs($block::DYNAMIC) ?>') { if ($attribute) { $attribute.disabled = true; $attribute.value = ''; @@ -43,10 +41,10 @@ $isElementReadonly = $block->getElement() } else { if ($attribute) { <?php if ($attributeCode === 'price' && !$block->getCanEditPrice() && $block->getCanReadPrice() - && $block->getProduct()->isObjectNew()): ?> - <?php $defaultProductPrice = $block->getDefaultProductPrice() ?: "''"; ?> - $attribute.value = <?= /* @escapeNotVerified */ $defaultProductPrice ?>; - <?php else: ?> + && $block->getProduct()->isObjectNew()) : ?> + <?php $defaultProductPrice = $block->getDefaultProductPrice() ?: "''"; ?> + $attribute.value = <?= /* @noEscape */ (string)$defaultProductPrice ?>; + <?php else : ?> $attribute.disabled = false; $attribute.addClassName('required-entry'); <?php endif; ?> @@ -58,11 +56,11 @@ $isElementReadonly = $block->getElement() } <?php if (!($attributeCode === 'price' && !$block->getCanEditPrice() - && !$block->getProduct()->isObjectNew())): ?> - $('<?= /* @escapeNotVerified */ $switchAttributeCode ?>').observe('change', <?= /* @escapeNotVerified */ $switchAttributeCode ?>_change); + && !$block->getProduct()->isObjectNew())) : ?> + $('<?= /* @noEscape */ $switchAttributeCode ?>').observe('change', <?= /* @noEscape */ $switchAttributeCode ?>_change); <?php endif; ?> Event.observe(window, 'load', function(){ - <?= /* @escapeNotVerified */ $switchAttributeCode ?>_change(); + <?= /* @noEscape */ $switchAttributeCode ?>_change(); }); }); </script> diff --git a/app/code/Magento/Bundle/view/adminhtml/templates/product/composite/fieldset/options/bundle.phtml b/app/code/Magento/Bundle/view/adminhtml/templates/product/composite/fieldset/options/bundle.phtml index 87798a6ba622f..53ad0a963244d 100644 --- a/app/code/Magento/Bundle/view/adminhtml/templates/product/composite/fieldset/options/bundle.phtml +++ b/app/code/Magento/Bundle/view/adminhtml/templates/product/composite/fieldset/options/bundle.phtml @@ -3,17 +3,16 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <?php /* @var $block \Magento\Bundle\Block\Adminhtml\Catalog\Product\Composite\Fieldset\Bundle */ ?> <?php $options = $block->decorateArray($block->getOptions(true)); ?> -<?php if (count($options)): ?> +<?php if (count($options)) : ?> <fieldset id="catalog_product_composite_configure_fields_bundle" class="fieldset admin__fieldset composite-bundle<?= $block->getIsLastFieldset() ? ' last-fieldset' : '' ?>"> - <legend class="legend admin__legend"><span><?= /* @escapeNotVerified */ __('Bundle Items') ?></span></legend><br /> + <legend class="legend admin__legend"> + <span><?= $block->escapeHtml(__('Bundle Items')) ?></span> + </legend><br /> <?php foreach ($options as $option) : ?> <?php if ($option->getSelections()) : ?> <?= $block->getOptionHtml($option) ?> @@ -71,7 +70,7 @@ require([ } } }; - ProductConfigure.bundleControl = new BundleControl(<?= /* @escapeNotVerified */ $block->getJsonConfig() ?>); + ProductConfigure.bundleControl = new BundleControl(<?= /* @noEscape */ $block->getJsonConfig() ?>); }); </script> diff --git a/app/code/Magento/Bundle/view/adminhtml/templates/product/composite/fieldset/options/type/checkbox.phtml b/app/code/Magento/Bundle/view/adminhtml/templates/product/composite/fieldset/options/type/checkbox.phtml index 44ed02f2758d0..08e89699b1f71 100644 --- a/app/code/Magento/Bundle/view/adminhtml/templates/product/composite/fieldset/options/type/checkbox.phtml +++ b/app/code/Magento/Bundle/view/adminhtml/templates/product/composite/fieldset/options/type/checkbox.phtml @@ -3,60 +3,58 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - +// phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis ?> <?php /* @var $block \Magento\Bundle\Block\Adminhtml\Catalog\Product\Composite\Fieldset\Options\Type\Checkbox */ ?> <?php $_option = $block->getOption(); ?> <?php $_selections = $_option->getSelections(); ?> -<?php $_skipSaleableCheck = $this->helper('Magento\Catalog\Helper\Product')->getSkipSaleableCheck(); ?> +<?php $_skipSaleableCheck = $this->helper(Magento\Catalog\Helper\Product::class)->getSkipSaleableCheck(); ?> -<div class="field admin__field options<?php if ($_option->getRequired()) echo ' required _required' ?>"> +<div class="field admin__field options<?php if ($_option->getRequired()) { echo ' required _required'; } ?>"> <label class="label admin__field-label"> <span><?= $block->escapeHtml($_option->getTitle()) ?></span> </label> <div class="control admin__field-control"> - <div class="nested <?php if ($_option->getDecoratedIsLast()):?> last<?php endif;?>"> + <div class="nested <?php if ($_option->getDecoratedIsLast()) :?> last<?php endif;?>"> - <?php if (count($_selections) == 1 && $_option->getRequired()): ?> - <?= /* @escapeNotVerified */ $block->getSelectionQtyTitlePrice($_selections[0]) ?> + <?php if (count($_selections) == 1 && $_option->getRequired()) : ?> + <?= /* @noEscape */ $block->getSelectionQtyTitlePrice($_selections[0]) ?> <input type="hidden" - name="bundle_option[<?= /* @escapeNotVerified */ $_option->getId() ?>]" - value="<?= /* @escapeNotVerified */ $_selections[0]->getSelectionId() ?>" - price="<?= /* @escapeNotVerified */ $block->getSelectionPrice($_selections[0]) ?>" /> - <?php else:?> + name="bundle_option[<?= $block->escapeHtmlAttr($_option->getId()) ?>]" + value="<?= $block->escapeHtmlAttr($_selections[0]->getSelectionId()) ?>" + price="<?= $block->escapeHtmlAttr($block->getSelectionPrice($_selections[0])) ?>" /> + <?php else :?> - <?php foreach ($_selections as $_selection): ?> + <?php foreach ($_selections as $_selection) : ?> <div class="field choice admin__field admin__field-option"> <input - class="change-container-classname admin__control-checkbox checkbox bundle-option-<?= /* @escapeNotVerified */ $_option->getId() ?> <?php if ($_option->getRequired()) echo 'validate-one-required-by-name' ?>" - id="bundle-option-<?= /* @escapeNotVerified */ $_option->getId() ?>-<?= /* @escapeNotVerified */ $_selection->getSelectionId() ?>" + class="change-container-classname admin__control-checkbox checkbox bundle-option-<?= $block->escapeHtmlAttr($_option->getId()) ?> <?php if ($_option->getRequired()) { echo 'validate-one-required-by-name'; } ?>" + id="bundle-option-<?= $block->escapeHtmlAttr($_option->getId()) ?>-<?= $block->escapeHtmlAttr($_selection->getSelectionId()) ?>" type="checkbox" - name="bundle_option[<?= /* @escapeNotVerified */ $_option->getId() ?>][<?= /* @escapeNotVerified */ $_selection->getId() ?>]" - <?php if ($block->isSelected($_selection)):?> + name="bundle_option[<?= $block->escapeHtmlAttr($_option->getId()) ?>][<?= $block->escapeHtmlAttr($_selection->getId()) ?>]" + <?php if ($block->isSelected($_selection)) :?> <?= ' checked="checked"' ?> <?php endif;?> - <?php if (!$_selection->isSaleable() && !$_skipSaleableCheck):?> + <?php if (!$_selection->isSaleable() && !$_skipSaleableCheck) :?> <?= ' disabled="disabled"' ?> <?php endif;?> - value="<?= /* @escapeNotVerified */ $_selection->getSelectionId() ?>" + value="<?= $block->escapeHtmlAttr($_selection->getSelectionId()) ?>" onclick="ProductConfigure.bundleControl.changeSelection(this)" - price="<?= /* @escapeNotVerified */ $block->getSelectionPrice($_selection) ?>" /> + price="<?= $block->escapeHtmlAttr($block->getSelectionPrice($_selection)) ?>" /> <label class="admin__field-label" - for="bundle-option-<?= /* @escapeNotVerified */ $_option->getId() ?>-<?= /* @escapeNotVerified */ $_selection->getSelectionId() ?>"> - <span><?= /* @escapeNotVerified */ $block->getSelectionQtyTitlePrice($_selection) ?></span> + for="bundle-option-<?= $block->escapeHtmlAttr($_option->getId()) ?>-<?= $block->escapeHtmlAttr($_selection->getSelectionId()) ?>"> + <span><?= /* @noEscape */ $block->getSelectionQtyTitlePrice($_selection) ?></span> </label> - <?php if ($_option->getRequired()): ?> - <?= /* @escapeNotVerified */ $block->setValidationContainer('bundle-option-' . $_option->getId() . '-' . $_selection->getSelectionId(), 'bundle-option-' . $_option->getId() . '-container') ?> + <?php if ($_option->getRequired()) : ?> + <?= /* @noEscape */ $block->setValidationContainer('bundle-option-' . $_option->getId() . '-' . $_selection->getSelectionId(), 'bundle-option-' . $_option->getId() . '-container') ?> <?php endif;?> </div> <?php endforeach; ?> - <div id="bundle-option-<?= /* @escapeNotVerified */ $_option->getId() ?>-container"></div> + <div id="bundle-option-<?= $block->escapeHtmlAttr($_option->getId()) ?>-container"></div> <?php endif; ?> </div> </div> diff --git a/app/code/Magento/Bundle/view/adminhtml/templates/product/composite/fieldset/options/type/multi.phtml b/app/code/Magento/Bundle/view/adminhtml/templates/product/composite/fieldset/options/type/multi.phtml index 8c13dd6479d4d..f4c4e3e51ae09 100644 --- a/app/code/Magento/Bundle/view/adminhtml/templates/product/composite/fieldset/options/type/multi.phtml +++ b/app/code/Magento/Bundle/view/adminhtml/templates/product/composite/fieldset/options/type/multi.phtml @@ -3,32 +3,34 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - +// phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis ?> <?php /* @var $block \Magento\Bundle\Block\Adminhtml\Catalog\Product\Composite\Fieldset\Options\Type\Multi */ ?> <?php $_option = $block->getOption(); ?> <?php $_selections = $_option->getSelections(); ?> -<?php $_skipSaleableCheck = $this->helper('Magento\Catalog\Helper\Product')->getSkipSaleableCheck(); ?> -<div class="field admin__field <?php if ($_option->getRequired()) echo ' required' ?><?php if ($_option->getDecoratedIsLast()):?> last<?php endif; ?>"> +<?php $_skipSaleableCheck = $this->helper(Magento\Catalog\Helper\Product::class)->getSkipSaleableCheck(); ?> +<div class="field admin__field <?php if ($_option->getRequired()) { echo ' required'; } ?><?php if ($_option->getDecoratedIsLast()) :?> last<?php endif; ?>"> <label class="label admin__field-label"><span><?= $block->escapeHtml($_option->getTitle()) ?></span></label> <div class="control admin__field-control"> - <?php if (count($_selections) == 1 && $_option->getRequired()): ?> - <?= /* @escapeNotVerified */ $block->getSelectionQtyTitlePrice($_selections[0]) ?> - <input type="hidden" name="bundle_option[<?= /* @escapeNotVerified */ $_option->getId() ?>]" - value="<?= /* @escapeNotVerified */ $_selections[0]->getSelectionId() ?>" - price="<?= /* @escapeNotVerified */ $block->getSelectionPrice($_selections[0]) ?>" /> - <?php else: ?> - <select multiple="multiple" size="5" id="bundle-option-<?= /* @escapeNotVerified */ $_option->getId() ?>" - name="bundle_option[<?= /* @escapeNotVerified */ $_option->getId() ?>][]" - class="admin__control-multiselect bundle-option-<?= /* @escapeNotVerified */ $_option->getId() ?><?php if ($_option->getRequired()) echo ' required-entry' ?> multiselect change-container-classname" + <?php if (count($_selections) == 1 && $_option->getRequired()) : ?> + <?= /* @noEscape */ $block->getSelectionQtyTitlePrice($_selections[0]) ?> + <input type="hidden" name="bundle_option[<?= $block->escapeHtmlAttr($_option->getId()) ?>]" + value="<?= $block->escapeHtmlAttr($_selections[0]->getSelectionId()) ?>" + price="<?= $block->escapeHtmlAttr($block->getSelectionPrice($_selections[0])) ?>" /> + <?php else : ?> + <select multiple="multiple" size="5" id="bundle-option-<?= $block->escapeHtmlAttr($_option->getId()) ?>" + name="bundle_option[<?= $block->escapeHtmlAttr($_option->getId()) ?>][]" + class="admin__control-multiselect bundle-option-<?= $block->escapeHtmlAttr($_option->getId()) ?><?php if ($_option->getRequired()) { echo ' required-entry'; } ?> multiselect change-container-classname" onchange="ProductConfigure.bundleControl.changeSelection(this)"> - <?php if(!$_option->getRequired()): ?> - <option value=""><?= /* @escapeNotVerified */ __('None') ?></option> + <?php if (!$_option->getRequired()) : ?> + <option value=""><?= $block->escapeHtml(__('None')) ?></option> <?php endif; ?> - <?php foreach ($_selections as $_selection): ?> - <option value="<?= /* @escapeNotVerified */ $_selection->getSelectionId() ?>"<?php if ($block->isSelected($_selection)) echo ' selected="selected"' ?><?php if (!$_selection->isSaleable() && !$_skipSaleableCheck) echo ' disabled="disabled"' ?> price="<?= /* @escapeNotVerified */ $block->getSelectionPrice($_selection) ?>"><?= /* @escapeNotVerified */ $block->getSelectionQtyTitlePrice($_selection, false) ?></option> + <?php foreach ($_selections as $_selection) : ?> + <option value="<?= $block->escapeHtmlAttr($_selection->getSelectionId()) ?>" + <?php if ($block->isSelected($_selection)) { echo ' selected="selected"'; } ?> + <?php if (!$_selection->isSaleable() && !$_skipSaleableCheck) { echo ' disabled="disabled"'; } ?> + price="<?= $block->escapeHtmlAttr($block->getSelectionPrice($_selection)) ?>"> + <?= /* @noEscape */ $block->getSelectionQtyTitlePrice($_selection, false) ?></option> <?php endforeach; ?> </select> <?php endif; ?> diff --git a/app/code/Magento/Bundle/view/adminhtml/templates/product/composite/fieldset/options/type/radio.phtml b/app/code/Magento/Bundle/view/adminhtml/templates/product/composite/fieldset/options/type/radio.phtml index f0912979a9248..0c3835fb32af8 100644 --- a/app/code/Magento/Bundle/view/adminhtml/templates/product/composite/fieldset/options/type/radio.phtml +++ b/app/code/Magento/Bundle/view/adminhtml/templates/product/composite/fieldset/options/type/radio.phtml @@ -3,69 +3,73 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - +// phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis ?> <?php /* @var $block \Magento\Bundle\Block\Adminhtml\Catalog\Product\Composite\Fieldset\Options\Type\Radio */ ?> <?php $_option = $block->getOption(); ?> <?php $_selections = $_option->getSelections(); ?> <?php $_default = $_option->getDefaultSelection(); ?> -<?php $_skipSaleableCheck = $this->helper('Magento\Catalog\Helper\Product')->getSkipSaleableCheck(); ?> +<?php $_skipSaleableCheck = $this->helper(Magento\Catalog\Helper\Product::class)->getSkipSaleableCheck(); ?> <?php list($_defaultQty, $_canChangeQty) = $block->getDefaultValues(); ?> -<div class="field admin__field options<?php if ($_option->getRequired()) echo ' required' ?>"> +<div class="field admin__field options<?php if ($_option->getRequired()) { echo ' required'; } ?>"> <label class="label admin__field-label"><span><?= $block->escapeHtml($_option->getTitle()) ?></span></label> <div class="control admin__field-control"> - <div class="nested<?php if ($_option->getDecoratedIsLast()):?> last<?php endif; ?>"> - <?php if ($block->showSingle()): ?> - <?= /* @escapeNotVerified */ $block->getSelectionTitlePrice($_selections[0]) ?> + <div class="nested<?php if ($_option->getDecoratedIsLast()) :?> last<?php endif; ?>"> + <?php if ($block->showSingle()) : ?> + <?= /* @noEscape */ $block->getSelectionTitlePrice($_selections[0]) ?> <input type="hidden" - name="bundle_option[<?= /* @escapeNotVerified */ $_option->getId() ?>]" - value="<?= /* @escapeNotVerified */ $_selections[0]->getSelectionId() ?>" - price="<?= /* @escapeNotVerified */ $block->getSelectionPrice($_selections[0]) ?>" /> - <?php else:?> - <?php if (!$_option->getRequired()): ?> + name="bundle_option[<?= $block->escapeHtmlAttr($_option->getId()) ?>]" + value="<?= $block->escapeHtmlAttr($_selections[0]->getSelectionId()) ?>" + price="<?= $block->escapeHtmlAttr($block->getSelectionPrice($_selections[0])) ?>" /> + <?php else :?> + <?php if (!$_option->getRequired()) : ?> <div class="field choice admin__field admin__field-option"> <input type="radio" class="radio admin__control-radio" - id="bundle-option-<?= /* @escapeNotVerified */ $_option->getId() ?>" - name="bundle_option[<?= /* @escapeNotVerified */ $_option->getId() ?>]"<?= ($_default && $_default->isSalable()) ? '' : ' checked="checked" ' ?> + id="bundle-option-<?= $block->escapeHtmlAttr($_option->getId()) ?>" + name="bundle_option[<?= $block->escapeHtmlAttr($_option->getId()) ?>]"<?= ($_default && $_default->isSalable()) ? '' : ' checked="checked" ' ?> value="" onclick="ProductConfigure.bundleControl.changeSelection(this)" /> <label class="admin__field-label" - for="bundle-option-<?= /* @escapeNotVerified */ $_option->getId() ?>"><span><?= /* @escapeNotVerified */ __('None') ?></span></label> + for="bundle-option-<?= $block->escapeHtmlAttr($_option->getId()) ?>"><span><?= $block->escapeHtml(__('None')) ?></span></label> </div> <?php endif; ?> - <?php foreach ($_selections as $_selection): ?> + <?php foreach ($_selections as $_selection) : ?> <div class="field choice admin__field admin__field-option"> <input type="radio" class="radio admin__control-radio <?= $_option->getRequired() ? ' validate-one-required-by-name' : '' ?> change-container-classname" - id="bundle-option-<?= /* @escapeNotVerified */ $_option->getId() ?>-<?= /* @escapeNotVerified */ $_selection->getSelectionId() ?>" - name="bundle_option[<?= /* @escapeNotVerified */ $_option->getId() ?>]" - <?php if ($block->isSelected($_selection)) echo ' checked="checked"' ?><?php if (!$_selection->isSaleable() && !$_skipSaleableCheck) echo ' disabled="disabled"' ?> - value="<?= /* @escapeNotVerified */ $_selection->getSelectionId() ?>" + id="bundle-option-<?= $block->escapeHtmlAttr($_option->getId()) ?>-<?= $block->escapeHtmlAttr($_selection->getSelectionId()) ?>" + name="bundle_option[<?= $block->escapeHtmlAttr($_option->getId()) ?>]" + <?php if ($block->isSelected($_selection)) { echo ' checked="checked"'; } ?> + <?php if (!$_selection->isSaleable() && !$_skipSaleableCheck) { echo ' disabled="disabled"'; } ?> + value="<?= $block->escapeHtmlAttr($_selection->getSelectionId()) ?>" onclick="ProductConfigure.bundleControl.changeSelection(this)" - price="<?= /* @escapeNotVerified */ $block->getSelectionPrice($_selection) ?>" - qtyId="bundle-option-<?= /* @escapeNotVerified */ $_option->getId() ?>-qty-input" /> + price="<?= $block->escapeHtmlAttr($block->getSelectionPrice($_selection)) ?>" + qtyId="bundle-option-<?= $block->escapeHtmlAttr($_option->getId()) ?>-qty-input" /> <label class="admin__field-label" - for="bundle-option-<?= /* @escapeNotVerified */ $_option->getId() ?>-<?= /* @escapeNotVerified */ $_selection->getSelectionId() ?>"><span><?= /* @escapeNotVerified */ $block->getSelectionTitlePrice($_selection) ?></span></label> - <?php if ($_option->getRequired()): ?> - <?= /* @escapeNotVerified */ $block->setValidationContainer('bundle-option-'.$_option->getId().'-'.$_selection->getSelectionId(), 'bundle-option-'.$_option->getId().'-container') ?> + for="bundle-option-<?= $block->escapeHtmlAttr($_option->getId()) ?>-<?= $block->escapeHtmlAttr($_selection->getSelectionId()) ?>"> + <span><?= /* @noEscape */ $block->getSelectionTitlePrice($_selection) ?></span> + </label> + <?php if ($_option->getRequired()) : ?> + <?= /* @noEscape */ $block->setValidationContainer('bundle-option-'.$_option->getId().'-'.$_selection->getSelectionId(), 'bundle-option-'.$_option->getId().'-container') ?> <?php endif; ?> </div> <?php endforeach; ?> - <div id="bundle-option-<?= /* @escapeNotVerified */ $_option->getId() ?>-container"></div> + <div id="bundle-option-<?= $block->escapeHtmlAttr($_option->getId()) ?>-container"></div> <?php endif; ?> <div class="field admin__field qty"> <label class="label admin__field-label" - for="bundle-option-<?= /* @escapeNotVerified */ $_option->getId() ?>-qty-input"><span><?= /* @escapeNotVerified */ __('Quantity:') ?></span></label> - <div class="control admin__field-control"><input <?php if (!$_canChangeQty) echo ' disabled="disabled"' ?> - id="bundle-option-<?= /* @escapeNotVerified */ $_option->getId() ?>-qty-input" - class="input-text admin__control-text qty<?php if (!$_canChangeQty) echo ' qty-disabled' ?>" + for="bundle-option-<?= $block->escapeHtmlAttr($_option->getId()) ?>-qty-input"> + <span><?= $block->escapeHtml(__('Quantity:')) ?></span> + </label> + <div class="control admin__field-control"><input <?php if (!$_canChangeQty) { echo ' disabled="disabled"'; } ?> + id="bundle-option-<?= $block->escapeHtmlAttr($_option->getId()) ?>-qty-input" + class="input-text admin__control-text qty<?php if (!$_canChangeQty) { echo ' qty-disabled'; } ?>" type="text" - name="bundle_option_qty[<?= /* @escapeNotVerified */ $_option->getId() ?>]" value="<?= /* @escapeNotVerified */ $_defaultQty ?>" /> + name="bundle_option_qty[<?= $block->escapeHtmlAttr($_option->getId()) ?>]" + value="<?= $block->escapeHtmlAttr($_defaultQty) ?>" /> </div> </div> </div> diff --git a/app/code/Magento/Bundle/view/adminhtml/templates/product/composite/fieldset/options/type/select.phtml b/app/code/Magento/Bundle/view/adminhtml/templates/product/composite/fieldset/options/type/select.phtml index 32766f62163ed..fbb7f7fbb7b38 100644 --- a/app/code/Magento/Bundle/view/adminhtml/templates/product/composite/fieldset/options/type/select.phtml +++ b/app/code/Magento/Bundle/view/adminhtml/templates/product/composite/fieldset/options/type/select.phtml @@ -3,36 +3,39 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - +// phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis ?> <?php /* @var $block \Magento\Bundle\Block\Adminhtml\Catalog\Product\Composite\Fieldset\Options\Type\Select */ ?> <?php $_option = $block->getOption(); ?> <?php $_selections = $_option->getSelections(); ?> <?php $_default = $_option->getDefaultSelection(); ?> -<?php $_skipSaleableCheck = $this->helper('Magento\Catalog\Helper\Product')->getSkipSaleableCheck(); ?> +<?php $_skipSaleableCheck = $this->helper(Magento\Catalog\Helper\Product::class)->getSkipSaleableCheck(); ?> <?php list($_defaultQty, $_canChangeQty) = $block->getDefaultValues(); ?> -<div class="field admin__field option<?php if ($_option->getDecoratedIsLast()):?> last<?php endif; ?><?php if ($_option->getRequired()) echo ' required _required' ?>"> +<div class="field admin__field option<?php if ($_option->getDecoratedIsLast()) :?> last<?php endif; ?><?php if ($_option->getRequired()) { echo ' required _required'; } ?>"> <label class="label admin__field-label"><span><?= $block->escapeHtml($_option->getTitle()) ?></span></label> <div class="control admin__field-control"> - <?php if ($block->showSingle()): ?> - <?= /* @escapeNotVerified */ $block->getSelectionTitlePrice($_selections[0]) ?> - <input type="hidden" name="bundle_option[<?= /* @escapeNotVerified */ $_option->getId() ?>]" - value="<?= /* @escapeNotVerified */ $_selections[0]->getSelectionId() ?>" - price="<?= /* @escapeNotVerified */ $block->getSelectionPrice($_selections[0]) ?>" /> - <?php else:?> - <select id="bundle-option-<?= /* @escapeNotVerified */ $_option->getId() ?>" - name="bundle_option[<?= /* @escapeNotVerified */ $_option->getId() ?>]" - class="bundle-option-<?= /* @escapeNotVerified */ $_option->getId() ?><?php if ($_option->getRequired()) echo ' required-entry' ?> select admin__control-select change-container-classname" + <?php if ($block->showSingle()) : ?> + <?= /* @noEscape */ $block->getSelectionTitlePrice($_selections[0]) ?> + <input type="hidden" + name="bundle_option[<?= $block->escapeHtmlAttr($_option->getId()) ?>]" + value="<?= $block->escapeHtmlAttr($_selections[0]->getSelectionId()) ?>" + price="<?= $block->escapeHtmlAttr($block->getSelectionPrice($_selections[0])) ?>" /> + <?php else :?> + <select id="bundle-option-<?= $block->escapeHtmlAttr($_option->getId()) ?>" + name="bundle_option[<?= $block->escapeHtmlAttr($_option->getId()) ?>]" + class="bundle-option-<?= $block->escapeHtmlAttr($_option->getId()) ?><?php if ($_option->getRequired()) { echo ' required-entry'; } ?> select admin__control-select change-container-classname" onchange="ProductConfigure.bundleControl.changeSelection(this)"> - <option value=""><?= /* @escapeNotVerified */ __('Choose a selection...') ?></option> - <?php foreach ($_selections as $_selection): ?> + <option value=""><?= $block->escapeHtml(__('Choose a selection...')) ?></option> + <?php foreach ($_selections as $_selection) : ?> <option - value="<?= /* @escapeNotVerified */ $_selection->getSelectionId() ?>"<?php if ($block->isSelected($_selection)) echo ' selected="selected"' ?><?php if (!$_selection->isSaleable() && !$_skipSaleableCheck) echo ' disabled="disabled"' ?> - price="<?= /* @escapeNotVerified */ $block->getSelectionPrice($_selection) ?>" - qtyId="bundle-option-<?= /* @escapeNotVerified */ $_option->getId() ?>-qty-input"><?= /* @escapeNotVerified */ $block->getSelectionTitlePrice($_selection, false) ?></option> + value="<?= $block->escapeHtmlAttr($_selection->getSelectionId()) ?>" + <?php if ($block->isSelected($_selection)) { echo ' selected="selected"'; } ?> + <?php if (!$_selection->isSaleable() && !$_skipSaleableCheck) { echo ' disabled="disabled"'; } ?> + price="<?= $block->escapeHtmlAttr($block->getSelectionPrice($_selection)) ?>" + qtyId="bundle-option-<?= $block->escapeHtmlAttr($_option->getId()) ?>-qty-input"> + <?= /* @noEscape */ $block->getSelectionTitlePrice($_selection, false) ?> + </option> <?php endforeach; ?> </select> <?php endif; ?> @@ -40,12 +43,16 @@ <div class="nested"> <div class="field admin__field qty"> <label class="label admin__field-label" - for="bundle-option-<?= /* @escapeNotVerified */ $_option->getId() ?>-qty-input"><span><?= /* @escapeNotVerified */ __('Quantity:') ?></span></label> + for="bundle-option-<?= $block->escapeHtmlAttr($_option->getId()) ?>-qty-input"> + <span><?= $block->escapeHtml(__('Quantity:')) ?></span> + </label> <div class="control admin__field-control"> - <input <?php if (!$_canChangeQty) echo ' disabled="disabled"' ?> - id="bundle-option-<?= /* @escapeNotVerified */ $_option->getId() ?>-qty-input" - class="input-text admin__control-text qty<?php if (!$_canChangeQty) echo ' qty-disabled' ?>" type="text" - name="bundle_option_qty[<?= /* @escapeNotVerified */ $_option->getId() ?>]" value="<?= /* @escapeNotVerified */ $_defaultQty ?>" /> + <input <?php if (!$_canChangeQty) { echo ' disabled="disabled"'; } ?> + id="bundle-option-<?= $block->escapeHtmlAttr($_option->getId()) ?>-qty-input" + class="input-text admin__control-text qty<?php if (!$_canChangeQty) { echo ' qty-disabled'; } ?>" + type="text" + name="bundle_option_qty[<?= $block->escapeHtmlAttr($_option->getId()) ?>]" + value="<?= $block->escapeHtmlAttr($_defaultQty) ?>" /> </div> </div> </div> diff --git a/app/code/Magento/Bundle/view/adminhtml/templates/product/edit/bundle.phtml b/app/code/Magento/Bundle/view/adminhtml/templates/product/edit/bundle.phtml index 5b27412dd885b..c8ab6cc5b98d2 100644 --- a/app/code/Magento/Bundle/view/adminhtml/templates/product/edit/bundle.phtml +++ b/app/code/Magento/Bundle/view/adminhtml/templates/product/edit/bundle.phtml @@ -4,8 +4,6 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - /** @var $block \Magento\Bundle\Block\Adminhtml\Catalog\Product\Edit\Tab\Bundle */ ?> <script> @@ -19,14 +17,20 @@ if(typeof Bundle=='undefined') { <div id="bundle_product_container" class="entry-edit form-inline"> <fieldset class="fieldset"> <div class="field field-ship-bundle-items"> - <label for="shipment_type" class="label"><?= /* @escapeNotVerified */ __('Ship Bundle Items') ?></label> + <label for="shipment_type" class="label"><?= $block->escapeHtml(__('Ship Bundle Items')) ?></label> <div class="control"> - <select <?php if ($block->isReadonly()): ?>disabled="disabled" <?php endif;?> + <select <?php if ($block->isReadonly()) : ?>disabled="disabled" <?php endif;?> id="shipment_type" - name="<?= /* @escapeNotVerified */ $block->getFieldSuffix() ?>[shipment_type]" + name="<?= $block->escapeHtmlAttr($block->getFieldSuffix()) ?>[shipment_type]" class="select"> - <option value="1"><?= /* @escapeNotVerified */ __('Separately') ?></option> - <option value="0"<?php if ($block->getProduct()->getShipmentType() == 0): ?> selected="selected"<?php endif; ?>><?= /* @escapeNotVerified */ __('Together') ?></option> + <option value="1"><?= $block->escapeHtml(__('Separately')) ?></option> + <option value="0" + <?php if ($block->getProduct()->getShipmentType() == 0) : ?> + selected="selected" + <?php endif; ?> + > + <?= $block->escapeHtml(__('Together')) ?> + </option> </select> </div> </div> @@ -48,7 +52,7 @@ require(["prototype", "mage/adminhtml/form"], function(){ // re-bind form elements onchange varienWindowOnload(true); - <?php if ($block->isReadonly()):?> + <?php if ($block->isReadonly()) :?> $('product_bundle_container').select('input', 'select', 'textarea', 'button').each(function(input){ input.disabled = true; if (input.tagName.toLowerCase() == 'button') { diff --git a/app/code/Magento/Bundle/view/adminhtml/templates/product/edit/bundle/option.phtml b/app/code/Magento/Bundle/view/adminhtml/templates/product/edit/bundle/option.phtml index 783d71beb1646..4d68d363b7484 100644 --- a/app/code/Magento/Bundle/view/adminhtml/templates/product/edit/bundle/option.phtml +++ b/app/code/Magento/Bundle/view/adminhtml/templates/product/edit/bundle/option.phtml @@ -3,16 +3,14 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - +// phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis /** @var $block \Magento\Bundle\Block\Adminhtml\Catalog\Product\Edit\Tab\Bundle\Option */ ?> <script id="bundle-option-template" type="text/x-magento-template"> - <div id="<?= /* @escapeNotVerified */ $block->getFieldId() ?>_<%- data.index %>" class="option-box"> - <div class="fieldset-wrapper admin__collapsible-block-wrapper opened" id="<?= /* @escapeNotVerified */ $block->getFieldId() ?>_<%- data.index %>-wrapper"> + <div id="<?= $block->escapeHtmlAttr($block->getFieldId()) ?>_<%- data.index %>" class="option-box"> + <div class="fieldset-wrapper admin__collapsible-block-wrapper opened" id="<?= $block->escapeHtmlAttr($block->getFieldId()) ?>_<%- data.index %>-wrapper"> <div class="fieldset-wrapper-title"> - <strong class="admin__collapsible-title" data-toggle="collapse" data-target="#<?= /* @escapeNotVerified */ $block->getFieldId() ?>_<%- data.index %>-content"> + <strong class="admin__collapsible-title" data-toggle="collapse" data-target="#<?= $block->escapeHtmlAttr($block->getFieldId()) ?>_<%- data.index %>-content"> <span><%- data.default_title %></span> </strong> <div class="actions"> @@ -20,55 +18,56 @@ </div> <div data-role="draggable-handle" class="draggable-handle"></div> </div> - <div class="fieldset-wrapper-content in collapse" id="<?= /* @escapeNotVerified */ $block->getFieldId() ?>_<%- data.index %>-content"> + <div class="fieldset-wrapper-content in collapse" id="<?= $block->escapeHtmlAttr($block->getFieldId()) ?>_<%- data.index %>-content"> <fieldset class="fieldset"> <fieldset class="fieldset-alt"> <div class="field field-option-title required"> - <label class="label" for="id_<?= /* @escapeNotVerified */ $block->getFieldName() ?>_<%- data.index %>_title"> - <?= /* @escapeNotVerified */ __('Option Title') ?> + <label class="label" for="id_<?= $block->escapeHtmlAttr($block->getFieldName()) ?>_<%- data.index %>_title"> + <?= $block->escapeHtml(__('Option Title')) ?> </label> <div class="control"> - <?php if ($block->isDefaultStore()): ?> + <?php if ($block->isDefaultStore()) : ?> <input class="input-text required-entry" type="text" - name="<?= /* @escapeNotVerified */ $block->getFieldName() ?>[<%- data.index %>][title]" - id="id_<?= /* @escapeNotVerified */ $block->getFieldName() ?>_<%- data.index %>_title" + name="<?= $block->escapeHtmlAttr($block->getFieldName()) ?>[<%- data.index %>][title]" + id="id_<?= $block->escapeHtmlAttr($block->getFieldName()) ?>_<%- data.index %>_title" value="<%- data.title %>" data-original-value="<%- data.title %>" /> - <?php else: ?> + <?php else : ?> <input class="input-text required-entry" type="text" - name="<?= /* @escapeNotVerified */ $block->getFieldName() ?>[<%- data.index %>][default_title]" - id="id_<?= /* @escapeNotVerified */ $block->getFieldName() ?>_<%- data.index %>_default_title" + name="<?= $block->escapeHtmlAttr($block->getFieldName()) ?>[<%- data.index %>][default_title]" + id="id_<?= $block->escapeHtmlAttr($block->getFieldName()) ?>_<%- data.index %>_default_title" value="<%- data.default_title %>" data-original-value="<%- data.default_title %>" /> <?php endif; ?> <input type="hidden" - id="<?= /* @escapeNotVerified */ $block->getFieldId() ?>_id_<%- data.index %>" - name="<?= /* @escapeNotVerified */ $block->getFieldName() ?>[<%- data.index %>][option_id]" + id="<?= $block->escapeHtmlAttr($block->getFieldId()) ?>_id_<%- data.index %>" + name="<?= $block->escapeHtmlAttr($block->getFieldName()) ?>[<%- data.index %>][option_id]" value="<%- data.option_id %>" /> <input type="hidden" - name="<?= /* @escapeNotVerified */ $block->getFieldName() ?>[<%- data.index %>][delete]" + name="<?= $block->escapeHtmlAttr($block->getFieldName()) ?>[<%- data.index %>][delete]" value="" data-state="deleted" /> </div> </div> - <?php if (!$block->isDefaultStore()): ?> + <?php if (!$block->isDefaultStore()) : ?> <div class="field field-option-store-view required"> - <label class="label" for="id_<?= /* @escapeNotVerified */ $block->getFieldName() ?>_<%- data.index %>_title_store"> - <?= /* @escapeNotVerified */ __('Store View Title') ?> + <label class="label" for="id_<?= $block->escapeHtmlAttr($block->getFieldName()) ?>_<%- data.index %>_title_store"> + <?= $block->escapeHtml(__('Store View Title')) ?> </label> <div class="control"> - <input class="input-text required-entry" type="text" - name="<?= /* @escapeNotVerified */ $block->getFieldName() ?>[<%- data.index %>][title]" - id="id_<?= /* @escapeNotVerified */ $block->getFieldName() ?>_<%- data.index %>_title_store" + <input class="input-text required-entry" + type="text" + name="<?= $block->escapeHtmlAttr($block->getFieldName()) ?>[<%- data.index %>][title]" + id="id_<?= $block->escapeHtmlAttr($block->getFieldName()) ?>_<%- data.index %>_title_store" value="<%- data.title %>" /> </div> </div> <?php endif; ?> <div class="field field-option-input-type required"> - <label class="label" for="<?= /* @escapeNotVerified */ $block->getFieldId() . '_<%- data.index %>_type' ?>"> - <?= /* @escapeNotVerified */ __('Input Type') ?> + <label class="label" for="<?= $block->escapeHtmlAttr($block->getFieldId() . '_<%- data.index %>_type') ?>"> + <?= $block->escapeHtml(__('Input Type')) ?> </label> <div class="control"> <?= $block->getTypeSelectHtml() ?> @@ -81,19 +80,19 @@ checked="checked" id="field-option-req" /> <label for="field-option-req"> - <?= /* @escapeNotVerified */ __('Required') ?> + <?= $block->escapeHtml(__('Required')) ?> </label> <span style="display:none"><?= $block->getRequireSelectHtml() ?></span> </div> </div> <div class="field field-option-position no-display"> <label class="label" for="field-option-position"> - <?= /* @escapeNotVerified */ __('Position') ?> + <?= $block->escapeHtml(__('Position')) ?> </label> <div class="control"> <input class="input-text validate-zero-or-greater" type="text" - name="<?= /* @escapeNotVerified */ $block->getFieldName() ?>[<%- data.index %>][position]" + name="<?= $block->escapeHtmlAttr($block->getFieldName()) ?>[<%- data.index %>][position]" value="<%- data.position %>" id="field-option-position" /> </div> @@ -101,13 +100,13 @@ </fieldset> <div class="no-products-message"> - <?= /* @escapeNotVerified */ __('There are no products in this option.') ?> + <?= $block->escapeHtml(__('There are no products in this option.')) ?> </div> <?= $block->getAddSelectionButtonHtml() ?> </fieldset> </div> </div> - <div id="<?= /* @escapeNotVerified */ $block->getFieldId() ?>_search_<%- data.index %>" class="selection-search"></div> + <div id="<?= $block->escapeHtmlAttr($block->getFieldId()) ?>_search_<%- data.index %>" class="selection-search"></div> </div> </script> @@ -141,7 +140,7 @@ function changeInputType(oldObject, oType) { Bundle.Option = Class.create(); Bundle.Option.prototype = { - idLabel : '<?= /* @escapeNotVerified */ $block->getFieldId() ?>', + idLabel : '<?= $block->escapeJs($block->getFieldId()) ?>', templateText : '', itemsCount : 0, initialize : function(template) { @@ -150,7 +149,7 @@ Bundle.Option.prototype = { add : function(data) { if (!data) { - data = <?= /* @escapeNotVerified */ $this->helper('Magento\Framework\Json\Helper\Data')->jsonEncode(['default_title' => __('New Option')]) ?>; + data = <?= /* @noEscape */ $this->helper(Magento\Framework\Json\Helper\Data::class)->jsonEncode(['default_title' => __('New Option')]) ?>; } else { data.title = data.title.replace(/</g, "<"); data.title = data.title.replace(/"/g, """); @@ -280,17 +279,17 @@ Bundle.Option.prototype = { var optionIndex = 0; bOption = new Bundle.Option(optionTemplate); <?php - foreach ($block->getOptions() as $_option) { - /** @var $_option \Magento\Bundle\Model\Option */ - /* @escapeNotVerified */ echo 'optionIndex = bOption.add(', $_option->toJson(), ');', PHP_EOL; - if ($_option->getSelections()) { - foreach ($_option->getSelections() as $_selection) { - /** @var $_selection \Magento\Catalog\Model\Product */ - $_selection->setName($block->escapeHtml($_selection->getName())); - /* @escapeNotVerified */ echo 'bSelection.addRow(optionIndex,', $_selection->toJson(), ');', PHP_EOL; - } +foreach ($block->getOptions() as $_option) { + /** @var $_option \Magento\Bundle\Model\Option */ + /* @noEscape */ echo 'optionIndex = bOption.add(', $_option->toJson(), ');', PHP_EOL; + if ($_option->getSelections()) { + foreach ($_option->getSelections() as $_selection) { + /** @var $_selection \Magento\Catalog\Model\Product */ + $_selection->setName($block->escapeHtml($_selection->getName())); + /* @noEscape */ echo 'bSelection.addRow(optionIndex,', $_selection->toJson(), ');', PHP_EOL; } } +} ?> function togglePriceType() { bOption['priceType' + ($('price_type').value == '1' ? 'Fixed' : 'Dynamic')](); diff --git a/app/code/Magento/Bundle/view/adminhtml/templates/product/edit/bundle/option/selection.phtml b/app/code/Magento/Bundle/view/adminhtml/templates/product/edit/bundle/option/selection.phtml index 91c245afe5717..0f1167f3d3eaa 100644 --- a/app/code/Magento/Bundle/view/adminhtml/templates/product/edit/bundle/option/selection.phtml +++ b/app/code/Magento/Bundle/view/adminhtml/templates/product/edit/bundle/option/selection.phtml @@ -4,8 +4,6 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - /** @var $block \Magento\Bundle\Block\Adminhtml\Catalog\Product\Edit\Tab\Bundle\Option\Selection */ ?> <script id="bundle-option-selection-box-template" type="text/x-magento-template"> @@ -13,16 +11,16 @@ <thead> <tr class="headings"> <th class="col-draggable"></th> - <th class="col-default"><?= /* @escapeNotVerified */ __('Default') ?></th> - <th class="col-name"><?= /* @escapeNotVerified */ __('Name') ?></th> - <th class="col-sku"><?= /* @escapeNotVerified */ __('SKU') ?></th> - <?php if ($block->getCanReadPrice() !== false): ?> - <th class="col-price price-type-box"><?= /* @escapeNotVerified */ __('Price') ?></th> - <th class="col-price price-type-box"><?= /* @escapeNotVerified */ __('Price Type') ?></th> + <th class="col-default"><?= $block->escapeHtml(__('Default')) ?></th> + <th class="col-name"><?= $block->escapeHtml(__('Name')) ?></th> + <th class="col-sku"><?= $block->escapeHtml(__('SKU')) ?></th> + <?php if ($block->getCanReadPrice() !== false) : ?> + <th class="col-price price-type-box"><?= $block->escapeHtml(__('Price')) ?></th> + <th class="col-price price-type-box"><?= $block->escapeHtml(__('Price Type')) ?></th> <?php endif; ?> - <th class="col-qty"><?= /* @escapeNotVerified */ __('Default Quantity') ?></th> - <th class="col-uqty qty-box"><?= /* @escapeNotVerified */ __('User Defined') ?></th> - <th class="col-order type-order" style="display:none"><?= /* @escapeNotVerified */ __('Position') ?></th> + <th class="col-qty"><?= $block->escapeHtml(__('Default Quantity')) ?></th> + <th class="col-uqty qty-box"><?= $block->escapeHtml(__('User Defined')) ?></th> + <th class="col-order type-order" style="display:none"><?= $block->escapeHtml(__('Position')) ?></th> <th class="col-actions"></th> </tr> </thead> @@ -33,31 +31,38 @@ <script id="bundle-option-selection-row-template" type="text/x-magento-template"> <td class="col-draggable"> <span data-role="draggable-handle" class="draggable-handle"></span> - <input type="hidden" id="<?= /* @escapeNotVerified */ $block->getFieldId() ?>_id<%- data.index %>" - name="<?= /* @escapeNotVerified */ $block->getFieldName() ?>[<%- data.parentIndex %>][<%- data.index %>][selection_id]" + <input type="hidden" + id="<?= $block->escapeHtmlAttr($block->getFieldId()) ?>_id<%- data.index %>" + name="<?= $block->escapeHtmlAttr($block->getFieldName()) ?>[<%- data.parentIndex %>][<%- data.index %>][selection_id]" value="<%- data.selection_id %>"/> - <input type="hidden" name="<?= /* @escapeNotVerified */ $block->getFieldName() ?>[<%- data.parentIndex %>][<%- data.index %>][option_id]" + <input type="hidden" + name="<?= $block->escapeHtmlAttr($block->getFieldName()) ?>[<%- data.parentIndex %>][<%- data.index %>][option_id]" value="<%- data.option_id %>"/> - <input type="hidden" class="product" - name="<?= /* @escapeNotVerified */ $block->getFieldName() ?>[<%- data.parentIndex %>][<%- data.index %>][product_id]" + <input type="hidden" + class="product" + name="<?= $block->escapeHtmlAttr($block->getFieldName()) ?>[<%- data.parentIndex %>][<%- data.index %>][product_id]" value="<%- data.product_id %>"/> - <input type="hidden" name="<?= /* @escapeNotVerified */ $block->getFieldName() ?>[<%- data.parentIndex %>][<%- data.index %>][delete]" - value="" class="delete"/> + <input type="hidden" name="<?= $block->escapeHtmlAttr($block->getFieldName()) ?>[<%- data.parentIndex %>][<%- data.index %>][delete]" + value="" + class="delete"/> </td> <td class="col-default"> - <input onclick="bSelection.checkGroup(event)" type="<%- data.option_type %>" class="default" - name="<?= /* @escapeNotVerified */ $block->getFieldName() ?>[<%- data.parentIndex %>][<%- data.index %>][is_default]" + <input onclick="bSelection.checkGroup(event)" + type="<%- data.option_type %>" + class="default" + name="<?= $block->escapeHtmlAttr($block->getFieldName()) ?>[<%- data.parentIndex %>][<%- data.index %>][is_default]" value="1" <%- data.checked %> /> </td> <td class="col-name"><%- data.name %></td> <td class="col-sku"><%- data.sku %></td> -<?php if ($block->getCanReadPrice() !== false): ?> +<?php if ($block->getCanReadPrice() !== false) : ?> <td class="col-price price-type-box"> - <input id="<?= /* @escapeNotVerified */ $block->getFieldId() ?>_<%- data.index %>_price_value" - class="input-text required-entry validate-zero-or-greater" type="text" - name="<?= /* @escapeNotVerified */ $block->getFieldName() ?>[<%- data.parentIndex %>][<%- data.index %>][selection_price_value]" + <input id="<?= $block->escapeHtmlAttr($block->getFieldId()) ?>_<%- data.index %>_price_value" + class="input-text required-entry validate-zero-or-greater" + type="text" + name="<?= $block->escapeHtmlAttr($block->getFieldName()) ?>[<%- data.parentIndex %>][<%- data.index %>][selection_price_value]" value="<%- data.selection_price_value %>" - <?php if ($block->getCanEditPrice() === false): ?> + <?php if ($block->getCanEditPrice() === false) : ?> disabled="disabled" <?php endif; ?>/> </td> @@ -65,19 +70,23 @@ <?= $block->getPriceTypeSelectHtml() ?> <div><?= $block->getCheckboxScopeHtml() ?></div> </td> -<?php else: ?> - <input type="hidden" id="<?= /* @escapeNotVerified */ $block->getFieldId() ?>_<%- data.index %>_price_value" - name="<?= /* @escapeNotVerified */ $block->getFieldName() ?>[<%- data.parentIndex %>][<%- data.index %>][selection_price_value]" value="0" /> - <input type="hidden" id="<?= /* @escapeNotVerified */ $block->getFieldId() ?>_<%- data.index %>_price_type" - name="<?= /* @escapeNotVerified */ $block->getFieldName() ?>[<%- data.parentIndex %>][<%- data.index %>][selection_price_type]" value="0" /> - <?php if ($block->isUsedWebsitePrice()): ?> - <input type="hidden" id="<?= /* @escapeNotVerified */ $block->getFieldId() ?>_<%- data.index %>_price_scope" - name="<?= /* @escapeNotVerified */ $block->getFieldName() ?>[<%- data.parentIndex %>][<%- data.index %>][default_price_scope]" value="1" /> +<?php else : ?> + <input type="hidden" + id="<?= $block->escapeHtmlAttr($block->getFieldId()) ?>_<%- data.index %>_price_value" + name="<?= $block->escapeHtmlAttr($block->getFieldName()) ?>[<%- data.parentIndex %>][<%- data.index %>][selection_price_value]" value="0" /> + <input type="hidden" + id="<?= $block->escapeHtmlAttr($block->getFieldId()) ?>_<%- data.index %>_price_type" + name="<?= $block->escapeHtmlAttr($block->getFieldName()) ?>[<%- data.parentIndex %>][<%- data.index %>][selection_price_type]" value="0" /> + <?php if ($block->isUsedWebsitePrice()) : ?> + <input type="hidden" + id="<?= $block->escapeHtmlAttr($block->getFieldId()) ?>_<%- data.index %>_price_scope" + name="<?= $block->escapeHtmlAttr($block->getFieldName()) ?>[<%- data.parentIndex %>][<%- data.index %>][default_price_scope]" value="1" /> <?php endif; ?> <?php endif; ?> <td class="col-qty"> - <input class="input-text required-entry validate-greater-zero-based-on-option validate-zero-or-greater" type="text" - name="<?= /* @escapeNotVerified */ $block->getFieldName() ?>[<%- data.parentIndex %>][<%- data.index %>][selection_qty]" + <input class="input-text required-entry validate-greater-zero-based-on-option validate-zero-or-greater" + type="text" + name="<?= $block->escapeHtmlAttr($block->getFieldName()) ?>[<%- data.parentIndex %>][<%- data.index %>][selection_qty]" value="<%- data.selection_qty %>" /> </td> <td class="col-uqty qty-box"> @@ -85,8 +94,9 @@ <span style="display:none"><?= $block->getQtyTypeSelectHtml() ?></span> </td> <td class="col-order type-order" style="display:none"> - <input class="input-text required-entry validate-zero-or-greater" type="text" - name="<?= /* @escapeNotVerified */ $block->getFieldName() ?>[<%- data.parentIndex %>][<%- data.index %>][position]" + <input class="input-text required-entry validate-zero-or-greater" + type="text" + name="<?= $block->escapeHtmlAttr($block->getFieldName()) ?>[<%- data.parentIndex %>][<%- data.index %>][position]" value="<%- data.position %>" /> </td> <td class="col-actions"> @@ -106,7 +116,7 @@ var bundleTemplateBox = jQuery('#bundle-option-selection-box-template').html(), Bundle.Selection = Class.create(); Bundle.Selection.prototype = { - idLabel : '<?= /* @escapeNotVerified */ $block->getFieldId() ?>', + idLabel : '<?= $block->escapeJs($block->getFieldId()) ?>', scopePrice : <?= (int)$block->isUsedWebsitePrice() ?>, templateBox : '', templateRow : '', @@ -115,7 +125,7 @@ Bundle.Selection.prototype = { gridSelection: new Hash(), gridRemoval: new Hash(), gridSelectedProductSkus: [], - selectionSearchUrl: '<?= /* @escapeNotVerified */ $block->getSelectionSearchUrl() ?>', + selectionSearchUrl: '<?= $block->escapeUrl($block->getSelectionSearchUrl()) ?>', initialize : function() { this.templateBox = '<div class="tier form-list" id="' + this.idLabel + '_box_<%- data.parentIndex %>">' + bundleTemplateBox + '</div>'; diff --git a/app/code/Magento/Bundle/view/adminhtml/templates/sales/creditmemo/create/items/renderer.phtml b/app/code/Magento/Bundle/view/adminhtml/templates/sales/creditmemo/create/items/renderer.phtml index 3aba02fadffbb..e65269559a3a9 100644 --- a/app/code/Magento/Bundle/view/adminhtml/templates/sales/creditmemo/create/items/renderer.phtml +++ b/app/code/Magento/Bundle/view/adminhtml/templates/sales/creditmemo/create/items/renderer.phtml @@ -3,9 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - +// phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis ?> <?php /** @@ -21,19 +19,19 @@ <?php $_prevOptionId = '' ?> -<?php if ($block->getOrderOptions() || $_item->getDescription()): ?> +<?php if ($block->getOrderOptions() || $_item->getDescription()) : ?> <?php $_showlastRow = true ?> -<?php else: ?> +<?php else : ?> <?php $_showlastRow = false ?> <?php endif; ?> -<?php foreach ($items as $_item): ?> +<?php foreach ($items as $_item) : ?> <?php $block->setPriceDataObject($_item) ?> <?php $attributes = $block->getSelectionAttributes($_item) ?> - <?php if ($_item->getOrderItem()->getParentItem()): ?> - <?php if ($_prevOptionId != $attributes['option_id']): ?> + <?php if ($_item->getOrderItem()->getParentItem()) : ?> + <?php if ($_prevOptionId != $attributes['option_id']) : ?> <tr> - <td class="col-product"><div class="option-label"><?= /* @escapeNotVerified */ $attributes['option_label'] ?></div></td> + <td class="col-product"><div class="option-label"><?= $block->escapeHtml($attributes['option_label']) ?></div></td> <td> </td> <td> </td> <td> </td> @@ -43,165 +41,164 @@ <td> </td> <td class="last"> </td> </tr> - <?php $_prevOptionId = $attributes['option_id'] ?> + <?php $_prevOptionId = $attributes['option_id'] ?> <?php endif; ?> <?php endif; ?> <tr<?= (++$_index == $_count && !$_showlastRow) ? ' class="border"' : '' ?>> - <?php if (!$_item->getOrderItem()->getParentItem()): ?> + <?php if (!$_item->getOrderItem()->getParentItem()) : ?> <td class="col-product"> <div class="product-title"><?= $block->escapeHtml($_item->getName()) ?></div> <div class="product-sku-block"> - <span><?= /* @escapeNotVerified */ __('SKU') ?>:</span> - <?= implode('<br />', $this->helper('Magento\Catalog\Helper\Data')->splitSku($block->escapeHtml($_item->getSku()))) ?> + <span><?= $block->escapeHtml(__('SKU')) ?>:</span> + <?= /* @noEscape */ implode('<br />', $this->helper(Magento\Catalog\Helper\Data::class)->splitSku($_item->getSku())) ?> </div> </td> - <?php else: ?> + <?php else : ?> <td class="col-product"><div class="option-value"><?= $block->getValueHtml($_item) ?></div></td> <?php endif; ?> <td class="col-price"> - <?php if ($block->canShowPriceInfo($_item)): ?> + <?php if ($block->canShowPriceInfo($_item)) : ?> <?= $block->getColumnHtml($_item, 'price') ?> - <?php else: ?> + <?php else : ?>   <?php endif; ?> </td> <td class="col-ordered-qty"> - <?php if ($block->canShowPriceInfo($_item)): ?> + <?php if ($block->canShowPriceInfo($_item)) : ?> <table class="qty-table"> <tr> - <th><?= /* @escapeNotVerified */ __('Ordered') ?></th> - <td><?= /* @escapeNotVerified */ $_item->getOrderItem()->getQtyOrdered()*1 ?></td> + <th><?= $block->escapeHtml(__('Ordered')) ?></th> + <td><?= $block->escapeHtml($_item->getOrderItem()->getQtyOrdered()*1) ?></td> </tr> - <?php if ((float) $_item->getOrderItem()->getQtyInvoiced()): ?> + <?php if ((float) $_item->getOrderItem()->getQtyInvoiced()) : ?> <tr> - <th><?= /* @escapeNotVerified */ __('Invoiced') ?></th> - <td><?= /* @escapeNotVerified */ $_item->getOrderItem()->getQtyInvoiced()*1 ?></td> + <th><?= $block->escapeHtml(__('Invoiced')) ?></th> + <td><?= $block->escapeHtml($_item->getOrderItem()->getQtyInvoiced()*1) ?></td> </tr> <?php endif; ?> - <?php if ((float) $_item->getOrderItem()->getQtyShipped() && $block->isShipmentSeparately($_item)): ?> + <?php if ((float) $_item->getOrderItem()->getQtyShipped() && $block->isShipmentSeparately($_item)) : ?> <tr> - <th><?= /* @escapeNotVerified */ __('Shipped') ?></th> - <td><?= /* @escapeNotVerified */ $_item->getOrderItem()->getQtyShipped()*1 ?></td> + <th><?= $block->escapeHtml(__('Shipped')) ?></th> + <td><?= $block->escapeHtml($_item->getOrderItem()->getQtyShipped()*1) ?></td> </tr> <?php endif; ?> - <?php if ((float) $_item->getOrderItem()->getQtyRefunded()): ?> + <?php if ((float) $_item->getOrderItem()->getQtyRefunded()) : ?> <tr> - <th><?= /* @escapeNotVerified */ __('Refunded') ?></th> - <td><?= /* @escapeNotVerified */ $_item->getOrderItem()->getQtyRefunded()*1 ?></td> + <th><?= $block->escapeHtml(__('Refunded')) ?></th> + <td><?= $block->escapeHtml($_item->getOrderItem()->getQtyRefunded()*1) ?></td> </tr> <?php endif; ?> - <?php if ((float) $_item->getOrderItem()->getQtyCanceled()): ?> + <?php if ((float) $_item->getOrderItem()->getQtyCanceled()) : ?> <tr> - <th><?= /* @escapeNotVerified */ __('Canceled') ?></th> - <td><?= /* @escapeNotVerified */ $_item->getOrderItem()->getQtyCanceled()*1 ?></td> + <th><?= $block->escapeHtml(__('Canceled')) ?></th> + <td><?= $block->escapeHtml($_item->getOrderItem()->getQtyCanceled()*1) ?></td> </tr> <?php endif; ?> </table> - <?php elseif ($block->isShipmentSeparately($_item)): ?> + <?php elseif ($block->isShipmentSeparately($_item)) : ?> <table class="qty-table"> <tr> - <th><?= /* @escapeNotVerified */ __('Ordered') ?></th> - <td><?= /* @escapeNotVerified */ $_item->getOrderItem()->getQtyOrdered()*1 ?></td> + <th><?= $block->escapeHtml(__('Ordered')) ?></th> + <td><?= $block->escapeHtml($_item->getOrderItem()->getQtyOrdered()*1) ?></td> </tr> - <?php if ((float) $_item->getOrderItem()->getQtyShipped()): ?> + <?php if ((float) $_item->getOrderItem()->getQtyShipped()) : ?> <tr> - <th><?= /* @escapeNotVerified */ __('Shipped') ?></th> - <td><?= /* @escapeNotVerified */ $_item->getOrderItem()->getQtyShipped()*1 ?></td> + <th><?= $block->escapeHtml(__('Shipped')) ?></th> + <td><?= $block->escapeHtml($_item->getOrderItem()->getQtyShipped()*1) ?></td> </tr> <?php endif; ?> </table> - <?php else: ?> + <?php else : ?>   <?php endif; ?> </td> <?php if ($block->canParentReturnToStock($_item)) : ?> <td class="col-return-to-stock"> - <?php if ($block->canShowPriceInfo($_item)): ?> + <?php if ($block->canShowPriceInfo($_item)) : ?> <?php if ($block->canReturnItemToStock($_item)) : ?> <input type="checkbox" class="admin__control-checkbox" - name="creditmemo[items][<?= /* @escapeNotVerified */ $_item->getOrderItemId() ?>][back_to_stock]" - value="1"<?php if ($_item->getBackToStock()):?> checked="checked"<?php endif;?> /> + name="creditmemo[items][<?= $block->escapeHtmlAttr($_item->getOrderItemId()) ?>][back_to_stock]" + value="1"<?php if ($_item->getBackToStock()) :?> checked="checked"<?php endif;?> /> <label class="admin__field-label"></label> <?php endif; ?> - <?php else: ?> + <?php else : ?>   <?php endif; ?> </td> <?php endif; ?> <td class="col-refund col-qty"> - <?php if ($block->canShowPriceInfo($_item)): ?> + <?php if ($block->canShowPriceInfo($_item)) : ?> <?php if ($block->canEditQty()) : ?> <input type="text" class="input-text admin__control-text qty-input" - name="creditmemo[items][<?= /* @escapeNotVerified */ $_item->getOrderItemId() ?>][qty]" - value="<?= /* @escapeNotVerified */ $_item->getQty()*1 ?>" /> - <?php else: ?> - <?= /* @escapeNotVerified */ $_item->getQty()*1 ?> + name="creditmemo[items][<?= $block->escapeHtmlAttr($_item->getOrderItemId()) ?>][qty]" + value="<?= $block->escapeHtmlAttr($_item->getQty()*1) ?>" /> + <?php else : ?> + <?= $block->escapeHtml($_item->getQty()*1) ?> <?php endif; ?> - <?php else: ?> + <?php else : ?>   <?php endif; ?> </td> <td class="col-subtotal"> - <?php if ($block->canShowPriceInfo($_item)): ?> + <?php if ($block->canShowPriceInfo($_item)) : ?> <?= $block->getColumnHtml($_item, 'subtotal') ?> - <?php else: ?> + <?php else : ?>   <?php endif; ?> </td> <td class="col-tax-amount"> - <?php if ($block->canShowPriceInfo($_item)): ?> - <?= /* @escapeNotVerified */ $block->displayPriceAttribute('tax_amount') ?> - <?php else: ?> + <?php if ($block->canShowPriceInfo($_item)) : ?> + <?= /* @noEscape */ $block->displayPriceAttribute('tax_amount') ?> + <?php else : ?>   <?php endif; ?> </td> <td class="col-discont"> - <?php if ($block->canShowPriceInfo($_item)): ?> - <?= /* @escapeNotVerified */ $block->displayPriceAttribute('discount_amount') ?> - <?php else: ?> + <?php if ($block->canShowPriceInfo($_item)) : ?> + <?= /* @noEscape */ $block->displayPriceAttribute('discount_amount') ?> + <?php else : ?>   <?php endif; ?> </td> <td class="col-total last"> - <?php if ($block->canShowPriceInfo($_item)): ?> + <?php if ($block->canShowPriceInfo($_item)) : ?> <?= $block->getColumnHtml($_item, 'total') ?> - <?php else: ?> + <?php else : ?>   <?php endif; ?> </td> </tr> <?php endforeach; ?> -<?php if ($_showlastRow): ?> +<?php if ($_showlastRow) : ?> <tr class="border"> <td class="col-product"> - <?php if ($block->getOrderOptions($_item->getOrderItem())): ?> + <?php if ($block->getOrderOptions($_item->getOrderItem())) : ?> <dl class="item-options"> - <?php foreach ($block->getOrderOptions($_item->getOrderItem()) as $option): ?> - <dt><?= /* @escapeNotVerified */ $option['label'] ?></dt> + <?php foreach ($block->getOrderOptions($_item->getOrderItem()) as $option) : ?> + <dt><?= $block->escapeHtml($option['label']) ?></dt> <dd> - <?php if (isset($option['custom_view']) && $option['custom_view']): ?> - <?= /* @escapeNotVerified */ $option['value'] ?> - <?php else: ?> - <?= $block->truncateString($option['value'], 55, '', $_remainder) ?> - <?php if ($_remainder):?> - ... <span id="<?= /* @escapeNotVerified */ $_id = 'id' . uniqid() ?>"><?= /* @escapeNotVerified */ $_remainder ?></span> + <?php if (isset($option['custom_view']) && $option['custom_view']) : ?> + <?= $block->escapeHtml($option['value']) ?> + <?php else : ?> + <?= $block->escapeHtml($block->truncateString($option['value'], 55, '', $_remainder)) ?> + <?php if ($_remainder) :?> + ... <span id="<?= $block->escapeHtmlAttr($_id = 'id' . uniqid()) ?>"><?= $block->escapeHtml($_remainder) ?></span> <script> -require(['prototype'], function(){ - - $('<?= /* @escapeNotVerified */ $_id ?>').hide(); - $('<?= /* @escapeNotVerified */ $_id ?>').up().observe('mouseover', function(){$('<?= /* @escapeNotVerified */ $_id ?>').show();}); - $('<?= /* @escapeNotVerified */ $_id ?>').up().observe('mouseout', function(){$('<?= /* @escapeNotVerified */ $_id ?>').hide();}); - -}); -</script> + require(['prototype'], function(){ + <?php $escapedId = $block->escapeJs($_id) ?> + $('<?= /* @noEscape */ $escapedId ?>').hide(); + $('<?= /* @noEscape */ $escapedId ?>').up().observe('mouseover', function(){$('<?= /* @noEscape */ $escapedId ?>').show();}); + $('<?= /* @noEscape */ $escapedId ?>').up().observe('mouseout', function(){$('<?= /* @noEscape */ $escapedId ?>').hide();}); + }); + </script> <?php endif;?> <?php endif;?> </dd> <?php endforeach; ?> </dl> - <?php else: ?> + <?php else : ?>   <?php endif; ?> <?= $block->escapeHtml($_item->getDescription()) ?> diff --git a/app/code/Magento/Bundle/view/adminhtml/templates/sales/creditmemo/view/items/renderer.phtml b/app/code/Magento/Bundle/view/adminhtml/templates/sales/creditmemo/view/items/renderer.phtml index a9e71a79f8977..18dc5db23d562 100644 --- a/app/code/Magento/Bundle/view/adminhtml/templates/sales/creditmemo/view/items/renderer.phtml +++ b/app/code/Magento/Bundle/view/adminhtml/templates/sales/creditmemo/view/items/renderer.phtml @@ -3,9 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - +// phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis ?> <?php /** @@ -21,19 +19,19 @@ <?php $_prevOptionId = '' ?> -<?php if ($block->getOrderOptions() || $_item->getDescription()): ?> +<?php if ($block->getOrderOptions() || $_item->getDescription()) : ?> <?php $_showlastRow = true ?> -<?php else: ?> +<?php else : ?> <?php $_showlastRow = false ?> <?php endif; ?> -<?php foreach ($items as $_item): ?> +<?php foreach ($items as $_item) : ?> <?php $block->setPriceDataObject($_item) ?> <?php $attributes = $block->getSelectionAttributes($_item) ?> - <?php if ($_item->getOrderItem()->getParentItem()): ?> - <?php if ($_prevOptionId != $attributes['option_id']): ?> + <?php if ($_item->getOrderItem()->getParentItem()) : ?> + <?php if ($_prevOptionId != $attributes['option_id']) : ?> <tr> - <td class="col-product"><div class="option-label"><?= /* @escapeNotVerified */ $attributes['option_label'] ?></div></td> + <td class="col-product"><div class="option-label"><?= $block->escapeHtml($attributes['option_label']) ?></div></td> <td> </td> <td> </td> <td> </td> @@ -41,88 +39,87 @@ <td> </td> <td class="last"> </td> </tr> - <?php $_prevOptionId = $attributes['option_id'] ?> + <?php $_prevOptionId = $attributes['option_id'] ?> <?php endif; ?> <?php endif; ?> <tr<?= (++$_index == $_count && !$_showlastRow) ? ' class="border"' : '' ?>> - <?php if (!$_item->getOrderItem()->getParentItem()): ?> + <?php if (!$_item->getOrderItem()->getParentItem()) : ?> <td class="col-product"> <div class="product-title"><?= $block->escapeHtml($_item->getName()) ?></div> <div class="product-sku-block"> - <span><?= /* @escapeNotVerified */ __('SKU') ?>:</span> - <?= implode('<br />', $this->helper('Magento\Catalog\Helper\Data')->splitSku($block->escapeHtml($_item->getSku()))) ?> + <span><?= $block->escapeHtml(__('SKU')) ?>:</span> + <?= /* @noEscape */ implode('<br />', $this->helper(Magento\Catalog\Helper\Data::class)->splitSku($_item->getSku())) ?> </div> </td> - <?php else: ?> + <?php else : ?> <td class="col-product"><div class="option-value"><?= $block->getValueHtml($_item) ?></div></td> <?php endif; ?> <td class="col-price"> - <?php if ($block->canShowPriceInfo($_item)): ?> + <?php if ($block->canShowPriceInfo($_item)) : ?> <?= $block->getColumnHtml($_item, 'price') ?> - <?php else: ?> + <?php else : ?>   <?php endif; ?> </td> <td class="col-qty"> - <?php if ($block->canShowPriceInfo($_item)): ?> - <?= /* @escapeNotVerified */ $_item->getQty()*1 ?> - <?php else: ?> + <?php if ($block->canShowPriceInfo($_item)) : ?> + <?= $block->escapeHtml($_item->getQty()*1) ?> + <?php else : ?>   <?php endif; ?> </td> <td class="col-subtotal"> - <?php if ($block->canShowPriceInfo($_item)): ?> + <?php if ($block->canShowPriceInfo($_item)) : ?> <?= $block->getColumnHtml($_item, 'subtotal') ?> - <?php else: ?> + <?php else : ?>   <?php endif; ?> </td> <td class="col-tax"> - <?php if ($block->canShowPriceInfo($_item)): ?> - <?= /* @escapeNotVerified */ $block->displayPriceAttribute('tax_amount') ?> - <?php else: ?> + <?php if ($block->canShowPriceInfo($_item)) : ?> + <?= /* @noEscape */ $block->displayPriceAttribute('tax_amount') ?> + <?php else : ?>   <?php endif; ?> </td> <td class="col-discount"> - <?php if ($block->canShowPriceInfo($_item)): ?> - <?= /* @escapeNotVerified */ $block->displayPriceAttribute('discount_amount') ?> - <?php else: ?> + <?php if ($block->canShowPriceInfo($_item)) : ?> + <?= /* @noEscape */ $block->displayPriceAttribute('discount_amount') ?> + <?php else : ?>   <?php endif; ?> </td> <td class="col-total last"> - <?php if ($block->canShowPriceInfo($_item)): ?> + <?php if ($block->canShowPriceInfo($_item)) : ?> <?= $block->getColumnHtml($_item, 'total') ?> - <?php else: ?> + <?php else : ?>   <?php endif; ?> </td> </tr> <?php endforeach; ?> -<?php if ($_showlastRow): ?> +<?php if ($_showlastRow) : ?> <tr class="border"> <td class="col-product"> - <?php if ($block->getOrderOptions()): ?> + <?php if ($block->getOrderOptions()) : ?> <dl class="item-options"> - <?php foreach ($block->getOrderOptions() as $option): ?> - <dt><?= /* @escapeNotVerified */ $option['label'] ?></dt> + <?php foreach ($block->getOrderOptions() as $option) : ?> + <dt><?= $block->escapeHtml($option['label']) ?></dt> <dd> - <?php if (isset($option['custom_view']) && $option['custom_view']): ?> - <?= /* @escapeNotVerified */ $option['value'] ?> - <?php else: ?> - <?= $block->truncateString($option['value'], 55, '', $_remainder) ?> - <?php if ($_remainder):?> - ... <span id="<?= /* @escapeNotVerified */ $_id = 'id' . uniqid() ?>"><?= /* @escapeNotVerified */ $_remainder ?></span> + <?php if (isset($option['custom_view']) && $option['custom_view']) : ?> + <?= $block->escapeHtml($option['value']) ?> + <?php else : ?> + <?= $block->escapeHtml($block->truncateString($option['value'], 55, '', $_remainder)) ?> + <?php if ($_remainder) :?> + ... <span id="<?= $block->escapeHtmlAttr($_id = 'id' . uniqid()) ?>"><?= $block->escapeHtml($_remainder) ?></span> <script> -require(['prototype'], function(){ - - $('<?= /* @escapeNotVerified */ $_id ?>').hide(); - $('<?= /* @escapeNotVerified */ $_id ?>').up().observe('mouseover', function(){$('<?= /* @escapeNotVerified */ $_id ?>').show();}); - $('<?= /* @escapeNotVerified */ $_id ?>').up().observe('mouseout', function(){$('<?= /* @escapeNotVerified */ $_id ?>').hide();}); - -}); -</script> + require(['prototype'], function(){ + <?php $escapedId = $block->escapeJs($_id) ?> + $('<?= /* @noEscape */ $escapedId ?>').hide(); + $('<?= /* @noEscape */ $escapedId ?>').up().observe('mouseover', function(){$('<?= /* @noEscape */ $escapedId ?>').show();}); + $('<?= /* @noEscape */ $escapedId ?>').up().observe('mouseout', function(){$('<?= /* @noEscape */ $escapedId ?>').hide();}); + }); + </script> <?php endif;?> <?php endif;?> </dd> diff --git a/app/code/Magento/Bundle/view/adminhtml/templates/sales/invoice/create/items/renderer.phtml b/app/code/Magento/Bundle/view/adminhtml/templates/sales/invoice/create/items/renderer.phtml index 12da960a9c6cf..de0ac23340cc5 100644 --- a/app/code/Magento/Bundle/view/adminhtml/templates/sales/invoice/create/items/renderer.phtml +++ b/app/code/Magento/Bundle/view/adminhtml/templates/sales/invoice/create/items/renderer.phtml @@ -3,9 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - +// phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis ?> <?php /** @@ -21,28 +19,28 @@ <?php $_prevOptionId = '' ?> -<?php if ($block->getOrderOptions() || $_item->getDescription()): ?> +<?php if ($block->getOrderOptions() || $_item->getDescription()) : ?> <?php $_showlastRow = true ?> -<?php else: ?> +<?php else : ?> <?php $_showlastRow = false ?> <?php endif; ?> -<?php foreach ($items as $_item): ?> +<?php foreach ($items as $_item) : ?> <?php $shipTogether = ($_item->getOrderItem()->getProductType() == \Magento\Catalog\Model\Product\Type::TYPE_BUNDLE) ? !$_item->getOrderItem()->isShipSeparately() : !$_item->getOrderItem()->getParentItem()->isShipSeparately() ?> <?php $block->setPriceDataObject($_item) ?> - <?php if ($_item->getOrderItem()->getParentItem()): ?> + <?php if ($_item->getOrderItem()->getParentItem()) : ?> <?php - if ($shipTogether) { - continue; - } + if ($shipTogether) { + continue; + } ?> <?php $attributes = $block->getSelectionAttributes($_item) ?> - <?php if ($_prevOptionId != $attributes['option_id']): ?> + <?php if ($_prevOptionId != $attributes['option_id']) : ?> <tr> - <td class="col-product"><div class="option-label"><?= /* @escapeNotVerified */ $attributes['option_label'] ?></div></td> + <td class="col-product"><div class="option-label"><?= $block->escapeHtml($attributes['option_label']) ?></div></td> <td> </td> <td> </td> <td> </td> @@ -51,152 +49,152 @@ <td> </td> <td class="last"> </td> </tr> - <?php $_prevOptionId = $attributes['option_id'] ?> + <?php $_prevOptionId = $attributes['option_id'] ?> <?php endif; ?> <?php endif; ?> <tr<?= (++$_index == $_count && !$_showlastRow) ? ' class="border"' : '' ?>> - <?php if (!$_item->getOrderItem()->getParentItem()): ?> + <?php if (!$_item->getOrderItem()->getParentItem()) : ?> <td class="col-product"> <div class="product-title"><?= $block->escapeHtml($_item->getName()) ?></div> <div class="product-sku-block"> - <span><?= /* @escapeNotVerified */ __('SKU') ?>:</span> - <?= implode('<br />', $this->helper('Magento\Catalog\Helper\Data')->splitSku($block->escapeHtml($_item->getSku()))) ?> + <span><?= $block->escapeHtml(__('SKU')) ?>:</span> + <?= /* @noEscape */ implode('<br />', $this->helper(Magento\Catalog\Helper\Data::class)->splitSku($_item->getSku())) ?> </div> </td> - <?php else: ?> + <?php else : ?> <td class="col-product"> <div class="option-value"><?= $block->getValueHtml($_item) ?></div> </td> <?php endif; ?> <td class="col-price"> - <?php if ($block->canShowPriceInfo($_item) || $shipTogether): ?> + <?php if ($block->canShowPriceInfo($_item) || $shipTogether) : ?> <?= $block->getColumnHtml($_item, 'price') ?> - <?php else: ?> + <?php else : ?>   <?php endif; ?> </td> <td class="col-qty"> - <?php if ($block->canShowPriceInfo($_item) || $shipTogether): ?> + <?php if ($block->canShowPriceInfo($_item) || $shipTogether) : ?> <table class="qty-table"> <tr> - <th><?= /* @escapeNotVerified */ __('Ordered') ?></th> - <td><span><?= /* @escapeNotVerified */ $_item->getOrderItem()->getQtyOrdered()*1 ?></span></td> + <th><?= $block->escapeHtml(__('Ordered')) ?></th> + <td><span><?= $block->escapeHtml($_item->getOrderItem()->getQtyOrdered()*1) ?></span></td> </tr> - <?php if ((float) $_item->getOrderItem()->getQtyInvoiced()): ?> + <?php if ((float) $_item->getOrderItem()->getQtyInvoiced()) : ?> <tr> - <th><?= /* @escapeNotVerified */ __('Invoiced') ?></th> - <td><?= /* @escapeNotVerified */ $_item->getOrderItem()->getQtyInvoiced()*1 ?></td> + <th><?= $block->escapeHtml(__('Invoiced')) ?></th> + <td><?= $block->escapeHtml($_item->getOrderItem()->getQtyInvoiced()*1) ?></td> </tr> <?php endif; ?> - <?php if ((float) $_item->getOrderItem()->getQtyShipped() && $block->isShipmentSeparately($_item)): ?> + <?php if ((float) $_item->getOrderItem()->getQtyShipped() && $block->isShipmentSeparately($_item)) : ?> <tr> - <th><?= /* @escapeNotVerified */ __('Shipped') ?></th> - <td><?= /* @escapeNotVerified */ $_item->getOrderItem()->getQtyShipped()*1 ?></td> + <th><?= $block->escapeHtml(__('Shipped')) ?></th> + <td><?= $block->escapeHtml($_item->getOrderItem()->getQtyShipped()*1) ?></td> </tr> <?php endif; ?> - <?php if ((float) $_item->getOrderItem()->getQtyRefunded()): ?> + <?php if ((float) $_item->getOrderItem()->getQtyRefunded()) : ?> <tr> - <th><?= /* @escapeNotVerified */ __('Refunded') ?></th> - <td><?= /* @escapeNotVerified */ $_item->getOrderItem()->getQtyRefunded()*1 ?></td> + <th><?= $block->escapeHtml(__('Refunded')) ?></th> + <td><?= $block->escapeHtml($_item->getOrderItem()->getQtyRefunded()*1) ?></td> </tr> <?php endif; ?> - <?php if ((float) $_item->getOrderItem()->getQtyCanceled()): ?> + <?php if ((float) $_item->getOrderItem()->getQtyCanceled()) : ?> <tr> - <th><?= /* @escapeNotVerified */ __('Canceled') ?></th> - <td><?= /* @escapeNotVerified */ $_item->getOrderItem()->getQtyCanceled()*1 ?></td> + <th><?= $block->escapeHtml(__('Canceled')) ?></th> + <td><?= $block->escapeHtml($_item->getOrderItem()->getQtyCanceled()*1) ?></td> </tr> <?php endif; ?> </table> - <?php elseif ($block->isShipmentSeparately($_item)): ?> + <?php elseif ($block->isShipmentSeparately($_item)) : ?> <table class="qty-table"> <tr> - <th><?= /* @escapeNotVerified */ __('Ordered') ?></th> - <td><?= /* @escapeNotVerified */ $_item->getOrderItem()->getQtyOrdered()*1 ?></td> + <th><?= $block->escapeHtml(__('Ordered')) ?></th> + <td><?= $block->escapeHtml($_item->getOrderItem()->getQtyOrdered()*1) ?></td> </tr> - <?php if ((float) $_item->getOrderItem()->getQtyShipped()): ?> + <?php if ((float) $_item->getOrderItem()->getQtyShipped()) : ?> <tr> - <th><?= /* @escapeNotVerified */ __('Shipped') ?></th> - <td><?= /* @escapeNotVerified */ $_item->getOrderItem()->getQtyShipped()*1 ?></td> + <th><?= $block->escapeHtml(__('Shipped')) ?></th> + <td><?= $block->escapeHtml($_item->getOrderItem()->getQtyShipped()*1) ?></td> </tr> <?php endif; ?> </table> - <?php else: ?> + <?php else : ?>   <?php endif; ?> </td> <td class="col-qty-invoice"> - <?php if ($block->canShowPriceInfo($_item) || $shipTogether): ?> + <?php if ($block->canShowPriceInfo($_item) || $shipTogether) : ?> <?php if ($block->canEditQty()) : ?> <input type="text" class="input-text admin__control-text qty-input" - name="invoice[items][<?= /* @escapeNotVerified */ $_item->getOrderItemId() ?>]" - value="<?= /* @escapeNotVerified */ $_item->getQty()*1 ?>" /> + name="invoice[items][<?= $block->escapeHtmlAttr($_item->getOrderItemId()) ?>]" + value="<?= $block->escapeHtmlAttr($_item->getQty()*1) ?>" /> <?php else : ?> - <?= /* @escapeNotVerified */ $_item->getQty()*1 ?> + <?= $block->escapeHtml($_item->getQty()*1) ?> <?php endif; ?> - <?php else: ?> + <?php else : ?>   <?php endif; ?> </td> <td class="col-subtotal"> - <?php if ($block->canShowPriceInfo($_item)): ?> + <?php if ($block->canShowPriceInfo($_item)) : ?> <?= $block->getColumnHtml($_item, 'subtotal') ?> - <?php else: ?> + <?php else : ?>   <?php endif; ?> </td> <td class="col-tax"> - <?php if ($block->canShowPriceInfo($_item)): ?> - <?= /* @escapeNotVerified */ $block->displayPriceAttribute('tax_amount') ?> - <?php else: ?> + <?php if ($block->canShowPriceInfo($_item)) : ?> + <?= /* @noEscape */ $block->displayPriceAttribute('tax_amount') ?> + <?php else : ?>   <?php endif; ?> </td> <td class="col-discount"> - <?php if ($block->canShowPriceInfo($_item)): ?> - <?= /* @escapeNotVerified */ $block->displayPriceAttribute('discount_amount') ?> - <?php else: ?> + <?php if ($block->canShowPriceInfo($_item)) : ?> + <?= /* @noEscape */ $block->displayPriceAttribute('discount_amount') ?> + <?php else : ?>   <?php endif; ?> </td> <td class="col-total last"> - <?php if ($block->canShowPriceInfo($_item)): ?> + <?php if ($block->canShowPriceInfo($_item)) : ?> <?= $block->getColumnHtml($_item, 'total') ?> - <?php else: ?> + <?php else : ?>   <?php endif; ?> </td> </tr> <?php endforeach; ?> -<?php if ($_showlastRow): ?> +<?php if ($_showlastRow) : ?> <tr class="border"> <td class="col-product"> - <?php if ($block->getOrderOptions($_item->getOrderItem())): ?> + <?php if ($block->getOrderOptions($_item->getOrderItem())) : ?> <dl class="item-options"> - <?php foreach ($block->getOrderOptions($_item->getOrderItem()) as $option): ?> - <dt><?= /* @escapeNotVerified */ $option['label'] ?></dt> + <?php foreach ($block->getOrderOptions($_item->getOrderItem()) as $option) : ?> + <dt><?= $block->escapeHtml($option['label']) ?></dt> <dd> - <?php if (isset($option['custom_view']) && $option['custom_view']): ?> - <?= /* @escapeNotVerified */ $option['value'] ?> - <?php else: ?> - <?= $block->truncateString($option['value'], 55, '', $_remainder) ?> - <?php if ($_remainder):?> - ... <span id="<?= /* @escapeNotVerified */ $_id = 'id' . uniqid() ?>"><?= /* @escapeNotVerified */ $_remainder ?></span> + <?php if (isset($option['custom_view']) && $option['custom_view']) : ?> + <?= $block->escapeHtml($option['value']) ?> + <?php else : ?> + <?= $block->escapeHtml($block->truncateString($option['value'], 55, '', $_remainder)) ?> + <?php if ($_remainder) :?> + ... <span id="<?= $block->escapeHtmlAttr($_id = 'id' . uniqid()) ?>"><?= $block->escapeHtml($_remainder) ?></span> <script> -require(['prototype'], function(){ - - $('<?= /* @escapeNotVerified */ $_id ?>').hide(); - $('<?= /* @escapeNotVerified */ $_id ?>').up().observe('mouseover', function(){$('<?= /* @escapeNotVerified */ $_id ?>').show();}); - $('<?= /* @escapeNotVerified */ $_id ?>').up().observe('mouseout', function(){$('<?= /* @escapeNotVerified */ $_id ?>').hide();}); + require(['prototype'], function(){ + <?php $escapedId = $block->escapeJs($_id) ?> + $('<?= /* @noEscape */ $escapedId ?>').hide(); + $('<?= /* @noEscape */ $escapedId ?>').up().observe('mouseover', function(){$('<?= /* @noEscape */ $escapedId?>').show();}); + $('<?= /* @noEscape */ $escapedId ?>').up().observe('mouseout', function(){$('<?= /* @noEscape */ $escapedId ?>').hide();}); -}); -</script> + }); + </script> <?php endif;?> <?php endif;?> </dd> <?php endforeach; ?> </dl> - <?php else: ?> + <?php else : ?>   <?php endif; ?> <?= $block->escapeHtml($_item->getDescription()) ?> diff --git a/app/code/Magento/Bundle/view/adminhtml/templates/sales/invoice/view/items/renderer.phtml b/app/code/Magento/Bundle/view/adminhtml/templates/sales/invoice/view/items/renderer.phtml index 5f344409b6a4c..b8a2c0db82d4b 100644 --- a/app/code/Magento/Bundle/view/adminhtml/templates/sales/invoice/view/items/renderer.phtml +++ b/app/code/Magento/Bundle/view/adminhtml/templates/sales/invoice/view/items/renderer.phtml @@ -3,9 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - +// phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis ?> <?php /** @@ -21,19 +19,19 @@ <?php $_prevOptionId = '' ?> -<?php if ($block->getOrderOptions() || $_item->getDescription()): ?> +<?php if ($block->getOrderOptions() || $_item->getDescription()) : ?> <?php $_showlastRow = true ?> -<?php else: ?> +<?php else : ?> <?php $_showlastRow = false ?> <?php endif; ?> -<?php foreach ($items as $_item): ?> +<?php foreach ($items as $_item) : ?> <?php $block->setPriceDataObject($_item) ?> - <?php if ($_item->getOrderItem()->getParentItem()): ?> + <?php if ($_item->getOrderItem()->getParentItem()) : ?> <?php $attributes = $block->getSelectionAttributes($_item) ?> - <?php if ($_prevOptionId != $attributes['option_id']): ?> + <?php if ($_prevOptionId != $attributes['option_id']) : ?> <tr> - <td class="col-product"><div class="option-label"><?= /* @escapeNotVerified */ $attributes['option_label'] ?></div></td> + <td class="col-product"><div class="option-label"><?= $block->escapeHtml($attributes['option_label']) ?></div></td> <td> </td> <td> </td> <td> </td> @@ -41,89 +39,88 @@ <td> </td> <td class="last"> </td> </tr> - <?php $_prevOptionId = $attributes['option_id'] ?> + <?php $_prevOptionId = $attributes['option_id'] ?> <?php endif; ?> <?php endif; ?> <tr<?= (++$_index == $_count && !$_showlastRow) ? ' class="border"' : '' ?>> - <?php if (!$_item->getOrderItem()->getParentItem()): ?> + <?php if (!$_item->getOrderItem()->getParentItem()) : ?> <td class="col-product"> <div class="product-title"><?= $block->escapeHtml($_item->getName()) ?></div> <div class="product-sku-block"> - <span><?= /* @escapeNotVerified */ __('SKU') ?>:</span> - <?= implode('<br />', $this->helper('Magento\Catalog\Helper\Data')->splitSku($block->escapeHtml($_item->getSku()))) ?> + <span><?= $block->escapeHtml(__('SKU')) ?>:</span> + <?= /* @noEscape */ implode('<br />', $this->helper(Magento\Catalog\Helper\Data::class)->splitSku($_item->getSku())) ?> </div> - <?php else: ?> + <?php else : ?> <td class="col-product"> <div class="option-value"><?= $block->getValueHtml($_item) ?></div> </td> <?php endif; ?> <td class="col-price"> - <?php if ($block->canShowPriceInfo($_item)): ?> + <?php if ($block->canShowPriceInfo($_item)) : ?> <?= $block->getColumnHtml($_item, 'price') ?> - <?php else: ?> + <?php else : ?>   <?php endif; ?> </td> <td class="col-qty"> - <?php if ($block->canShowPriceInfo($_item)): ?> - <?= /* @escapeNotVerified */ $_item->getQty()*1 ?> - <?php else: ?> + <?php if ($block->canShowPriceInfo($_item)) : ?> + <?= $block->escapeHtml($_item->getQty()*1) ?> + <?php else : ?>   <?php endif; ?> </td> <td class="col-subtotal"> - <?php if ($block->canShowPriceInfo($_item)): ?> + <?php if ($block->canShowPriceInfo($_item)) : ?> <?= $block->getColumnHtml($_item, 'subtotal') ?> - <?php else: ?> + <?php else : ?>   <?php endif; ?> </td> <td class="col-tax"> - <?php if ($block->canShowPriceInfo($_item)): ?> - <?= /* @escapeNotVerified */ $block->displayPriceAttribute('tax_amount') ?> - <?php else: ?> + <?php if ($block->canShowPriceInfo($_item)) : ?> + <?= /* @noEscape */ $block->displayPriceAttribute('tax_amount') ?> + <?php else : ?>   <?php endif; ?> </td> <td class="col-discount"> - <?php if ($block->canShowPriceInfo($_item)): ?> - <?= /* @escapeNotVerified */ $block->displayPriceAttribute('discount_amount') ?> - <?php else: ?> + <?php if ($block->canShowPriceInfo($_item)) : ?> + <?= /* @noEscape */ $block->displayPriceAttribute('discount_amount') ?> + <?php else : ?>   <?php endif; ?> </td> <td class="col-total last"> - <?php if ($block->canShowPriceInfo($_item)): ?> + <?php if ($block->canShowPriceInfo($_item)) : ?> <?= $block->getColumnHtml($_item, 'total') ?> - <?php else: ?> + <?php else : ?>   <?php endif; ?> </td> </tr> <?php endforeach; ?> -<?php if ($_showlastRow): ?> +<?php if ($_showlastRow) : ?> <tr class="border"> <td class="col-product"> - <?php if ($block->getOrderOptions()): ?> + <?php if ($block->getOrderOptions()) : ?> <dl class="item-options"> - <?php foreach ($block->getOrderOptions() as $option): ?> - <dt><?= /* @escapeNotVerified */ $option['label'] ?></dt> + <?php foreach ($block->getOrderOptions() as $option) : ?> + <dt><?= $block->escapeHtml($option['label']) ?></dt> <dd> - <?php if (isset($option['custom_view']) && $option['custom_view']): ?> - <?= /* @escapeNotVerified */ $option['value'] ?> - <?php else: ?> - <?= $block->truncateString($option['value'], 55, '', $_remainder) ?> - <?php if ($_remainder):?> - ... <span id="<?= /* @escapeNotVerified */ $_id = 'id' . uniqid() ?>"><?= /* @escapeNotVerified */ $_remainder ?></span> + <?php if (isset($option['custom_view']) && $option['custom_view']) : ?> + <?= $block->escapeHtml($option['value']) ?> + <?php else : ?> + <?= $block->escapeHtml($block->truncateString($option['value'], 55, '', $_remainder)) ?> + <?php if ($_remainder) :?> + ... <span id="<?= $block->escapeHtmlAttr($_id = 'id' . uniqid()) ?>"><?= $block->escapeHtml($_remainder) ?></span> <script> -require(['protoype'], function(){ - - $('<?= /* @escapeNotVerified */ $_id ?>').hide(); - $('<?= /* @escapeNotVerified */ $_id ?>').up().observe('mouseover', function(){$('<?= /* @escapeNotVerified */ $_id ?>').show();}); - $('<?= /* @escapeNotVerified */ $_id ?>').up().observe('mouseout', function(){$('<?= /* @escapeNotVerified */ $_id ?>').hide();}); - -}); -</script> + require(['prototype'], function(){ + <?php $escapedId = $block->escapeJs($_id) ?> + $('<?= /* @noEscape */ $escapedId ?>').hide(); + $('<?= /* @noEscape */ $escapedId ?>').up().observe('mouseover', function(){$('<?= /* @noEscape */ $escapedId ?>').show();}); + $('<?= /* @noEscape */ $escapedId ?>').up().observe('mouseout', function(){$('<?= /* @noEscape */ $escapedId ?>').hide();}); + }); + </script> <?php endif;?> <?php endif;?> </dd> diff --git a/app/code/Magento/Bundle/view/adminhtml/templates/sales/order/view/items/renderer.phtml b/app/code/Magento/Bundle/view/adminhtml/templates/sales/order/view/items/renderer.phtml index bb0857a80d689..280bf67d63787 100644 --- a/app/code/Magento/Bundle/view/adminhtml/templates/sales/order/view/items/renderer.phtml +++ b/app/code/Magento/Bundle/view/adminhtml/templates/sales/order/view/items/renderer.phtml @@ -3,9 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - +// phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis ?> <?php /** @@ -16,24 +14,24 @@ <?php $_item = $block->getItem() ?> <?php $items = array_merge([$_item], $_item->getChildrenItems()); ?> -<?php $_count = count ($items) ?> +<?php $_count = count($items) ?> <?php $_index = 0 ?> <?php $_prevOptionId = '' ?> -<?php if($block->getOrderOptions() || $_item->getDescription() || $block->canDisplayGiftmessage()): ?> +<?php if ($block->getOrderOptions() || $_item->getDescription() || $block->canDisplayGiftmessage()) : ?> <?php $_showlastRow = true ?> -<?php else: ?> +<?php else : ?> <?php $_showlastRow = false ?> <?php endif; ?> -<?php foreach ($items as $_item): ?> +<?php foreach ($items as $_item) : ?> <?php $block->setPriceDataObject($_item) ?> <?php $attributes = $block->getSelectionAttributes($_item) ?> - <?php if ($_item->getParentItem()): ?> - <?php if ($_prevOptionId != $attributes['option_id']): ?> + <?php if ($_item->getParentItem()) : ?> + <?php if ($_prevOptionId != $attributes['option_id']) : ?> <tr> - <td class="col-product"><div class="option-label"><?= /* @escapeNotVerified */ $attributes['option_label'] ?></div></td> + <td class="col-product"><div class="option-label"><?= $block->escapeHtml($attributes['option_label']) ?></div></td> <td> </td> <td> </td> <td> </td> @@ -44,161 +42,160 @@ <td> </td> <td class="last"> </td> </tr> - <?php $_prevOptionId = $attributes['option_id'] ?> + <?php $_prevOptionId = $attributes['option_id'] ?> <?php endif; ?> <?php endif; ?> <tr<?= (++$_index==$_count && !$_showlastRow)?' class="border"':'' ?>> - <?php if (!$_item->getParentItem()): ?> + <?php if (!$_item->getParentItem()) : ?> <td class="col-product"> - <div class="product-title" id="order_item_<?= /* @escapeNotVerified */ $_item->getId() ?>_title"> + <div class="product-title" id="order_item_<?= $block->escapeHtmlAttr($_item->getId()) ?>_title"> <?= $block->escapeHtml($_item->getName()) ?> </div> <div class="product-sku-block"> - <span><?= /* @escapeNotVerified */ __('SKU') ?>:</span> - <?= implode('<br />', $this->helper('Magento\Catalog\Helper\Data')->splitSku($block->escapeHtml($_item->getSku()))) ?> + <span><?= $block->escapeHtml(__('SKU')) ?>:</span> + <?= /* @noEscape */ implode('<br />', $this->helper(Magento\Catalog\Helper\Data::class)->splitSku($_item->getSku())) ?> </div> </td> - <?php else: ?> + <?php else : ?> <td class="col-product"> <div class="option-value"><?= $block->getValueHtml($_item) ?></div> </td> <?php endif; ?> <td class="col-status"> - <?php if ($block->canShowPriceInfo($_item)): ?> - <?= /* @escapeNotVerified */ $_item->getStatus() ?> - <?php else: ?> + <?php if ($block->canShowPriceInfo($_item)) : ?> + <?= $block->escapeHtml($_item->getStatus()) ?> + <?php else : ?>   <?php endif; ?> </td> <td class="col-price-original"> - <?php if ($block->canShowPriceInfo($_item)): ?> - <?= /* @escapeNotVerified */ $block->displayPriceAttribute('original_price') ?> - <?php else: ?> + <?php if ($block->canShowPriceInfo($_item)) : ?> + <?= /* @noEscape */ $block->displayPriceAttribute('original_price') ?> + <?php else : ?>   <?php endif; ?> </td> <td class="col-price"> - <?php if ($block->canShowPriceInfo($_item)): ?> + <?php if ($block->canShowPriceInfo($_item)) : ?> <?= $block->getColumnHtml($_item, 'price') ?> - <?php else: ?> + <?php else : ?>   <?php endif; ?> </td> <td class="col-ordered-qty"> - <?php if ($block->canShowPriceInfo($_item)): ?> + <?php if ($block->canShowPriceInfo($_item)) : ?> <table class="qty-table"> <tr> - <th><?= /* @escapeNotVerified */ __('Ordered') ?></th> - <td><?= /* @escapeNotVerified */ $_item->getQtyOrdered()*1 ?></td> + <th><?= $block->escapeHtml(__('Ordered')) ?></th> + <td><?= $block->escapeHtml($_item->getQtyOrdered()*1) ?></td> </tr> - <?php if ((float) $_item->getQtyInvoiced()): ?> + <?php if ((float) $_item->getQtyInvoiced()) : ?> <tr> - <th><?= /* @escapeNotVerified */ __('Invoiced') ?></th> - <td><?= /* @escapeNotVerified */ $_item->getQtyInvoiced()*1 ?></td> + <th><?= $block->escapeHtml(__('Invoiced')) ?></th> + <td><?= $block->escapeHtml($_item->getQtyInvoiced()*1) ?></td> </tr> <?php endif; ?> - <?php if ((float) $_item->getQtyShipped() && $block->isShipmentSeparately($_item)): ?> + <?php if ((float) $_item->getQtyShipped() && $block->isShipmentSeparately($_item)) : ?> <tr> - <th><?= /* @escapeNotVerified */ __('Shipped') ?></th> - <td><?= /* @escapeNotVerified */ $_item->getQtyShipped()*1 ?></td> + <th><?= $block->escapeHtml(__('Shipped')) ?></th> + <td><?= $block->escapeHtml($_item->getQtyShipped()*1) ?></td> </tr> <?php endif; ?> - <?php if ((float) $_item->getQtyRefunded()): ?> + <?php if ((float) $_item->getQtyRefunded()) : ?> <tr> - <th><?= /* @escapeNotVerified */ __('Refunded') ?></th> - <td><?= /* @escapeNotVerified */ $_item->getQtyRefunded()*1 ?></td> + <th><?= $block->escapeHtml(__('Refunded')) ?></th> + <td><?= $block->escapeHtml($_item->getQtyRefunded()*1) ?></td> </tr> <?php endif; ?> - <?php if ((float) $_item->getQtyCanceled()): ?> + <?php if ((float) $_item->getQtyCanceled()) : ?> <tr> - <th><?= /* @escapeNotVerified */ __('Canceled') ?></th> - <td><?= /* @escapeNotVerified */ $_item->getQtyCanceled()*1 ?></td> + <th><?= $block->escapeHtml(__('Canceled')) ?></th> + <td><?= $block->escapeHtml($_item->getQtyCanceled()*1) ?></td> </tr> <?php endif; ?> </table> - <?php elseif ($block->isShipmentSeparately($_item)): ?> + <?php elseif ($block->isShipmentSeparately($_item)) : ?> <table class="qty-table"> <tr> - <th><?= /* @escapeNotVerified */ __('Ordered') ?></th> - <td><?= /* @escapeNotVerified */ $_item->getQtyOrdered()*1 ?></td> + <th><?= $block->escapeHtml(__('Ordered')) ?></th> + <td><?= $block->escapeHtml($_item->getQtyOrdered()*1) ?></td> </tr> - <?php if ((float) $_item->getQtyShipped()): ?> + <?php if ((float) $_item->getQtyShipped()) : ?> <tr> - <th><?= /* @escapeNotVerified */ __('Shipped') ?></th> - <td><?= /* @escapeNotVerified */ $_item->getQtyShipped()*1 ?></td> + <th><?= $block->escapeHtml(__('Shipped')) ?></th> + <td><?= $block->escapeHtml($_item->getQtyShipped()*1) ?></td> </tr> <?php endif; ?> </table> - <?php else: ?> + <?php else : ?>   <?php endif; ?> </td> <td class="col-subtotal"> - <?php if ($block->canShowPriceInfo($_item)): ?> + <?php if ($block->canShowPriceInfo($_item)) : ?> <?= $block->getColumnHtml($_item, 'subtotal') ?> - <?php else: ?> + <?php else : ?>   <?php endif; ?> </td> <td class="col-tax-amount"> - <?php if ($block->canShowPriceInfo($_item)): ?> - <?= /* @escapeNotVerified */ $block->displayPriceAttribute('tax_amount') ?> - <?php else: ?> + <?php if ($block->canShowPriceInfo($_item)) : ?> + <?= /* @noEscape */ $block->displayPriceAttribute('tax_amount') ?> + <?php else : ?>   <?php endif; ?> </td> <td class="col-tax-percent"> - <?php if ($block->canShowPriceInfo($_item)): ?> - <?= /* @escapeNotVerified */ $block->displayTaxPercent($_item) ?> - <?php else: ?> + <?php if ($block->canShowPriceInfo($_item)) : ?> + <?= /* @noEscape */ $block->displayTaxPercent($_item) ?> + <?php else : ?>   <?php endif; ?> </td> <td class="col-discont"> - <?php if ($block->canShowPriceInfo($_item)): ?> - <?= /* @escapeNotVerified */ $block->displayPriceAttribute('discount_amount') ?> - <?php else: ?> + <?php if ($block->canShowPriceInfo($_item)) : ?> + <?= $block->escapeHtml($block->displayPriceAttribute('discount_amount')) ?> + <?php else : ?>   <?php endif; ?> </td> <td class="col-total last"> - <?php if ($block->canShowPriceInfo($_item)): ?> + <?php if ($block->canShowPriceInfo($_item)) : ?> <?= $block->getColumnHtml($_item, 'total') ?> - <?php else: ?> + <?php else : ?>   <?php endif; ?> </td> </tr> <?php endforeach; ?> -<?php if($_showlastRow): ?> - <tr<?php if (!$block->canDisplayGiftmessage()) echo ' class="border"' ?>> +<?php if ($_showlastRow) : ?> + <tr<?php if (!$block->canDisplayGiftmessage()) { echo ' class="border"'; } ?>> <td class="col-product"> - <?php if ($block->getOrderOptions()): ?> + <?php if ($block->getOrderOptions()) : ?> <dl class="item-options"> - <?php foreach ($block->getOrderOptions() as $option): ?> - <dt><?= /* @escapeNotVerified */ $option['label'] ?>:</dt> + <?php foreach ($block->getOrderOptions() as $option) : ?> + <dt><?= $block->escapeHtml($option['label']) ?>:</dt> <dd> - <?php if (isset($option['custom_view']) && $option['custom_view']): ?> - <?= /* @escapeNotVerified */ $option['value'] ?> - <?php else: ?> - <?= $block->truncateString($option['value'], 55, '', $_remainder) ?> - <?php if ($_remainder):?> - ... <span id="<?= /* @escapeNotVerified */ $_id = 'id' . uniqid() ?>"><?= /* @escapeNotVerified */ $_remainder ?></span> + <?php if (isset($option['custom_view']) && $option['custom_view']) : ?> + <?= $block->escapeHtml($option['value']) ?> + <?php else : ?> + <?= $block->escapeHtml($block->truncateString($option['value'], 55, '', $_remainder)) ?> + <?php if ($_remainder) :?> + ... <span id="<?= $block->escapeHtmlAttr($_id = 'id' . uniqid()) ?>"><?= $block->escapeHtml($_remainder) ?></span> <script> -require(['prototype'], function(){ - - $('<?= /* @escapeNotVerified */ $_id ?>').hide(); - $('<?= /* @escapeNotVerified */ $_id ?>').up().observe('mouseover', function(){$('<?= /* @escapeNotVerified */ $_id ?>').show();}); - $('<?= /* @escapeNotVerified */ $_id ?>').up().observe('mouseout', function(){$('<?= /* @escapeNotVerified */ $_id ?>').hide();}); - -}); -</script> + require(['prototype'], function(){ + <?php $escapedId = $block->escapeJs($_id) ?> + $('<?= /* @noEscape */ $escapedId ?>').hide(); + $('<?= /* @noEscape */ $escapedId ?>').up().observe('mouseover', function(){$('<?= /* @noEscape */ $escapedId ?>').show();}); + $('<?= /* @noEscape */ $escapedId ?>').up().observe('mouseout', function(){$('<?= /* @noEscape */ $escapedId ?>').hide();}); + }); + </script> <?php endif;?> <?php endif;?> </dd> <?php endforeach; ?> </dl> - <?php else: ?> + <?php else : ?>   <?php endif; ?> <?= $block->escapeHtml($_item->getDescription()) ?> diff --git a/app/code/Magento/Bundle/view/adminhtml/templates/sales/shipment/create/items/renderer.phtml b/app/code/Magento/Bundle/view/adminhtml/templates/sales/shipment/create/items/renderer.phtml index 2ede8277bcfc9..fe446ec095719 100644 --- a/app/code/Magento/Bundle/view/adminhtml/templates/sales/shipment/create/items/renderer.phtml +++ b/app/code/Magento/Bundle/view/adminhtml/templates/sales/shipment/create/items/renderer.phtml @@ -3,9 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - +// phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis ?> <?php /** @var $block \Magento\Bundle\Block\Adminhtml\Sales\Order\Items\Renderer */ ?> @@ -17,85 +15,84 @@ <?php $_prevOptionId = '' ?> -<?php if ($block->getOrderOptions() || $_item->getDescription()): ?> +<?php if ($block->getOrderOptions() || $_item->getDescription()) : ?> <?php $_showlastRow = true ?> -<?php else: ?> +<?php else : ?> <?php $_showlastRow = false ?> <?php endif; ?> -<?php foreach ($items as $_item): ?> +<?php foreach ($items as $_item) : ?> <?php $block->setPriceDataObject($_item) ?> - <?php if ($_item->getOrderItem()->getParentItem()): ?> + <?php if ($_item->getOrderItem()->getParentItem()) : ?> <?php $attributes = $block->getSelectionAttributes($_item) ?> - <?php if ($_prevOptionId != $attributes['option_id']): ?> + <?php if ($_prevOptionId != $attributes['option_id']) : ?> <tr> - <td class="col-product"><div class="option-label"><?= /* @escapeNotVerified */ $attributes['option_label'] ?></div></td> + <td class="col-product"><div class="option-label"><?= $block->escapeHtml($attributes['option_label']) ?></div></td> <td class="col-product"> </td> <td class="col-qty last"> </td> </tr> - <?php $_prevOptionId = $attributes['option_id'] ?> + <?php $_prevOptionId = $attributes['option_id'] ?> <?php endif; ?> <?php endif; ?> <tr class="<?= (++$_index == $_count && !$_showlastRow) ? 'border' : '' ?>"> - <?php if (!$_item->getOrderItem()->getParentItem()): ?> + <?php if (!$_item->getOrderItem()->getParentItem()) : ?> <td class="col-product"> <div class="product-title"><?= $block->escapeHtml($_item->getName()) ?></div> <div class="product-sku-block"> - <span><?= /* @escapeNotVerified */ __('SKU') ?>:</span> - <?= implode('<br />', $this->helper('Magento\Catalog\Helper\Data')->splitSku($block->escapeHtml($_item->getSku()))) ?> + <span><?= $block->escapeHtml(__('SKU')) ?>:</span> + <?= /* @noEscape */ implode('<br />', $this->helper(Magento\Catalog\Helper\Data::class)->splitSku($_item->getSku())) ?> </div> </td> - <?php else: ?> + <?php else : ?> <td class="col-product"><div class="option-value"><?= $block->getValueHtml($_item) ?></div></td> <?php endif; ?> <td class="col-ordered-qty"> - <?php if ($block->isShipmentSeparately($_item)): ?> + <?php if ($block->isShipmentSeparately($_item)) : ?> <?= $block->getColumnHtml($_item, 'qty') ?> - <?php else: ?> + <?php else : ?>   <?php endif; ?> </td> <td class="col-qty last"> - <?php if ($block->isShipmentSeparately($_item)): ?> + <?php if ($block->isShipmentSeparately($_item)) : ?> <input type="text" class="input-text admin__control-text" - name="shipment[items][<?= /* @escapeNotVerified */ $_item->getOrderItemId() ?>]" - value="<?= /* @escapeNotVerified */ $_item->getQty()*1 ?>" /> - <?php else: ?> + name="shipment[items][<?= $block->escapeHtmlAttr($_item->getOrderItemId()) ?>]" + value="<?= $block->escapeHtmlAttr($_item->getQty()*1) ?>" /> + <?php else : ?>   <?php endif; ?> </td> </tr> <?php endforeach; ?> -<?php if ($_showlastRow): ?> +<?php if ($_showlastRow) : ?> <tr class="border"> <td class="col-product"> - <?php if ($block->getOrderOptions($_item->getOrderItem())): ?> + <?php if ($block->getOrderOptions($_item->getOrderItem())) : ?> <dl class="item-options"> - <?php foreach ($block->getOrderOptions($_item->getOrderItem()) as $option): ?> - <dt><?= /* @escapeNotVerified */ $option['label'] ?></dt> + <?php foreach ($block->getOrderOptions($_item->getOrderItem()) as $option) : ?> + <dt><?= $block->escapeHtml($option['label']) ?></dt> <dd> - <?php if (isset($option['custom_view']) && $option['custom_view']): ?> - <?= /* @escapeNotVerified */ $option['value'] ?> - <?php else: ?> - <?= $block->truncateString($option['value'], 55, '', $_remainder) ?> - <?php if ($_remainder):?> - ... <span id="<?= /* @escapeNotVerified */ $_id = 'id' . uniqid() ?>"><?= /* @escapeNotVerified */ $_remainder ?></span> + <?php if (isset($option['custom_view']) && $option['custom_view']) : ?> + <?= $block->escapeHtml($option['value']) ?> + <?php else : ?> + <?= $block->escapeHtml($block->truncateString($option['value'], 55, '', $_remainder)) ?> + <?php if ($_remainder) :?> + ... <span id="<?= $block->escapeHtmlAttr($_id = 'id' . uniqid()) ?>"><?= $block->escapeHtml($_remainder) ?></span> <script> -require(['prototype'], function(){ - - $('<?= /* @escapeNotVerified */ $_id ?>').hide(); - $('<?= /* @escapeNotVerified */ $_id ?>').up().observe('mouseover', function(){$('<?= /* @escapeNotVerified */ $_id ?>').show();}); - $('<?= /* @escapeNotVerified */ $_id ?>').up().observe('mouseout', function(){$('<?= /* @escapeNotVerified */ $_id ?>').hide();}); - -}); -</script> + require(['prototype'], function(){ + <?php $escapedId = $block->escapeJs($_id) ?> + $('<?= /* @noEscape */ $escapedId ?>').hide(); + $('<?= /* @noEscape */ $escapedId ?>').up().observe('mouseover', function(){$('<?= /* @noEscape */ $escapedId ?>').show();}); + $('<?= /* @noEscape */ $escapedId ?>').up().observe('mouseout', function(){$('<?= /* @noEscape */ $escapedId ?>').hide();}); + }); + </script> <?php endif;?> <?php endif;?> </dd> <?php endforeach; ?> </dl> - <?php else: ?> + <?php else : ?>   <?php endif; ?> <?= $block->escapeHtml($_item->getDescription()) ?> diff --git a/app/code/Magento/Bundle/view/adminhtml/templates/sales/shipment/view/items/renderer.phtml b/app/code/Magento/Bundle/view/adminhtml/templates/sales/shipment/view/items/renderer.phtml index 71eabd45cbb57..d99cabe41420b 100644 --- a/app/code/Magento/Bundle/view/adminhtml/templates/sales/shipment/view/items/renderer.phtml +++ b/app/code/Magento/Bundle/view/adminhtml/templates/sales/shipment/view/items/renderer.phtml @@ -3,9 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - +// phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis ?> <?php /** @var $block \Magento\Bundle\Block\Adminhtml\Sales\Order\Items\Renderer */ ?> @@ -17,74 +15,73 @@ <?php $_prevOptionId = '' ?> -<?php if ($block->getOrderOptions() || $_item->getDescription()): ?> +<?php if ($block->getOrderOptions() || $_item->getDescription()) : ?> <?php $_showlastRow = true ?> -<?php else: ?> +<?php else : ?> <?php $_showlastRow = false ?> <?php endif; ?> -<?php foreach ($items as $_item): ?> +<?php foreach ($items as $_item) : ?> <?php $block->setPriceDataObject($_item) ?> - <?php if ($_item->getParentItem()): ?> + <?php if ($_item->getParentItem()) : ?> <?php $attributes = $block->getSelectionAttributes($_item) ?> - <?php if ($_prevOptionId != $attributes['option_id']): ?> + <?php if ($_prevOptionId != $attributes['option_id']) : ?> <tr> - <td class="col-product"><div class="option-label"><?= /* @escapeNotVerified */ $attributes['option_label'] ?></div></td> + <td class="col-product"><div class="option-label"><?= $block->escapeHtml($attributes['option_label']) ?></div></td> <td class="col-qty last"> </td> </tr> - <?php $_prevOptionId = $attributes['option_id'] ?> + <?php $_prevOptionId = $attributes['option_id'] ?> <?php endif; ?> <?php endif; ?> <tr<?= (++$_index == $_count && !$_showlastRow) ? ' class="border"' : '' ?>> - <?php if (!$_item->getParentItem()): ?> + <?php if (!$_item->getParentItem()) : ?> <td class="col-product"> <div class="product-title"><?= $block->escapeHtml($_item->getName()) ?></div> <div class="product-sku-block"> - <span><?= /* @escapeNotVerified */ __('SKU') ?>:</span> - <?= implode('<br />', $this->helper('Magento\Catalog\Helper\Data')->splitSku($block->escapeHtml($_item->getSku()))) ?> + <span><?= $block->escapeHtml(__('SKU')) ?>:</span> + <?= /* @noEscape */ implode('<br />', $this->helper(Magento\Catalog\Helper\Data::class)->splitSku($_item->getSku())) ?> </div> </td> - <?php else: ?> + <?php else : ?> <td class="col-product"><div class="option-value"><?= $block->getValueHtml($_item) ?></div></td> <?php endif; ?> <td class="col-qty last"> - <?php if (($block->isShipmentSeparately() && $_item->getParentItem()) || (!$block->isShipmentSeparately() && !$_item->getParentItem())): ?> - <?php if (isset($shipItems[$_item->getId()])): ?> - <?= /* @escapeNotVerified */ $shipItems[$_item->getId()]->getQty()*1 ?> - <?php elseif ($_item->getIsVirtual()): ?> - <?= /* @escapeNotVerified */ __('N/A') ?> - <?php else: ?> + <?php if (($block->isShipmentSeparately() && $_item->getParentItem()) || (!$block->isShipmentSeparately() && !$_item->getParentItem())) : ?> + <?php if (isset($shipItems[$_item->getId()])) : ?> + <?= $block->escapeHtml($shipItems[$_item->getId()]->getQty()*1) ?> + <?php elseif ($_item->getIsVirtual()) : ?> + <?= $block->escapeHtml(__('N/A')) ?> + <?php else : ?> 0 <?php endif; ?> - <?php else: ?> + <?php else : ?>   <?php endif; ?> </td> </tr> <?php endforeach; ?> -<?php if ($_showlastRow): ?> +<?php if ($_showlastRow) : ?> <tr class="border"> <td class="col-product"> - <?php if ($block->getOrderOptions($_item->getOrderItem())): ?> + <?php if ($block->getOrderOptions($_item->getOrderItem())) : ?> <dl class="item-options"> - <?php foreach ($block->getOrderOptions($_item->getOrderItem()) as $option): ?> - <dt><?= /* @escapeNotVerified */ $option['label'] ?></dt> + <?php foreach ($block->getOrderOptions($_item->getOrderItem()) as $option) : ?> + <dt><?= $block->escapeHtml($option['label']) ?></dt> <dd> - <?php if (isset($option['custom_view']) && $option['custom_view']): ?> - <?= /* @escapeNotVerified */ $option['value'] ?> - <?php else: ?> - <?= $block->truncateString($option['value'], 55, '', $_remainder) ?> - <?php if ($_remainder):?> - ... <span id="<?= /* @escapeNotVerified */ $_id = 'id' . uniqid() ?>"><?= /* @escapeNotVerified */ $_remainder ?></span> + <?php if (isset($option['custom_view']) && $option['custom_view']) : ?> + <?= $block->escapeHtml($option['value']) ?> + <?php else : ?> + <?= $block->escapeHtml($block->truncateString($option['value'], 55, '', $_remainder)) ?> + <?php if ($_remainder) :?> + ... <span id="<?= $block->escapeHtmlAttr($_id = 'id' . uniqid()) ?>"><?= $block->escapeHtml($_remainder) ?></span> <script> -require(['prototype'], function(){ - - $('<?= /* @escapeNotVerified */ $_id ?>').hide(); - $('<?= /* @escapeNotVerified */ $_id ?>').up().observe('mouseover', function(){$('<?= /* @escapeNotVerified */ $_id ?>').show();}); - $('<?= /* @escapeNotVerified */ $_id ?>').up().observe('mouseout', function(){$('<?= /* @escapeNotVerified */ $_id ?>').hide();}); - -}); -</script> + require(['prototype'], function(){ + <?php $escapedId = $block->escapeJs($_id) ?> + $('<?= /* @noEscape */ $escapedId ?>').hide(); + $('<?= /* @noEscape */ $escapedId ?>').up().observe('mouseover', function(){$('<?= /* @noEscape */ $escapedId ?>').show();}); + $('<?= /* @noEscape */ $escapedId ?>').up().observe('mouseout', function(){$('<?= /* @noEscape */ $escapedId ?>').hide();}); + }); + </script> <?php endif;?> <?php endif;?> </dd> diff --git a/app/code/Magento/Bundle/view/base/templates/product/price/final_price.phtml b/app/code/Magento/Bundle/view/base/templates/product/price/final_price.phtml index 5955d0337bb6e..26264cc2cc87f 100644 --- a/app/code/Magento/Bundle/view/base/templates/product/price/final_price.phtml +++ b/app/code/Magento/Bundle/view/base/templates/product/price/final_price.phtml @@ -3,9 +3,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <?php @@ -21,76 +18,63 @@ $maximalPrice = $finalPriceModel->getMaximalPrice(); $regularPriceModel = $block->getPriceType('regular_price'); $maximalRegularPrice = $regularPriceModel->getMaximalPrice(); $minimalRegularPrice = $regularPriceModel->getMinimalPrice(); +$regularPriceAttributes = [ + 'display_label' => __('Regular Price'), + 'price_id' => $block->getPriceId('old-price-' . $idSuffix), + 'include_container' => true, + 'skip_adjustments' => true +]; +$renderMinimalRegularPrice = $block->renderAmount($minimalRegularPrice, $regularPriceAttributes); ?> -<?php if ($block->getSaleableItem()->getPriceView()): ?> +<?php if ($block->getSaleableItem()->getPriceView()) : ?> <p class="minimal-price"> - <?php /* @escapeNotVerified */ echo $block->renderAmount($minimalPrice, [ + <?= /* @noEscape */ $block->renderAmount($minimalPrice, [ 'display_label' => __('As low as'), 'price_id' => $block->getPriceId('from-'), 'include_container' => true ]); ?> - <?php if ($minimalPrice < $minimalRegularPrice): ?> + <?php if ($minimalPrice < $minimalRegularPrice) : ?> <span class="old-price"> - <?php /* @escapeNotVerified */ echo $block->renderAmount($minimalRegularPrice, [ - 'display_label' => __('Regular Price'), - 'price_id' => $block->getPriceId('old-price-' . $idSuffix), - 'include_container' => true, - 'skip_adjustments' => true - ]); ?> + <?= /* @noEscape */ $renderMinimalRegularPrice ?> </span> <?php endif ?> </p> -<?php else: ?> - <?php if ($block->showRangePrice()): ?> +<?php else : ?> + <?php if ($block->showRangePrice()) : ?> <p class="price-from"> - <?php /* @escapeNotVerified */ echo $block->renderAmount($minimalPrice, [ + <?= /* @noEscape */ $block->renderAmount($minimalPrice, [ 'display_label' => __('From'), 'price_id' => $block->getPriceId('from-'), 'price_type' => 'minPrice', 'include_container' => true ]); ?> - <?php if ($minimalPrice < $minimalRegularPrice): ?> + <?php if ($minimalPrice < $minimalRegularPrice) : ?> <span class="old-price"> - <?php /* @escapeNotVerified */ echo $block->renderAmount($minimalRegularPrice, [ - 'display_label' => __('Regular Price'), - 'price_id' => $block->getPriceId('old-price-' . $idSuffix), - 'include_container' => true, - 'skip_adjustments' => true - ]); ?> + <?= /* @noEscape */ $renderMinimalRegularPrice ?> </span> <?php endif ?> </p> <p class="price-to"> - <?php /* @escapeNotVerified */ echo $block->renderAmount($maximalPrice, [ + <?= /* @noEscape */ $block->renderAmount($maximalPrice, [ 'display_label' => __('To'), 'price_id' => $block->getPriceId('to-'), 'price_type' => 'maxPrice', 'include_container' => true ]); ?> - <?php if ($maximalPrice < $maximalRegularPrice): ?> + <?php if ($maximalPrice < $maximalRegularPrice) : ?> <span class="old-price"> - <?php /* @escapeNotVerified */ echo $block->renderAmount($maximalRegularPrice, [ - 'display_label' => __('Regular Price'), - 'price_id' => $block->getPriceId('old-price-' . $idSuffix), - 'include_container' => true, - 'skip_adjustments' => true - ]); ?> + <?= /* @noEscape */ $block->renderAmount($maximalRegularPrice, $regularPriceAttributes); ?> </span> <?php endif ?> </p> - <?php else: ?> - <?php /* @escapeNotVerified */ echo $block->renderAmount($minimalPrice, [ + <?php else : ?> + <?= /* @noEscape */ $block->renderAmount($minimalPrice, [ 'price_id' => $block->getPriceId('product-price-'), 'include_container' => true ]); ?> - <?php if ($minimalPrice < $minimalRegularPrice): ?> + <?php if ($minimalPrice < $minimalRegularPrice) : ?> <span class="old-price"> - <?php /* @escapeNotVerified */ echo $block->renderAmount($minimalRegularPrice, [ - 'display_label' => __('Regular Price'), - 'price_id' => $block->getPriceId('old-price-' . $idSuffix), - 'include_container' => true, - 'skip_adjustments' => true - ]); ?> + <?= /* @noEscape */ $renderMinimalRegularPrice ?> </span> <?php endif ?> <?php endif ?> diff --git a/app/code/Magento/Bundle/view/base/templates/product/price/selection/amount.phtml b/app/code/Magento/Bundle/view/base/templates/product/price/selection/amount.phtml index 53d24dd7c2c07..00bd9955632e5 100644 --- a/app/code/Magento/Bundle/view/base/templates/product/price/selection/amount.phtml +++ b/app/code/Magento/Bundle/view/base/templates/product/price/selection/amount.phtml @@ -3,11 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <?php /** @var \Magento\Framework\Pricing\Render\Amount $block */ ?> -<?= /* @escapeNotVerified */ $block->formatCurrency($block->getDisplayValue(), (bool) $block->getIncludeContainer()) ?> +<?= /* @noEscape */ $block->formatCurrency($block->getDisplayValue(), (bool) $block->getIncludeContainer()) ?> diff --git a/app/code/Magento/Bundle/view/base/templates/product/price/tier_prices.phtml b/app/code/Magento/Bundle/view/base/templates/product/price/tier_prices.phtml index 5f152c4bbefbc..f5f67588a1c49 100644 --- a/app/code/Magento/Bundle/view/base/templates/product/price/tier_prices.phtml +++ b/app/code/Magento/Bundle/view/base/templates/product/price/tier_prices.phtml @@ -3,9 +3,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <?php @@ -16,10 +13,10 @@ $tierPriceModel = $block->getPrice(); $tierPrices = $tierPriceModel->getTierPriceList(); ?> <?php if (count($tierPrices)) : ?> - <ul class="<?= /* @escapeNotVerified */ ($block->hasListClass() ? $block->getListClass() : 'prices-tier items') ?>"> + <ul class="<?= $block->escapeHtmlAttr(($block->hasListClass() ? $block->getListClass() : 'prices-tier items')) ?>"> <?php foreach ($tierPrices as $index => $price) : ?> <li class="item"> - <?php /* @escapeNotVerified */ echo __( + <?= /* @noEscape */ __( 'Buy %1 with %2 discount each', $price['price_qty'], '<strong class="benefit">' . round($price['percentage_value']) . '%</strong>' diff --git a/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/backbutton.phtml b/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/backbutton.phtml index 31a39c1cd162c..ba58544c26af5 100644 --- a/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/backbutton.phtml +++ b/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/backbutton.phtml @@ -6,5 +6,5 @@ ?> <button type="button" class="action back customization"> - <span><?= /* @escapeNotVerified */ __('Go back to product details') ?></span> + <span><?= $block->escapeHtml(__('Go back to product details')) ?></span> </button> diff --git a/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/customize.phtml b/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/customize.phtml index d7aea4237b100..480ffea5bc8b3 100644 --- a/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/customize.phtml +++ b/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/customize.phtml @@ -3,18 +3,15 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <?php $_product = $block->getProduct() ?> -<?php if ($_product->isSaleable() && $block->hasOptions()):?> +<?php if ($_product->isSaleable() && $block->hasOptions()) :?> <div class="bundle-actions"> <button id="bundle-slide" class="action primary customize" type="button"> - <span><?= /* @escapeNotVerified */ __('Customize and Add to Cart') ?></span> + <span><?= $block->escapeHtml(__('Customize and Add to Cart')) ?></span> </button> </div> <?php endif;?> diff --git a/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/options/notice.phtml b/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/options/notice.phtml index 2dbea0fd21395..927b64352d591 100644 --- a/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/options/notice.phtml +++ b/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/options/notice.phtml @@ -4,4 +4,4 @@ * See COPYING.txt for license details. */ ?> -<p class="required"><?= /* @escapeNotVerified */ __('* Required Fields') ?></p> +<p class="required"><?= $block->escapeHtml(__('* Required Fields')) ?></p> diff --git a/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/summary.phtml b/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/summary.phtml index bc4337fa7ae24..9bf179e622f17 100644 --- a/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/summary.phtml +++ b/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/summary.phtml @@ -3,40 +3,37 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <?php $_product = $block->getProduct(); ?> -<?php if ($_product->isSaleable() && $block->hasOptions()): ?> +<?php if ($_product->isSaleable() && $block->hasOptions()) : ?> <div id="bundleSummary" class="block-bundle-summary" data-mage-init='{"sticky":{"container": ".product-add-form"}}'> <div class="title"> - <strong><?= /* @escapeNotVerified */ __('Your Customization') ?></strong> + <strong><?= $block->escapeHtml(__('Your Customization')) ?></strong> </div> <div class="content"> <div class="bundle-info"> <?= $block->getImage($_product, 'bundled_product_customization_page')->toHtml() ?> <div class="product-details"> <strong class="product name"><?= $block->escapeHtml($_product->getName()) ?></strong> - <?php if ($_product->getIsSalable()): ?> - <p class="available stock" title="<?= /* @escapeNotVerified */ __('Availability') ?>"> - <span><?= /* @escapeNotVerified */ __('In stock') ?></span> + <?php if ($_product->getIsSalable()) : ?> + <p class="available stock" title="<?= $block->escapeHtmlAttr(__('Availability')) ?>"> + <span><?= $block->escapeHtml(__('In stock')) ?></span> </p> - <?php else: ?> - <p class="unavailable stock" title="<?= /* @escapeNotVerified */ __('Availability') ?>"> - <span><?= /* @escapeNotVerified */ __('Out of stock') ?></span> + <?php else : ?> + <p class="unavailable stock" title="<?= $block->escapeHtmlAttr(__('Availability')) ?>"> + <span><?= $block->escapeHtml(__('Out of stock')) ?></span> </p> <?php endif; ?> <?= $block->getChildHtml('', true) ?> </div> </div> <div class="bundle-summary"> - <strong class="subtitle"><?= /* @escapeNotVerified */ __('Summary') ?></strong> + <strong class="subtitle"><?= $block->escapeHtml(__('Summary')) ?></strong> <div id="bundle-summary" data-container="product-summary"> <ul data-mage-init='{"productSummary": []}' class="bundle items"></ul> <script data-template="bundle-summary" type="text/x-magento-template"> @@ -46,7 +43,7 @@ </li> </script> <script data-template="bundle-option" type="text/x-magento-template"> - <div><?= /* @escapeNotVerified */ __('%1 x %2', '<%- data._quantity_ %>', '<%- data._label_ %>') ?></div> + <div><?= /* @noEscape */ __('%1 x %2', '<%- data._quantity_ %>', '<%- data._label_ %>') ?></div> </script> </div> </div> @@ -61,7 +58,7 @@ "slideBackSelector": ".action.customization.back", "bundleProductSelector": "#bundleProduct", "bundleOptionsContainer": ".product-add-form" - <?php if ($block->isStartCustomization()): ?> + <?php if ($block->isStartCustomization()) : ?> ,"autostart": true <?php endif;?> } diff --git a/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/type/bundle.phtml b/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/type/bundle.phtml index ce9ef89a82bd1..ee29fc61d0145 100644 --- a/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/type/bundle.phtml +++ b/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/type/bundle.phtml @@ -4,19 +4,17 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - /* @var $block \Magento\Bundle\Block\Catalog\Product\View\Type\Bundle */ ?> <?php $_product = $block->getProduct() ?> -<?php if ($block->displayProductStockStatus()): ?> - <?php if ($_product->isAvailable()): ?> - <p class="stock available" title="<?= /* @escapeNotVerified */ __('Availability:') ?>"> - <span><?= /* @escapeNotVerified */ __('In stock') ?></span> +<?php if ($block->displayProductStockStatus()) : ?> + <?php if ($_product->isAvailable()) : ?> + <p class="stock available" title="<?= $block->escapeHtmlAttr(__('Availability:')) ?>"> + <span><?= $block->escapeHtml(__('In stock')) ?></span> </p> - <?php else: ?> - <p class="stock unavailable" title="<?= /* @escapeNotVerified */ __('Availability:') ?>"> - <span><?= /* @escapeNotVerified */ __('Out of stock') ?></span> + <?php else : ?> + <p class="stock unavailable" title="<?= $block->escapeHtmlAttr(__('Availability:')) ?>"> + <span><?= $block->escapeHtml(__('Out of stock')) ?></span> </p> <?php endif; ?> <?php endif; ?> diff --git a/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/type/bundle/option/checkbox.phtml b/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/type/bundle/option/checkbox.phtml index 830d03c826f32..5b56598dc58e2 100644 --- a/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/type/bundle/option/checkbox.phtml +++ b/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/type/bundle/option/checkbox.phtml @@ -3,9 +3,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <?php /* @var $block \Magento\Bundle\Block\Catalog\Product\View\Type\Bundle\Option\Checkbox */ ?> @@ -17,34 +14,34 @@ </label> <div class="control"> <div class="nested options-list"> - <?php if ($block->showSingle()): ?> - <?= /* @escapeNotVerified */ $block->getSelectionQtyTitlePrice($_selections[0]) ?> + <?php if ($block->showSingle()) : ?> + <?= /* @noEscape */ $block->getSelectionQtyTitlePrice($_selections[0]) ?> <?= /* @noEscape */ $block->getTierPriceRenderer()->renderTierPrice($_selections[0]) ?> <input type="hidden" - class="bundle-option-<?= /* @escapeNotVerified */ $_option->getId() ?> product bundle option" - name="bundle_option[<?= /* @escapeNotVerified */ $_option->getId() ?>]" - value="<?= /* @escapeNotVerified */ $_selections[0]->getSelectionId() ?>"/> - <?php else:?> - <?php foreach($_selections as $_selection): ?> + class="bundle-option-<?= $block->escapeHtmlAttr($_option->getId()) ?> product bundle option" + name="bundle_option[<?= $block->escapeHtml($_option->getId()) ?>]" + value="<?= $block->escapeHtmlAttr($_selections[0]->getSelectionId()) ?>"/> + <?php else :?> + <?php foreach ($_selections as $_selection) : ?> <div class="field choice"> - <input class="bundle-option-<?= /* @escapeNotVerified */ $_option->getId() ?> checkbox product bundle option change-container-classname" - id="bundle-option-<?= /* @escapeNotVerified */ $_option->getId() ?>-<?= /* @escapeNotVerified */ $_selection->getSelectionId() ?>" + <input class="bundle-option-<?= $block->escapeHtmlAttr($_option->getId()) ?> checkbox product bundle option change-container-classname" + id="bundle-option-<?= $block->escapeHtmlAttr($_option->getId()) ?>-<?= $block->escapeHtmlAttr($_selection->getSelectionId()) ?>" type="checkbox" - <?php if ($_option->getRequired()) /* @escapeNotVerified */ echo 'data-validate="{\'validate-one-required-by-name\':\'input[name^="bundle_option[' . $_option->getId() . ']"]:checked\'}"'?> - name="bundle_option[<?= /* @escapeNotVerified */ $_option->getId() ?>][<?= /* @escapeNotVerified */ $_selection->getId() ?>]" - data-selector="bundle_option[<?= /* @escapeNotVerified */ $_option->getId() ?>][<?= /* @escapeNotVerified */ $_selection->getId() ?>]" - <?php if ($block->isSelected($_selection)) echo ' checked="checked"' ?> - <?php if (!$_selection->isSaleable()) echo ' disabled="disabled"' ?> - value="<?= /* @escapeNotVerified */ $_selection->getSelectionId() ?>"/> + <?php if ($_option->getRequired()) { echo 'data-validate="{\'validate-one-required-by-name\':\'input[name^="bundle_option[' . $block->escapeHtmlAttr($_option->getId()) . ']"]:checked\'}"'; } ?> + name="bundle_option[<?= $block->escapeHtmlAttr($_option->getId()) ?>][<?= $block->escapeHtmlAttr($_selection->getId()) ?>]" + data-selector="bundle_option[<?= $block->escapeHtmlAttr($_option->getId()) ?>][<?= $block->escapeHtmlAttr($_selection->getId()) ?>]" + <?php if ($block->isSelected($_selection)) { echo ' checked="checked"'; } ?> + <?php if (!$_selection->isSaleable()) { echo ' disabled="disabled"'; } ?> + value="<?= $block->escapeHtmlAttr($_selection->getSelectionId()) ?>"/> <label class="label" - for="bundle-option-<?= /* @escapeNotVerified */ $_option->getId() ?>-<?= /* @escapeNotVerified */ $_selection->getSelectionId() ?>"> - <span><?= /* @escapeNotVerified */ $block->getSelectionQtyTitlePrice($_selection) ?></span> + for="bundle-option-<?= $block->escapeHtmlAttr($_option->getId()) ?>-<?= $block->escapeHtmlAttr($_selection->getSelectionId()) ?>"> + <span><?= /* @noEscape */ $block->getSelectionQtyTitlePrice($_selection) ?></span> <br/> <?= /* @noEscape */ $block->getTierPriceRenderer()->renderTierPrice($_selection) ?> </label> </div> <?php endforeach; ?> - <div id="bundle-option-<?= /* @escapeNotVerified */ $_option->getId() ?>-container"></div> + <div id="bundle-option-<?= $block->escapeHtmlAttr($_option->getId()) ?>-container"></div> <?php endif; ?> </div> </div> diff --git a/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/type/bundle/option/multi.phtml b/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/type/bundle/option/multi.phtml index 718d43070a5fd..d6f9fdb74ef62 100644 --- a/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/type/bundle/option/multi.phtml +++ b/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/type/bundle/option/multi.phtml @@ -3,39 +3,36 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <?php /* @var $block \Magento\Bundle\Block\Catalog\Product\View\Type\Bundle\Option\Multi */ ?> <?php $_option = $block->getOption() ?> <?php $_selections = $_option->getSelections() ?> <div class="field option <?= ($_option->getRequired()) ? ' required': '' ?>"> - <label class="label" for="bundle-option-<?= /* @escapeNotVerified */ $_option->getId() ?>"> + <label class="label" for="bundle-option-<?= $block->escapeHtmlAttr($_option->getId()) ?>"> <span><?= $block->escapeHtml($_option->getTitle()) ?></span> </label> <div class="control"> - <?php if ($block->showSingle()): ?> - <?= /* @escapeNotVerified */ $block->getSelectionQtyTitlePrice($_selections[0]) ?> + <?php if ($block->showSingle()) : ?> + <?= /* @noEscape */ $block->getSelectionQtyTitlePrice($_selections[0]) ?> <input type="hidden" - name="bundle_option[<?= /* @escapeNotVerified */ $_option->getId() ?>]" - value="<?= /* @escapeNotVerified */ $_selections[0]->getSelectionId() ?>"/> - <?php else: ?> + name="bundle_option[<?= $block->escapeHtmlAttr($_option->getId()) ?>]" + value="<?= $block->escapeHtmlAttr($_selections[0]->getSelectionId()) ?>"/> + <?php else : ?> <select multiple="multiple" size="5" - id="bundle-option-<?= /* @escapeNotVerified */ $_option->getId() ?>" - name="bundle_option[<?= /* @escapeNotVerified */ $_option->getId() ?>][]" - data-selector="bundle_option[<?= /* @escapeNotVerified */ $_option->getId() ?>][]" - class="bundle-option-<?= /* @escapeNotVerified */ $_option->getId() ?> multiselect product bundle option change-container-classname" - <?php if ($_option->getRequired()) echo 'data-validate={required:true}' ?>> - <?php if(!$_option->getRequired()): ?> - <option value=""><?= /* @escapeNotVerified */ __('None') ?></option> + id="bundle-option-<?= $block->escapeHtmlAttr($_option->getId()) ?>" + name="bundle_option[<?= $block->escapeHtmlAttr($_option->getId()) ?>][]" + data-selector="bundle_option[<?= $block->escapeHtmlAttr($_option->getId()) ?>][]" + class="bundle-option-<?= $block->escapeHtmlAttr($_option->getId()) ?> multiselect product bundle option change-container-classname" + <?php if ($_option->getRequired()) { echo 'data-validate={required:true}'; } ?>> + <?php if (!$_option->getRequired()) : ?> + <option value=""><?= $block->escapeHtml(__('None')) ?></option> <?php endif; ?> - <?php foreach ($_selections as $_selection): ?> - <option value="<?= /* @escapeNotVerified */ $_selection->getSelectionId() ?>" - <?php if ($block->isSelected($_selection)) echo ' selected="selected"' ?> - <?php if (!$_selection->isSaleable()) echo ' disabled="disabled"' ?>> - <?= /* @escapeNotVerified */ $block->getSelectionQtyTitlePrice($_selection, false) ?> + <?php foreach ($_selections as $_selection) : ?> + <option value="<?= $block->escapeHtmlAttr($_selection->getSelectionId()) ?>" + <?php if ($block->isSelected($_selection)) { echo ' selected="selected"'; } ?> + <?php if (!$_selection->isSaleable()) { echo ' disabled="disabled"'; } ?>> + <?= /* @noEscape */ $block->getSelectionQtyTitlePrice($_selection, false) ?> </option> <?php endforeach; ?> </select> diff --git a/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/type/bundle/option/radio.phtml b/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/type/bundle/option/radio.phtml index 1f33d97227ea3..11ed226c176c8 100644 --- a/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/type/bundle/option/radio.phtml +++ b/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/type/bundle/option/radio.phtml @@ -3,9 +3,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <?php /* @var $block \Magento\Bundle\Block\Catalog\Product\View\Type\Bundle\Option\Radio */ ?> <?php $_option = $block->getOption(); ?> @@ -19,8 +16,8 @@ </label> <div class="control"> <div class="nested options-list"> - <?php if ($block->showSingle()): ?> - <?= /* @escapeNotVerified */ $block->getSelectionTitlePrice($_selections[0]) ?> + <?php if ($block->showSingle()) : ?> + <?= /* @noEscape */ $block->getSelectionTitlePrice($_selections[0]) ?> <?= /* @noEscape */ $block->getTierPriceRenderer()->renderTierPrice($_selections[0]) ?> <input type="hidden" class="bundle-option-<?= (int)$_option->getId() ?> product bundle option" @@ -29,54 +26,54 @@ id="bundle-option-<?= (int)$_option->getId() ?>-<?= (int)$_selections[0]->getSelectionId() ?>" checked="checked" /> - <?php else:?> - <?php if (!$_option->getRequired()): ?> + <?php else :?> + <?php if (!$_option->getRequired()) : ?> <div class="field choice"> <input type="radio" class="radio product bundle option" - id="bundle-option-<?= /* @escapeNotVerified */ $_option->getId() ?>" - name="bundle_option[<?= /* @escapeNotVerified */ $_option->getId() ?>]" - data-selector="bundle_option[<?= /* @escapeNotVerified */ $_option->getId() ?>]" + id="bundle-option-<?= $block->escapeHtmlAttr($_option->getId()) ?>" + name="bundle_option[<?= $block->escapeHtmlAttr($_option->getId()) ?>]" + data-selector="bundle_option[<?= $block->escapeHtmlAttr($_option->getId()) ?>]" <?= ($_default && $_default->isSalable())?'':' checked="checked" ' ?> value=""/> - <label class="label" for="bundle-option-<?= /* @escapeNotVerified */ $_option->getId() ?>"> - <span><?= /* @escapeNotVerified */ __('None') ?></span> + <label class="label" for="bundle-option-<?= $block->escapeHtmlAttr($_option->getId()) ?>"> + <span><?= $block->escapeHtml(__('None')) ?></span> </label> </div> <?php endif; ?> - <?php foreach ($_selections as $_selection): ?> + <?php foreach ($_selections as $_selection) : ?> <div class="field choice"> <input type="radio" class="radio product bundle option change-container-classname" - id="bundle-option-<?= /* @escapeNotVerified */ $_option->getId() ?>-<?= /* @escapeNotVerified */ $_selection->getSelectionId() ?>" - <?php if ($_option->getRequired()) echo 'data-validate="{\'validate-one-required-by-name\':true}"'?> - name="bundle_option[<?= /* @escapeNotVerified */ $_option->getId() ?>]" - data-selector="bundle_option[<?= /* @escapeNotVerified */ $_option->getId() ?>]" - <?php if ($block->isSelected($_selection)) echo ' checked="checked"' ?> - <?php if (!$_selection->isSaleable()) echo ' disabled="disabled"' ?> - value="<?= /* @escapeNotVerified */ $_selection->getSelectionId() ?>"/> + id="bundle-option-<?= $block->escapeHtmlAttr($_option->getId()) ?>-<?= $block->escapeHtmlAttr($_selection->getSelectionId()) ?>" + <?php if ($_option->getRequired()) { echo 'data-validate="{\'validate-one-required-by-name\':true}"'; }?> + name="bundle_option[<?= $block->escapeHtmlAttr($_option->getId()) ?>]" + data-selector="bundle_option[<?= $block->escapeHtmlAttr($_option->getId()) ?>]" + <?php if ($block->isSelected($_selection)) { echo ' checked="checked"'; } ?> + <?php if (!$_selection->isSaleable()) { echo ' disabled="disabled"'; } ?> + value="<?= $block->escapeHtmlAttr($_selection->getSelectionId()) ?>"/> <label class="label" - for="bundle-option-<?= /* @escapeNotVerified */ $_option->getId() ?>-<?= /* @escapeNotVerified */ $_selection->getSelectionId() ?>"> - <span><?= /* @escapeNotVerified */ $block->getSelectionTitlePrice($_selection) ?></span> + for="bundle-option-<?= $block->escapeHtmlAttr($_option->getId()) ?>-<?= $block->escapeHtmlAttr($_selection->getSelectionId()) ?>"> + <span><?= /* @noEscape */ $block->getSelectionTitlePrice($_selection) ?></span> <br/> <?= /* @noEscape */ $block->getTierPriceRenderer()->renderTierPrice($_selection) ?> </label> </div> <?php endforeach; ?> - <div id="bundle-option-<?= /* @escapeNotVerified */ $_option->getId() ?>-container"></div> + <div id="bundle-option-<?= $block->escapeHtmlAttr($_option->getId()) ?>-container"></div> <?php endif; ?> <div class="field qty qty-holder"> - <label class="label" for="bundle-option-<?= /* @escapeNotVerified */ $_option->getId() ?>-qty-input"> - <span><?= /* @escapeNotVerified */ __('Quantity') ?></span> + <label class="label" for="bundle-option-<?= $block->escapeHtmlAttr($_option->getId()) ?>-qty-input"> + <span><?= $block->escapeHtml(__('Quantity')) ?></span> </label> <div class="control"> - <input <?php if (!$_canChangeQty) echo ' disabled="disabled"' ?> - id="bundle-option-<?= /* @escapeNotVerified */ $_option->getId() ?>-qty-input" - class="input-text qty<?php if (!$_canChangeQty) echo ' qty-disabled' ?>" + <input <?php if (!$_canChangeQty) { echo ' disabled="disabled"'; } ?> + id="bundle-option-<?= $block->escapeHtmlAttr($_option->getId()) ?>-qty-input" + class="input-text qty<?php if (!$_canChangeQty) { echo ' qty-disabled'; } ?>" type="number" - name="bundle_option_qty[<?= /* @escapeNotVerified */ $_option->getId() ?>]" - data-selector="bundle_option_qty[<?= /* @escapeNotVerified */ $_option->getId() ?>]" - value="<?= /* @escapeNotVerified */ $_defaultQty ?>"/> + name="bundle_option_qty[<?= $block->escapeHtmlAttr($_option->getId()) ?>]" + data-selector="bundle_option_qty[<?= $block->escapeHtmlAttr($_option->getId()) ?>]" + value="<?= $block->escapeHtmlAttr($_defaultQty) ?>"/> </div> </div> </div> diff --git a/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/type/bundle/option/select.phtml b/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/type/bundle/option/select.phtml index 4ea00f62b2043..1edf45fe8ca99 100644 --- a/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/type/bundle/option/select.phtml +++ b/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/type/bundle/option/select.phtml @@ -3,9 +3,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <?php /* @var $block \Magento\Bundle\Block\Catalog\Product\View\Type\Bundle\Option\Select */ ?> @@ -14,36 +11,36 @@ <?php $_default = $_option->getDefaultSelection(); ?> <?php list($_defaultQty, $_canChangeQty) = $block->getDefaultValues(); ?> <div class="field option <?= ($_option->getRequired()) ? ' required': '' ?>"> - <label class="label" for="bundle-option-<?= /* @escapeNotVerified */ $_option->getId() ?>"> + <label class="label" for="bundle-option-<?= $block->escapeHtmlAttr($_option->getId()) ?>"> <span><?= $block->escapeHtml($_option->getTitle()) ?></span> </label> <div class="control"> - <?php if ($block->showSingle()): ?> - <?= /* @escapeNotVerified */ $block->getSelectionTitlePrice($_selections[0]) ?> + <?php if ($block->showSingle()) : ?> + <?= /* @noEscape */ $block->getSelectionTitlePrice($_selections[0]) ?> <?= /* @noEscape */ $block->getTierPriceRenderer()->renderTierPrice($_selections[0]) ?> <input type="hidden" - class="bundle-option-<?= /* @escapeNotVerified */ $_option->getId() ?> product bundle option" - name="bundle_option[<?= /* @escapeNotVerified */ $_option->getId() ?>]" - value="<?= /* @escapeNotVerified */ $_selections[0]->getSelectionId() ?>"/> - <?php else:?> - <select id="bundle-option-<?= /* @escapeNotVerified */ $_option->getId() ?>" - name="bundle_option[<?= /* @escapeNotVerified */ $_option->getId() ?>]" - data-selector="bundle_option[<?= /* @escapeNotVerified */ $_option->getId() ?>]" - class="bundle-option-<?= /* @escapeNotVerified */ $_option->getId() ?> product bundle option bundle-option-select change-container-classname" - <?php if ($_option->getRequired()) echo 'data-validate = {required:true}' ?>> - <option value=""><?= /* @escapeNotVerified */ __('Choose a selection...') ?></option> - <?php foreach ($_selections as $_selection): ?> - <option value="<?= /* @escapeNotVerified */ $_selection->getSelectionId() ?>" - <?php if ($block->isSelected($_selection)) echo ' selected="selected"' ?> - <?php if (!$_selection->isSaleable()) echo ' disabled="disabled"' ?>> - <?= /* @escapeNotVerified */ $block->getSelectionTitlePrice($_selection, false) ?> + class="bundle-option-<?= $block->escapeHtmlAttr($_option->getId()) ?> product bundle option" + name="bundle_option[<?= $block->escapeHtmlAttr($_option->getId()) ?>]" + value="<?= $block->escapeHtmlAttr($_selections[0]->getSelectionId()) ?>"/> + <?php else :?> + <select id="bundle-option-<?= $block->escapeHtmlAttr($_option->getId()) ?>" + name="bundle_option[<?= $block->escapeHtmlAttr($_option->getId()) ?>]" + data-selector="bundle_option[<?= $block->escapeHtmlAttr($_option->getId()) ?>]" + class="bundle-option-<?= $block->escapeHtmlAttr($_option->getId()) ?> product bundle option bundle-option-select change-container-classname" + <?php if ($_option->getRequired()) { echo 'data-validate = {required:true}'; } ?>> + <option value=""><?= $block->escapeHtml(__('Choose a selection...')) ?></option> + <?php foreach ($_selections as $_selection) : ?> + <option value="<?= $block->escapeHtmlAttr($_selection->getSelectionId()) ?>" + <?php if ($block->isSelected($_selection)) { echo ' selected="selected"'; } ?> + <?php if (!$_selection->isSaleable()) { echo ' disabled="disabled"'; } ?>> + <?= /* @noEscape */ $block->getSelectionTitlePrice($_selection, false) ?> </option> <?php endforeach; ?> </select> - <div id="option-tier-prices-<?= /* @escapeNotVerified */ $_option->getId() ?>" class="option-tier-prices"> - <?php foreach ($_selections as $_selection): ?> + <div id="option-tier-prices-<?= $block->escapeHtmlAttr($_option->getId()) ?>" class="option-tier-prices"> + <?php foreach ($_selections as $_selection) : ?> <div data-role="selection-tier-prices" - data-selection-id="<?= /* @escapeNotVerified */ $_selection->getSelectionId() ?>" + data-selection-id="<?= $block->escapeHtmlAttr($_selection->getSelectionId()) ?>" class="selection-tier-prices"> <?= /* @noEscape */ $block->getTierPriceRenderer()->renderTierPrice($_selection) ?> </div> @@ -52,17 +49,17 @@ <?php endif; ?> <div class="nested"> <div class="field qty qty-holder"> - <label class="label" for="bundle-option-<?= /* @escapeNotVerified */ $_option->getId() ?>-qty-input"> - <span><?= /* @escapeNotVerified */ __('Quantity') ?></span> + <label class="label" for="bundle-option-<?= $block->escapeHtmlAttr($_option->getId()) ?>-qty-input"> + <span><?= $block->escapeHtml(__('Quantity')) ?></span> </label> <div class="control"> - <input <?php if (!$_canChangeQty) echo ' disabled="disabled"' ?> - id="bundle-option-<?= /* @escapeNotVerified */ $_option->getId() ?>-qty-input" - class="input-text qty<?php if (!$_canChangeQty) echo ' qty-disabled' ?>" + <input <?php if (!$_canChangeQty) { echo ' disabled="disabled"'; } ?> + id="bundle-option-<?= $block->escapeHtmlAttr($_option->getId()) ?>-qty-input" + class="input-text qty<?php if (!$_canChangeQty) { echo ' qty-disabled'; } ?>" type="number" - name="bundle_option_qty[<?= /* @escapeNotVerified */ $_option->getId() ?>]" - data-selector="bundle_option_qty[<?= /* @escapeNotVerified */ $_option->getId() ?>]" - value="<?= /* @escapeNotVerified */ $_defaultQty ?>"/> + name="bundle_option_qty[<?= $block->escapeHtmlAttr($_option->getId()) ?>]" + data-selector="bundle_option_qty[<?= $block->escapeHtmlAttr($_option->getId()) ?>]" + value="<?= $block->escapeHtmlAttr($_defaultQty) ?>"/> </div> </div> </div> diff --git a/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/type/bundle/options.phtml b/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/type/bundle/options.phtml index 157e2d959720b..cac96a8aca7c3 100644 --- a/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/type/bundle/options.phtml +++ b/app/code/Magento/Bundle/view/frontend/templates/catalog/product/view/type/bundle/options.phtml @@ -3,24 +3,22 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - +// phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis ?> <?php /** @var $block Magento\Bundle\Block\Catalog\Product\View\Type\Bundle */ ?> <?php $product = $block->getProduct(); -$helper = $this->helper('Magento\Catalog\Helper\Output'); +$helper = $this->helper(Magento\Catalog\Helper\Output::class); $stripSelection = $product->getConfigureMode() ? true : false; $options = $block->decorateArray($block->getOptions($stripSelection)); ?> -<?php if ($product->isSaleable()):?> - <?php if (count($options)): ?> +<?php if ($product->isSaleable()) :?> + <?php if (count($options)) : ?> <script type="text/x-magento-init"> { "#product_addtocart_form": { "priceBundle": { - "optionConfig": <?= /* @escapeNotVerified */ $block->getJsonConfig() ?>, + "optionConfig": <?= /* @noEscape */ $block->getJsonConfig() ?>, "controlContainer": ".field.option" } } @@ -28,17 +26,20 @@ $options = $block->decorateArray($block->getOptions($stripSelection)); </script> <fieldset class="fieldset fieldset-bundle-options"> <legend id="customizeTitle" class="legend title"> - <span><?= /* @escapeNotVerified */ __('Customize %1', $helper->productAttribute($product, $product->getName(), 'name')) ?></span> + <span><?= /* @noEscape */ __('Customize %1', $helper->productAttribute($product, $product->getName(), 'name')) ?></span> </legend><br /> <?= $block->getChildHtml('product_info_bundle_options_top') ?> - <?php foreach ($options as $option): ?> - <?php if (!$option->getSelections()): ?> - <?php continue; ?> - <?php endif; ?> - <?= $block->getOptionHtml($option) ?> + <?php foreach ($options as $option) : ?> + <?php + if (!$option->getSelections()) { + continue; + } else { + echo $block->getOptionHtml($option); + } + ?> <?php endforeach; ?> </fieldset> - <?php else: ?> - <p class="empty"><?= /* @escapeNotVerified */ __('No options of this product are available.') ?></p> + <?php else : ?> + <p class="empty"><?= $block->escapeHtml(__('No options of this product are available.')) ?></p> <?php endif; ?> <?php endif;?> diff --git a/app/code/Magento/Bundle/view/frontend/templates/email/order/items/creditmemo/default.phtml b/app/code/Magento/Bundle/view/frontend/templates/email/order/items/creditmemo/default.phtml index a87c2167e66d4..47f9eade7f6a7 100644 --- a/app/code/Magento/Bundle/view/frontend/templates/email/order/items/creditmemo/default.phtml +++ b/app/code/Magento/Bundle/view/frontend/templates/email/order/items/creditmemo/default.phtml @@ -3,9 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - +// phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis ?> <?php /** @var $block \Magento\Bundle\Block\Sales\Order\Items\Renderer */ ?> <?php $parentItem = $block->getItem() ?> @@ -13,15 +11,15 @@ <?php $items = $block->getChildren($parentItem) ?> -<?php if ($block->getItemOptions() || $parentItem->getDescription() || $this->helper('Magento\GiftMessage\Helper\Message')->isMessagesAllowed('order_item', $parentItem) && $parentItem->getGiftMessageId()): ?> +<?php if ($block->getItemOptions() || $parentItem->getDescription() || $this->helper(Magento\GiftMessage\Helper\Message::class)->isMessagesAllowed('order_item', $parentItem) && $parentItem->getGiftMessageId()) : ?> <?php $_showlastRow = true ?> -<?php else: ?> +<?php else : ?> <?php $_showlastRow = false ?> <?php endif; ?> <?php $_prevOptionId = '' ?> -<?php foreach ($items as $_item): ?> +<?php foreach ($items as $_item) : ?> <?php // As part of invoice item renderer logic, the order is set on each invoice item. @@ -30,40 +28,40 @@ $_item->setOrder($_order); ?> - <?php if ($_item->getOrderItem()->getParentItem()): ?> + <?php if ($_item->getOrderItem()->getParentItem()) : ?> <?php $attributes = $block->getSelectionAttributes($_item) ?> - <?php if ($_prevOptionId != $attributes['option_id']): ?> + <?php if ($_prevOptionId != $attributes['option_id']) : ?> <tr class="bundle-option-label"> <td colspan="3"> - <strong><?= /* @escapeNotVerified */ $attributes['option_label'] ?></strong> + <strong><?= $block->escapeHtml($attributes['option_label']) ?></strong> </td> </tr> <?php $_prevOptionId = $attributes['option_id'] ?> <?php endif; ?> <?php endif; ?> - <?php if (!$_item->getOrderItem()->getParentItem()): ?> + <?php if (!$_item->getOrderItem()->getParentItem()) : ?> <tr class="bundle-item bundle-parent"> <td class="item-info"> <p class="product-name"><?= $block->escapeHtml($_item->getName()) ?></p> - <p class="sku"><?= /* @escapeNotVerified */ __('SKU') ?>: <?= $block->escapeHtml($block->getSku($_item)) ?></p> + <p class="sku"><?= $block->escapeHtml(__('SKU')) ?>: <?= $block->escapeHtml($block->getSku($_item)) ?></p> </td> - <?php else: ?> + <?php else : ?> <tr class="bundle-item bundle-option-value"> <td class="item-info"> <p><?= $block->getValueHtml($_item) ?></p> </td> <?php endif; ?> <td class="item-qty"> - <?php if ($block->canShowPriceInfo($_item)): ?> - <?= /* @escapeNotVerified */ $_item->getQty() * 1 ?> - <?php else: ?> + <?php if ($block->canShowPriceInfo($_item)) : ?> + <?= $block->escapeHtml($_item->getQty() * 1) ?> + <?php else : ?>   <?php endif; ?> </td> <td class="item-price"> - <?php if ($block->canShowPriceInfo($_item)): ?> - <?= /* @escapeNotVerified */ $block->getItemPrice($_item) ?> - <?php else: ?> + <?php if ($block->canShowPriceInfo($_item)) : ?> + <?= $block->escapeHtml($block->getItemPrice($_item)) ?> + <?php else : ?>   <?php endif; ?> </td> @@ -71,14 +69,14 @@ <?php endforeach; ?> -<?php if ($_showlastRow): ?> +<?php if ($_showlastRow) : ?> <tr> <td colspan="3" class="item-extra"> - <?php if ($block->getItemOptions()): ?> + <?php if ($block->getItemOptions()) : ?> <dl> - <?php foreach ($block->getItemOptions() as $option): ?> - <dt><strong><em><?= /* @escapeNotVerified */ $option['label'] ?></em></strong></dt> - <dd><?= /* @escapeNotVerified */ $option['value'] ?></dd> + <?php foreach ($block->getItemOptions() as $option) : ?> + <dt><strong><em><?= $block->escapeHtml($option['label']) ?></em></strong></dt> + <dd><?= $block->escapeHtml($option['value']) ?></dd> <?php endforeach; ?> </dl> <?php endif; ?> diff --git a/app/code/Magento/Bundle/view/frontend/templates/email/order/items/invoice/default.phtml b/app/code/Magento/Bundle/view/frontend/templates/email/order/items/invoice/default.phtml index ee79ca3b0a7a5..544e5f60f54b4 100644 --- a/app/code/Magento/Bundle/view/frontend/templates/email/order/items/invoice/default.phtml +++ b/app/code/Magento/Bundle/view/frontend/templates/email/order/items/invoice/default.phtml @@ -3,9 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - +// phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis ?> <?php /** @var $block \Magento\Bundle\Block\Sales\Order\Items\Renderer */ ?> @@ -14,15 +12,15 @@ <?php $_index = 0 ?> <?php $_order = $block->getItem()->getOrder(); ?> -<?php if ($block->getItemOptions() || $parentItem->getDescription() || $this->helper('Magento\GiftMessage\Helper\Message')->isMessagesAllowed('order_item', $parentItem) && $parentItem->getGiftMessageId()): ?> +<?php if ($block->getItemOptions() || $parentItem->getDescription() || $this->helper(Magento\GiftMessage\Helper\Message::class)->isMessagesAllowed('order_item', $parentItem) && $parentItem->getGiftMessageId()) : ?> <?php $_showlastRow = true ?> -<?php else: ?> +<?php else : ?> <?php $_showlastRow = false ?> <?php endif; ?> <?php $_prevOptionId = '' ?> -<?php foreach ($items as $_item): ?> +<?php foreach ($items as $_item) : ?> <?php // As part of invoice item renderer logic, the order is set on each invoice item. @@ -31,40 +29,40 @@ $_item->setOrder($_order); ?> - <?php if ($_item->getOrderItem()->getParentItem()): ?> + <?php if ($_item->getOrderItem()->getParentItem()) : ?> <?php $attributes = $block->getSelectionAttributes($_item) ?> - <?php if ($_prevOptionId != $attributes['option_id']): ?> + <?php if ($_prevOptionId != $attributes['option_id']) : ?> <tr class="bundle-option-label"> <td colspan="3"> - <strong><em><?= /* @escapeNotVerified */ $attributes['option_label'] ?></em></strong> + <strong><em><?= $block->escapeHtml($attributes['option_label']) ?></em></strong> </td> </tr> <?php $_prevOptionId = $attributes['option_id'] ?> <?php endif; ?> <?php endif; ?> - <?php if (!$_item->getOrderItem()->getParentItem()): ?> + <?php if (!$_item->getOrderItem()->getParentItem()) : ?> <tr class="bundle-item bundle-parent"> <td class="item-info"> <p class="product-name"><?= $block->escapeHtml($_item->getName()) ?></p> - <p class="sku"><?= /* @escapeNotVerified */ __('SKU') ?>: <?= $block->escapeHtml($block->getSku($_item)) ?></p> + <p class="sku"><?= $block->escapeHtml(__('SKU')) ?>: <?= $block->escapeHtml($block->getSku($_item)) ?></p> </td> - <?php else: ?> + <?php else : ?> <tr class="bundle-item bundle-option-value"> <td class="item-info"> <p><?= $block->getValueHtml($_item) ?></p> </td> <?php endif; ?> <td class="item-qty"> - <?php if ($block->canShowPriceInfo($_item)): ?> - <?= /* @escapeNotVerified */ $_item->getQty() * 1 ?> - <?php else: ?> + <?php if ($block->canShowPriceInfo($_item)) : ?> + <?= $block->escapeHtml($_item->getQty() * 1) ?> + <?php else : ?>   <?php endif; ?> </td> <td class="item-price"> - <?php if ($block->canShowPriceInfo($_item)): ?> - <?= /* @escapeNotVerified */ $block->getItemPrice($_item) ?> - <?php else: ?> + <?php if ($block->canShowPriceInfo($_item)) : ?> + <?= $block->escapeHtml($block->getItemPrice($_item)) ?> + <?php else : ?>   <?php endif; ?> </td> @@ -72,14 +70,14 @@ <?php endforeach; ?> -<?php if ($_showlastRow): ?> +<?php if ($_showlastRow) : ?> <tr> <td colspan="3" class="item-extra"> - <?php if ($block->getItemOptions()): ?> + <?php if ($block->getItemOptions()) : ?> <dl> - <?php foreach ($block->getItemOptions() as $option): ?> - <dt><strong><em><?= /* @escapeNotVerified */ $option['label'] ?></em></strong></dt> - <dd><?= /* @escapeNotVerified */ $option['value'] ?></dd> + <?php foreach ($block->getItemOptions() as $option) : ?> + <dt><strong><em><?= $block->escapeHtml($option['label']) ?></em></strong></dt> + <dd><?= $block->escapeHtml($option['value']) ?></dd> <?php endforeach; ?> </dl> <?php endif; ?> diff --git a/app/code/Magento/Bundle/view/frontend/templates/email/order/items/order/default.phtml b/app/code/Magento/Bundle/view/frontend/templates/email/order/items/order/default.phtml index c9c3103c448e4..2b34016f81508 100644 --- a/app/code/Magento/Bundle/view/frontend/templates/email/order/items/order/default.phtml +++ b/app/code/Magento/Bundle/view/frontend/templates/email/order/items/order/default.phtml @@ -3,9 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - +// phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis ?> <?php /** @var $block \Magento\Bundle\Block\Sales\Order\Items\Renderer */ ?> <?php $_item = $block->getItem() ?> @@ -14,41 +12,41 @@ <?php $parentItem = $block->getItem() ?> <?php $items = array_merge([$parentItem], $parentItem->getChildrenItems()); ?> -<?php if ($block->getItemOptions() || $_item->getDescription() || $this->helper('Magento\GiftMessage\Helper\Message')->isMessagesAllowed('order_item', $_item) && $_item->getGiftMessageId()): ?> +<?php if ($block->getItemOptions() || $_item->getDescription() || $this->helper(Magento\GiftMessage\Helper\Message::class)->isMessagesAllowed('order_item', $_item) && $_item->getGiftMessageId()) : ?> <?php $_showlastRow = true ?> -<?php else: ?> +<?php else : ?> <?php $_showlastRow = false ?> <?php endif; ?> <?php $_prevOptionId = '' ?> -<?php foreach ($items as $_item): ?> +<?php foreach ($items as $_item) : ?> - <?php if ($_item->getParentItem()): ?> + <?php if ($_item->getParentItem()) : ?> <?php $attributes = $block->getSelectionAttributes($_item) ?> - <?php if ($_prevOptionId != $attributes['option_id']): ?> + <?php if ($_prevOptionId != $attributes['option_id']) : ?> <tr class="bundle-option-label"> <td colspan="3"> - <strong><em><?= /* @escapeNotVerified */ $attributes['option_label'] ?></em></strong> + <strong><em><?= $block->escapeHtml($attributes['option_label']) ?></em></strong> </td> </tr> <?php $_prevOptionId = $attributes['option_id'] ?> <?php endif; ?> <?php endif; ?> - <?php if (!$_item->getParentItem()): ?> + <?php if (!$_item->getParentItem()) : ?> <tr class="bundle-item bundle-parent"> <td class="item-info"> <p class="product-name"><?= $block->escapeHtml($_item->getName()) ?></p> - <p class="sku"><?= /* @escapeNotVerified */ __('SKU') ?>: <?= $block->escapeHtml($block->getSku($_item)) ?></p> + <p class="sku"><?= $block->escapeHtml(__('SKU')) ?>: <?= $block->escapeHtml($block->getSku($_item)) ?></p> </td> <td class="item-qty"> - <?= /* @escapeNotVerified */ $_item->getQtyOrdered() * 1 ?> + <?= $block->escapeHtml($_item->getQtyOrdered() * 1) ?> </td> <td class="item-price"> - <?= /* @escapeNotVerified */ $block->getItemPrice($_item) ?> + <?= $block->escapeHtml($block->getItemPrice($_item)) ?> </td> </tr> - <?php else: ?> + <?php else : ?> <tr class="bundle-item bundle-option-value"> <td class="item-info" colspan="3"> <p><?= $block->getValueHtml($_item) ?></p> @@ -58,25 +56,25 @@ <?php endforeach; ?> -<?php if ($_showlastRow): ?> +<?php if ($_showlastRow) : ?> <tr> <td colspan="3" class="item-extra"> - <?php if ($block->getItemOptions()): ?> + <?php if ($block->getItemOptions()) : ?> <dl> - <?php foreach ($block->getItemOptions() as $option): ?> - <dt><strong><em><?= /* @escapeNotVerified */ $option['label'] ?></em></strong></dt> - <dd><?= /* @escapeNotVerified */ $option['value'] ?></dd> + <?php foreach ($block->getItemOptions() as $option) : ?> + <dt><strong><em><?= $block->escapeHtml($option['label']) ?></em></strong></dt> + <dd><?= $block->escapeHtml($option['value']) ?></dd> <?php endforeach; ?> </dl> <?php endif; ?> - <?php if ($_item->getGiftMessageId() && $_giftMessage = $this->helper('Magento\GiftMessage\Helper\Message')->getGiftMessage($_item->getGiftMessageId())): ?> + <?php if ($_item->getGiftMessageId() && $_giftMessage = $this->helper(Magento\GiftMessage\Helper\Message::class)->getGiftMessage($_item->getGiftMessageId())) : ?> <table class="message-gift"> <tr> <td> - <h3><?= /* @escapeNotVerified */ __('Gift Message') ?></h3> - <strong><?= /* @escapeNotVerified */ __('From:') ?></strong> <?= $block->escapeHtml($_giftMessage->getSender()) ?> - <br /><strong><?= /* @escapeNotVerified */ __('To:') ?></strong> <?= $block->escapeHtml($_giftMessage->getRecipient()) ?> - <br /><strong><?= /* @escapeNotVerified */ __('Message:') ?></strong> + <h3><?= $block->escapeHtml(__('Gift Message')) ?></h3> + <strong><?= $block->escapeHtml(__('From:')) ?></strong> <?= $block->escapeHtml($_giftMessage->getSender()) ?> + <br /><strong><?= $block->escapeHtml(__('To:')) ?></strong> <?= $block->escapeHtml($_giftMessage->getRecipient()) ?> + <br /><strong><?= $block->escapeHtml(__('Message:')) ?></strong> <br /><?= $block->escapeHtml($_giftMessage->getMessage()) ?> </td> </tr> diff --git a/app/code/Magento/Bundle/view/frontend/templates/email/order/items/shipment/default.phtml b/app/code/Magento/Bundle/view/frontend/templates/email/order/items/shipment/default.phtml index 61582087d1fcc..0dbcbbb392bdc 100644 --- a/app/code/Magento/Bundle/view/frontend/templates/email/order/items/shipment/default.phtml +++ b/app/code/Magento/Bundle/view/frontend/templates/email/order/items/shipment/default.phtml @@ -3,9 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - +// phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis ?> <?php /** @var $block \Magento\Bundle\Block\Sales\Order\Items\Renderer */ ?> @@ -14,49 +12,49 @@ <?php $items = array_merge([$parentItem->getOrderItem()], $parentItem->getOrderItem()->getChildrenItems()) ?> <?php $shipItems = $block->getChildren($parentItem) ?> -<?php if ($block->getItemOptions() || $parentItem->getDescription() || $this->helper('Magento\GiftMessage\Helper\Message')->isMessagesAllowed('order_item', $parentItem) && $parentItem->getGiftMessageId()): ?> +<?php if ($block->getItemOptions() || $parentItem->getDescription() || $this->helper(Magento\GiftMessage\Helper\Message::class)->isMessagesAllowed('order_item', $parentItem) && $parentItem->getGiftMessageId()) : ?> <?php $_showlastRow = true ?> -<?php else: ?> +<?php else : ?> <?php $_showlastRow = false ?> <?php endif; ?> <?php $_prevOptionId = '' ?> -<?php foreach ($items as $_item): ?> +<?php foreach ($items as $_item) : ?> - <?php if ($_item->getParentItem()): ?> + <?php if ($_item->getParentItem()) : ?> <?php $attributes = $block->getSelectionAttributes($_item) ?> - <?php if ($_prevOptionId != $attributes['option_id']): ?> + <?php if ($_prevOptionId != $attributes['option_id']) : ?> <tr class="bundle-option-label"> <td colspan="2"> - <strong><em><?= /* @escapeNotVerified */ $attributes['option_label'] ?></em></strong> + <strong><em><?= $block->escapeHtml($attributes['option_label']) ?></em></strong> </td> </tr> <?php $_prevOptionId = $attributes['option_id'] ?> <?php endif; ?> <?php endif; ?> - <?php if (!$_item->getParentItem()): ?> + <?php if (!$_item->getParentItem()) : ?> <tr class="bundle-item bundle-parent"> <td class="item-info"> <p class="product-name"><?= $block->escapeHtml($_item->getName()) ?></p> - <p class="sku"><?= /* @escapeNotVerified */ __('SKU') ?>: <?= $block->escapeHtml($block->getSku($_item)) ?></p> + <p class="sku"><?= $block->escapeHtml(__('SKU')) ?>: <?= $block->escapeHtml($block->getSku($_item)) ?></p> </td> - <?php else: ?> + <?php else : ?> <tr class="bundle-item bundle-option-value"> <td class="item-info"> <p><?= $block->getValueHtml($_item) ?></p> </td> <?php endif; ?> <td class="item-qty"> - <?php if (($block->isShipmentSeparately() && $_item->getParentItem()) || (!$block->isShipmentSeparately() && !$_item->getParentItem())): ?> - <?php if (isset($shipItems[$_item->getId()])): ?> - <?= /* @escapeNotVerified */ $shipItems[$_item->getId()]->getQty() * 1 ?> - <?php elseif ($_item->getIsVirtual()): ?> - <?= /* @escapeNotVerified */ __('N/A') ?> - <?php else: ?> + <?php if (($block->isShipmentSeparately() && $_item->getParentItem()) || (!$block->isShipmentSeparately() && !$_item->getParentItem())) : ?> + <?php if (isset($shipItems[$_item->getId()])) : ?> + <?= $block->escapeHtml($shipItems[$_item->getId()]->getQty() * 1) ?> + <?php elseif ($_item->getIsVirtual()) : ?> + <?= $block->escapeHtml(__('N/A')) ?> + <?php else : ?> 0 <?php endif; ?> - <?php else: ?> + <?php else : ?>   <?php endif; ?> </td> @@ -64,14 +62,14 @@ <?php endforeach; ?> -<?php if ($_showlastRow): ?> +<?php if ($_showlastRow) : ?> <tr> <td colspan="2" class="item-extra"> - <?php if ($block->getItemOptions()): ?> + <?php if ($block->getItemOptions()) : ?> <dl> - <?php foreach ($block->getItemOptions() as $option): ?> - <dt><strong><em><?= /* @escapeNotVerified */ $option['label'] ?></em></strong></dt> - <dd><?= /* @escapeNotVerified */ $option['value'] ?></dd> + <?php foreach ($block->getItemOptions() as $option) : ?> + <dt><strong><em><?= $block->escapeHtml($option['label']) ?></em></strong></dt> + <dd><?= $block->escapeHtml($option['value']) ?></dd> <?php endforeach; ?> </dl> <?php endif; ?> diff --git a/app/code/Magento/Bundle/view/frontend/templates/js/components.phtml b/app/code/Magento/Bundle/view/frontend/templates/js/components.phtml index bad5acc209b5f..1bd7e554a73d6 100644 --- a/app/code/Magento/Bundle/view/frontend/templates/js/components.phtml +++ b/app/code/Magento/Bundle/view/frontend/templates/js/components.phtml @@ -3,8 +3,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <?= $block->getChildHtml() ?> + diff --git a/app/code/Magento/Bundle/view/frontend/templates/sales/order/creditmemo/items/renderer.phtml b/app/code/Magento/Bundle/view/frontend/templates/sales/order/creditmemo/items/renderer.phtml index b9d075966c5d1..2c204da2cd13d 100644 --- a/app/code/Magento/Bundle/view/frontend/templates/sales/order/creditmemo/items/renderer.phtml +++ b/app/code/Magento/Bundle/view/frontend/templates/sales/order/creditmemo/items/renderer.phtml @@ -3,9 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - +// phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis ?> <?php /** @var $block \Magento\Bundle\Block\Sales\Order\Items\Renderer */ ?> <?php $parentItem = $block->getItem() ?> @@ -16,91 +14,95 @@ <?php $_prevOptionId = '' ?> -<?php foreach ($items as $_item): ?> +<?php foreach ($items as $_item) : ?> - <?php if ($block->getItemOptions() || $parentItem->getDescription() || $this->helper('Magento\GiftMessage\Helper\Message')->isMessagesAllowed('order_item', $parentItem) && $parentItem->getGiftMessageId()): ?> + <?php if ($block->getItemOptions() || $parentItem->getDescription() || $this->helper(Magento\GiftMessage\Helper\Message::class)->isMessagesAllowed('order_item', $parentItem) && $parentItem->getGiftMessageId()) : ?> <?php $_showlastRow = true ?> - <?php else: ?> + <?php else : ?> <?php $_showlastRow = false ?> <?php endif; ?> - <?php if ($_item->getOrderItem()->getParentItem()): ?> + <?php if ($_item->getOrderItem()->getParentItem()) : ?> <?php $attributes = $block->getSelectionAttributes($_item) ?> - <?php if ($_prevOptionId != $attributes['option_id']): ?> + <?php if ($_prevOptionId != $attributes['option_id']) : ?> <tr class="options-label"> - <td class="col label" colspan="7"><div class="option label"><?= /* @escapeNotVerified */ $attributes['option_label'] ?></div></td> + <td class="col label" colspan="7"><div class="option label"><?= $block->escapeHtml($attributes['option_label']) ?></div></td> </tr> <?php $_prevOptionId = $attributes['option_id'] ?> <?php endif; ?> <?php endif; ?> -<tr id="order-item-row-<?= /* @escapeNotVerified */ $_item->getId() ?>" class="<?php if ($_item->getOrderItem()->getParentItem()): ?>item-options-container<?php else: ?>item-parent<?php endif; ?>"<?php if ($_item->getParentItem()): ?> data-th="<?= /* @escapeNotVerified */ $attributes['option_label'] ?>"<?php endif; ?>> - <?php if (!$_item->getOrderItem()->getParentItem()): ?> +<tr id="order-item-row-<?= $block->escapeHtmlAttr($_item->getId()) ?>" + class="<?php if ($_item->getOrderItem()->getParentItem()) : ?>item-options-container<?php else : ?>item-parent<?php endif; ?>" + <?php if ($_item->getParentItem()) : ?> + data-th="<?= $block->escapeHtmlAttr($attributes['option_label']) ?>" + <?php endif; ?>> + <?php if (!$_item->getOrderItem()->getParentItem()) : ?> <td class="col name" data-th="<?= $block->escapeHtml(__('Product Name')) ?>"> <strong class="product name product-item-name"><?= $block->escapeHtml($_item->getName()) ?></strong> </td> - <?php else: ?> + <?php else : ?> <td class="col value" data-th="<?= $block->escapeHtml(__('Product Name')) ?>"><?= $block->getValueHtml($_item) ?></td> <?php endif; ?> <td class="col sku" data-th="<?= $block->escapeHtml(__('SKU')) ?>"><?= $block->escapeHtml($_item->getSku()) ?></td> <td class="col price" data-th="<?= $block->escapeHtml(__('Price')) ?>"> - <?php if ($block->canShowPriceInfo($_item)): ?> + <?php if ($block->canShowPriceInfo($_item)) : ?> <?= $block->getItemPriceHtml($_item) ?> - <?php else: ?> + <?php else : ?>   <?php endif; ?> </td> <td class="col qty" data-th="<?= $block->escapeHtml(__('Quantity')) ?>"> - <?php if ($block->canShowPriceInfo($_item)): ?> - <?= /* @escapeNotVerified */ $_item->getQty()*1 ?> - <?php else: ?> + <?php if ($block->canShowPriceInfo($_item)) : ?> + <?= $block->escapeHtml($_item->getQty()*1) ?> + <?php else : ?>   <?php endif; ?> </td> <td class="col subtotal" data-th="<?= $block->escapeHtml(__('Subtotal')) ?>"> - <?php if ($block->canShowPriceInfo($_item)): ?> + <?php if ($block->canShowPriceInfo($_item)) : ?> <?= $block->getItemRowTotalHtml($_item) ?> - <?php else: ?> + <?php else : ?>   <?php endif; ?> </td> <td class="col discount" data-th="<?= $block->escapeHtml(__('Discount Amount')) ?>"> - <?php if ($block->canShowPriceInfo($_item)): ?> - <?= /* @escapeNotVerified */ $block->getOrder()->formatPrice(-$_item->getDiscountAmount()) ?> - <?php else: ?> + <?php if ($block->canShowPriceInfo($_item)) : ?> + <?= $block->escapeHtml($block->getOrder()->formatPrice(-$_item->getDiscountAmount())) ?> + <?php else : ?>   <?php endif; ?> </td> <td class="col rowtotal" data-th="<?= $block->escapeHtml(__('Row Total')) ?>"> - <?php if ($block->canShowPriceInfo($_item)): ?> + <?php if ($block->canShowPriceInfo($_item)) : ?> <?= $block->getItemRowTotalAfterDiscountHtml($_item) ?> - <?php else: ?> + <?php else : ?>   <?php endif; ?> </td> </tr> <?php endforeach; ?> -<?php if ($_showlastRow && (($_options = $block->getItemOptions()) || $block->escapeHtml($_item->getDescription()))): ?> +<?php if ($_showlastRow && (($_options = $block->getItemOptions()) || $block->escapeHtml($_item->getDescription()))) : ?> <tr> <td class="col options" colspan="7"> - <?php if ($_options = $block->getItemOptions()): ?> + <?php if ($_options = $block->getItemOptions()) : ?> <dl class="item-options"> <?php foreach ($_options as $_option) : ?> <dt><?= $block->escapeHtml($_option['label']) ?></dt> - <?php if (!$block->getPrintStatus()): ?> + <?php if (!$block->getPrintStatus()) : ?> <?php $_formatedOptionValue = $block->getFormatedOptionValue($_option) ?> - <dd<?php if (isset($_formatedOptionValue['full_view'])): ?> class="tooltip wrapper"<?php endif; ?>> - <?= /* @escapeNotVerified */ $_formatedOptionValue['value'] ?> - <?php if (isset($_formatedOptionValue['full_view'])): ?> + <dd<?php if (isset($_formatedOptionValue['full_view'])) : ?> class="tooltip wrapper"<?php endif; ?>> + <?= /* @noEscape */ $_formatedOptionValue['value'] ?> + <?php if (isset($_formatedOptionValue['full_view'])) : ?> <div class="tooltip content"> <dl class="item options"> <dt><?= $block->escapeHtml($_option['label']) ?></dt> - <dd><?= /* @escapeNotVerified */ $_formatedOptionValue['full_view'] ?></dd> + <dd><?= /* @noEscape */ $_formatedOptionValue['full_view'] ?></dd> </dl> </div> <?php endif; ?> </dd> - <?php else: ?> + <?php else : ?> <dd><?= $block->escapeHtml((isset($_option['print_value']) ? $_option['print_value'] : $_option['value'])) ?></dd> <?php endif; ?> <?php endforeach; ?> diff --git a/app/code/Magento/Bundle/view/frontend/templates/sales/order/invoice/items/renderer.phtml b/app/code/Magento/Bundle/view/frontend/templates/sales/order/invoice/items/renderer.phtml index e18d75ce77b9c..097139fbace0e 100644 --- a/app/code/Magento/Bundle/view/frontend/templates/sales/order/invoice/items/renderer.phtml +++ b/app/code/Magento/Bundle/view/frontend/templates/sales/order/invoice/items/renderer.phtml @@ -3,9 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - +// phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis ?> <?php /** @var $block \Magento\Bundle\Block\Sales\Order\Items\Renderer */ ?> <?php $parentItem = $block->getItem() ?> @@ -15,77 +13,85 @@ <?php $_index = 0 ?> <?php $_prevOptionId = '' ?> -<?php foreach ($items as $_item): ?> +<?php foreach ($items as $_item) : ?> - <?php if ($block->getItemOptions() || $parentItem->getDescription() || $this->helper('Magento\GiftMessage\Helper\Message')->isMessagesAllowed('order_item', $parentItem) && $parentItem->getGiftMessageId()): ?> + <?php if ($block->getItemOptions() || $parentItem->getDescription() || $this->helper(Magento\GiftMessage\Helper\Message::class)->isMessagesAllowed('order_item', $parentItem) && $parentItem->getGiftMessageId()) : ?> <?php $_showlastRow = true ?> - <?php else: ?> + <?php else : ?> <?php $_showlastRow = false ?> <?php endif; ?> - <?php if ($_item->getOrderItem()->getParentItem()): ?> + <?php if ($_item->getOrderItem()->getParentItem()) : ?> <?php $attributes = $block->getSelectionAttributes($_item) ?> - <?php if ($_prevOptionId != $attributes['option_id']): ?> + <?php if ($_prevOptionId != $attributes['option_id']) : ?> <tr class="options-label"> - <td class="col label" colspan="5"><div class="option label"><?= /* @escapeNotVerified */ $attributes['option_label'] ?></div></td> + <td class="col label" colspan="5"> + <div class="option label"><?= $block->escapeHtml($attributes['option_label']) ?></div> + </td> </tr> <?php $_prevOptionId = $attributes['option_id'] ?> <?php endif; ?> <?php endif; ?> - <tr id="order-item-row-<?= /* @escapeNotVerified */ $_item->getId() ?>" class="<?php if ($_item->getOrderItem()->getParentItem()): ?>item-options-container<?php else: ?>item-parent<?php endif; ?>"<?php if ($_item->getOrderItem()->getParentItem()): ?> data-th="<?= /* @escapeNotVerified */ $attributes['option_label'] ?>"<?php endif; ?>> - <?php if (!$_item->getOrderItem()->getParentItem()): ?> + <tr id="order-item-row-<?= $block->escapeHtmlAttr($_item->getId()) ?>" + class="<?php if ($_item->getOrderItem()->getParentItem()) : ?>item-options-container + <?php else : ?>item-parent + <?php endif; ?>" + <?php if ($_item->getOrderItem()->getParentItem()) : ?> + data-th="<?= $block->escapeHtmlAttr($attributes['option_label']) ?>" + <?php endif; ?>> + <?php if (!$_item->getOrderItem()->getParentItem()) : ?> <td class="col name" data-th="<?= $block->escapeHtml(__('Product Name')) ?>"> <strong class="product name product-item-name"><?= $block->escapeHtml($_item->getName()) ?></strong> </td> - <?php else: ?> + <?php else : ?> <td class="col value" data-th="<?= $block->escapeHtml(__('Product Name')) ?>"><?= $block->getValueHtml($_item) ?></td> <?php endif; ?> <td class="col sku" data-th="<?= $block->escapeHtml(__('SKU')) ?>"><?= $block->escapeHtml($_item->getSku()) ?></td> <td class="col price" data-th="<?= $block->escapeHtml(__('Price')) ?>"> - <?php if ($block->canShowPriceInfo($_item)): ?> + <?php if ($block->canShowPriceInfo($_item)) : ?> <?= $block->getItemPriceHtml($_item) ?> - <?php else: ?> + <?php else : ?>   <?php endif; ?> </td> <td class="col qty" data-th="<?= $block->escapeHtml(__('Qty Invoiced')) ?>"> - <?php if ($block->canShowPriceInfo($_item)): ?> - <?= /* @escapeNotVerified */ $_item->getQty()*1 ?> - <?php else: ?> + <?php if ($block->canShowPriceInfo($_item)) : ?> + <?= $block->escapeHtml($_item->getQty()*1) ?> + <?php else : ?>   <?php endif; ?> </td> <td class="col subtotal" data-th="<?= $block->escapeHtml(__('Subtotal')) ?>"> - <?php if ($block->canShowPriceInfo($_item)): ?> + <?php if ($block->canShowPriceInfo($_item)) : ?> <?= $block->getItemRowTotalHtml($_item) ?> - <?php else: ?> + <?php else : ?>   <?php endif; ?> </td> </tr> <?php endforeach; ?> -<?php if ($_showlastRow && (($_options = $block->getItemOptions()) || $block->escapeHtml($_item->getDescription()))): ?> +<?php if ($_showlastRow && (($_options = $block->getItemOptions()) || $block->escapeHtml($_item->getDescription()))) : ?> <tr> <td class="col options" colspan="5"> - <?php if ($_options = $block->getItemOptions()): ?> + <?php if ($_options = $block->getItemOptions()) : ?> <dl class="item-options"> <?php foreach ($_options as $_option) : ?> <dt><?= $block->escapeHtml($_option['label']) ?></dt> - <?php if (!$block->getPrintStatus()): ?> + <?php if (!$block->getPrintStatus()) : ?> <?php $_formatedOptionValue = $block->getFormatedOptionValue($_option) ?> - <dd<?php if (isset($_formatedOptionValue['full_view'])): ?> class="tooltip wrapper"<?php endif; ?>> - <?= /* @escapeNotVerified */ $_formatedOptionValue['value'] ?> - <?php if (isset($_formatedOptionValue['full_view'])): ?> + <dd<?php if (isset($_formatedOptionValue['full_view'])) : ?> class="tooltip wrapper"<?php endif; ?>> + <?= /* @noEscape */ $_formatedOptionValue['value'] ?> + <?php if (isset($_formatedOptionValue['full_view'])) : ?> <div class="tooltip content"> <dl class="item options"> <dt><?= $block->escapeHtml($_option['label']) ?></dt> - <dd><?= /* @escapeNotVerified */ $_formatedOptionValue['full_view'] ?></dd> + <dd><?= /* @noEscape */ $_formatedOptionValue['full_view'] ?></dd> </dl> </div> <?php endif; ?> </dd> - <?php else: ?> + <?php else : ?> <dd><?= $block->escapeHtml((isset($_option['print_value']) ? $_option['print_value'] : $_option['value'])) ?></dd> <?php endif; ?> <?php endforeach; ?> diff --git a/app/code/Magento/Bundle/view/frontend/templates/sales/order/items/renderer.phtml b/app/code/Magento/Bundle/view/frontend/templates/sales/order/items/renderer.phtml index 74e1c5f874954..14c7de8429fdf 100644 --- a/app/code/Magento/Bundle/view/frontend/templates/sales/order/items/renderer.phtml +++ b/app/code/Magento/Bundle/view/frontend/templates/sales/order/items/renderer.phtml @@ -3,9 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - +// phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis /** @var $block \Magento\Bundle\Block\Sales\Order\Items\Renderer */ $parentItem = $block->getItem(); $items = array_merge([$parentItem], $parentItem->getChildrenItems()); @@ -13,20 +11,20 @@ $index = 0; $prevOptionId = ''; ?> -<?php foreach ($items as $item): ?> +<?php foreach ($items as $item) : ?> <?php if ($block->getItemOptions() || $parentItem->getDescription() - || $this->helper('Magento\GiftMessage\Helper\Message')->isMessagesAllowed('order_item', $parentItem) - && $parentItem->getGiftMessageId()): ?> + || $this->helper(Magento\GiftMessage\Helper\Message::class)->isMessagesAllowed('order_item', $parentItem) + && $parentItem->getGiftMessageId()) : ?> <?php $showLastRow = true; ?> - <?php else: ?> + <?php else : ?> <?php $showLastRow = false; ?> <?php endif; ?> - <?php if ($item->getParentItem()): ?> + <?php if ($item->getParentItem()) : ?> <?php $attributes = $block->getSelectionAttributes($item) ?> - <?php if ($prevOptionId != $attributes['option_id']): ?> + <?php if ($prevOptionId != $attributes['option_id']) : ?> <tr class="options-label"> <td class="col label" colspan="5"><?= $block->escapeHtml($attributes['option_label']); ?></td> </tr> @@ -34,19 +32,19 @@ $prevOptionId = ''; <?php endif; ?> <?php endif; ?> <tr id="order-item-row-<?= /* @noEscape */ $item->getId() ?>" - class="<?php if ($item->getParentItem()): ?> + class="<?php if ($item->getParentItem()) : ?> item-options-container - <?php else: ?> + <?php else : ?> item-parent <?php endif; ?>" - <?php if ($item->getParentItem()): ?> + <?php if ($item->getParentItem()) : ?> data-th="<?= $block->escapeHtml($attributes['option_label']); ?>" <?php endif; ?>> - <?php if (!$item->getParentItem()): ?> + <?php if (!$item->getParentItem()) : ?> <td class="col name" data-th="<?= $block->escapeHtml(__('Product Name')); ?>"> <strong class="product name product-item-name"><?= $block->escapeHtml($item->getName()); ?></strong> </td> - <?php else: ?> + <?php else : ?> <td class="col value" data-th="<?= $block->escapeHtml(__('Product Name')); ?>"> <?= $block->getValueHtml($item); ?> </td> @@ -55,84 +53,82 @@ $prevOptionId = ''; <?= /* @noEscape */ $block->prepareSku($item->getSku()); ?> </td> <td class="col price" data-th="<?= $block->escapeHtml(__('Price')); ?>"> - <?php if (!$item->getParentItem()): ?> + <?php if (!$item->getParentItem()) : ?> <?= /* @noEscape */ $block->getItemPriceHtml(); ?> - <?php else: ?> + <?php else : ?>   <?php endif; ?> </td> <td class="col qty" data-th="<?= $block->escapeHtml(__('Quantity')); ?>"> - <?php if ( - ($item->getParentItem() && $block->isChildCalculated()) || + <?php if (($item->getParentItem() && $block->isChildCalculated()) || (!$item->getParentItem() && !$block->isChildCalculated()) || - ($item->getQtyShipped() > 0 && $item->getParentItem() && $block->isShipmentSeparately())): ?> + ($item->getQtyShipped() > 0 && $item->getParentItem() && $block->isShipmentSeparately())) : ?> <ul class="items-qty"> <?php endif; ?> <?php if (($item->getParentItem() && $block->isChildCalculated()) || - (!$item->getParentItem() && !$block->isChildCalculated())): ?> - <?php if ($item->getQtyOrdered() > 0): ?> + (!$item->getParentItem() && !$block->isChildCalculated())) : ?> + <?php if ($item->getQtyOrdered() > 0) : ?> <li class="item"> <span class="title"><?= $block->escapeHtml(__('Ordered')); ?></span> <span class="content"><?= /* @noEscape */ $item->getQtyOrdered() * 1; ?></span> </li> <?php endif; ?> - <?php if ($item->getQtyShipped() > 0 && !$block->isShipmentSeparately()): ?> + <?php if ($item->getQtyShipped() > 0 && !$block->isShipmentSeparately()) : ?> <li class="item"> <span class="title"><?= $block->escapeHtml(__('Shipped')); ?></span> <span class="content"><?= /* @noEscape */ $item->getQtyShipped() * 1; ?></span> </li> <?php endif; ?> - <?php if ($item->getQtyCanceled() > 0): ?> + <?php if ($item->getQtyCanceled() > 0) : ?> <li class="item"> <span class="title"><?= $block->escapeHtml(__('Canceled')); ?></span> <span class="content"><?= /* @noEscape */ $item->getQtyCanceled() * 1; ?></span> </li> <?php endif; ?> - <?php if ($item->getQtyRefunded() > 0): ?> + <?php if ($item->getQtyRefunded() > 0) : ?> <li class="item"> <span class="title"><?= $block->escapeHtml(__('Refunded')); ?></span> <span class="content"><?= /* @noEscape */ $item->getQtyRefunded() * 1; ?></span> </li> <?php endif; ?> - <?php elseif ($item->getQtyShipped() > 0 && $item->getParentItem() && $block->isShipmentSeparately()): ?> + <?php elseif ($item->getQtyShipped() > 0 && $item->getParentItem() && $block->isShipmentSeparately()) : ?> <li class="item"> <span class="title"><?= $block->escapeHtml(__('Shipped')); ?></span> <span class="content"><?= /* @noEscape */ $item->getQtyShipped() * 1; ?></span> </li> - <?php else: ?> + <?php else : ?> <span class="content"><?= /* @noEscape */ $parentItem->getQtyOrdered() * 1; ?></span> <?php endif; ?> - <?php if ( - ($item->getParentItem() && $block->isChildCalculated()) || + <?php if (($item->getParentItem() && $block->isChildCalculated()) || (!$item->getParentItem() && !$block->isChildCalculated()) || - ($item->getQtyShipped() > 0 && $item->getParentItem() && $block->isShipmentSeparately())):?> + ($item->getQtyShipped() > 0 && $item->getParentItem() && $block->isShipmentSeparately())) :?> </ul> <?php endif; ?> </td> <td class="col subtotal" data-th="<?= $block->escapeHtml(__('Subtotal')) ?>"> - <?php if (!$item->getParentItem()): ?> + <?php if (!$item->getParentItem()) : ?> <?= /* @noEscape */ $block->getItemRowTotalHtml(); ?> - <?php else: ?> + <?php else : ?>   <?php endif; ?> </td> </tr> <?php endforeach; ?> -<?php if ($showLastRow && (($options = $block->getItemOptions()) || $block->escapeHtml($item->getDescription()))): ?> +<?php if ($showLastRow && (($options = $block->getItemOptions()) || $block->escapeHtml($item->getDescription()))) : ?> <tr> <td class="col options" colspan="5"> - <?php if ($options = $block->getItemOptions()): ?> + <?php if ($options = $block->getItemOptions()) : ?> <dl class="item-options"> <?php foreach ($options as $option) : ?> <dt><?= $block->escapeHtml($option['label']) ?></dt> - <?php if (!$block->getPrintStatus()): ?> + <?php if (!$block->getPrintStatus()) : ?> <?php $formattedOptionValue = $block->getFormatedOptionValue($option) ?> - <dd<?php if (isset($formattedOptionValue['full_view'])): ?> + <dd<?php if (isset($formattedOptionValue['full_view'])) : ?> class="tooltip wrapper" <?php endif; ?>> <?= /* @noEscape */ $formattedOptionValue['value'] ?> - <?php if (isset($formattedOptionValue['full_view'])): ?> + <?php if (isset($formattedOptionValue['full_view'])) : ?> <div class="tooltip content"> <dl class="item options"> <dt><?= $block->escapeHtml($option['label']); ?></dt> @@ -141,7 +137,7 @@ $prevOptionId = ''; </div> <?php endif; ?> </dd> - <?php else: ?> + <?php else : ?> <dd><?= $block->escapeHtml((isset($option['print_value']) ? $option['print_value'] : $option['value'])); ?> diff --git a/app/code/Magento/Bundle/view/frontend/templates/sales/order/shipment/items/renderer.phtml b/app/code/Magento/Bundle/view/frontend/templates/sales/order/shipment/items/renderer.phtml index 0cd39156b2513..db658964253ea 100644 --- a/app/code/Magento/Bundle/view/frontend/templates/sales/order/shipment/items/renderer.phtml +++ b/app/code/Magento/Bundle/view/frontend/templates/sales/order/shipment/items/renderer.phtml @@ -3,9 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - +// phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis ?> <?php /** @var $block \Magento\Bundle\Block\Sales\Order\Items\Renderer */ ?> @@ -16,69 +14,69 @@ <?php $_prevOptionId = '' ?> -<?php foreach ($items as $_item): ?> +<?php foreach ($items as $_item) : ?> - <?php if ($block->getItemOptions() || $parentItem->getDescription() || $this->helper('Magento\GiftMessage\Helper\Message')->isMessagesAllowed('order_item', $parentItem) && $parentItem->getGiftMessageId()): ?> + <?php if ($block->getItemOptions() || $parentItem->getDescription() || $this->helper(Magento\GiftMessage\Helper\Message::class)->isMessagesAllowed('order_item', $parentItem) && $parentItem->getGiftMessageId()) : ?> <?php $_showlastRow = true ?> - <?php else: ?> + <?php else : ?> <?php $_showlastRow = false ?> <?php endif; ?> - <?php if ($_item->getParentItem()): ?> + <?php if ($_item->getParentItem()) : ?> <?php $attributes = $block->getSelectionAttributes($_item) ?> - <?php if ($_prevOptionId != $attributes['option_id']): ?> + <?php if ($_prevOptionId != $attributes['option_id']) : ?> <tr class="options-label"> - <td colspan="3" class="col label"><div class="option label"><?= /* @escapeNotVerified */ $attributes['option_label'] ?></div></td> + <td colspan="3" class="col label"><div class="option label"><?= $block->escapeHtml($attributes['option_label']) ?></div></td> </tr> <?php $_prevOptionId = $attributes['option_id'] ?> <?php endif; ?> <?php endif; ?> - <tr id="order-item-row-<?= /* @escapeNotVerified */ $_item->getId() ?>" class="<?php if ($_item->getParentItem()): ?>item-options-container<?php else: ?>item-parent<?php endif; ?>"<?php if ($_item->getParentItem()): ?> data-th="<?= /* @escapeNotVerified */ $attributes['option_label'] ?>"<?php endif; ?>> - <?php if (!$_item->getParentItem()): ?> + <tr id="order-item-row-<?= $block->escapeHtmlAttr($_item->getId()) ?>" class="<?php if ($_item->getParentItem()) : ?>item-options-container<?php else : ?>item-parent<?php endif; ?>"<?php if ($_item->getParentItem()) : ?> data-th="<?= $block->escapeHtmlAttr($attributes['option_label']) ?>"<?php endif; ?>> + <?php if (!$_item->getParentItem()) : ?> <td class="col name" data-th="<?= $block->escapeHtml(__('Product Name')) ?>"> <strong class="product name product-item-name"><?= $block->escapeHtml($_item->getName()) ?></strong> </td> - <?php else: ?> + <?php else : ?> <td class="col value" data-th="<?= $block->escapeHtml(__('Product Name')) ?>"><?= $block->getValueHtml($_item) ?></td> <?php endif; ?> <td class="col sku" data-th="<?= $block->escapeHtml(__('SKU')) ?>"><?= $block->escapeHtml($_item->getSku()) ?></td> <td class="col qty" data-th="<?= $block->escapeHtml(__('Qty Shipped')) ?>"> - <?php if (($block->isShipmentSeparately() && $_item->getParentItem()) || (!$block->isShipmentSeparately() && !$_item->getParentItem())): ?> - <?php if (isset($shipItems[$_item->getId()])): ?> - <?= /* @escapeNotVerified */ $shipItems[$_item->getId()]->getQty()*1 ?> - <?php elseif ($_item->getIsVirtual()): ?> - <?= /* @escapeNotVerified */ __('N/A') ?> - <?php else: ?> + <?php if (($block->isShipmentSeparately() && $_item->getParentItem()) || (!$block->isShipmentSeparately() && !$_item->getParentItem())) : ?> + <?php if (isset($shipItems[$_item->getId()])) : ?> + <?= $block->escapeHtml($shipItems[$_item->getId()]->getQty()*1) ?> + <?php elseif ($_item->getIsVirtual()) : ?> + <?= $block->escapeHtml(__('N/A')) ?> + <?php else : ?> 0 <?php endif; ?> - <?php else: ?> + <?php else : ?>   <?php endif; ?> </td> </tr> <?php endforeach; ?> -<?php if ($_showlastRow && (($_options = $block->getItemOptions()) || $block->escapeHtml($_item->getDescription()))): ?> +<?php if ($_showlastRow && (($_options = $block->getItemOptions()) || $block->escapeHtml($_item->getDescription()))) : ?> <tr> <td class="col options" colspan="3"> - <?php if ($_options = $block->getItemOptions()): ?> + <?php if ($_options = $block->getItemOptions()) : ?> <dl class="item-options"> <?php foreach ($_options as $_option) : ?> <dt><?= $block->escapeHtml($_option['label']) ?></dt> - <?php if (!$block->getPrintStatus()): ?> + <?php if (!$block->getPrintStatus()) : ?> <?php $_formatedOptionValue = $block->getFormatedOptionValue($_option) ?> - <dd<?php if (isset($_formatedOptionValue['full_view'])): ?> class="tooltip wrapper"<?php endif; ?>> - <?= /* @escapeNotVerified */ $_formatedOptionValue['value'] ?> - <?php if (isset($_formatedOptionValue['full_view'])): ?> + <dd<?php if (isset($_formatedOptionValue['full_view'])) : ?> class="tooltip wrapper"<?php endif; ?>> + <?= /* @noEscape */ $_formatedOptionValue['value'] ?> + <?php if (isset($_formatedOptionValue['full_view'])) : ?> <div class="tooltip content"> <dl class="item options"> <dt><?= $block->escapeHtml($_option['label']) ?></dt> - <dd><?= /* @escapeNotVerified */ $_formatedOptionValue['full_view'] ?></dd> + <dd><?= /* @noEscape */ $_formatedOptionValue['full_view'] ?></dd> </dl> </div> <?php endif; ?> </dd> - <?php else: ?> + <?php else : ?> <dd><?= $block->escapeHtml((isset($_option['print_value']) ? $_option['print_value'] : $_option['value'])) ?></dd> <?php endif; ?> <?php endforeach; ?> diff --git a/app/code/Magento/BundleGraphQl/etc/schema.graphqls b/app/code/Magento/BundleGraphQl/etc/schema.graphqls index edde6079dfb2f..26d2cbcd704fc 100644 --- a/app/code/Magento/BundleGraphQl/etc/schema.graphqls +++ b/app/code/Magento/BundleGraphQl/etc/schema.graphqls @@ -1,35 +1,35 @@ # Copyright © Magento, Inc. All rights reserved. # See COPYING.txt for license details. -type BundleItem @doc(description: "BundleItem defines an individual item in a bundle product") { - option_id: Int @doc(description: "An ID assigned to each type of item in a bundle product") - title: String @doc(description: "The display name of the item") - required: Boolean @doc(description: "Indicates whether the item must be included in the bundle") - type: String @doc(description: "The input type that the customer uses to select the item. Examples include radio button and checkbox") - position: Int @doc(description: "he relative position of this item compared to the other bundle items") - sku: String @doc(description: "The SKU of the bundle product") - options: [BundleItemOption] @doc(description: "An array of additional options for this bundle item") @resolver(class: "Magento\\BundleGraphQl\\Model\\Resolver\\BundleItemLinks") +type BundleItem @doc(description: "BundleItem defines an individual item in a bundle product.") { + option_id: Int @doc(description: "An ID assigned to each type of item in a bundle product.") + title: String @doc(description: "The display name of the item.") + required: Boolean @doc(description: "Indicates whether the item must be included in the bundle.") + type: String @doc(description: "The input type that the customer uses to select the item. Examples include radio button and checkbox.") + position: Int @doc(description: "he relative position of this item compared to the other bundle items.") + sku: String @doc(description: "The SKU of the bundle product.") + options: [BundleItemOption] @doc(description: "An array of additional options for this bundle item.") @resolver(class: "Magento\\BundleGraphQl\\Model\\Resolver\\BundleItemLinks") } -type BundleItemOption @doc(description: "BundleItemOption defines characteristics and options for a specific bundle item") { - id: Int @doc(description: "The ID assigned to the bundled item option") - label: String @doc(description: "The text that identifies the bundled item option") @resolver(class: "Magento\\BundleGraphQl\\Model\\Resolver\\Options\\Label") - qty: Float @doc(description: "Indicates the quantity of this specific bundle item") - position: Int @doc(description: "When a bundle item contains multiple options, the relative position of this option compared to the other options") - is_default: Boolean @doc(description: "Indicates whether this option is the default option") - price: Float @doc(description: "The price of the selected option") - price_type: PriceTypeEnum @doc(description: "One of FIXED, PERCENT, or DYNAMIC") - can_change_quantity: Boolean @doc(description: "Indicates whether the customer can change the number of items for this option") - product: ProductInterface @doc(description: "Contains details about this product option") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product") +type BundleItemOption @doc(description: "BundleItemOption defines characteristics and options for a specific bundle item.") { + id: Int @doc(description: "The ID assigned to the bundled item option.") + label: String @doc(description: "The text that identifies the bundled item option.") @resolver(class: "Magento\\BundleGraphQl\\Model\\Resolver\\Options\\Label") + qty: Float @doc(description: "Indicates the quantity of this specific bundle item.") + position: Int @doc(description: "When a bundle item contains multiple options, the relative position of this option compared to the other options.") + is_default: Boolean @doc(description: "Indicates whether this option is the default option.") + price: Float @doc(description: "The price of the selected option.") + price_type: PriceTypeEnum @doc(description: "One of FIXED, PERCENT, or DYNAMIC.") + can_change_quantity: Boolean @doc(description: "Indicates whether the customer can change the number of items for this option.") + product: ProductInterface @doc(description: "Contains details about this product option.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product") } -type BundleProduct implements ProductInterface, PhysicalProductInterface, CustomizableProductInterface @doc(description: "BundleProduct defines basic features of a bundle product and contains multiple BundleItems") { - price_view: PriceViewEnum @doc(description: "One of PRICE_RANGE or AS_LOW_AS") @resolver(class: "Magento\\BundleGraphQl\\Model\\Resolver\\Product\\Fields\\PriceView") - dynamic_price: Boolean @doc(description: "Indicates whether the bundle product has a dynamic price") @resolver(class: "Magento\\BundleGraphQl\\Model\\Resolver\\Product\\Fields\\DynamicPrice") - dynamic_sku: Boolean @doc(description: "Indicates whether the bundle product has a dynamic SK") @resolver(class: "Magento\\BundleGraphQl\\Model\\Resolver\\Product\\Fields\\DynamicSku") - ship_bundle_items: ShipBundleItemsEnum @doc(description: "Indicates whether to ship bundle items together or individually") @resolver(class: "Magento\\BundleGraphQl\\Model\\Resolver\\Product\\Fields\\ShipBundleItems") - dynamic_weight: Boolean @doc(description: "Indicates whether the bundle product has a dynamically calculated weight") @resolver(class: "Magento\\BundleGraphQl\\Model\\Resolver\\Product\\Fields\\DynamicWeight") - items: [BundleItem] @doc(description: "An array containing information about individual bundle items") @resolver(class: "Magento\\BundleGraphQl\\Model\\Resolver\\BundleItems") +type BundleProduct implements ProductInterface, PhysicalProductInterface, CustomizableProductInterface @doc(description: "BundleProduct defines basic features of a bundle product and contains multiple BundleItems.") { + price_view: PriceViewEnum @doc(description: "One of PRICE_RANGE or AS_LOW_AS.") @resolver(class: "Magento\\BundleGraphQl\\Model\\Resolver\\Product\\Fields\\PriceView") + dynamic_price: Boolean @doc(description: "Indicates whether the bundle product has a dynamic price.") @resolver(class: "Magento\\BundleGraphQl\\Model\\Resolver\\Product\\Fields\\DynamicPrice") + dynamic_sku: Boolean @doc(description: "Indicates whether the bundle product has a dynamic SK.") @resolver(class: "Magento\\BundleGraphQl\\Model\\Resolver\\Product\\Fields\\DynamicSku") + ship_bundle_items: ShipBundleItemsEnum @doc(description: "Indicates whether to ship bundle items together or individually.") @resolver(class: "Magento\\BundleGraphQl\\Model\\Resolver\\Product\\Fields\\ShipBundleItems") + dynamic_weight: Boolean @doc(description: "Indicates whether the bundle product has a dynamically calculated weight.") @resolver(class: "Magento\\BundleGraphQl\\Model\\Resolver\\Product\\Fields\\DynamicWeight") + items: [BundleItem] @doc(description: "An array containing information about individual bundle items.") @resolver(class: "Magento\\BundleGraphQl\\Model\\Resolver\\BundleItems") } enum PriceViewEnum @doc(description: "This enumeration defines whether a bundle product's price is displayed as the lowest possible value or as a range.") { diff --git a/app/code/Magento/Captcha/Test/Mftf/Test/CaptchaFormsDisplayingTest.xml b/app/code/Magento/Captcha/Test/Mftf/Test/CaptchaFormsDisplayingTest.xml index 035e58de06ccf..977ee78c0d201 100644 --- a/app/code/Magento/Captcha/Test/Mftf/Test/CaptchaFormsDisplayingTest.xml +++ b/app/code/Magento/Captcha/Test/Mftf/Test/CaptchaFormsDisplayingTest.xml @@ -92,6 +92,8 @@ <amOnPage url="{{StorefrontProductPage.url($$createSimpleProduct.sku$$)}}" stepKey="openProductPage"/> <waitForPageLoad stepKey="waitForPageLoad"/> <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addToCart" /> + <waitForPageLoad stepKey="waitForAddToCart"/> + <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessage"/> <waitForText userInput="You added $$createSimpleProduct.name$$ to your shopping cart." stepKey="waitForText"/> <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickCart"/> <click selector="{{StorefrontMinicartSection.goToCheckout}}" stepKey="goToCheckout"/> diff --git a/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/AttributeSet.php b/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/AttributeSet.php index 3467c7aac289b..d95ee7f8f2cf9 100644 --- a/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/AttributeSet.php +++ b/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/AttributeSet.php @@ -11,6 +11,9 @@ */ namespace Magento\Catalog\Block\Adminhtml\Product\Edit; +/** + * Admin AttributeSet block + */ class AttributeSet extends \Magento\Backend\Block\Widget\Form { /** @@ -42,12 +45,14 @@ public function __construct( public function getSelectorOptions() { return [ - 'source' => $this->getUrl('catalog/product/suggestAttributeSets'), + 'source' => $this->escapeUrl($this->getUrl('catalog/product/suggestAttributeSets')), 'className' => 'category-select', 'showRecent' => true, 'storageKey' => 'product-template-key', 'minLength' => 0, - 'currentlySelected' => $this->_coreRegistry->registry('product')->getAttributeSetId() + 'currentlySelected' => $this->escapeHtml( + $this->_coreRegistry->registry('product')->getAttributeSetId() + ) ]; } } diff --git a/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Tab/Attributes/Search.php b/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Tab/Attributes/Search.php index e1b97f996c769..42463354926dd 100644 --- a/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Tab/Attributes/Search.php +++ b/app/code/Magento/Catalog/Block/Adminhtml/Product/Edit/Tab/Attributes/Search.php @@ -11,6 +11,9 @@ */ namespace Magento\Catalog\Block\Adminhtml\Product\Edit\Tab\Attributes; +/** + * Admin product attribute search block + */ class Search extends \Magento\Backend\Block\Widget { /** @@ -62,13 +65,15 @@ protected function _construct() } /** + * Get selector options + * * @return array */ public function getSelectorOptions() { $templateId = $this->_coreRegistry->registry('product')->getAttributeSetId(); return [ - 'source' => $this->getUrl('catalog/product/suggestAttributes'), + 'source' => $this->escapeUrl($this->getUrl('catalog/product/suggestAttributes')), 'minLength' => 0, 'ajaxOptions' => ['data' => ['template_id' => $templateId]], 'template' => '[data-template-for="product-attribute-search-' . $this->getGroupId() . '"]', @@ -110,6 +115,8 @@ public function getSuggestedAttributes($labelPart, $templateId = null) } /** + * Get add attribute url + * * @return string */ public function getAddAttributeUrl() diff --git a/app/code/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Gallery/Content.php b/app/code/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Gallery/Content.php index 063503682f4db..f5d0ec7da617e 100644 --- a/app/code/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Gallery/Content.php +++ b/app/code/Magento/Catalog/Block/Adminhtml/Product/Helper/Form/Gallery/Content.php @@ -19,6 +19,7 @@ use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\Exception\FileSystemException; use Magento\Backend\Block\DataProviders\ImageUploadConfig as ImageUploadConfigDataProvider; +use Magento\MediaStorage\Helper\File\Storage\Database; /** * Block for gallery content. @@ -50,25 +51,34 @@ class Content extends \Magento\Backend\Block\Widget */ private $imageUploadConfigDataProvider; + /** + * @var Database + */ + private $fileStorageDatabase; + /** * @param \Magento\Backend\Block\Template\Context $context * @param \Magento\Framework\Json\EncoderInterface $jsonEncoder * @param \Magento\Catalog\Model\Product\Media\Config $mediaConfig * @param array $data * @param ImageUploadConfigDataProvider $imageUploadConfigDataProvider + * @param Database $fileStorageDatabase */ public function __construct( \Magento\Backend\Block\Template\Context $context, \Magento\Framework\Json\EncoderInterface $jsonEncoder, \Magento\Catalog\Model\Product\Media\Config $mediaConfig, array $data = [], - ImageUploadConfigDataProvider $imageUploadConfigDataProvider = null + ImageUploadConfigDataProvider $imageUploadConfigDataProvider = null, + Database $fileStorageDatabase = null ) { $this->_jsonEncoder = $jsonEncoder; $this->_mediaConfig = $mediaConfig; parent::__construct($context, $data); $this->imageUploadConfigDataProvider = $imageUploadConfigDataProvider ?: ObjectManager::getInstance()->get(ImageUploadConfigDataProvider::class); + $this->fileStorageDatabase = $fileStorageDatabase + ?: ObjectManager::getInstance()->get(Database::class); } /** @@ -164,6 +174,13 @@ public function getImagesJson() $images = $this->sortImagesByPosition($value['images']); foreach ($images as &$image) { $image['url'] = $this->_mediaConfig->getMediaUrl($image['file']); + if ($this->fileStorageDatabase->checkDbUsage() && + !$mediaDir->isFile($this->_mediaConfig->getMediaPath($image['file'])) + ) { + $this->fileStorageDatabase->saveFileToFilesystem( + $this->_mediaConfig->getMediaPath($image['file']) + ); + } try { $fileHandler = $mediaDir->stat($this->_mediaConfig->getMediaPath($image['file'])); $image['size'] = $fileHandler['size']; @@ -187,9 +204,12 @@ public function getImagesJson() private function sortImagesByPosition($images) { if (is_array($images)) { - usort($images, function ($imageA, $imageB) { - return ($imageA['position'] < $imageB['position']) ? -1 : 1; - }); + usort( + $images, + function ($imageA, $imageB) { + return ($imageA['position'] < $imageB['position']) ? -1 : 1; + } + ); } return $images; } diff --git a/app/code/Magento/Catalog/Block/Product/Gallery.php b/app/code/Magento/Catalog/Block/Product/Gallery.php index e7c7b81ec29c9..54f848a92e958 100644 --- a/app/code/Magento/Catalog/Block/Product/Gallery.php +++ b/app/code/Magento/Catalog/Block/Product/Gallery.php @@ -16,6 +16,8 @@ use Magento\Framework\Data\Collection; /** + * Product gallery block + * * @api * @since 100.0.2 */ @@ -43,6 +45,8 @@ public function __construct( } /** + * Prepare layout + * * @return $this */ protected function _prepareLayout() @@ -52,6 +56,8 @@ protected function _prepareLayout() } /** + * Get product + * * @return Product */ public function getProduct() @@ -60,6 +66,8 @@ public function getProduct() } /** + * Get gallery collection + * * @return Collection */ public function getGalleryCollection() @@ -68,6 +76,8 @@ public function getGalleryCollection() } /** + * Get current image + * * @return Image|null */ public function getCurrentImage() @@ -85,6 +95,8 @@ public function getCurrentImage() } /** + * Get image url + * * @return string */ public function getImageUrl() @@ -93,6 +105,8 @@ public function getImageUrl() } /** + * Get image file + * * @return mixed */ public function getImageFile() @@ -115,7 +129,7 @@ public function getImageWidth() if ($size[0] > 600) { return 600; } else { - return $size[0]; + return (int) $size[0]; } } } @@ -124,6 +138,8 @@ public function getImageWidth() } /** + * Get previous image + * * @return Image|false */ public function getPreviousImage() @@ -143,6 +159,8 @@ public function getPreviousImage() } /** + * Get next image + * * @return Image|false */ public function getNextImage() @@ -166,6 +184,8 @@ public function getNextImage() } /** + * Get previous image url + * * @return false|string */ public function getPreviousImageUrl() @@ -178,6 +198,8 @@ public function getPreviousImageUrl() } /** + * Get next image url + * * @return false|string */ public function getNextImageUrl() diff --git a/app/code/Magento/Catalog/Block/Product/ListProduct.php b/app/code/Magento/Catalog/Block/Product/ListProduct.php index c1d79894162ae..144cb682e2d22 100644 --- a/app/code/Magento/Catalog/Block/Product/ListProduct.php +++ b/app/code/Magento/Catalog/Block/Product/ListProduct.php @@ -373,7 +373,7 @@ public function getAddToCartPostParams(Product $product) return [ 'action' => $url, 'data' => [ - 'product' => $product->getEntityId(), + 'product' => (int) $product->getEntityId(), ActionInterface::PARAM_NAME_URL_ENCODED => $this->urlHelper->getEncodedUrl($url), ] ]; diff --git a/app/code/Magento/Catalog/Block/Product/View.php b/app/code/Magento/Catalog/Block/Product/View.php index 8055d17a64a84..ed6278c2b585d 100644 --- a/app/code/Magento/Catalog/Block/Product/View.php +++ b/app/code/Magento/Catalog/Block/Product/View.php @@ -169,8 +169,7 @@ public function getAddToCartUrl($product, $additional = []) } /** - * Get JSON encoded configuration array which can be used for JS dynamic - * price calculation depending on product options + * Get JSON encoded configuration which can be used for JS dynamic price calculation depending on product options * * @return string */ @@ -188,24 +187,25 @@ public function getJsonConfig() } $tierPrices = []; - $tierPricesList = $product->getPriceInfo()->getPrice('tier_price')->getTierPriceList(); + $priceInfo = $product->getPriceInfo(); + $tierPricesList = $priceInfo->getPrice('tier_price')->getTierPriceList(); foreach ($tierPricesList as $tierPrice) { - $tierPrices[] = $tierPrice['price']->getValue(); + $tierPrices[] = $tierPrice['price']->getValue() * 1; } $config = [ - 'productId' => $product->getId(), + 'productId' => (int)$product->getId(), 'priceFormat' => $this->_localeFormat->getPriceFormat(), 'prices' => [ 'oldPrice' => [ - 'amount' => $product->getPriceInfo()->getPrice('regular_price')->getAmount()->getValue(), + 'amount' => $priceInfo->getPrice('regular_price')->getAmount()->getValue() * 1, 'adjustments' => [] ], 'basePrice' => [ - 'amount' => $product->getPriceInfo()->getPrice('final_price')->getAmount()->getBaseAmount(), + 'amount' => $priceInfo->getPrice('final_price')->getAmount()->getBaseAmount() * 1, 'adjustments' => [] ], 'finalPrice' => [ - 'amount' => $product->getPriceInfo()->getPrice('final_price')->getAmount()->getValue(), + 'amount' => $priceInfo->getPrice('final_price')->getAmount()->getValue() * 1, 'adjustments' => [] ] ], @@ -262,6 +262,7 @@ public function isStartCustomization() /** * Get default qty - either as preconfigured, or as 1. + * * Also restricts it by minimal qty. * * @param null|\Magento\Catalog\Model\Product $product @@ -323,10 +324,7 @@ public function getQuantityValidators() public function getIdentities() { $identities = $this->getProduct()->getIdentities(); - $category = $this->_coreRegistry->registry('current_category'); - if ($category) { - $identities[] = Category::CACHE_TAG . '_' . $category->getId(); - } + return $identities; } diff --git a/app/code/Magento/Catalog/Block/Product/View/Gallery.php b/app/code/Magento/Catalog/Block/Product/View/Gallery.php index 8b98fbdc8f7ef..b6a58c07c428b 100644 --- a/app/code/Magento/Catalog/Block/Product/View/Gallery.php +++ b/app/code/Magento/Catalog/Block/Product/View/Gallery.php @@ -137,16 +137,18 @@ public function getGalleryImagesJson() $imagesItems = []; /** @var DataObject $image */ foreach ($this->getGalleryImages() as $image) { - $imageItem = new DataObject([ - 'thumb' => $image->getData('small_image_url'), - 'img' => $image->getData('medium_image_url'), - 'full' => $image->getData('large_image_url'), - 'caption' => ($image->getLabel() ?: $this->getProduct()->getName()), - 'position' => $image->getData('position'), - 'isMain' => $this->isMainImage($image), - 'type' => str_replace('external-', '', $image->getMediaType()), - 'videoUrl' => $image->getVideoUrl(), - ]); + $imageItem = new DataObject( + [ + 'thumb' => $image->getData('small_image_url'), + 'img' => $image->getData('medium_image_url'), + 'full' => $image->getData('large_image_url'), + 'caption' => ($image->getLabel() ?: $this->getProduct()->getName()), + 'position' => $image->getData('position'), + 'isMain' => $this->isMainImage($image), + 'type' => str_replace('external-', '', $image->getMediaType()), + 'videoUrl' => $image->getVideoUrl(), + ] + ); foreach ($this->getGalleryImagesConfig()->getItems() as $imageConfig) { $imageItem->setData( $imageConfig->getData('json_object_key'), diff --git a/app/code/Magento/Catalog/Block/Product/View/Options/AbstractOptions.php b/app/code/Magento/Catalog/Block/Product/View/Options/AbstractOptions.php index 059580b9b5eae..030b6e1d2204c 100644 --- a/app/code/Magento/Catalog/Block/Product/View/Options/AbstractOptions.php +++ b/app/code/Magento/Catalog/Block/Product/View/Options/AbstractOptions.php @@ -15,8 +15,9 @@ use Magento\Catalog\Pricing\Price\CustomOptionPriceInterface; /** - * Product aoptions section abstract block. + * Product options section abstract block. * + * phpcs:disable Magento2.Classes.AbstractApi * @api * @since 100.0.2 */ diff --git a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper/AttributeFilter.php b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper/AttributeFilter.php index 188b0b22f33bf..49165c85f85d7 100644 --- a/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper/AttributeFilter.php +++ b/app/code/Magento/Catalog/Controller/Adminhtml/Product/Initialization/Helper/AttributeFilter.php @@ -46,6 +46,8 @@ public function prepareProductAttributes(Product $product, array $productData, a } /** + * Reset "Use Config Settings" to false in product data. + * * @param Product $product * @param string $attributeCode * @param array $productData @@ -62,6 +64,8 @@ private function prepareConfigData(Product $product, string $attributeCode, arra } /** + * Prepare default attribute data for product. + * * @param array $attributeList * @param string $attributeCode * @param array $productData @@ -73,7 +77,7 @@ private function prepareDefaultData(array $attributeList, string $attributeCode, /** @var \Magento\Catalog\Model\ResourceModel\Eav\Attribute $attribute */ $attribute = $attributeList[$attributeCode]; $attributeType = $attribute->getBackendType(); - // For non-numberic types set the attributeValue to 'false' to trigger their removal from the db + // For non-numeric types set the attributeValue to 'false' to trigger their removal from the db if ($attributeType === 'varchar' || $attributeType === 'text' || $attributeType === 'datetime') { $attribute->setIsRequired(false); $productData[$attributeCode] = false; @@ -86,6 +90,8 @@ private function prepareDefaultData(array $attributeList, string $attributeCode, } /** + * Check, whether attribute should not be updated. + * * @param Product $product * @param array $useDefaults * @param string $attribute diff --git a/app/code/Magento/Catalog/Model/Category/FileInfo.php b/app/code/Magento/Catalog/Model/Category/FileInfo.php index 9715bb2b1616e..cd3592ef40a0f 100644 --- a/app/code/Magento/Catalog/Model/Category/FileInfo.php +++ b/app/code/Magento/Catalog/Model/Category/FileInfo.php @@ -43,6 +43,11 @@ class FileInfo */ private $baseDirectory; + /** + * @var ReadInterface + */ + private $pubDirectory; + /** * @param Filesystem $filesystem * @param Mime $mime @@ -82,6 +87,20 @@ private function getBaseDirectory() return $this->baseDirectory; } + /** + * Get Pub Directory read instance + * + * @return ReadInterface + */ + private function getPubDirectory() + { + if (!isset($this->pubDirectory)) { + $this->pubDirectory = $this->filesystem->getDirectoryRead(DirectoryList::PUB); + } + + return $this->pubDirectory; + } + /** * Retrieve MIME type of requested file * @@ -135,7 +154,7 @@ private function getFilePath($fileName) { $filePath = ltrim($fileName, '/'); - $mediaDirectoryRelativeSubpath = $this->getMediaDirectoryPathRelativeToBaseDirectoryPath(); + $mediaDirectoryRelativeSubpath = $this->getMediaDirectoryPathRelativeToBaseDirectoryPath($filePath); $isFileNameBeginsWithMediaDirectoryPath = $this->isBeginsWithMediaDirectoryPath($fileName); // if the file is not using a relative path, it resides in the catalog/category media directory @@ -160,7 +179,7 @@ public function isBeginsWithMediaDirectoryPath($fileName) { $filePath = ltrim($fileName, '/'); - $mediaDirectoryRelativeSubpath = $this->getMediaDirectoryPathRelativeToBaseDirectoryPath(); + $mediaDirectoryRelativeSubpath = $this->getMediaDirectoryPathRelativeToBaseDirectoryPath($filePath); $isFileNameBeginsWithMediaDirectoryPath = strpos($filePath, $mediaDirectoryRelativeSubpath) === 0; return $isFileNameBeginsWithMediaDirectoryPath; @@ -169,14 +188,22 @@ public function isBeginsWithMediaDirectoryPath($fileName) /** * Get media directory subpath relative to base directory path * + * @param string $filePath * @return string */ - private function getMediaDirectoryPathRelativeToBaseDirectoryPath() + private function getMediaDirectoryPathRelativeToBaseDirectoryPath(string $filePath = '') { - $baseDirectoryPath = $this->getBaseDirectory()->getAbsolutePath(); + $baseDirectory = $this->getBaseDirectory(); + $baseDirectoryPath = $baseDirectory->getAbsolutePath(); $mediaDirectoryPath = $this->getMediaDirectory()->getAbsolutePath(); + $pubDirectoryPath = $this->getPubDirectory()->getAbsolutePath(); $mediaDirectoryRelativeSubpath = substr($mediaDirectoryPath, strlen($baseDirectoryPath)); + $pubDirectory = $baseDirectory->getRelativePath($pubDirectoryPath); + + if (strpos($mediaDirectoryRelativeSubpath, $pubDirectory) === 0 && strpos($filePath, $pubDirectory) !== 0) { + $mediaDirectoryRelativeSubpath = substr($mediaDirectoryRelativeSubpath, strlen($pubDirectory)); + } return $mediaDirectoryRelativeSubpath; } diff --git a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/TierPrice/UpdateHandler.php b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/TierPrice/UpdateHandler.php index 663b7facf4257..f1943bc108878 100644 --- a/app/code/Magento/Catalog/Model/Product/Attribute/Backend/TierPrice/UpdateHandler.php +++ b/app/code/Magento/Catalog/Model/Product/Attribute/Backend/TierPrice/UpdateHandler.php @@ -7,8 +7,9 @@ namespace Magento\Catalog\Model\Product\Attribute\Backend\TierPrice; -use Magento\Framework\EntityManager\Operation\ExtensionInterface; use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\Locale\FormatInterface; use Magento\Store\Model\StoreManagerInterface; use Magento\Catalog\Api\ProductAttributeRepositoryInterface; use Magento\Customer\Api\GroupManagementInterface; @@ -40,19 +41,26 @@ class UpdateHandler extends AbstractHandler */ private $tierPriceResource; + /** + * @var FormatInterface + */ + private $localeFormat; + /** * @param \Magento\Store\Model\StoreManagerInterface $storeManager * @param \Magento\Catalog\Api\ProductAttributeRepositoryInterface $attributeRepository * @param \Magento\Customer\Api\GroupManagementInterface $groupManagement * @param \Magento\Framework\EntityManager\MetadataPool $metadataPool * @param \Magento\Catalog\Model\ResourceModel\Product\Attribute\Backend\Tierprice $tierPriceResource + * @param FormatInterface|null $localeFormat */ public function __construct( StoreManagerInterface $storeManager, ProductAttributeRepositoryInterface $attributeRepository, GroupManagementInterface $groupManagement, MetadataPool $metadataPool, - Tierprice $tierPriceResource + Tierprice $tierPriceResource, + FormatInterface $localeFormat = null ) { parent::__construct($groupManagement); @@ -60,6 +68,7 @@ public function __construct( $this->attributeRepository = $attributeRepository; $this->metadataPoll = $metadataPool; $this->tierPriceResource = $tierPriceResource; + $this->localeFormat = $localeFormat ?: ObjectManager::getInstance()->get(FormatInterface::class); } /** @@ -125,8 +134,9 @@ private function updateValues(array $valuesToUpdate, array $oldValues): bool { $isChanged = false; foreach ($valuesToUpdate as $key => $value) { - if ((!empty($value['value']) && (float)$oldValues[$key]['price'] !== (float)$value['value']) - || $this->getPercentage($oldValues[$key]) !== $this->getPercentage($value) + if ((!empty($value['value']) + && (float)$oldValues[$key]['price'] !== $this->localeFormat->getNumber($value['value']) + ) || $this->getPercentage($oldValues[$key]) !== $this->getPercentage($value) ) { $price = new \Magento\Framework\DataObject( [ diff --git a/app/code/Magento/Catalog/Model/Product/Option/Type/Select.php b/app/code/Magento/Catalog/Model/Product/Option/Type/Select.php index d88dd58362896..31e178f0bd9b4 100644 --- a/app/code/Magento/Catalog/Model/Product/Option/Type/Select.php +++ b/app/code/Magento/Catalog/Model/Product/Option/Type/Select.php @@ -10,6 +10,8 @@ /** * Catalog product option select type + * + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) */ class Select extends \Magento\Catalog\Model\Product\Option\Type\DefaultType { @@ -30,23 +32,35 @@ class Select extends \Magento\Catalog\Model\Product\Option\Type\DefaultType */ protected $string; + /** + * @var array + */ + private $singleSelectionTypes; + /** * @param \Magento\Checkout\Model\Session $checkoutSession * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig * @param \Magento\Framework\Stdlib\StringUtils $string * @param \Magento\Framework\Escaper $escaper * @param array $data + * @param array $singleSelectionTypes */ public function __construct( \Magento\Checkout\Model\Session $checkoutSession, \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, \Magento\Framework\Stdlib\StringUtils $string, \Magento\Framework\Escaper $escaper, - array $data = [] + array $data = [], + array $singleSelectionTypes = [] ) { $this->string = $string; $this->_escaper = $escaper; parent::__construct($checkoutSession, $scopeConfig, $data); + + $this->singleSelectionTypes = $singleSelectionTypes ?: [ + 'drop_down' => \Magento\Catalog\Api\Data\ProductCustomOptionInterface::OPTION_TYPE_DROP_DOWN, + 'radio' => \Magento\Catalog\Api\Data\ProductCustomOptionInterface::OPTION_TYPE_RADIO, + ]; } /** @@ -310,10 +324,6 @@ public function getOptionSku($optionValue, $skuDelimiter) */ protected function _isSingleSelection() { - $single = [ - \Magento\Catalog\Api\Data\ProductCustomOptionInterface::OPTION_TYPE_DROP_DOWN, - \Magento\Catalog\Api\Data\ProductCustomOptionInterface::OPTION_TYPE_RADIO, - ]; - return in_array($this->getOption()->getType(), $single); + return in_array($this->getOption()->getType(), $this->singleSelectionTypes, true); } } diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product.php b/app/code/Magento/Catalog/Model/ResourceModel/Product.php index 24174391be829..99a7efe6c9895 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product.php @@ -9,6 +9,8 @@ use Magento\Framework\App\ObjectManager; use Magento\Catalog\Model\Indexer\Category\Product\TableMaintainer; use Magento\Eav\Model\Entity\Attribute\UniqueValidationInterface; +use Magento\Framework\EntityManager\EntityManager; +use Magento\Framework\Model\AbstractModel; /** * Product entity resource model @@ -44,7 +46,7 @@ class Product extends AbstractResource /** * Category collection factory * - * @var \Magento\Catalog\Model\ResourceModel\Category\CollectionFactory + * @var Category\CollectionFactory */ protected $_categoryCollectionFactory; @@ -64,7 +66,7 @@ class Product extends AbstractResource protected $typeFactory; /** - * @var \Magento\Framework\EntityManager\EntityManager + * @var EntityManager * @since 101.0.0 */ protected $entityManager; @@ -81,7 +83,7 @@ class Product extends AbstractResource protected $availableCategoryIdsCache = []; /** - * @var \Magento\Catalog\Model\ResourceModel\Product\CategoryLink + * @var Product\CategoryLink */ private $productCategoryLink; @@ -110,7 +112,7 @@ public function __construct( \Magento\Eav\Model\Entity\Context $context, \Magento\Store\Model\StoreManagerInterface $storeManager, \Magento\Catalog\Model\Factory $modelFactory, - \Magento\Catalog\Model\ResourceModel\Category\CollectionFactory $categoryCollectionFactory, + Category\CollectionFactory $categoryCollectionFactory, Category $catalogCategory, \Magento\Framework\Event\ManagerInterface $eventManager, \Magento\Eav\Model\Entity\Attribute\SetFactory $setFactory, @@ -236,7 +238,7 @@ public function getWebsiteIdsByProductIds($productIds) /** * Retrieve product category identifiers * - * @param \Magento\Catalog\Model\Product $product + * @param \Magento\Catalog\Model\Product $product * @return array */ public function getCategoryIds($product) @@ -248,7 +250,7 @@ public function getCategoryIds($product) /** * Get product identifier by sku * - * @param string $sku + * @param string $sku * @return int|false */ public function getIdBySku($sku) @@ -348,11 +350,11 @@ protected function _saveCategories(\Magento\Framework\DataObject $object) * Get collection of product categories * * @param \Magento\Catalog\Model\Product $product - * @return \Magento\Catalog\Model\ResourceModel\Category\Collection + * @return Category\Collection */ public function getCategoryCollection($product) { - /** @var \Magento\Catalog\Model\ResourceModel\Category\Collection $collection */ + /** @var Category\Collection $collection */ $collection = $this->_categoryCollectionFactory->create(); $collection->joinField( 'product_id', @@ -428,18 +430,26 @@ public function getDefaultAttributeSourceModel() /** * Check availability display product in category * - * @param \Magento\Catalog\Model\Product $product + * @param \Magento\Catalog\Model\Product|int $product * @param int $categoryId * @return string */ public function canBeShowInCategory($product, $categoryId) { + if ($product instanceof \Magento\Catalog\Model\Product) { + $productId = $product->getEntityId(); + $storeId = $product->getStoreId(); + } else { + $productId = $product; + $storeId = $this->_storeManager->getStore()->getId(); + } + $select = $this->getConnection()->select()->from( - $this->tableMaintainer->getMainTable($product->getStoreId()), + $this->tableMaintainer->getMainTable($storeId), 'product_id' )->where( 'product_id = ?', - (int)$product->getEntityId() + (int)$productId )->where( 'category_id = ?', (int)$categoryId @@ -614,7 +624,7 @@ public function validate($object) /** * Reset firstly loaded attributes * - * @param \Magento\Framework\Model\AbstractModel $object + * @param AbstractModel $object * @param integer $entityId * @param array|null $attributes * @return $this @@ -667,12 +677,12 @@ protected function evaluateDelete($object, $id, $connection) /** * Save entity's attributes into the object's resource * - * @param \Magento\Framework\Model\AbstractModel $object + * @param AbstractModel $object * @return $this * @throws \Exception * @since 101.0.0 */ - public function save(\Magento\Framework\Model\AbstractModel $object) + public function save(AbstractModel $object) { $this->getEntityManager()->save($object); return $this; @@ -681,13 +691,13 @@ public function save(\Magento\Framework\Model\AbstractModel $object) /** * Retrieve entity manager object * - * @return \Magento\Framework\EntityManager\EntityManager + * @return EntityManager */ private function getEntityManager() { if (null === $this->entityManager) { - $this->entityManager = \Magento\Framework\App\ObjectManager::getInstance() - ->get(\Magento\Framework\EntityManager\EntityManager::class); + $this->entityManager = ObjectManager::getInstance() + ->get(EntityManager::class); } return $this->entityManager; } @@ -707,13 +717,13 @@ private function getProductWebsiteLink() * Retrieve CategoryLink object * * @deprecated 101.1.0 - * @return \Magento\Catalog\Model\ResourceModel\Product\CategoryLink + * @return Product\CategoryLink */ private function getProductCategoryLink() { if (null === $this->productCategoryLink) { - $this->productCategoryLink = \Magento\Framework\App\ObjectManager::getInstance() - ->get(\Magento\Catalog\Model\ResourceModel\Product\CategoryLink::class); + $this->productCategoryLink = ObjectManager::getInstance() + ->get(Product\CategoryLink::class); } return $this->productCategoryLink; } diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php index 0cdf8b39f7d52..384b6ddcefc31 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php @@ -13,13 +13,13 @@ use Magento\Catalog\Model\Product\Gallery\ReadHandler as GalleryReadHandler; use Magento\Catalog\Model\ResourceModel\Product\Collection\ProductLimitationFactory; use Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator; +use Magento\CatalogUrlRewrite\Model\Storage\DbStorage; use Magento\Customer\Api\GroupManagementInterface; use Magento\Customer\Model\Indexer\CustomerGroupDimensionProvider; use Magento\Framework\App\ObjectManager; use Magento\Framework\DB\Select; use Magento\Framework\EntityManager\MetadataPool; use Magento\Framework\Indexer\DimensionFactory; -use Magento\Framework\Model\ResourceModel\ResourceModelPoolInterface; use Magento\Store\Model\Indexer\WebsiteDimensionProvider; use Magento\Store\Model\Store; @@ -297,6 +297,11 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Collection\Abstrac */ private $emptyItem; + /** + * @var DbStorage + */ + private $urlFinder; + /** * Collection constructor * @@ -389,6 +394,19 @@ public function __construct( ?: ObjectManager::getInstance()->get(DimensionFactory::class); } + /** + * Retrieve urlFinder + * + * @return GalleryReadHandler + */ + private function getUrlFinder() + { + if ($this->urlFinder === null) { + $this->urlFinder = ObjectManager::getInstance()->get(DbStorage::class); + } + return $this->urlFinder; + } + /** * Get cloned Select after dispatching 'catalog_prepare_price_select' event * @@ -808,7 +826,7 @@ public function load($printQuery = false, $logQuery = false) } /** - * Processs adding product website names to result collection + * Process adding product website names to result collection * * @return $this */ @@ -1418,44 +1436,21 @@ protected function _addUrlRewrite() foreach ($this->getItems() as $item) { $productIds[] = $item->getEntityId(); } - if (!$productIds) { - return; - } - - $select = $this->getConnection() - ->select() - ->from(['u' => $this->getTable('url_rewrite')], ['u.entity_id', 'u.request_path']) - ->where('u.store_id = ?', $this->_storeManager->getStore($this->getStoreId())->getId()) - ->where('u.is_autogenerated = 1') - ->where('u.entity_type = ?', ProductUrlRewriteGenerator::ENTITY_TYPE) - ->where('u.entity_id IN(?)', $productIds); + $filter = [ + 'entity_type' => 'product', + 'entity_id' => $productIds, + 'store_id' => $this->getStoreId(), + 'is_autogenerated' => 1 + ]; if ($this->_urlRewriteCategory) { - $select->joinInner( - ['cu' => $this->getTable('catalog_url_rewrite_product_category')], - 'u.url_rewrite_id=cu.url_rewrite_id' - )->where('cu.category_id IN (?)', $this->_urlRewriteCategory); - } else { - $select->joinLeft( - ['cu' => $this->getTable('catalog_url_rewrite_product_category')], - 'u.url_rewrite_id=cu.url_rewrite_id' - )->where('cu.url_rewrite_id IS NULL'); - } - - // more priority is data with category id - $urlRewrites = []; - - foreach ($this->getConnection()->fetchAll($select) as $row) { - if (!isset($urlRewrites[$row['entity_id']])) { - $urlRewrites[$row['entity_id']] = $row['request_path']; - } + $filter['metadata']['category_id'] = $this->_urlRewriteCategory; } - foreach ($this->getItems() as $item) { - if (isset($urlRewrites[$item->getEntityId()])) { - $item->setData('request_path', $urlRewrites[$item->getEntityId()]); - } else { - $item->setData('request_path', false); + $rewrites = $this->getUrlFinder()->findAllByData($filter); + foreach ($rewrites as $rewrite) { + if ($item = $this->getItemById($rewrite->getEntityId())) { + $item->setData('request_path', $rewrite->getRequestPath()); } } } @@ -1583,26 +1578,9 @@ public function addAttributeToFilter($attribute, $condition = null, $joinType = $this->_allIdsCache = null; if (is_string($attribute) && $attribute == 'is_saleable') { - $columns = $this->getSelect()->getPart(\Magento\Framework\DB\Select::COLUMNS); - foreach ($columns as $columnEntry) { - list($correlationName, $column, $alias) = $columnEntry; - if ($alias == 'is_saleable') { - if ($column instanceof \Zend_Db_Expr) { - $field = $column; - } else { - $connection = $this->getSelect()->getConnection(); - if (empty($correlationName)) { - $field = $connection->quoteColumnAs($column, $alias, true); - } else { - $field = $connection->quoteColumnAs([$correlationName, $column], $alias, true); - } - } - $this->getSelect()->where("{$field} = ?", $condition); - break; - } - } - - return $this; + $this->addIsSaleableAttributeToFilter($condition); + } elseif (is_string($attribute) && $attribute == 'tier_price') { + $this->addTierPriceAttributeToFilter($attribute, $condition); } else { return parent::addAttributeToFilter($attribute, $condition, $joinType); } @@ -1977,8 +1955,7 @@ protected function _productLimitationPrice($joinLeft = false) } // Set additional field filters foreach ($this->_priceDataFieldFilters as $filterData) { - // phpcs:ignore Magento2.Functions.DiscouragedFunction - $select->where(call_user_func_array('sprintf', $filterData)); + $select->where(sprintf(...$filterData)); } } else { $fromPart['price_index']['joinCondition'] = $joinCond; @@ -2283,8 +2260,7 @@ private function getBackend() public function addPriceDataFieldFilter($comparisonFormat, $fields) { if (!preg_match('/^%s( (<|>|=|<=|>=|<>) %s)*$/', $comparisonFormat)) { - // phpcs:ignore Magento2.Exceptions.DirectThrow - throw new \Exception('Invalid comparison format.'); + throw new \InvalidArgumentException('Invalid comparison format.'); } if (!is_array($fields)) { @@ -2488,4 +2464,71 @@ public function getPricesCount() return $this->_pricesCount; } + + /** + * Add is_saleable attribute to filter + * + * @param array|null $condition + * @return $this + */ + private function addIsSaleableAttributeToFilter(?array $condition): self + { + $columns = $this->getSelect()->getPart(Select::COLUMNS); + foreach ($columns as $columnEntry) { + list($correlationName, $column, $alias) = $columnEntry; + if ($alias == 'is_saleable') { + if ($column instanceof \Zend_Db_Expr) { + $field = $column; + } else { + $connection = $this->getSelect()->getConnection(); + if (empty($correlationName)) { + $field = $connection->quoteColumnAs($column, $alias, true); + } else { + $field = $connection->quoteColumnAs([$correlationName, $column], $alias, true); + } + } + $this->getSelect()->where("{$field} = ?", $condition); + break; + } + } + + return $this; + } + + /** + * Add tier price attribute to filter + * + * @param string $attribute + * @param array|null $condition + * @return $this + */ + private function addTierPriceAttributeToFilter(string $attribute, ?array $condition): self + { + $attrCode = $attribute; + $connection = $this->getConnection(); + $attrTable = $this->_getAttributeTableAlias($attrCode); + $entity = $this->getEntity(); + $fKey = 'e.' . $this->getEntityPkName($entity); + $pKey = $attrTable . '.' . $this->getEntityPkName($entity); + $attribute = $entity->getAttribute($attrCode); + $attrFieldName = $attrTable . '.value'; + $fKey = $connection->quoteColumnAs($fKey, null); + $pKey = $connection->quoteColumnAs($pKey, null); + + $condArr = ["{$pKey} = {$fKey}"]; + $this->getSelect()->join( + [$attrTable => $this->getTable('catalog_product_entity_tier_price')], + '(' . implode(') AND (', $condArr) . ')', + [$attrCode => $attrFieldName] + ); + $this->removeAttributeToSelect($attrCode); + $this->_filterAttributes[$attrCode] = $attribute->getId(); + $this->_joinFields[$attrCode] = ['table' => '', 'field' => $attrFieldName]; + $field = $this->_getAttributeTableAlias($attrCode) . '.value'; + $conditionSql = $this->_getConditionSql($field, $condition); + $this->getSelect()->where($conditionSql, null, Select::TYPE_CONDITION); + $this->_totalRecords = null; + + return $this; + } } diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/Query/BaseFinalPrice.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/Query/BaseFinalPrice.php index 95fecc832fa26..499312aadf6a1 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/Query/BaseFinalPrice.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Indexer/Price/Query/BaseFinalPrice.php @@ -221,11 +221,8 @@ public function getQuery(array $dimensions, string $productType, array $entityId $select->where("e.type_id = ?", $productType); if ($entityIds !== null) { - if (count($entityIds) > 1) { - $select->where(sprintf('e.entity_id BETWEEN %s AND %s', min($entityIds), max($entityIds))); - } else { - $select->where('e.entity_id = ?', $entityIds); - } + $select->where(sprintf('e.entity_id BETWEEN %s AND %s', min($entityIds), max($entityIds))); + $select->where('e.entity_id IN(?)', $entityIds); } /** diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Option.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Option.php index 179da06b59990..2238ad91550e4 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Option.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Option.php @@ -6,6 +6,10 @@ namespace Magento\Catalog\Model\ResourceModel\Product; use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Framework\DataObject; +use Magento\Framework\Model\AbstractModel; +use Magento\Store\Model\ScopeInterface; +use Magento\Store\Model\Store; /** * Catalog product custom option resource model @@ -76,10 +80,10 @@ protected function _construct() /** * Save options store data * - * @param \Magento\Framework\Model\AbstractModel $object + * @param AbstractModel $object * @return \Magento\Framework\Model\ResourceModel\Db\AbstractDb */ - protected function _afterSave(\Magento\Framework\Model\AbstractModel $object) + protected function _afterSave(AbstractModel $object) { $this->_saveValuePrices($object); $this->_saveValueTitles($object); @@ -90,136 +94,38 @@ protected function _afterSave(\Magento\Framework\Model\AbstractModel $object) /** * Save value prices * - * @param \Magento\Framework\Model\AbstractModel $object + * @param AbstractModel $object * @return $this - * @SuppressWarnings(PHPMD.CyclomaticComplexity) - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ - protected function _saveValuePrices(\Magento\Framework\Model\AbstractModel $object) + protected function _saveValuePrices(AbstractModel $object) { - $priceTable = $this->getTable('catalog_product_option_price'); - $connection = $this->getConnection(); - /* * Better to check param 'price' and 'price_type' for saving. * If there is not price skip saving price */ - if (in_array($object->getType(), $this->getPriceTypes())) { - //save for store_id = 0 + // save for store_id = 0 if (!$object->getData('scope', 'price')) { - $statement = $connection->select()->from( - $priceTable, - 'option_id' - )->where( - 'option_id = ?', - $object->getId() - )->where( - 'store_id = ?', - \Magento\Store\Model\Store::DEFAULT_STORE_ID - ); - $optionId = $connection->fetchOne($statement); - - if ($optionId) { - $data = $this->_prepareDataForTable( - new \Magento\Framework\DataObject( - ['price' => $object->getPrice(), 'price_type' => $object->getPriceType()] - ), - $priceTable - ); - - $connection->update( - $priceTable, - $data, - [ - 'option_id = ?' => $object->getId(), - 'store_id = ?' => \Magento\Store\Model\Store::DEFAULT_STORE_ID - ] - ); - } else { - $data = $this->_prepareDataForTable( - new \Magento\Framework\DataObject( - [ - 'option_id' => $object->getId(), - 'store_id' => \Magento\Store\Model\Store::DEFAULT_STORE_ID, - 'price' => $object->getPrice(), - 'price_type' => $object->getPriceType(), - ] - ), - $priceTable - ); - $connection->insert($priceTable, $data); - } + $this->savePriceByStore($object, Store::DEFAULT_STORE_ID); } $scope = (int)$this->_config->getValue( - \Magento\Store\Model\Store::XML_PATH_PRICE_SCOPE, + Store::XML_PATH_PRICE_SCOPE, \Magento\Store\Model\ScopeInterface::SCOPE_STORE ); - if ($object->getStoreId() != '0' && $scope == \Magento\Store\Model\Store::PRICE_SCOPE_WEBSITE) { - $baseCurrency = $this->_config->getValue( - \Magento\Directory\Model\Currency::XML_PATH_CURRENCY_BASE, - 'default' - ); - + if ($object->getStoreId() != '0' && $scope == Store::PRICE_SCOPE_WEBSITE) { $storeIds = $this->_storeManager->getStore($object->getStoreId())->getWebsite()->getStoreIds(); - if (is_array($storeIds)) { - foreach ($storeIds as $storeId) { - if ($object->getPriceType() == 'fixed') { - $storeCurrency = $this->_storeManager->getStore($storeId)->getBaseCurrencyCode(); - $rate = $this->_currencyFactory->create()->load($baseCurrency)->getRate($storeCurrency); - if (!$rate) { - $rate = 1; - } - $newPrice = $object->getPrice() * $rate; - } else { - $newPrice = $object->getPrice(); - } - - $statement = $connection->select()->from( - $priceTable - )->where( - 'option_id = ?', - $object->getId() - )->where( - 'store_id = ?', - $storeId - ); - - if ($connection->fetchOne($statement)) { - $data = $this->_prepareDataForTable( - new \Magento\Framework\DataObject( - ['price' => $newPrice, 'price_type' => $object->getPriceType()] - ), - $priceTable - ); - - $connection->update( - $priceTable, - $data, - ['option_id = ?' => $object->getId(), 'store_id = ?' => $storeId] - ); - } else { - $data = $this->_prepareDataForTable( - new \Magento\Framework\DataObject( - [ - 'option_id' => $object->getId(), - 'store_id' => $storeId, - 'price' => $newPrice, - 'price_type' => $object->getPriceType(), - ] - ), - $priceTable - ); - $connection->insert($priceTable, $data); - } - } + if (empty($storeIds)) { + return $this; } - } elseif ($scope == \Magento\Store\Model\Store::PRICE_SCOPE_WEBSITE && $object->getData('scope', 'price') - ) { - $connection->delete( - $priceTable, + foreach ($storeIds as $storeId) { + $newPrice = $this->calculateStorePrice($object, $storeId); + $this->savePriceByStore($object, (int)$storeId, $newPrice); + } + } elseif ($scope == Store::PRICE_SCOPE_WEBSITE && $object->getData('scope', 'price')) { + $this->getConnection()->delete( + $this->getTable('catalog_product_option_price'), ['option_id = ?' => $object->getId(), 'store_id = ?' => $object->getStoreId()] ); } @@ -228,31 +134,114 @@ protected function _saveValuePrices(\Magento\Framework\Model\AbstractModel $obje return $this; } + /** + * Save option price by store + * + * @param AbstractModel $object + * @param int $storeId + * @param float|null $newPrice + */ + private function savePriceByStore(AbstractModel $object, int $storeId, float $newPrice = null): void + { + $priceTable = $this->getTable('catalog_product_option_price'); + $connection = $this->getConnection(); + $price = $newPrice === null ? $object->getPrice() : $newPrice; + + $statement = $connection->select()->from($priceTable, 'option_id') + ->where('option_id = ?', $object->getId()) + ->where('store_id = ?', $storeId); + $optionId = $connection->fetchOne($statement); + + if (!$optionId) { + $data = $this->_prepareDataForTable( + new DataObject( + [ + 'option_id' => $object->getId(), + 'store_id' => $storeId, + 'price' => $price, + 'price_type' => $object->getPriceType(), + ] + ), + $priceTable + ); + $connection->insert($priceTable, $data); + } else { + // skip to update the default price when the store price is saving + if ($storeId === Store::DEFAULT_STORE_ID && (int)$object->getStoreId() !== $storeId) { + return; + } + + $data = $this->_prepareDataForTable( + new DataObject( + [ + 'price' => $price, + 'price_type' => $object->getPriceType() + ] + ), + $priceTable + ); + + $connection->update( + $priceTable, + $data, + [ + 'option_id = ?' => $object->getId(), + 'store_id = ?' => $storeId + ] + ); + } + } + + /** + * Calculate price by store + * + * @param AbstractModel $object + * @param int $storeId + * @return float + */ + private function calculateStorePrice(AbstractModel $object, int $storeId): float + { + $price = $object->getPrice(); + if ($object->getPriceType() == 'fixed') { + $website = $this->_storeManager->getStore($storeId)->getWebsite(); + $websiteBaseCurrency = $this->_config->getValue( + \Magento\Directory\Model\Currency::XML_PATH_CURRENCY_BASE, + ScopeInterface::SCOPE_WEBSITE, + $website + ); + $storeCurrency = $this->_storeManager->getStore($storeId)->getBaseCurrencyCode(); + $rate = $this->_currencyFactory->create()->load($websiteBaseCurrency)->getRate($storeCurrency); + $price = $object->getPrice() * ($rate ?: 1); + } + + return (float)$price; + } + /** * Save titles * - * @param \Magento\Framework\Model\AbstractModel $object + * @param AbstractModel $object * @return void * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ - protected function _saveValueTitles(\Magento\Framework\Model\AbstractModel $object) + protected function _saveValueTitles(AbstractModel $object) { $connection = $this->getConnection(); $titleTableName = $this->getTable('catalog_product_option_title'); - foreach ([\Magento\Store\Model\Store::DEFAULT_STORE_ID, $object->getStoreId()] as $storeId) { + foreach ([Store::DEFAULT_STORE_ID, $object->getStoreId()] as $storeId) { $existInCurrentStore = $this->getColFromOptionTable($titleTableName, (int)$object->getId(), (int)$storeId); - $existInDefaultStore = (int)$storeId == \Magento\Store\Model\Store::DEFAULT_STORE_ID ? + $existInDefaultStore = (int)$storeId == Store::DEFAULT_STORE_ID ? $existInCurrentStore : $this->getColFromOptionTable( $titleTableName, (int)$object->getId(), - \Magento\Store\Model\Store::DEFAULT_STORE_ID + Store::DEFAULT_STORE_ID ); if ($object->getTitle()) { $isDeleteStoreTitle = (bool)$object->getData('is_delete_store_title'); if ($existInCurrentStore) { - if ($isDeleteStoreTitle && (int)$storeId != \Magento\Store\Model\Store::DEFAULT_STORE_ID) { + if ($isDeleteStoreTitle && (int)$storeId != Store::DEFAULT_STORE_ID) { $connection->delete($titleTableName, ['option_title_id = ?' => $existInCurrentStore]); } elseif ($object->getStoreId() == $storeId) { $data = $this->_prepareDataForTable( @@ -270,9 +259,9 @@ protected function _saveValueTitles(\Magento\Framework\Model\AbstractModel $obje } } else { // we should insert record into not default store only of if it does not exist in default store - if (($storeId == \Magento\Store\Model\Store::DEFAULT_STORE_ID && !$existInDefaultStore) || + if (($storeId == Store::DEFAULT_STORE_ID && !$existInDefaultStore) || ( - $storeId != \Magento\Store\Model\Store::DEFAULT_STORE_ID && + $storeId != Store::DEFAULT_STORE_ID && !$existInCurrentStore && !$isDeleteStoreTitle ) @@ -291,7 +280,7 @@ protected function _saveValueTitles(\Magento\Framework\Model\AbstractModel $obje } } } else { - if ($object->getId() && $object->getStoreId() > \Magento\Store\Model\Store::DEFAULT_STORE_ID + if ($object->getId() && $object->getStoreId() > Store::DEFAULT_STORE_ID && $storeId ) { $connection->delete( @@ -470,7 +459,7 @@ public function getSearchableData($productId, $storeId) 'option_title_default.option_id=product_option.option_id', $connection->quoteInto( 'option_title_default.store_id = ?', - \Magento\Store\Model\Store::DEFAULT_STORE_ID + Store::DEFAULT_STORE_ID ) ] ); @@ -517,7 +506,7 @@ public function getSearchableData($productId, $storeId) 'option_title_default.option_type_id=option_type.option_type_id', $connection->quoteInto( 'option_title_default.store_id = ?', - \Magento\Store\Model\Store::DEFAULT_STORE_ID + Store::DEFAULT_STORE_ID ) ] ); @@ -582,6 +571,8 @@ public function getPriceTypes() } /** + * Get Metadata Pool + * * @return \Magento\Framework\EntityManager\MetadataPool */ private function getMetadataPool() diff --git a/app/code/Magento/Catalog/Observer/SetSpecialPriceStartDate.php b/app/code/Magento/Catalog/Observer/SetSpecialPriceStartDate.php index ed9f89efc6891..a898075befd16 100644 --- a/app/code/Magento/Catalog/Observer/SetSpecialPriceStartDate.php +++ b/app/code/Magento/Catalog/Observer/SetSpecialPriceStartDate.php @@ -38,10 +38,9 @@ public function execute(\Magento\Framework\Event\Observer $observer) { /** @var $product \Magento\Catalog\Model\Product */ $product = $observer->getEvent()->getProduct(); - if ($product->getSpecialPrice() && ! $product->getSpecialFromDate()) { + if ($product->getSpecialPrice() && $product->getSpecialFromDate() === null) { $product->setData('special_from_date', $this->localeDate->date()->setTime(0, 0)); } - return $this; } } diff --git a/app/code/Magento/Catalog/Pricing/Render/PriceBox.php b/app/code/Magento/Catalog/Pricing/Render/PriceBox.php index 190168ed583fc..678b45ce97e7b 100644 --- a/app/code/Magento/Catalog/Pricing/Render/PriceBox.php +++ b/app/code/Magento/Catalog/Pricing/Render/PriceBox.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Catalog\Pricing\Render; use Magento\Catalog\Model\Product; @@ -71,7 +73,9 @@ public function jsonEncode($valueToEncode) * * @param int $length * @param string|null $chars + * * @return string + * @throws \Magento\Framework\Exception\LocalizedException */ public function getRandomString($length, $chars = null) { @@ -93,4 +97,21 @@ public function getCanDisplayQty(Product $product) } return true; } + + /** + * Format percent + * + * @param float $percent + * + * @return string + */ + public function formatPercent(float $percent): string + { + /*First rtrim - trim zeros. So, 10.00 -> 10.*/ + /*Second rtrim - trim dot. So, 10. -> 10*/ + return rtrim( + rtrim(number_format($percent, 2), '0'), + '.' + ); + } } diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddProductToCartActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddProductToCartActionGroup.xml index 97d4ac5389311..b47e13a118272 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddProductToCartActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AddProductToCartActionGroup.xml @@ -21,4 +21,10 @@ <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" time="30" stepKey="waitForProductAddedMessage"/> <see selector="{{StorefrontMessagesSection.success}}" userInput="You added {{product.name}} to your shopping cart." stepKey="seeAddToCartSuccessMessage"/> </actionGroup> + <actionGroup name="StorefrontAddSimpleProductWithQtyActionGroup" extends="AddSimpleProductToCart"> + <arguments> + <argument name="quantity" type="string" defaultValue="1"/> + </arguments> + <fillField userInput="{{quantity}}" selector="{{StorefrontProductPageSection.qtyInput}}" stepKey="fillProductQty" after="goToProductPage"/> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml index 3c44a8f1898ad..95e24f1f6fcfc 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductActionGroup.xml @@ -304,9 +304,11 @@ </arguments> <waitForPageLoad stepKey="waitForPageLoad"/> <click selector="{{AdminProductFormSection.advancedPricingLink}}" stepKey="clickAdvancedPricingLink"/> + <waitForPageLoad stepKey="waitForAdvancedPricingModal"/> <waitForElementVisible selector="{{AdminProductFormAdvancedPricingSection.specialPrice}}" stepKey="waitSpecialPrice"/> <fillField userInput="{{price}}" selector="{{AdminProductFormAdvancedPricingSection.specialPrice}}" stepKey="fillSpecialPrice"/> <click selector="{{AdminProductFormAdvancedPricingSection.doneButton}}" stepKey="clickDone"/> + <waitForPageLoad stepKey="waitForAdvancedPricingModalGone"/> <waitForElementNotVisible selector="{{AdminProductFormAdvancedPricingSection.specialPrice}}" stepKey="waitForCloseModalWindow"/> </actionGroup> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml index ad32b8edbd243..3e967cb9c6901 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/AdminProductGridActionGroup.xml @@ -256,7 +256,7 @@ <click selector="{{AdminProductGridSection.bulkActionDropdown}}" stepKey="clickActionDropdown"/> <click selector="{{AdminProductGridSection.bulkActionOption('Change status')}}" stepKey="clickChangeStatusAction"/> - <click selector="{{AdminProductGridSection.changeStatus('status')}}" stepKey="clickChangeStatusDisabled" parameterized="true"/> + <click selector="{{AdminProductGridSection.changeStatus('status')}}" stepKey="clickChangeStatusDisabled"/> <waitForPageLoad stepKey="waitForStatusToBeChanged"/> <see selector="{{AdminMessagesSection.success}}" userInput="A total of 1 record(s) have been updated." stepKey="seeSuccessMessage"/> <waitForLoadingMaskToDisappear stepKey="waitForMaskToDisappear"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CustomOptionsActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CustomOptionsActionGroup.xml index b914d5e20712d..838ac7d288ead 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CustomOptionsActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/CustomOptionsActionGroup.xml @@ -55,7 +55,7 @@ <actionGroup name="AddProductCustomOptionField"> <arguments> <argument name="option" defaultValue="ProductOptionField"/> - <argiment name="optionIndex" type="string"/> + <argument name="optionIndex" type="string"/> </arguments> <conditionalClick selector="{{AdminProductCustomizableOptionsSection.customizableOptions}}" dependentSelector="{{AdminProductCustomizableOptionsSection.addOptionBtn}}" visible="false" stepKey="openCustomOptionSection"/> <click selector="{{AdminProductCustomizableOptionsSection.addOptionBtn}}" stepKey="clickAddOption"/> @@ -90,7 +90,7 @@ <actionGroup name="checkCustomizableOptionImport"> <arguments> <argument name="option" defaultValue="ProductOptionField"/> - <argiment name="optionIndex" type="string"/> + <argument name="optionIndex" type="string"/> </arguments> <grabValueFrom selector="{{AdminProductCustomizableOptionsSection.optionTitleInput(optionIndex)}}" stepKey="grabOptionTitle"/> <grabValueFrom selector="{{AdminProductCustomizableOptionsSection.optionPrice(optionIndex)}}" stepKey="grabOptionPrice"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAddProductToCartWithQtyActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAddProductToCartWithQtyActionGroup.xml index 816085cf0ca26..5432d547e8025 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAddProductToCartWithQtyActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAddProductToCartWithQtyActionGroup.xml @@ -16,6 +16,7 @@ <fillField selector="{{StorefrontProductPageSection.qtyInput}}" userInput="{{productQty}}" stepKey="fillProduct1Quantity"/> <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="clickOnAddToCartButton"/> <waitForPageLoad stepKey="waitForProductToAddInCart"/> + <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessage"/> <seeElement selector="{{StorefrontProductPageSection.successMsg}}" stepKey="seeSuccessSaveMessage"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAddToCartCustomOptionsProductPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAddToCartCustomOptionsProductPageActionGroup.xml index c7ae52d2b37c3..080b374c60b43 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAddToCartCustomOptionsProductPageActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAddToCartCustomOptionsProductPageActionGroup.xml @@ -14,6 +14,7 @@ </arguments> <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addToCart"/> <waitForPageLoad stepKey="waitForPageLoad"/> + <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessage"/> <see selector="{{StorefrontMessagesSection.success}}" userInput="You added {{productName}} to your shopping cart." stepKey="seeAddToCartSuccessMessage"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertProductSpecialPriceOnProductPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertProductSpecialPriceOnProductPageActionGroup.xml index ee71a3ace4449..9fefa71f10209 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertProductSpecialPriceOnProductPageActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontAssertProductSpecialPriceOnProductPageActionGroup.xml @@ -13,6 +13,7 @@ </arguments> <amOnPage url="{{StorefrontProductPage.url(product.name)}}" stepKey="onFirstProductPage"/> <waitForPageLoad stepKey="waitForFirstProductPage"/> + <waitForElementVisible selector="{{StorefrontProductInfoMainSection.specialPriceValue}}" stepKey="waitForProductSpecialPrice"/> <grabTextFrom selector="{{StorefrontProductInfoMainSection.specialPriceValue}}" stepKey="grabProductSpecialPrice"/> <assertEquals actual="$grabProductSpecialPrice" expectedType="string" expected="{{specialPrice}}" stepKey="assertProductPriceValuesAreEqual"/> </actionGroup> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCategoryActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCategoryActionGroup.xml index 4c7c011028c92..7e79182616fd0 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCategoryActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontCategoryActionGroup.xml @@ -62,6 +62,13 @@ <seeElement selector="{{StorefrontCategoryProductSection.ProductAddToCartByName(product.name)}}" stepKey="AssertAddToCart" /> </actionGroup> + <actionGroup name="StorefrontCheckAddToCartButtonAbsence"> + <arguments> + <argument name="product" defaultValue="_defaultProduct"/> + </arguments> + <moveMouseOver selector="{{StorefrontCategoryProductSection.ProductInfoByName(product.name)}}" stepKey="moveMouseOverProduct" /> + <dontSeeElement selector="{{StorefrontCategoryProductSection.ProductAddToCartByName(product.name)}}" stepKey="checkAddToCartButtonAbsence"/> + </actionGroup> <actionGroup name="StorefrontSwitchCategoryViewToListMode"> <click selector="{{StorefrontCategoryMainSection.modeListButton}}" stepKey="switchCategoryViewToListMode"/> <waitForElement selector="{{StorefrontCategoryMainSection.CategoryTitle}}" time="30" stepKey="waitForCategoryReload"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontClickAddToCartOnProductPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontClickAddToCartOnProductPageActionGroup.xml index fb2065d228d5a..1dcbc738c7651 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontClickAddToCartOnProductPageActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontClickAddToCartOnProductPageActionGroup.xml @@ -9,6 +9,7 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="StorefrontClickAddToCartOnProductPageActionGroup"> <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addToCart" /> + <waitForPageLoad stepKey="waitForAddToCart"/> <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessage" /> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontProductPageActionGroup.xml b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontProductPageActionGroup.xml index 8e0ef3ccbcf28..e6392118f79b8 100644 --- a/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontProductPageActionGroup.xml +++ b/app/code/Magento/Catalog/Test/Mftf/ActionGroup/StorefrontProductPageActionGroup.xml @@ -13,9 +13,11 @@ <argument name="productName"/> </arguments> <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addToCart"/> + <waitForPageLoad stepKey="waitForAddToCart"/> <waitForElementNotVisible selector="{{StorefrontProductActionSection.addToCartButtonTitleIsAdding}}" stepKey="waitForElementNotVisibleAddToCartButtonTitleIsAdding"/> <waitForElementNotVisible selector="{{StorefrontProductActionSection.addToCartButtonTitleIsAdded}}" stepKey="waitForElementNotVisibleAddToCartButtonTitleIsAdded"/> <waitForPageLoad stepKey="waitForPageLoad"/> + <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessage"/> <see selector="{{StorefrontMessagesSection.success}}" userInput="You added {{productName}} to your shopping cart." stepKey="seeAddToCartSuccessMessage"/> </actionGroup> diff --git a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml index 62276b12b99b6..33dab7ee8fd7e 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Data/ProductData.xml @@ -240,6 +240,9 @@ <requiredEntity type="product_extension_attribute">EavStockItem</requiredEntity> <requiredEntity type="custom_attribute">CustomAttributeProductAttribute</requiredEntity> </entity> + <entity name="ApiSimpleProductWithShortSKU" type="product2" extends="ApiSimpleOne"> + <data key="sku" unique="suffix">pr</data> + </entity> <entity name="ApiSimpleOneHidden" type="product2"> <data key="sku" unique="suffix">api-simple-product</data> <data key="type_id">simple</data> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml index d24c501152b78..7ca2c0a56f224 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminCreateProductAttributeSection.xml @@ -13,6 +13,7 @@ <element name="DefaultLabel" type="input" selector="#attribute_label"/> <element name="InputType" type="select" selector="#frontend_input"/> <element name="ValueRequired" type="select" selector="#is_required"/> + <element name="UpdateProductPreviewImage" type="select" selector="[name='update_product_preview_image']"/> <element name="AdvancedProperties" type="button" selector="#advanced_fieldset-wrapper"/> <element name="DefaultValue" type="input" selector="#default_value_text"/> <element name="Scope" type="select" selector="#is_global"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Section/AdminEditProductAttributesSection.xml b/app/code/Magento/Catalog/Test/Mftf/Section/AdminEditProductAttributesSection.xml index 63bdcd52cdd20..b243fbfd6034a 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Section/AdminEditProductAttributesSection.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Section/AdminEditProductAttributesSection.xml @@ -21,5 +21,6 @@ <element name="ProductDataMayBeLostModal" type="button" selector="//aside[contains(@class,'_show')]//header[contains(.,'Product data may be lost')]"/> <element name="ProductDataMayBeLostConfirmButton" type="button" selector="//aside[contains(@class,'_show')]//button[.='Change Input Type']"/> <element name="defaultLabel" type="text" selector="//td[contains(text(), '{{attributeName}}')]/following-sibling::td[contains(@class, 'col-frontend_label')]" parameterized="true"/> + <element name="formByStoreId" type="block" selector="//form[contains(@action,'store/{{store_id}}')]" parameterized="true"/> </section> </sections> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AddOutOfStockProductToCompareListTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AddOutOfStockProductToCompareListTest.xml index 4f66395bd0fbf..044b38a19c4ea 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AddOutOfStockProductToCompareListTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AddOutOfStockProductToCompareListTest.xml @@ -11,8 +11,9 @@ <test name="AddOutOfStockProductToCompareListTest"> <annotations> <features value="Catalog"/> - <title value="Add out of stock product to compare list"/> - <description value="Add out of stock product to compare list"/> + <stories value="Product Comparison for products Out of Stock"/> + <title value="Add Product that is Out of Stock product to Product Comparison"/> + <description value="Customer should be able to add Product that is Out Of Stock to the Product Comparison"/> <severity value="MAJOR"/> <testCaseId value="MAGETWO-98644"/> <useCaseId value="MAGETWO-98522"/> @@ -58,7 +59,7 @@ <waitForPageLoad stepKey="waitForProdAddToCmpList"/> <!--Assert success message--> <comment userInput="Assert success message" stepKey="assertSuccessMsg"/> - <grabTextFrom selector="{{AdminProductMessagesSection.successMessage}}" stepKey="grabTextFromSuccessMessage"/> + <grabTextFrom selector="{{AdminProductMessagesSection.successMessage}}" stepKey="grabTextFromSuccessMessage"/> <assertEquals expected='You added product $$product.name$$ to the comparison list.' expectedType="string" actual="($grabTextFromSuccessMessage)" stepKey="assertSuccessMessage"/> <!--See product in the comparison list--> <comment userInput="See product in the comparison list" stepKey="seeProductInComparisonList"/> @@ -66,7 +67,7 @@ <waitForPageLoad stepKey="waitForStorefrontProductComparePageLoad"/> <seeElement selector="{{StorefrontProductCompareMainSection.ProductLinkByName($product.name$)}}" stepKey="seeProductInCompareList"/> <!--Go to Category page and delete product from comparison list--> - <comment userInput="Go to Category page and delete prduct from comparison list" stepKey="deletProdFromCmpList"/> + <comment userInput="Go to Category page and delete product from comparison list" stepKey="deleteProdFromCmpList"/> <amOnPage url="{{StorefrontCategoryPage.url($$category.name$$)}}" stepKey="onCategoryPage"/> <waitForPageLoad time="30" stepKey="waitForPageLoad1"/> <click selector="{{StorefrontComparisonSidebarSection.ClearAll}}" stepKey="clickClearAll"/> @@ -80,7 +81,7 @@ <waitForPageLoad stepKey="waitProdAddingToCmpList"/> <!--Assert success message--> <comment userInput="Assert success message" stepKey="assertSuccessMsg2"/> - <grabTextFrom selector="{{AdminProductMessagesSection.successMessage}}" stepKey="grabTextFromSuccessMessage2"/> + <grabTextFrom selector="{{AdminProductMessagesSection.successMessage}}" stepKey="grabTextFromSuccessMessage2"/> <assertEquals expected='You added product $$product.name$$ to the comparison list.' expectedType="string" actual="($grabTextFromSuccessMessage)" stepKey="assertSuccessMessage2"/> <!--Check that product displays on add to compare widget--> <comment userInput="Check that product displays on add to compare widget" stepKey="checkProdNameOnWidget"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddImageToWYSIWYGProductTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddImageToWYSIWYGProductTest.xml index 03f3e93bb30ec..f3d3e653b260b 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddImageToWYSIWYGProductTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddImageToWYSIWYGProductTest.xml @@ -16,6 +16,9 @@ <description value="Admin should be able to add image to WYSIWYG Editor on Product Page"/> <severity value="CRITICAL"/> <testCaseId value="MAGETWO-84375"/> + <skip> + <issueId value="MC-17232"/> + </skip> </annotations> <before> <actionGroup ref="LoginActionGroup" stepKey="login"/> @@ -47,9 +50,9 @@ <conditionalClick selector="{{ProductDescriptionWYSIWYGToolbarSection.WysiwygArrow}}" dependentSelector="{{ProductDescriptionWYSIWYGToolbarSection.checkIfWysiwygArrowExpand}}" stepKey="clickWysiwygArrowIfClosed" visible="true"/> <waitForText userInput="{{ImageFolder.name}}" stepKey="waitForNewFolder1" /> <click userInput="{{ImageFolder.name}}" stepKey="clickOnCreatedFolder1" /> - <waitForLoadingMaskToDisappear stepKey="waitForLoading4" timeout="45"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoading4"/> <attachFile selector="{{ProductDescriptionWYSIWYGToolbarSection.BrowseUploadImage}}" userInput="{{ImageUpload1.value}}" stepKey="uploadImage1"/> - <waitForLoadingMaskToDisappear stepKey="waitForFileUpload1" timeout="30"/> + <waitForLoadingMaskToDisappear stepKey="waitForFileUpload1"/> <waitForElementVisible selector="{{ProductDescriptionWYSIWYGToolbarSection.image(ImageUpload1.value)}}" stepKey="waitForUploadImage1" /> <seeElement selector="{{ProductDescriptionWYSIWYGToolbarSection.imageSelected(ImageUpload1.value)}}" stepKey="seeImageSelected1" /> <see selector="{{ProductDescriptionWYSIWYGToolbarSection.DeleteSelectedBtn}}" userInput="Delete Selected" stepKey="seeDeleteBtn1"/> @@ -60,7 +63,7 @@ <dontSeeElement selector="{{ProductDescriptionWYSIWYGToolbarSection.image(ImageUpload1.value)}}" stepKey="dontSeeImage1" /> <dontSeeElement selector="{{ProductDescriptionWYSIWYGToolbarSection.InsertFile}}" stepKey="dontSeeAddSelectedBtn2" /> <attachFile selector="{{ProductDescriptionWYSIWYGToolbarSection.BrowseUploadImage}}" userInput="{{ImageUpload1.value}}" stepKey="uploadImage2"/> - <waitForLoadingMaskToDisappear stepKey="waitForFileUpload2" timeout="45"/> + <waitForLoadingMaskToDisappear stepKey="waitForFileUpload2"/> <waitForElementVisible selector="{{ProductDescriptionWYSIWYGToolbarSection.image(ImageUpload1.value)}}" stepKey="waitForUploadImage2" /> <click selector="{{ProductDescriptionWYSIWYGToolbarSection.InsertFile}}" stepKey="clickInsertBtn1" /> <waitForElementVisible selector="{{ProductDescriptionWYSIWYGToolbarSection.ImageDescription}}" stepKey="waitForImageDescriptionButton1" /> @@ -72,12 +75,12 @@ <click selector="{{ProductShortDescriptionWYSIWYGToolbarSection.Browse}}" stepKey="clickBrowse2" /> <waitForElementVisible selector="{{ProductDescriptionWYSIWYGToolbarSection.CancelBtn}}" stepKey="waitForCancelButton2"/> <see selector="{{ProductShortDescriptionWYSIWYGToolbarSection.CancelBtn}}" userInput="Cancel" stepKey="seeCancelBtn2" /> - <waitForLoadingMaskToDisappear stepKey="waitForLoading13" timeout="30"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoading13"/> <see selector="{{ProductShortDescriptionWYSIWYGToolbarSection.CreateFolder}}" userInput="Create Folder" stepKey="seeCreateFolderBtn2" /> - <waitForLoadingMaskToDisappear stepKey="waitForLoading14" timeout="40"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoading14"/> <dontSeeElement selector="{{ProductShortDescriptionWYSIWYGToolbarSection.InsertFile}}" stepKey="dontSeeAddSelectedBtn3" /> <attachFile selector="{{ProductShortDescriptionWYSIWYGToolbarSection.BrowseUploadImage}}" userInput="{{ImageUpload3.value}}" stepKey="uploadImage3"/> - <waitForLoadingMaskToDisappear stepKey="waitForFileUpload3" timeout="45"/> + <waitForLoadingMaskToDisappear stepKey="waitForFileUpload3"/> <waitForElementVisible selector="{{ProductShortDescriptionWYSIWYGToolbarSection.image(ImageUpload3.value)}}" stepKey="waitForUploadImage3" /> <waitForElement selector="{{ProductShortDescriptionWYSIWYGToolbarSection.DeleteSelectedBtn}}" stepKey="waitForDeletebtn" /> <see selector="{{ProductShortDescriptionWYSIWYGToolbarSection.DeleteSelectedBtn}}" userInput="Delete Selected" stepKey="seeDeleteBtn2"/> @@ -86,7 +89,7 @@ <click selector="{{AdminConfirmationModalSection.ok}}" stepKey="confirmDelete2" /> <dontSeeElement selector="{{ProductDescriptionWYSIWYGToolbarSection.InsertFile}}" stepKey="dontSeeAddSelectedBtn4" /> <attachFile selector="{{ProductShortDescriptionWYSIWYGToolbarSection.BrowseUploadImage}}" userInput="{{ImageUpload3.value}}" stepKey="uploadImage4"/> - <waitForLoadingMaskToDisappear stepKey="waitForFileUpload4" timeout="45"/> + <waitForLoadingMaskToDisappear stepKey="waitForFileUpload4"/> <waitForElementVisible selector="{{ProductShortDescriptionWYSIWYGToolbarSection.image(ImageUpload3.value)}}" stepKey="waitForUploadImage4" /> <click selector="{{ProductShortDescriptionWYSIWYGToolbarSection.InsertFile}}" stepKey="clickInsertBtn" /> <waitForLoadingMaskToDisappear stepKey="waitForLoading11" /> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddInStockProductToTheCartTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddInStockProductToTheCartTest.xml index e3f4d6cbdde0d..feb4fffd12f5d 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddInStockProductToTheCartTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminAddInStockProductToTheCartTest.xml @@ -77,6 +77,7 @@ <fillField selector="{{StorefrontProductPageSection.qtyInput}}" userInput="1" stepKey="fillProductQuantity"/> <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="clickOnAddToCartButton"/> <waitForPageLoad stepKey="waitForProductToAddInCart"/> + <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessage"/> <seeElement selector="{{StorefrontProductPageSection.successMsg}}" stepKey="seeSuccessSaveMessage"/> <seeElement selector="{{StorefrontMinicartSection.quantity(1)}}" stepKey="seeAddedProductQuantityInCart"/> <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickOnMiniCart"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryTest.xml index 8806612c0f5de..15171fe3713c3 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateCategoryTest.xml @@ -96,7 +96,7 @@ <click selector="{{AdminCategoryMainActionsSection.SaveButton}}" stepKey="saveCategory"/> <see selector="{{AdminCategoryBasicFieldSection.FieldError('uid')}}" userInput="This is a required field." stepKey="seeErrorMessage"/> <!-- Verify that the Layered navigation price step field has the required indicator --> - <comment userInput="Check if Layered navigation price field has required indictor icon" stepKey="comment" /> + <comment userInput="Check if Layered navigation price field has required indicator icon" stepKey="comment" /> <executeJS function="{{CategoryDisplaySettingsSection.RequiredFieldIndicator('filter_price_range')}}" stepKey="getRequiredFieldIndicator"/> <assertEquals expected='"*"' expectedType="string" actualType="variable" actual="getRequiredFieldIndicator" message="pass" stepKey="assertRequiredFieldIndicator1"/> </test> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductOutOfStockWithTierPriceTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductOutOfStockWithTierPriceTest.xml index 26ad7a46a73d7..e516a046e489e 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductOutOfStockWithTierPriceTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductOutOfStockWithTierPriceTest.xml @@ -17,6 +17,9 @@ <severity value="CRITICAL"/> <group value="catalog"/> <group value="mtf_migrated"/> + <skip> + <issueId value="MC-17181"/> + </skip> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithCustomOptionsSuiteAndImportOptionsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithCustomOptionsSuiteAndImportOptionsTest.xml index 17769c79677f7..6ee3fa6c60b82 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithCustomOptionsSuiteAndImportOptionsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithCustomOptionsSuiteAndImportOptionsTest.xml @@ -17,6 +17,9 @@ <severity value="CRITICAL"/> <group value="catalog"/> <group value="mtf_migrated"/> + <skip> + <issueId value="MC-17181"/> + </skip> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithTierPriceForGeneralGroupTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithTierPriceForGeneralGroupTest.xml index 78247f4943596..b777babf3b3aa 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithTierPriceForGeneralGroupTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithTierPriceForGeneralGroupTest.xml @@ -17,6 +17,9 @@ <severity value="CRITICAL"/> <group value="catalog"/> <group value="mtf_migrated"/> + <skip> + <issueId value="MC-17181"/> + </skip> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithTierPriceTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithTierPriceTest.xml index 6ef2569945fa6..28923674f4ab3 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithTierPriceTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithTierPriceTest.xml @@ -17,6 +17,9 @@ <severity value="CRITICAL"/> <group value="catalog"/> <group value="mtf_migrated"/> + <skip> + <issueId value="MC-17181"/> + </skip> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithoutManageStockTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithoutManageStockTest.xml index cb41b0292d33a..8c5085501b799 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithoutManageStockTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminCreateVirtualProductWithoutManageStockTest.xml @@ -17,6 +17,9 @@ <severity value="CRITICAL"/> <group value="catalog"/> <group value="mtf_migrated"/> + <skip> + <issueId value="MC-17181"/> + </skip> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminEditTextEditorProductAttributeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminEditTextEditorProductAttributeTest.xml index e914b8c96d03e..53040993beb8f 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminEditTextEditorProductAttributeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminEditTextEditorProductAttributeTest.xml @@ -12,10 +12,10 @@ <features value="Catalog"/> <stories value="MAGETWO-51484-Input type configuration for custom Product Attributes"/> <group value="Catalog"/> - <title value="Admin should be able to switch between two versions of TinyMCE"/> - <description value="Admin should be able to switch between two versions of TinyMCE"/> + <title value="Admin are able to change Input Type of Text Editor product attribute"/> + <description value="Admin are able to change Input Type of Text Editor product attribute"/> <severity value="CRITICAL"/> - <testCaseId value="MAGETWO-85745"/> + <testCaseId value="MC-6215"/> </annotations> <before> <actionGroup ref="LoginActionGroup" stepKey="loginGetFromGeneralFile"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminImportCustomizableOptionToProductWithSKUTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminImportCustomizableOptionToProductWithSKUTest.xml index 4e182c2e0e5ac..877c3ecab7edf 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminImportCustomizableOptionToProductWithSKUTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminImportCustomizableOptionToProductWithSKUTest.xml @@ -18,6 +18,9 @@ <testCaseId value="MAGETWO-98211"/> <useCaseId value="MAGETWO-70232"/> <group value="catalog"/> + <skip> + <issueId value="MC-17140"/> + </skip> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesGlobalScopeTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesGlobalScopeTest.xml index 8a44c8093ca5e..87e0bf3d2e9a0 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesGlobalScopeTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminMassUpdateProductAttributesGlobalScopeTest.xml @@ -18,6 +18,9 @@ <testCaseId value="MC-56"/> <group value="Catalog"/> <group value="Product Attributes"/> + <skip> + <issueId value="MC-17140"/> + </skip> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductNameToVerifyDataOverridingOnStoreViewLevelTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductNameToVerifyDataOverridingOnStoreViewLevelTest.xml index 18e4ff9ee2c99..80f0c8ad10ede 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductNameToVerifyDataOverridingOnStoreViewLevelTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductNameToVerifyDataOverridingOnStoreViewLevelTest.xml @@ -52,9 +52,14 @@ <!-- Assign simple product to created store view --> <click selector="{{AdminCategoryMainActionsSection.CategoryStoreViewDropdownToggle}}" stepKey="clickCategoryStoreViewDropdownToggle"/> + <waitForPageLoad stepKey="waitForStoreViewDropdown"/> + <waitForElementVisible selector="{{AdminCategoryMainActionsSection.CategoryStoreViewOption(customStoreFR.name)}}" stepKey="waitForStoreViewOption"/> <click selector="{{AdminCategoryMainActionsSection.CategoryStoreViewOption(customStoreFR.name)}}" stepKey="selectCategoryStoreViewOption"/> + <waitForPageLoad stepKey="waitForAcceptModal"/> + <waitForElementVisible selector="{{AdminProductFormChangeStoreSection.acceptButton}}" stepKey="waitForAcceptButton"/> <click selector="{{AdminProductFormChangeStoreSection.acceptButton}}" stepKey="clickAcceptButton"/> <waitForPageLoad stepKey="waitForThePageToLoad"/> + <waitForElementNotVisible selector="{{AdminProductFormChangeStoreSection.acceptButton}}" stepKey="waitForAcceptButtonGone"/> <uncheckOption selector="{{AdminProductFormSection.productNameUseDefault}}" stepKey="uncheckProductStatus"/> <!-- Update default simple product with name --> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductPriceToVerifyDataOverridingOnStoreViewLevelTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductPriceToVerifyDataOverridingOnStoreViewLevelTest.xml index d5fc981b5b2e6..f698b3d89ffe9 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductPriceToVerifyDataOverridingOnStoreViewLevelTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductPriceToVerifyDataOverridingOnStoreViewLevelTest.xml @@ -52,7 +52,11 @@ <!-- Assign simple product to created store view --> <click selector="{{AdminCategoryMainActionsSection.CategoryStoreViewDropdownToggle}}" stepKey="clickCategoryStoreViewDropdownToggle"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <waitForElementVisible selector="{{AdminCategoryMainActionsSection.CategoryStoreViewOption(customStoreFR.name)}}" stepKey="waitForSelectCategoryStoreViewOption"/> <click selector="{{AdminCategoryMainActionsSection.CategoryStoreViewOption(customStoreFR.name)}}" stepKey="selectCategoryStoreViewOption"/> + <waitForPageLoad stepKey="waitForPageLoad1"/> + <waitForElementVisible selector="{{AdminProductFormChangeStoreSection.acceptButton}}" stepKey="waitForAcceptButton"/> <click selector="{{AdminProductFormChangeStoreSection.acceptButton}}" stepKey="clickAcceptButton"/> <waitForPageLoad stepKey="waitForPageToLoad"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductTieredPriceTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductTieredPriceTest.xml index 2c3aa5db75171..d151bae3ee110 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductTieredPriceTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductTieredPriceTest.xml @@ -17,6 +17,9 @@ <severity value="CRITICAL"/> <group value="catalog"/> <group value="mtf_migrated"/> + <skip> + <issueId value="MC-17181"/> + </skip> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockEnabledFlatTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockEnabledFlatTest.xml index a042c4d60ae4f..d30500de64a32 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockEnabledFlatTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockEnabledFlatTest.xml @@ -17,6 +17,9 @@ <severity value="CRITICAL"/> <group value="catalog"/> <group value="mtf_migrated"/> + <skip> + <issueId value="MC-17181"/> + </skip> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockNotVisibleIndividuallyTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockNotVisibleIndividuallyTest.xml index d08ef9c93999c..cb7b3d6278aa8 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockNotVisibleIndividuallyTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockNotVisibleIndividuallyTest.xml @@ -17,6 +17,9 @@ <severity value="CRITICAL"/> <group value="catalog"/> <group value="mtf_migrated"/> + <skip> + <issueId value="MC-17181"/> + </skip> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogAndSearchTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogAndSearchTest.xml index a695982921cfd..75b4a9728d08b 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogAndSearchTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogAndSearchTest.xml @@ -17,6 +17,9 @@ <severity value="CRITICAL"/> <group value="catalog"/> <group value="mtf_migrated"/> + <skip> + <issueId value="MC-17181"/> + </skip> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogOnlyTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogOnlyTest.xml index ba52c6d2bc261..f8b0b17c06253 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogOnlyTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInCatalogOnlyTest.xml @@ -17,6 +17,9 @@ <severity value="CRITICAL"/> <group value="catalog"/> <group value="mtf_migrated"/> + <skip> + <issueId value="MC-17181"/> + </skip> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInSearchOnlyTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInSearchOnlyTest.xml index cb5c24839e387..ee2a2514c9c7e 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInSearchOnlyTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockVisibleInSearchOnlyTest.xml @@ -17,6 +17,9 @@ <severity value="CRITICAL"/> <group value="catalog"/> <group value="mtf_migrated"/> + <skip> + <issueId value="MC-17181"/> + </skip> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockWithCustomOptionsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockWithCustomOptionsTest.xml index 318ab6555235e..7921ed6c3e459 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockWithCustomOptionsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceInStockWithCustomOptionsTest.xml @@ -17,6 +17,9 @@ <severity value="CRITICAL"/> <group value="catalog"/> <group value="mtf_migrated"/> + <skip> + <issueId value="MC-17181"/> + </skip> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> @@ -150,6 +153,7 @@ <fillField selector="{{StorefrontProductPageSection.qtyInput}}" userInput="1" stepKey="fillProductQuantity"/> <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="clickOnAddToCartButton"/> <waitForPageLoad stepKey="waitForProductToAddInCart"/> + <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessage"/> <seeElement selector="{{StorefrontProductPageSection.successMsg}}" stepKey="seeYouAddedSimpleprod4ToYourShoppingCartSuccessSaveMessage"/> <seeElement selector="{{StorefrontMinicartSection.quantity(1)}}" stepKey="seeAddedProductQuantityInCart"/> <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="clickOnMiniCart"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceOutOfStockTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceOutOfStockTest.xml index 54ed753b80a1c..0125b4c1e713d 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceOutOfStockTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateSimpleProductWithRegularPriceOutOfStockTest.xml @@ -17,6 +17,9 @@ <severity value="CRITICAL"/> <group value="catalog"/> <group value="mtf_migrated"/> + <skip> + <issueId value="MC-17181"/> + </skip> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceInStockVisibleInCategoryOnlyTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceInStockVisibleInCategoryOnlyTest.xml index 9bdc93e61e499..d7ceefb03d3b1 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceInStockVisibleInCategoryOnlyTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceInStockVisibleInCategoryOnlyTest.xml @@ -17,6 +17,9 @@ <severity value="CRITICAL"/> <group value="catalog"/> <group value="mtf_migrated"/> + <skip> + <issueId value="MC-17181"/> + </skip> </annotations> <before> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceInStockWithCustomOptionsVisibleInSearchOnlyTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceInStockWithCustomOptionsVisibleInSearchOnlyTest.xml index 34d85e7b46850..ef02b73ea16d4 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceInStockWithCustomOptionsVisibleInSearchOnlyTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceInStockWithCustomOptionsVisibleInSearchOnlyTest.xml @@ -17,6 +17,9 @@ <severity value="CRITICAL"/> <group value="catalog"/> <group value="mtf_migrated"/> + <skip> + <issueId value="MC-17181"/> + </skip> </annotations> <before> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceOutOfStockVisibleInCategoryOnlyTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceOutOfStockVisibleInCategoryOnlyTest.xml index e64022b311614..b5ee6d8112b50 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceOutOfStockVisibleInCategoryOnlyTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithRegularPriceOutOfStockVisibleInCategoryOnlyTest.xml @@ -17,6 +17,9 @@ <severity value="CRITICAL"/> <group value="catalog"/> <group value="mtf_migrated"/> + <skip> + <issueId value="MC-17181"/> + </skip> </annotations> <before> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithSpecialPriceInStockVisibleInCategoryAndSearchTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithSpecialPriceInStockVisibleInCategoryAndSearchTest.xml index 9b6a56d6f81d8..64342d18198c5 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithSpecialPriceInStockVisibleInCategoryAndSearchTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithSpecialPriceInStockVisibleInCategoryAndSearchTest.xml @@ -17,6 +17,9 @@ <severity value="CRITICAL"/> <group value="catalog"/> <group value="mtf_migrated"/> + <skip> + <issueId value="MC-17181"/> + </skip> </annotations> <before> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithSpecialPriceOutOfStockVisibleInCategoryAndSearchTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithSpecialPriceOutOfStockVisibleInCategoryAndSearchTest.xml index 920a0a494bae5..7378a7f111c30 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithSpecialPriceOutOfStockVisibleInCategoryAndSearchTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithSpecialPriceOutOfStockVisibleInCategoryAndSearchTest.xml @@ -17,6 +17,9 @@ <severity value="CRITICAL"/> <group value="catalog"/> <group value="mtf_migrated"/> + <skip> + <issueId value="MC-17181"/> + </skip> </annotations> <before> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithTierPriceInStockVisibleInCategoryAndSearchTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithTierPriceInStockVisibleInCategoryAndSearchTest.xml index d4ec5e410d9ff..ddb4002a5dba3 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithTierPriceInStockVisibleInCategoryAndSearchTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithTierPriceInStockVisibleInCategoryAndSearchTest.xml @@ -17,6 +17,9 @@ <severity value="CRITICAL"/> <group value="catalog"/> <group value="mtf_migrated"/> + <skip> + <issueId value="MC-17181"/> + </skip> </annotations> <before> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithTierPriceInStockVisibleInCategoryOnlyTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithTierPriceInStockVisibleInCategoryOnlyTest.xml index 717d710b4a288..9aff504f58f81 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithTierPriceInStockVisibleInCategoryOnlyTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithTierPriceInStockVisibleInCategoryOnlyTest.xml @@ -17,6 +17,9 @@ <severity value="CRITICAL"/> <group value="catalog"/> <group value="mtf_migrated"/> + <skip> + <issueId value="MC-17181"/> + </skip> </annotations> <before> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithTierPriceOutOfStockVisibleInCategoryAndSearchTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithTierPriceOutOfStockVisibleInCategoryAndSearchTest.xml index 703a4e24cdca9..41796bcc2c3a9 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithTierPriceOutOfStockVisibleInCategoryAndSearchTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/AdminUpdateVirtualProductWithTierPriceOutOfStockVisibleInCategoryAndSearchTest.xml @@ -17,6 +17,9 @@ <severity value="CRITICAL"/> <group value="catalog"/> <group value="mtf_migrated"/> + <skip> + <issueId value="MC-17181"/> + </skip> </annotations> <before> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml index 7c0de6da18caf..e3924099d2f27 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml @@ -17,6 +17,9 @@ <description value="User browses catalog, searches for product, adds product to cart, adds product to wishlist, compares products, uses coupon code and checks out."/> <severity value="CRITICAL"/> <testCaseId value="MAGETWO-87435"/> + <skip> + <issueId value="MC-16684"/> + </skip> </annotations> <before> <resetCookie userInput="PHPSESSID" stepKey="resetCookieForCart"/> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductCustomOptionsDifferentStoreViewsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductCustomOptionsDifferentStoreViewsTest.xml index fb95fc3f57bca..a0670bdee54c7 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductCustomOptionsDifferentStoreViewsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductCustomOptionsDifferentStoreViewsTest.xml @@ -15,7 +15,7 @@ <title value="Admin should be able to sell products with different variants of their own"/> <description value="Admin should be able to sell products with different variants of their own"/> <severity value="CRITICAL"/> - <testCaseId value="MAGETWO-58184"/> + <testCaseId value="MC-16476"/> <group value="product"/> </annotations> diff --git a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsTest.xml b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsTest.xml index a3bce2d4fe2f2..12a465188aa85 100644 --- a/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsTest.xml +++ b/app/code/Magento/Catalog/Test/Mftf/Test/StorefrontPurchaseProductWithCustomOptionsTest.xml @@ -12,10 +12,10 @@ <annotations> <features value="Catalog"/> <stories value="Purchase a product with Custom Options of different types"/> - <title value="Admin should be able to sell products with different variants of their own"/> - <description value="Admin should be able to sell products with different variants of their own"/> + <title value="Admin should be able to sell products with custom options of different types"/> + <description value="Admin should be able to sell products with custom options of different types"/> <severity value="CRITICAL"/> - <testCaseId value="MAGETWO-61717"/> + <testCaseId value="MC-16462"/> <group value="Catalog"/> </annotations> <before> diff --git a/app/code/Magento/Catalog/Test/Unit/Block/Adminhtml/Product/Helper/Form/Gallery/ContentTest.php b/app/code/Magento/Catalog/Test/Unit/Block/Adminhtml/Product/Helper/Form/Gallery/ContentTest.php index 249c32ff276c3..9a2199859a1df 100644 --- a/app/code/Magento/Catalog/Test/Unit/Block/Adminhtml/Product/Helper/Form/Gallery/ContentTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Block/Adminhtml/Product/Helper/Form/Gallery/ContentTest.php @@ -9,6 +9,7 @@ use Magento\Catalog\Model\Entity\Attribute; use Magento\Catalog\Model\Product; use Magento\Framework\Phrase; +use Magento\MediaStorage\Helper\File\Storage\Database; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -50,6 +51,11 @@ class ContentTest extends \PHPUnit\Framework\TestCase */ protected $imageHelper; + /** + * @var \Magento\MediaStorage\Helper\File\Storage\Database|\PHPUnit_Framework_MockObject_MockObject + */ + protected $databaseMock; + /** * @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager */ @@ -71,13 +77,18 @@ public function setUp() ->disableOriginalConstructor() ->getMock(); + $this->databaseMock = $this->getMockBuilder(Database::class) + ->disableOriginalConstructor() + ->getMock(); + $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); $this->content = $this->objectManager->getObject( \Magento\Catalog\Block\Adminhtml\Product\Helper\Form\Gallery\Content::class, [ 'mediaConfig' => $this->mediaConfigMock, 'jsonEncoder' => $this->jsonEncoderMock, - 'filesystem' => $this->fileSystemMock + 'filesystem' => $this->fileSystemMock, + 'fileStorageDatabase' => $this->databaseMock ] ); } @@ -143,6 +154,13 @@ public function testGetImagesJson() $this->readMock->expects($this->any())->method('stat')->willReturnMap($sizeMap); $this->jsonEncoderMock->expects($this->once())->method('encode')->willReturnCallback('json_encode'); + $this->readMock->expects($this->any()) + ->method('isFile') + ->will($this->returnValue(true)); + $this->databaseMock->expects($this->any()) + ->method('checkDbUsage') + ->will($this->returnValue(false)); + $this->assertSame(json_encode($imagesResult), $this->content->getImagesJson()); } @@ -210,6 +228,14 @@ public function testGetImagesJsonWithException() $this->fileSystemMock->expects($this->any())->method('getDirectoryRead')->willReturn($this->readMock); $this->mediaConfigMock->expects($this->any())->method('getMediaUrl'); $this->mediaConfigMock->expects($this->any())->method('getMediaPath'); + + $this->readMock->expects($this->any()) + ->method('isFile') + ->will($this->returnValue(true)); + $this->databaseMock->expects($this->any()) + ->method('checkDbUsage') + ->will($this->returnValue(false)); + $this->readMock->expects($this->any())->method('stat')->willReturnOnConsecutiveCalls( $this->throwException( new \Magento\Framework\Exception\FileSystemException(new Phrase('test')) @@ -365,4 +391,52 @@ private function getMediaAttribute(string $label, string $attributeCode) return $mediaAttribute; } + + /** + * Test GetImagesJson() calls MediaStorage functions to obtain image from DB prior to stat call + * + * @return void + */ + public function testGetImagesJsonMediaStorageMode() + { + $images = [ + 'images' => [ + [ + 'value_id' => '0', + 'file' => 'file_1.jpg', + 'media_type' => 'image', + 'position' => '0' + ] + ] + ]; + + $mediaPath = [ + ['file_1.jpg', 'catalog/product/image_1.jpg'] + ]; + + $this->content->setElement($this->galleryMock); + + $this->galleryMock->expects($this->once()) + ->method('getImages') + ->willReturn($images); + $this->fileSystemMock->expects($this->once()) + ->method('getDirectoryRead') + ->willReturn($this->readMock); + $this->mediaConfigMock->expects($this->any()) + ->method('getMediaPath') + ->willReturnMap($mediaPath); + + $this->readMock->expects($this->any()) + ->method('isFile') + ->will($this->returnValue(false)); + $this->databaseMock->expects($this->any()) + ->method('checkDbUsage') + ->will($this->returnValue(true)); + + $this->databaseMock->expects($this->once()) + ->method('saveFileToFilesystem') + ->with('catalog/product/image_1.jpg'); + + $this->content->getImagesJson(); + } } diff --git a/app/code/Magento/Catalog/Test/Unit/Block/Product/ViewTest.php b/app/code/Magento/Catalog/Test/Unit/Block/Product/ViewTest.php index af208c016c7e3..6563bdeb149e1 100644 --- a/app/code/Magento/Catalog/Test/Unit/Block/Product/ViewTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Block/Product/ViewTest.php @@ -8,6 +8,9 @@ namespace Magento\Catalog\Test\Unit\Block\Product; +/** + * Class ViewTest + */ class ViewTest extends \PHPUnit\Framework\TestCase { /** @@ -25,6 +28,9 @@ class ViewTest extends \PHPUnit\Framework\TestCase */ protected $registryMock; + /** + * @inheritDoc + */ protected function setUp() { $helper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); @@ -36,6 +42,9 @@ protected function setUp() ); } + /** + * @return void + */ public function testShouldRenderQuantity() { $productMock = $this->createMock(\Magento\Catalog\Model\Product::class); @@ -61,28 +70,26 @@ public function testShouldRenderQuantity() $this->assertEquals(false, $this->view->shouldRenderQuantity()); } + /** + * @return void + */ public function testGetIdentities() { $productTags = ['cat_p_1']; $product = $this->createMock(\Magento\Catalog\Model\Product::class); - $category = $this->createMock(\Magento\Catalog\Model\Category::class); $product->expects($this->once()) ->method('getIdentities') ->will($this->returnValue($productTags)); - $category->expects($this->once()) - ->method('getId') - ->will($this->returnValue(1)); $this->registryMock->expects($this->any()) ->method('registry') ->will( $this->returnValueMap( [ ['product', $product], - ['current_category', $category], ] ) ); - $this->assertEquals(['cat_p_1', 'cat_c_1'], $this->view->getIdentities()); + $this->assertEquals($productTags, $this->view->getIdentities()); } } diff --git a/app/code/Magento/Catalog/Test/Unit/Model/Category/FileInfoTest.php b/app/code/Magento/Catalog/Test/Unit/Model/Category/FileInfoTest.php index 8ca823127e66c..6c6a69ec39c85 100644 --- a/app/code/Magento/Catalog/Test/Unit/Model/Category/FileInfoTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Model/Category/FileInfoTest.php @@ -9,31 +9,41 @@ use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\File\Mime; use Magento\Framework\Filesystem; -use Magento\Framework\Filesystem\Directory\WriteInterface; use Magento\Framework\Filesystem\Directory\ReadInterface; +use Magento\Framework\Filesystem\Directory\WriteInterface; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; -class FileInfoTest extends \PHPUnit\Framework\TestCase +/** + * Test for Magento\Catalog\Model\Category\FileInfo class. + */ +class FileInfoTest extends TestCase { /** - * @var Filesystem|\PHPUnit_Framework_MockObject_MockObject + * @var Filesystem|MockObject */ private $filesystem; /** - * @var Mime|\PHPUnit_Framework_MockObject_MockObject + * @var Mime|MockObject */ private $mime; /** - * @var WriteInterface|\PHPUnit_Framework_MockObject_MockObject + * @var WriteInterface|MockObject */ private $mediaDirectory; /** - * @var ReadInterface|\PHPUnit_Framework_MockObject_MockObject + * @var ReadInterface|MockObject */ private $baseDirectory; + /** + * @var ReadInterface|MockObject + */ + private $pubDirectory; + /** * @var FileInfo */ @@ -44,30 +54,43 @@ protected function setUp() $this->mediaDirectory = $this->getMockBuilder(WriteInterface::class) ->getMockForAbstractClass(); - $this->baseDirectory = $this->getMockBuilder(ReadInterface::class) + $this->baseDirectory = $baseDirectory = $this->getMockBuilder(ReadInterface::class) + ->getMockForAbstractClass(); + + $this->pubDirectory = $pubDirectory = $this->getMockBuilder(ReadInterface::class) ->getMockForAbstractClass(); $this->filesystem = $this->getMockBuilder(Filesystem::class) ->disableOriginalConstructor() ->getMock(); - $this->filesystem->expects($this->any()) - ->method('getDirectoryWrite') + + $this->filesystem->method('getDirectoryWrite') ->with(DirectoryList::MEDIA) ->willReturn($this->mediaDirectory); - $this->filesystem->expects($this->any()) - ->method('getDirectoryRead') - ->with(DirectoryList::ROOT) - ->willReturn($this->baseDirectory); + $this->filesystem->method('getDirectoryRead') + ->willReturnCallback( + function ($arg) use ($baseDirectory, $pubDirectory) { + if ($arg === DirectoryList::PUB) { + return $pubDirectory; + } + return $baseDirectory; + } + ); $this->mime = $this->getMockBuilder(Mime::class) ->disableOriginalConstructor() ->getMock(); - $this->baseDirectory->expects($this->any()) - ->method('getAbsolutePath') - ->with(null) - ->willReturn('/a/b/c'); + $this->baseDirectory->method('getAbsolutePath') + ->willReturn('/a/b/c/'); + + $this->baseDirectory->method('getRelativePath') + ->with('/a/b/c/pub/') + ->willReturn('pub/'); + + $this->pubDirectory->method('getAbsolutePath') + ->willReturn('/a/b/c/pub/'); $this->model = new FileInfo( $this->filesystem, @@ -85,12 +108,12 @@ public function testGetMimeType() $this->mediaDirectory->expects($this->at(0)) ->method('getAbsolutePath') ->with(null) - ->willReturn('/a/b/c/pub/media'); + ->willReturn('/a/b/c/pub/media/'); $this->mediaDirectory->expects($this->at(1)) ->method('getAbsolutePath') ->with(null) - ->willReturn('/a/b/c/pub/media'); + ->willReturn('/a/b/c/pub/media/'); $this->mediaDirectory->expects($this->at(2)) ->method('getAbsolutePath') @@ -113,13 +136,11 @@ public function testGetStat() $expected = ['size' => 1]; - $this->mediaDirectory->expects($this->any()) - ->method('getAbsolutePath') + $this->mediaDirectory->method('getAbsolutePath') ->with(null) - ->willReturn('/a/b/c/pub/media'); + ->willReturn('/a/b/c/pub/media/'); - $this->mediaDirectory->expects($this->once()) - ->method('stat') + $this->mediaDirectory->method('stat') ->with($mediaPath . $fileName) ->willReturn($expected); @@ -130,22 +151,52 @@ public function testGetStat() $this->assertEquals(1, $result['size']); } - public function testIsExist() + /** + * @param $fileName + * @param $fileMediaPath + * @dataProvider isExistProvider + */ + public function testIsExist($fileName, $fileMediaPath) { - $mediaPath = '/catalog/category'; + $this->mediaDirectory->method('getAbsolutePath') + ->willReturn('/a/b/c/pub/media/'); - $fileName = '/filename.ext1'; - - $this->mediaDirectory->expects($this->any()) - ->method('getAbsolutePath') - ->with(null) - ->willReturn('/a/b/c/pub/media'); - - $this->mediaDirectory->expects($this->once()) - ->method('isExist') - ->with($mediaPath . $fileName) + $this->mediaDirectory->method('isExist') + ->with($fileMediaPath) ->willReturn(true); $this->assertTrue($this->model->isExist($fileName)); } + + public function isExistProvider() + { + return [ + ['/filename.ext1', '/catalog/category/filename.ext1'], + ['/pub/media/filename.ext1', 'filename.ext1'], + ['/media/filename.ext1', 'filename.ext1'] + ]; + } + + /** + * @param $fileName + * @param $expected + * @dataProvider isBeginsWithMediaDirectoryPathProvider + */ + public function testIsBeginsWithMediaDirectoryPath($fileName, $expected) + { + $this->mediaDirectory->method('getAbsolutePath') + ->willReturn('/a/b/c/pub/media/'); + + $this->assertEquals($expected, $this->model->isBeginsWithMediaDirectoryPath($fileName)); + } + + public function isBeginsWithMediaDirectoryPathProvider() + { + return [ + ['/pub/media/test/filename.ext1', true], + ['/media/test/filename.ext1', true], + ['/test/filename.ext1', false], + ['test2/filename.ext1', false] + ]; + } } diff --git a/app/code/Magento/Catalog/Test/Unit/Pricing/Price/TierPriceTest.php b/app/code/Magento/Catalog/Test/Unit/Pricing/Price/TierPriceTest.php index 7804104490958..ade8829b278ae 100644 --- a/app/code/Magento/Catalog/Test/Unit/Pricing/Price/TierPriceTest.php +++ b/app/code/Magento/Catalog/Test/Unit/Pricing/Price/TierPriceTest.php @@ -393,4 +393,40 @@ public function dataProviderGetSavePercent() ['basePrice' => '20.80', 'tierPrice' => '18.72', 'savedPercent' => '10'] ]; } + + /** + * @param null|string|float $quantity + * @param float $expectedValue + * @dataProvider getQuantityDataProvider + */ + public function testGetQuantity($quantity, $expectedValue) + { + $tierPrice = new TierPrice( + $this->product, + $quantity, + $this->calculator, + $this->priceCurrencyMock, + $this->session, + $this->groupManagement, + $this->customerGroupRetriever + ); + + $this->assertEquals($expectedValue, $tierPrice->getQuantity()); + } + + /** + * @return array + */ + public function getQuantityDataProvider() + { + return [ + [null, 1], + ['one', 1], + ['', 1], + [4, 4], + [4.5, 4.5], + ['0.7', 0.7], + ['0.0000000', 1] + ]; + } } diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Attributes.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Attributes.php index 683a96133ad30..a6b9856a4a0ed 100644 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Attributes.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/Form/Modifier/Attributes.php @@ -67,7 +67,8 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc + * * @since 101.0.0 */ public function modifyData(array $data) @@ -76,6 +77,8 @@ public function modifyData(array $data) } /** + * Check if can add attributes on product form. + * * @return boolean */ private function canAddAttributes() @@ -89,7 +92,8 @@ private function canAddAttributes() } /** - * {@inheritdoc} + * @inheritdoc + * * @since 101.0.0 */ public function modifyMeta(array $meta) @@ -111,6 +115,8 @@ public function modifyMeta(array $meta) } /** + * Modify meta customize attribute modal. + * * @param array $meta * @return array */ @@ -207,6 +213,8 @@ private function customizeAddAttributeModal(array $meta) } /** + * Modify meta to customize create attribute modal. + * * @param array $meta * @return array */ @@ -289,6 +297,8 @@ private function customizeCreateAttributeModal(array $meta) } /** + * Modify meta to customize attribute grid. + * * @param array $meta * @return array */ @@ -309,7 +319,7 @@ private function customizeAttributesGrid(array $meta) 'immediateUpdateBySelection' => true, 'behaviourType' => 'edit', 'externalFilterMode' => true, - 'dataLinks' => ['imports' => false, 'exports' => true], + 'dataLinks' => ['imports' => false, 'exports' => false], 'formProvider' => 'ns = ${ $.namespace }, index = product_form', 'groupCode' => static::GROUP_CODE, 'groupName' => static::GROUP_NAME, diff --git a/app/code/Magento/Catalog/Ui/DataProvider/Product/ProductRenderCollectorComposite.php b/app/code/Magento/Catalog/Ui/DataProvider/Product/ProductRenderCollectorComposite.php index 359b1a1a948f8..7edf25ff20cc1 100644 --- a/app/code/Magento/Catalog/Ui/DataProvider/Product/ProductRenderCollectorComposite.php +++ b/app/code/Magento/Catalog/Ui/DataProvider/Product/ProductRenderCollectorComposite.php @@ -9,8 +9,7 @@ use Magento\Catalog\Api\Data\ProductRenderInterface; /** - * Composite, which holds collectors, that collect enought information for - * product render + * Composite, which holds collectors, that collect enough information for product render */ class ProductRenderCollectorComposite implements ProductRenderCollectorInterface { diff --git a/app/code/Magento/Catalog/etc/acl.xml b/app/code/Magento/Catalog/etc/acl.xml index 358a798fc7579..4d4b7bdc672d1 100644 --- a/app/code/Magento/Catalog/etc/acl.xml +++ b/app/code/Magento/Catalog/etc/acl.xml @@ -11,7 +11,9 @@ <resource id="Magento_Backend::admin"> <resource id="Magento_Catalog::catalog" title="Catalog" translate="title" sortOrder="30"> <resource id="Magento_Catalog::catalog_inventory" title="Inventory" translate="title" sortOrder="10"> - <resource id="Magento_Catalog::products" title="Products" translate="title" sortOrder="10" /> + <resource id="Magento_Catalog::products" title="Products" translate="title" sortOrder="10"> + <resource id="Magento_Catalog::update_attributes" title="Update Attributes" translate="title" /> + </resource> <resource id="Magento_Catalog::categories" title="Categories" translate="title" sortOrder="20" /> </resource> </resource> @@ -23,7 +25,6 @@ </resource> <resource id="Magento_Backend::stores_attributes"> <resource id="Magento_Catalog::attributes_attributes" title="Product" translate="title" sortOrder="30" /> - <resource id="Magento_Catalog::update_attributes" title="Update Attributes" translate="title" sortOrder="35" /> <resource id="Magento_Catalog::sets" title="Attribute Set" translate="title" sortOrder="40"/> </resource> </resource> diff --git a/app/code/Magento/Catalog/etc/adminhtml/system.xml b/app/code/Magento/Catalog/etc/adminhtml/system.xml index 1d563244f1432..a6dd6cbd2e9a1 100644 --- a/app/code/Magento/Catalog/etc/adminhtml/system.xml +++ b/app/code/Magento/Catalog/etc/adminhtml/system.xml @@ -114,14 +114,14 @@ </group> <group id="seo" translate="label" type="text" sortOrder="500" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Search Engine Optimization</label> - <field id="title_separator" translate="label" type="text" sortOrder="6" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> + <field id="title_separator" translate="label" type="text" sortOrder="7" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Page Title Separator</label> </field> - <field id="category_canonical_tag" translate="label" type="select" sortOrder="7" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> + <field id="category_canonical_tag" translate="label" type="select" sortOrder="8" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Use Canonical Link Meta Tag For Categories</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> - <field id="product_canonical_tag" translate="label" type="select" sortOrder="8" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> + <field id="product_canonical_tag" translate="label" type="select" sortOrder="9" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1"> <label>Use Canonical Link Meta Tag For Products</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> diff --git a/app/code/Magento/Catalog/etc/crontab.xml b/app/code/Magento/Catalog/etc/crontab.xml index c48f1307a09f5..74c60323530e1 100644 --- a/app/code/Magento/Catalog/etc/crontab.xml +++ b/app/code/Magento/Catalog/etc/crontab.xml @@ -16,7 +16,7 @@ <job name="catalog_product_outdated_price_values_cleanup" instance="Magento\Catalog\Cron\DeleteOutdatedPriceValues" method="execute"> <schedule>* * * * *</schedule> </job> - <job name="catalog_product_frontend_actions_flush" instance="Magento\Catalog\Cron\DeleteOutdatedPriceValues" method="execute"> + <job name="catalog_product_frontend_actions_flush" instance="Magento\Catalog\Cron\FrontendActionsFlush" method="execute"> <schedule>* * * * *</schedule> </job> <job name="catalog_product_attribute_value_synchronize" instance="Magento\Catalog\Cron\SynchronizeWebsiteAttributes" method="execute"> diff --git a/app/code/Magento/Catalog/etc/db_schema.xml b/app/code/Magento/Catalog/etc/db_schema.xml index 17e3dddc41c3b..6fef4ca6e9128 100644 --- a/app/code/Magento/Catalog/etc/db_schema.xml +++ b/app/code/Magento/Catalog/etc/db_schema.xml @@ -77,7 +77,7 @@ default="0" comment="Store ID"/> <column xsi:type="int" name="entity_id" padding="10" unsigned="true" nullable="false" identity="false" default="0" comment="Entity ID"/> - <column xsi:type="decimal" name="value" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="value" scale="6" precision="20" unsigned="false" nullable="true" comment="Value"/> <constraint xsi:type="primary" referenceId="PRIMARY"> <column name="value_id"/> @@ -325,7 +325,7 @@ default="0" comment="Store ID"/> <column xsi:type="int" name="entity_id" padding="10" unsigned="true" nullable="false" identity="false" default="0" comment="Entity ID"/> - <column xsi:type="decimal" name="value" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="value" scale="6" precision="20" unsigned="false" nullable="true" comment="Value"/> <constraint xsi:type="primary" referenceId="PRIMARY"> <column name="value_id"/> @@ -658,7 +658,7 @@ identity="false" comment="Product Link Attribute ID"/> <column xsi:type="int" name="link_id" padding="10" unsigned="true" nullable="false" identity="false" comment="Link ID"/> - <column xsi:type="decimal" name="value" scale="4" precision="12" unsigned="false" nullable="false" default="0" + <column xsi:type="decimal" name="value" scale="6" precision="20" unsigned="false" nullable="false" default="0" comment="Value"/> <constraint xsi:type="primary" referenceId="PRIMARY"> <column name="value_id"/> @@ -745,7 +745,7 @@ default="0" comment="Customer Group ID"/> <column xsi:type="decimal" name="qty" scale="4" precision="12" unsigned="false" nullable="false" default="1" comment="QTY"/> - <column xsi:type="decimal" name="value" scale="4" precision="12" unsigned="false" nullable="false" default="0" + <column xsi:type="decimal" name="value" scale="6" precision="20" unsigned="false" nullable="false" default="0" comment="Value"/> <column xsi:type="smallint" name="website_id" padding="5" unsigned="true" nullable="false" identity="false" comment="Website ID"/> @@ -877,7 +877,7 @@ default="0" comment="Option ID"/> <column xsi:type="smallint" name="store_id" padding="5" unsigned="true" nullable="false" identity="false" default="0" comment="Store ID"/> - <column xsi:type="decimal" name="price" scale="4" precision="12" unsigned="false" nullable="false" default="0" + <column xsi:type="decimal" name="price" scale="6" precision="20" unsigned="false" nullable="false" default="0" comment="Price"/> <column xsi:type="varchar" name="price_type" nullable="false" length="7" default="fixed" comment="Price Type"/> <constraint xsi:type="primary" referenceId="PRIMARY"> @@ -950,7 +950,7 @@ default="0" comment="Option Type ID"/> <column xsi:type="smallint" name="store_id" padding="5" unsigned="true" nullable="false" identity="false" default="0" comment="Store ID"/> - <column xsi:type="decimal" name="price" scale="4" precision="12" unsigned="false" nullable="false" default="0" + <column xsi:type="decimal" name="price" scale="6" precision="20" unsigned="false" nullable="false" default="0" comment="Price"/> <column xsi:type="varchar" name="price_type" nullable="false" length="7" default="fixed" comment="Price Type"/> <constraint xsi:type="primary" referenceId="PRIMARY"> @@ -1142,15 +1142,15 @@ comment="Website ID"/> <column xsi:type="smallint" name="tax_class_id" padding="5" unsigned="true" nullable="true" identity="false" default="0" comment="Tax Class ID"/> - <column xsi:type="decimal" name="price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="price" scale="6" precision="20" unsigned="false" nullable="true" comment="Price"/> - <column xsi:type="decimal" name="final_price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="final_price" scale="6" precision="20" unsigned="false" nullable="true" comment="Final Price"/> - <column xsi:type="decimal" name="min_price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="min_price" scale="6" precision="20" unsigned="false" nullable="true" comment="Min Price"/> - <column xsi:type="decimal" name="max_price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="max_price" scale="6" precision="20" unsigned="false" nullable="true" comment="Max Price"/> - <column xsi:type="decimal" name="tier_price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="tier_price" scale="6" precision="20" unsigned="false" nullable="true" comment="Tier Price"/> <constraint xsi:type="primary" referenceId="PRIMARY"> <column name="entity_id"/> @@ -1177,7 +1177,7 @@ comment="Customer Group ID"/> <column xsi:type="smallint" name="website_id" padding="5" unsigned="true" nullable="false" identity="false" comment="Website ID"/> - <column xsi:type="decimal" name="min_price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="min_price" scale="6" precision="20" unsigned="false" nullable="true" comment="Min Price"/> <constraint xsi:type="primary" referenceId="PRIMARY"> <column name="entity_id"/> @@ -1227,9 +1227,9 @@ default="0" comment="Customer Group ID"/> <column xsi:type="smallint" name="website_id" padding="5" unsigned="true" nullable="false" identity="false" comment="Website ID"/> - <column xsi:type="decimal" name="price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="price" scale="6" precision="20" unsigned="false" nullable="true" comment="Price"/> - <column xsi:type="decimal" name="tier_price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="tier_price" scale="6" precision="20" unsigned="false" nullable="true" comment="Tier Price"/> <constraint xsi:type="primary" referenceId="PRIMARY"> <column name="parent_id"/> @@ -1248,9 +1248,9 @@ default="0" comment="Customer Group ID"/> <column xsi:type="smallint" name="website_id" padding="5" unsigned="true" nullable="false" identity="false" comment="Website ID"/> - <column xsi:type="decimal" name="price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="price" scale="6" precision="20" unsigned="false" nullable="true" comment="Price"/> - <column xsi:type="decimal" name="tier_price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="tier_price" scale="6" precision="20" unsigned="false" nullable="true" comment="Tier Price"/> <constraint xsi:type="primary" referenceId="PRIMARY"> <column name="parent_id"/> @@ -1267,11 +1267,11 @@ default="0" comment="Customer Group ID"/> <column xsi:type="smallint" name="website_id" padding="5" unsigned="true" nullable="false" identity="false" comment="Website ID"/> - <column xsi:type="decimal" name="min_price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="min_price" scale="6" precision="20" unsigned="false" nullable="true" comment="Min Price"/> - <column xsi:type="decimal" name="max_price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="max_price" scale="6" precision="20" unsigned="false" nullable="true" comment="Max Price"/> - <column xsi:type="decimal" name="tier_price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="tier_price" scale="6" precision="20" unsigned="false" nullable="true" comment="Tier Price"/> <constraint xsi:type="primary" referenceId="PRIMARY"> <column name="entity_id"/> @@ -1287,11 +1287,11 @@ default="0" comment="Customer Group ID"/> <column xsi:type="smallint" name="website_id" padding="5" unsigned="true" nullable="false" identity="false" comment="Website ID"/> - <column xsi:type="decimal" name="min_price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="min_price" scale="6" precision="20" unsigned="false" nullable="true" comment="Min Price"/> - <column xsi:type="decimal" name="max_price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="max_price" scale="6" precision="20" unsigned="false" nullable="true" comment="Max Price"/> - <column xsi:type="decimal" name="tier_price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="tier_price" scale="6" precision="20" unsigned="false" nullable="true" comment="Tier Price"/> <constraint xsi:type="primary" referenceId="PRIMARY"> <column name="entity_id"/> @@ -1309,17 +1309,17 @@ comment="Website ID"/> <column xsi:type="smallint" name="tax_class_id" padding="5" unsigned="true" nullable="true" identity="false" default="0" comment="Tax Class ID"/> - <column xsi:type="decimal" name="orig_price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="orig_price" scale="6" precision="20" unsigned="false" nullable="true" comment="Original Price"/> - <column xsi:type="decimal" name="price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="price" scale="6" precision="20" unsigned="false" nullable="true" comment="Price"/> - <column xsi:type="decimal" name="min_price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="min_price" scale="6" precision="20" unsigned="false" nullable="true" comment="Min Price"/> - <column xsi:type="decimal" name="max_price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="max_price" scale="6" precision="20" unsigned="false" nullable="true" comment="Max Price"/> - <column xsi:type="decimal" name="tier_price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="tier_price" scale="6" precision="20" unsigned="false" nullable="true" comment="Tier Price"/> - <column xsi:type="decimal" name="base_tier" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="base_tier" scale="6" precision="20" unsigned="false" nullable="true" comment="Base Tier"/> <constraint xsi:type="primary" referenceId="PRIMARY"> <column name="entity_id"/> @@ -1337,17 +1337,17 @@ comment="Website ID"/> <column xsi:type="smallint" name="tax_class_id" padding="5" unsigned="true" nullable="true" identity="false" default="0" comment="Tax Class ID"/> - <column xsi:type="decimal" name="orig_price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="orig_price" scale="6" precision="20" unsigned="false" nullable="true" comment="Original Price"/> - <column xsi:type="decimal" name="price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="price" scale="6" precision="20" unsigned="false" nullable="true" comment="Price"/> - <column xsi:type="decimal" name="min_price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="min_price" scale="6" precision="20" unsigned="false" nullable="true" comment="Min Price"/> - <column xsi:type="decimal" name="max_price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="max_price" scale="6" precision="20" unsigned="false" nullable="true" comment="Max Price"/> - <column xsi:type="decimal" name="tier_price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="tier_price" scale="6" precision="20" unsigned="false" nullable="true" comment="Tier Price"/> - <column xsi:type="decimal" name="base_tier" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="base_tier" scale="6" precision="20" unsigned="false" nullable="true" comment="Base Tier"/> <constraint xsi:type="primary" referenceId="PRIMARY"> <column name="entity_id"/> @@ -1363,11 +1363,11 @@ default="0" comment="Customer Group ID"/> <column xsi:type="smallint" name="website_id" padding="5" unsigned="true" nullable="false" identity="false" comment="Website ID"/> - <column xsi:type="decimal" name="min_price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="min_price" scale="6" precision="20" unsigned="false" nullable="true" comment="Min Price"/> - <column xsi:type="decimal" name="max_price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="max_price" scale="6" precision="20" unsigned="false" nullable="true" comment="Max Price"/> - <column xsi:type="decimal" name="tier_price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="tier_price" scale="6" precision="20" unsigned="false" nullable="true" comment="Tier Price"/> <constraint xsi:type="primary" referenceId="PRIMARY"> <column name="entity_id"/> @@ -1383,11 +1383,11 @@ default="0" comment="Customer Group ID"/> <column xsi:type="smallint" name="website_id" padding="5" unsigned="true" nullable="false" identity="false" comment="Website ID"/> - <column xsi:type="decimal" name="min_price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="min_price" scale="6" precision="20" unsigned="false" nullable="true" comment="Min Price"/> - <column xsi:type="decimal" name="max_price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="max_price" scale="6" precision="20" unsigned="false" nullable="true" comment="Max Price"/> - <column xsi:type="decimal" name="tier_price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="tier_price" scale="6" precision="20" unsigned="false" nullable="true" comment="Tier Price"/> <constraint xsi:type="primary" referenceId="PRIMARY"> <column name="entity_id"/> @@ -1405,11 +1405,11 @@ comment="Website ID"/> <column xsi:type="int" name="option_id" padding="10" unsigned="true" nullable="false" identity="false" default="0" comment="Option ID"/> - <column xsi:type="decimal" name="min_price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="min_price" scale="6" precision="20" unsigned="false" nullable="true" comment="Min Price"/> - <column xsi:type="decimal" name="max_price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="max_price" scale="6" precision="20" unsigned="false" nullable="true" comment="Max Price"/> - <column xsi:type="decimal" name="tier_price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="tier_price" scale="6" precision="20" unsigned="false" nullable="true" comment="Tier Price"/> <constraint xsi:type="primary" referenceId="PRIMARY"> <column name="entity_id"/> @@ -1428,11 +1428,11 @@ comment="Website ID"/> <column xsi:type="int" name="option_id" padding="10" unsigned="true" nullable="false" identity="false" default="0" comment="Option ID"/> - <column xsi:type="decimal" name="min_price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="min_price" scale="6" precision="20" unsigned="false" nullable="true" comment="Min Price"/> - <column xsi:type="decimal" name="max_price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="max_price" scale="6" precision="20" unsigned="false" nullable="true" comment="Max Price"/> - <column xsi:type="decimal" name="tier_price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="tier_price" scale="6" precision="20" unsigned="false" nullable="true" comment="Tier Price"/> <constraint xsi:type="primary" referenceId="PRIMARY"> <column name="entity_id"/> @@ -1567,15 +1567,15 @@ comment="Website ID"/> <column xsi:type="smallint" name="tax_class_id" padding="5" unsigned="true" nullable="true" identity="false" default="0" comment="Tax Class ID"/> - <column xsi:type="decimal" name="price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="price" scale="6" precision="20" unsigned="false" nullable="true" comment="Price"/> - <column xsi:type="decimal" name="final_price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="final_price" scale="6" precision="20" unsigned="false" nullable="true" comment="Final Price"/> - <column xsi:type="decimal" name="min_price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="min_price" scale="6" precision="20" unsigned="false" nullable="true" comment="Min Price"/> - <column xsi:type="decimal" name="max_price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="max_price" scale="6" precision="20" unsigned="false" nullable="true" comment="Max Price"/> - <column xsi:type="decimal" name="tier_price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="tier_price" scale="6" precision="20" unsigned="false" nullable="true" comment="Tier Price"/> <constraint xsi:type="primary" referenceId="PRIMARY"> <column name="entity_id"/> @@ -1602,15 +1602,15 @@ comment="Website ID"/> <column xsi:type="smallint" name="tax_class_id" padding="5" unsigned="true" nullable="true" identity="false" default="0" comment="Tax Class ID"/> - <column xsi:type="decimal" name="price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="price" scale="6" precision="20" unsigned="false" nullable="true" comment="Price"/> - <column xsi:type="decimal" name="final_price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="final_price" scale="6" precision="20" unsigned="false" nullable="true" comment="Final Price"/> - <column xsi:type="decimal" name="min_price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="min_price" scale="6" precision="20" unsigned="false" nullable="true" comment="Min Price"/> - <column xsi:type="decimal" name="max_price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="max_price" scale="6" precision="20" unsigned="false" nullable="true" comment="Max Price"/> - <column xsi:type="decimal" name="tier_price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="tier_price" scale="6" precision="20" unsigned="false" nullable="true" comment="Tier Price"/> <constraint xsi:type="primary" referenceId="PRIMARY"> <column name="entity_id"/> @@ -1738,15 +1738,15 @@ comment="Website ID"/> <column xsi:type="smallint" name="tax_class_id" padding="5" unsigned="true" nullable="true" identity="false" default="0" comment="Tax Class ID"/> - <column xsi:type="decimal" name="price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="price" scale="6" precision="20" unsigned="false" nullable="true" comment="Price"/> - <column xsi:type="decimal" name="final_price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="final_price" scale="6" precision="20" unsigned="false" nullable="true" comment="Final Price"/> - <column xsi:type="decimal" name="min_price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="min_price" scale="6" precision="20" unsigned="false" nullable="true" comment="Min Price"/> - <column xsi:type="decimal" name="max_price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="max_price" scale="6" precision="20" unsigned="false" nullable="true" comment="Max Price"/> - <column xsi:type="decimal" name="tier_price" scale="4" precision="12" unsigned="false" nullable="true" + <column xsi:type="decimal" name="tier_price" scale="6" precision="20" unsigned="false" nullable="true" comment="Tier Price"/> <constraint xsi:type="primary" referenceId="PRIMARY"> <column name="entity_id"/> diff --git a/app/code/Magento/Catalog/etc/di.xml b/app/code/Magento/Catalog/etc/di.xml index 49447447622f9..e30577a397668 100644 --- a/app/code/Magento/Catalog/etc/di.xml +++ b/app/code/Magento/Catalog/etc/di.xml @@ -1165,4 +1165,12 @@ </argument> </arguments> </type> + <type name="Magento\Catalog\Model\Product\Option\Type\Select"> + <arguments> + <argument name="singleSelectionTypes" xsi:type="array"> + <item name="drop_down" xsi:type="const">Magento\Catalog\Api\Data\ProductCustomOptionInterface::OPTION_TYPE_DROP_DOWN</item> + <item name="radio" xsi:type="const">Magento\Catalog\Api\Data\ProductCustomOptionInterface::OPTION_TYPE_RADIO</item> + </argument> + </arguments> + </type> </config> diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/category/checkboxes/tree.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/category/checkboxes/tree.phtml index ee67acd0ebd46..cea54e883d2aa 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/category/checkboxes/tree.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/category/checkboxes/tree.phtml @@ -4,7 +4,6 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile /** * @var $block \Magento\Catalog\Block\Adminhtml\Category\Tree */ diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/category/edit.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/category/edit.phtml index f58b39a819a0c..c77b66733afc4 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/category/edit.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/category/edit.phtml @@ -5,18 +5,18 @@ */ /** - * Template for \Magento\Catalog\Block\Adminhtml\Category\Edit + * @var $block \Magento\Catalog\Block\Adminhtml\Category\Edit */ ?> <div data-id="information-dialog-category" class="messages" style="display: none;"> <div class="message message-notice"> - <div><?= /* @escapeNotVerified */ __('This operation can take a long time') ?></div> + <div><?= $block->escapeHtml(__('This operation can take a long time')) ?></div> </div> </div> <script type="text/x-magento-init"> { "*": { - "categoryForm": {"refreshUrl": "<?= /* @escapeNotVerified */ $block->getRefreshPathUrl() ?>"} + "categoryForm": {"refreshUrl": "<?= $block->escapeJs($block->escapeUrl($block->getRefreshPathUrl())) ?>"} } } </script> diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/category/edit/assign_products.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/category/edit/assign_products.phtml index 4691a709cadeb..af7aec12a57ed 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/category/edit/assign_products.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/category/edit/assign_products.phtml @@ -16,8 +16,8 @@ $gridJsObjectName = $blockGrid->getJsObjectName(); { "*": { "Magento_Catalog/catalog/category/assign-products": { - "selectedProducts": <?= /* @escapeNotVerified */ $block->getProductsJson() ?>, - "gridJsObjectName": <?= /* @escapeNotVerified */ '"' . $gridJsObjectName . '"' ?: '{}' ?> + "selectedProducts": <?= /* @noEscape */ $block->getProductsJson() ?>, + "gridJsObjectName": <?= /* @noEscape */ '"' . $gridJsObjectName . '"' ?: '{}' ?> } } } diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/category/tree.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/category/tree.phtml index f448edc692ce2..b2d33f0d12b9d 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/category/tree.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/category/tree.phtml @@ -4,27 +4,26 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - +/** @var $block \Magento\Catalog\Block\Adminhtml\Category\Tree */ ?> <div class="categories-side-col"> <div class="sidebar-actions"> - <?php if ($block->getRoot()): ?> + <?php if ($block->getRoot()) :?> <?= $block->getAddRootButtonHtml() ?><br/> <?= $block->getAddSubButtonHtml() ?> <?php endif; ?> </div> <div class="tree-actions"> - <?php if ($block->getRoot()): ?> + <?php if ($block->getRoot()) :?> <?php //echo $block->getCollapseButtonHtml() ?> <?php //echo $block->getExpandButtonHtml() ?> <a href="#" - onclick="tree.collapseTree(); return false;"><?= /* @escapeNotVerified */ __('Collapse All') ?></a> + onclick="tree.collapseTree(); return false;"><?= $block->escapeHtml(__('Collapse All')) ?></a> <span class="separator">|</span> <a href="#" - onclick="tree.expandTree(); return false;"><?= /* @escapeNotVerified */ __('Expand All') ?></a> + onclick="tree.expandTree(); return false;"><?= $block->escapeHtml(_('Expand All')) ?></a> <?php endif; ?> </div> - <?php if ($block->getRoot()): ?> + <?php if ($block->getRoot()) :?> <div class="tree-holder"> <div id="tree-div" class="tree-wrapper"></div> </div> @@ -32,7 +31,7 @@ <div data-id="information-dialog-tree" class="messages" style="display: none;"> <div class="message message-notice"> - <div><?= /* @escapeNotVerified */ __('This operation can take a long time') ?></div> + <div><?= $block->escapeHtml(__('This operation can take a long time')) ?></div> </div> </div> <script> @@ -172,7 +171,7 @@ if (!this.collapsed) { this.collapsed = true; - this.loader.dataUrl = '<?= /* @escapeNotVerified */ $block->getLoadTreeUrl(false) ?>'; + this.loader.dataUrl = '<?= $block->escapeJs($block->escapeUrl($block->getLoadTreeUrl(false))) ?>'; this.request(this.loader.dataUrl, false); } }, @@ -181,7 +180,7 @@ this.expandAll(); if (this.collapsed) { this.collapsed = false; - this.loader.dataUrl = '<?= /* @escapeNotVerified */ $block->getLoadTreeUrl(true) ?>'; + this.loader.dataUrl = '<?= $block->escapeJs($block->escapeUrl($block->getLoadTreeUrl(true))) ?>'; this.request(this.loader.dataUrl, false); } }, @@ -216,7 +215,7 @@ if (tree && switcherParams) { var url; if (switcherParams.useConfirm) { - if (!confirm("<?= /* @escapeNotVerified */ __('Please confirm site switching. All data that hasn\'t been saved will be lost.') ?>")) { + if (!confirm("<?= $block->escapeJs(__('Please confirm site switching. All data that hasn\'t been saved will be lost.')) ?>")) { return false; } } @@ -259,7 +258,7 @@ } }); } else { - var baseUrl = '<?= /* @escapeNotVerified */ $block->getEditUrl() ?>'; + var baseUrl = '<?= $block->escapeJs($block->escapeUrl($block->getEditUrl())) ?>'; var urlExt = switcherParams.scopeParams + 'id/' + tree.currentNodeId + '/'; url = parseSidUrl(baseUrl, urlExt); setLocation(url); @@ -296,7 +295,7 @@ if (scopeParams) { url = url + scopeParams; } - <?php if ($block->isClearEdit()): ?> + <?php if ($block->isClearEdit()) :?> if (selectedNode) { url = url + 'id/' + config.parameters.category_id; } @@ -307,7 +306,7 @@ jQuery(function () { categoryLoader = new Ext.tree.TreeLoader({ - dataUrl: '<?= /* @escapeNotVerified */ $block->getLoadTreeUrl() ?>' + dataUrl: '<?= $block->escapeJs($block->escapeUrl($block->getLoadTreeUrl())) ?>' }); categoryLoader.processResponse = function (response, parent, callback) { @@ -389,26 +388,26 @@ enableDD: true, containerScroll: true, selModel: new Ext.tree.CheckNodeMultiSelectionModel(), - rootVisible: '<?= /* @escapeNotVerified */ $block->getRoot()->getIsVisible() ?>', - useAjax: <?= /* @escapeNotVerified */ $block->getUseAjax() ?>, - switchTreeUrl: '<?= /* @escapeNotVerified */ $block->getSwitchTreeUrl() ?>', - editUrl: '<?= /* @escapeNotVerified */ $block->getEditUrl() ?>', - currentNodeId: <?= /* @escapeNotVerified */ (int)$block->getCategoryId() ?>, - baseUrl: '<?= /* @escapeNotVerified */ $block->getEditUrl() ?>' + rootVisible: '<?= (bool)$block->getRoot()->getIsVisible() ?>', + useAjax: <?= $block->escapeJs($block->getUseAjax()) ?>, + switchTreeUrl: '<?= $block->escapeJs($block->escapeUrl($block->getSwitchTreeUrl())) ?>', + editUrl: '<?= $block->escapeJs($block->escapeUrl($block->getEditUrl())) ?>', + currentNodeId: <?= (int)$block->getCategoryId() ?>, + baseUrl: '<?= $block->escapeJs($block->escapeUrl($block->getEditUrl())) ?>' }; defaultLoadTreeParams = { parameters: { - text: <?= /* @escapeNotVerified */ json_encode(htmlentities($block->getRoot()->getName())) ?>, + text: <?= /* @noEscape */ json_encode(htmlentities($block->getRoot()->getName())) ?>, draggable: false, - allowDrop: <?php if ($block->getRoot()->getIsVisible()): ?>true<?php else : ?>false<?php endif; ?>, + allowDrop: <?php if ($block->getRoot()->getIsVisible()) :?>true<?php else :?>false<?php endif; ?>, id: <?= (int)$block->getRoot()->getId() ?>, expanded: <?= (int)$block->getIsWasExpanded() ?>, store_id: <?= (int)$block->getStore()->getId() ?>, category_id: <?= (int)$block->getCategoryId() ?>, parent: <?= (int)$block->getRequest()->getParam('parent') ?> }, - data: <?= /* @escapeNotVerified */ $block->getTreeJson() ?> + data: <?= /* @noEscape */ $block->getTreeJson() ?> }; reRenderTree(); @@ -486,7 +485,7 @@ click: function () { (function ($) { $.ajax({ - url: '<?= /* @escapeNotVerified */ $block->getMoveUrl() ?>', + url: '<?= $block->escapeJs($block->escapeUrl($block->getMoveUrl())) ?>', method: 'POST', data: registry.get('pd'), showLoader: true diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/category/widget/tree.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/category/widget/tree.phtml index 69737b8a37c1c..e24d676974b01 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/category/widget/tree.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/category/widget/tree.phtml @@ -3,24 +3,21 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <?php $_divId = 'tree' . $block->getId() ?> -<div id="<?= /* @escapeNotVerified */ $_divId ?>" class="tree"></div> +<div id="<?= $block->escapeHtmlAttr($_divId) ?>" class="tree"></div> <!--[if IE]> <script id="ie-deferred-loader" defer="defer" src="//:"></script> <![endif]--> <script> require(['jquery', "prototype", "extjs/ext-tree-checkbox"], function(jQuery){ -var tree<?= /* @escapeNotVerified */ $block->getId() ?>; +var tree<?= $block->escapeJs($block->getId()) ?>; -var useMassaction = <?= /* @escapeNotVerified */ $block->getUseMassaction() ? 1 : 0 ?>; +var useMassaction = <?= $block->getUseMassaction() ? 1 : 0 ?>; -var isAnchorOnly = <?= /* @escapeNotVerified */ $block->getIsAnchorOnly() ? 1 : 0 ?>; +var isAnchorOnly = <?= $block->getIsAnchorOnly() ? 1 : 0 ?>; Ext.tree.TreePanel.Enhanced = function(el, config) { @@ -44,8 +41,8 @@ Ext.extend(Ext.tree.TreePanel.Enhanced, Ext.tree.TreePanel, { this.setRootNode(root); if (firstLoad) { - <?php if ($block->getNodeClickListener()): ?> - this.addListener('click', <?= /* @escapeNotVerified */ $block->getNodeClickListener() ?>.createDelegate(this)); + <?php if ($block->getNodeClickListener()) :?> + this.addListener('click', <?= /* @noEscape */ $block->getNodeClickListener() ?>.createDelegate(this)); <?php endif; ?> } @@ -58,10 +55,10 @@ Ext.extend(Ext.tree.TreePanel.Enhanced, Ext.tree.TreePanel, { jQuery(function() { - var emptyNodeAdded = <?= /* @escapeNotVerified */ ($block->getWithEmptyNode() ? 'false' : 'true') ?>; + var emptyNodeAdded = <?= ($block->getWithEmptyNode() ? 'false' : 'true') ?>; var categoryLoader = new Ext.tree.TreeLoader({ - dataUrl: '<?= /* @escapeNotVerified */ $block->getLoadTreeUrl() ?>' + dataUrl: '<?= $block->escapeJs($block->escapeUrl($block->getLoadTreeUrl())) ?>' }); categoryLoader.buildCategoryTree = function(parent, config) @@ -80,7 +77,7 @@ jQuery(function() // Add empty node to reset category filter if(!emptyNodeAdded) { var empty = Object.clone(_node); - empty.text = '<?= /* @escapeNotVerified */ __('None') ?>'; + empty.text = '<?= $block->escapeJs(__('None')) ?>'; empty.children = []; empty.id = 'none'; empty.path = '1/none'; @@ -151,11 +148,11 @@ jQuery(function() }; categoryLoader.on("beforeload", function(treeLoader, node) { - $('<?= /* @escapeNotVerified */ $_divId ?>').fire('category:beforeLoad', {treeLoader:treeLoader}); + $('<?= $block->escapeJs($_divId) ?>').fire('category:beforeLoad', {treeLoader:treeLoader}); treeLoader.baseParams.id = node.attributes.id; }); - tree<?= /* @escapeNotVerified */ $block->getId() ?> = new Ext.tree.TreePanel.Enhanced('<?= /* @escapeNotVerified */ $_divId ?>', { + tree<?= $block->escapeJs($block->getId()) ?> = new Ext.tree.TreePanel.Enhanced('<?= $block->escapeJs($_divId) ?>', { animate: false, loader: categoryLoader, enableDD: false, @@ -167,9 +164,9 @@ jQuery(function() }); if (useMassaction) { - tree<?= /* @escapeNotVerified */ $block->getId() ?>.on('check', function(node) { - $('<?= /* @escapeNotVerified */ $_divId ?>').fire('node:changed', {node:node}); - }, tree<?= /* @escapeNotVerified */ $block->getId() ?>); + tree<?= $block->escapeJs($block->getId()) ?>.on('check', function(node) { + $('<?= $block->escapeJs($_divId) ?>').fire('node:changed', {node:node}); + }, tree<?= $block->escapeJs($block->getId()) ?>); } // set the root node @@ -181,7 +178,7 @@ jQuery(function() category_id: <?= (int) $block->getCategoryId() ?> }; - tree<?= /* @escapeNotVerified */ $block->getId() ?>.loadTree({parameters:parameters, data:<?= /* @escapeNotVerified */ $block->getTreeJson() ?>},true); + tree<?= $block->escapeJs($block->getId()) ?>.loadTree({parameters:parameters, data:<?= /* @noEscape */ $block->getTreeJson() ?>},true); }); diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/form/renderer/fieldset/element.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/form/renderer/fieldset/element.phtml index 680361eae448e..cbda491a64740 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/form/renderer/fieldset/element.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/form/renderer/fieldset/element.phtml @@ -3,19 +3,16 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <?php -/** - * @see \Magento\Catalog\Block\Adminhtml\Form\Renderer\Fieldset\Element - */ +// phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis + +/** @var $block \Magento\Catalog\Block\Adminhtml\Form\Renderer\Fieldset\Element */ ?> <?php /* @var $block \Magento\Backend\Block\Widget\Form\Renderer\Fieldset\Element */ $element = $block->getElement(); -$note = $element->getNote() ? '<div class="note admin__field-note">' . $element->getNote() . '</div>' : ''; +$note = $element->getNote() ? '<div class="note admin__field-note">' . $block->escapeHtml($element->getNote()) . '</div>' : ''; $elementBeforeLabel = $element->getExtType() == 'checkbox' || $element->getExtType() == 'radio'; $addOn = $element->getBeforeElementHtml() || $element->getAfterElementHtml(); $fieldId = ($element->getHtmlId()) ? ' id="attribute-' . $element->getHtmlId() . '-container"' : ''; @@ -27,8 +24,8 @@ $fieldClass .= ($element->getRequired()) ? ' required' : ''; $fieldClass .= ($note) ? ' with-note' : ''; $fieldClass .= ($entity && $entity->getIsUserDefined()) ? ' user-defined type-' . $entity->getFrontendInput() : ''; -$fieldAttributes = $fieldId . ' class="' . $fieldClass . '" ' - . $block->getUiId('form-field', $element->getId()); +$fieldAttributes = $fieldId . ' class="' . $block->escapeHtmlAttr($fieldClass) . '" ' + . $block->getUiId('form-field', $block->escapeHtmlAttr($element->getId())); ?> <?php $block->checkFieldDisable() ?> @@ -36,38 +33,38 @@ $fieldAttributes = $fieldId . ' class="' . $fieldClass . '" ' $elementToggleCode = $element->getToggleCode() ? $element->getToggleCode() : 'toggleValueElements(this, this.parentNode.parentNode.parentNode)'; ?> -<?php if (!$element->getNoDisplay()): ?> - <?php if ($element->getType() == 'hidden'): ?> +<?php if (!$element->getNoDisplay()) :?> + <?php if ($element->getType() == 'hidden') :?> <?= $element->getElementHtml() ?> - <?php else: ?> - <div<?= /* @escapeNotVerified */ $fieldAttributes ?> data-attribute-code="<?= $element->getHtmlId() ?>" - data-apply-to="<?= $block->escapeHtml($this->helper('Magento\Framework\Json\Helper\Data')->jsonEncode( + <?php else :?> + <div<?= /* @noEscape */ $fieldAttributes ?> data-attribute-code="<?= $element->getHtmlId() ?>" + data-apply-to="<?= $block->escapeHtmlAttr($this->helper(Magento\Framework\Json\Helper\Data::class)->jsonEncode( $element->hasEntityAttribute() ? $element->getEntityAttribute()->getApplyTo() : [] ))?>" > - <?php if ($elementBeforeLabel): ?> + <?php if ($elementBeforeLabel) :?> <?= $block->getElementHtml() ?> <?= $element->getLabelHtml('', $block->getScopeLabel()) ?> - <?= /* @escapeNotVerified */ $note ?> - <?php else: ?> + <?= /* @noEscape */ $note ?> + <?php else :?> <?= $element->getLabelHtml('', $block->getScopeLabel()) ?> <div class="admin__field-control control"> - <?= /* @escapeNotVerified */ ($addOn) ? '<div class="addon">' . $block->getElementHtml() . '</div>' : $block->getElementHtml() ?> - <?= /* @escapeNotVerified */ $note ?> + <?= ($addOn) ? '<div class="addon">' . $block->getElementHtml() . '</div>' : $block->getElementHtml() ?> + <?= /* @noEscape */ $note ?> </div> <?php endif; ?> <div class="field-service"> - <?php if ($block->canDisplayUseDefault()): ?> + <?php if ($block->canDisplayUseDefault()) :?> <label for="<?= $element->getHtmlId() ?>_default" class="choice use-default"> - <input <?php if ($element->getReadonly()):?> disabled="disabled"<?php endif; ?> + <input <?php if ($element->getReadonly()) :?> disabled="disabled"<?php endif; ?> type="checkbox" name="use_default[]" class="use-default-control" id="<?= $element->getHtmlId() ?>_default" - <?php if ($block->usedDefault()): ?> checked="checked"<?php endif; ?> - onclick="<?= /* @escapeNotVerified */ $elementToggleCode ?>" - value="<?= /* @escapeNotVerified */ $block->getAttributeCode() ?>"/> - <span class="use-default-label"><?= /* @escapeNotVerified */ __('Use Default Value') ?></span> + <?php if ($block->usedDefault()) :?> checked="checked"<?php endif; ?> + onclick="<?= $block->escapeHtmlAttr($elementToggleCode) ?>" + value="<?= $block->escapeHtmlAttr($block->getAttributeCode()) ?>"/> + <span class="use-default-label"><?= $block->escapeHtml(__('Use Default Value')) ?></span> </label> <?php endif; ?> </div> diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product.phtml index ce4d8450f5e63..9b9fff2cfc344 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product.phtml @@ -3,9 +3,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <?php /** diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/form.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/form.phtml index 124194519b978..e30b981ff36a6 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/form.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/form.phtml @@ -4,16 +4,14 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - ?> <?php /** * @var $block \Magento\Backend\Block\Widget\Form\Container */ ?> -<?= /* @escapeNotVerified */ $block->getFormInitScripts() ?> -<div data-mage-init='{"floatingHeader": {}}' class="page-actions attribute-popup-actions" <?= /* @escapeNotVerified */ $block->getUiId('content-header') ?>> +<?= /* @noEscape */ $block->getFormInitScripts() ?> +<div data-mage-init='{"floatingHeader": {}}' class="page-actions attribute-popup-actions" <?= /* @noEscape */ $block->getUiId('content-header') ?>> <?= $block->getButtonsHtml('header') ?> </div> @@ -25,9 +23,9 @@ { "#edit_form": { "Magento_Catalog/catalog/product/edit/attribute": { - "validationUrl": "<?= /* @escapeNotVerified */ $block->getValidationUrl() ?>" + "validationUrl": "<?= $block->escapeJs($block->escapeUrl($block->getValidationUrl())) ?>" } } } </script> -<?= /* @escapeNotVerified */ $block->getFormScripts() ?> +<?= /* @noEscape */ $block->getFormScripts() ?> diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/js.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/js.phtml index 195ac92422715..2dc39b97c3d95 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/js.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/js.phtml @@ -4,8 +4,7 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - +// phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis ?> <script> require([ @@ -45,11 +44,11 @@ function checkOptionsPanelVisibility(){ if($('frontend_input') && ($('frontend_input').value=='select' || $('frontend_input').value=='multiselect')){ panel.show(); - panel.addClass(activePanelClass); + jQuery(panel).addClass(activePanelClass); } else { panel.hide(); - panel.removeClass(activePanelClass); + jQuery(panel).removeClass(activePanelClass); } } } @@ -197,22 +196,22 @@ function switchDefaultValueField() setRowVisibility('frontend_class', false); break; - <?php foreach ($this->helper('Magento\Catalog\Helper\Data')->getAttributeHiddenFields() as $type => $fields): ?> - case '<?= /* @escapeNotVerified */ $type ?>': + <?php foreach ($this->helper(Magento\Catalog\Helper\Data::class)->getAttributeHiddenFields() as $type => $fields) :?> + case '<?= $block->escapeJs($type) ?>': var isFrontTabHidden = false; - <?php foreach ($fields as $one): ?> - <?php if ($one == '_front_fieldset'): ?> + <?php foreach ($fields as $one) :?> + <?php if ($one == '_front_fieldset') :?> getFrontTab().hide(); isFrontTabHidden = true; - <?php elseif ($one == '_default_value'): ?> + <?php elseif ($one == '_default_value') :?> defaultValueTextVisibility = defaultValueTextareaVisibility = defaultValueDateVisibility = defaultValueYesnoVisibility = false; - <?php elseif ($one == '_scope'): ?> + <?php elseif ($one == '_scope') :?> scopeVisibility = false; - <?php else: ?> - setRowVisibility('<?= /* @escapeNotVerified */ $one ?>', false); + <?php else :?> + setRowVisibility('<?= $block->escapeJs($one) ?>', false); <?php endif; ?> <?php endforeach; ?> diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/labels.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/labels.phtml index f3d39257c266c..1d5d251f00de9 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/labels.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/labels.phtml @@ -4,15 +4,13 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - /** @var $block \Magento\Eav\Block\Adminhtml\Attribute\Edit\Options\Labels */ ?> <div class="fieldset-wrapper admin__collapsible-block-wrapper opened" id="manage-titles-wrapper"> <div class="fieldset-wrapper-title"> <strong class="admin__collapsible-title" data-toggle="collapse" data-target="#manage-titles-content"> - <span><?= /* @escapeNotVerified */ __('Manage Titles (Size, Color, etc.)') ?></span> + <span><?= $block->escapeHtml(__('Manage Titles (Size, Color, etc.)')) ?></span> </strong> </div> <div class="fieldset-wrapper-content in collapse" id="manage-titles-content"> @@ -21,17 +19,23 @@ <table class="admin__control-table" id="attribute-labels-table"> <thead> <tr> - <?php foreach ($block->getStores() as $_store): ?> - <th class="col-store-view"><?= /* @escapeNotVerified */ $_store->getName() ?></th> + <?php foreach ($block->getStores() as $_store) :?> + <th class="col-store-view"><?= $block->escapeHtml($_store->getName()) ?></th> <?php endforeach; ?> </tr> </thead> <tbody> <tr> <?php $_labels = $block->getLabelValues() ?> - <?php foreach ($block->getStores() as $_store): ?> + <?php foreach ($block->getStores() as $_store) :?> <td class="col-store-view"> - <input class="input-text<?php if ($_store->getId() == \Magento\Store\Model\Store::DEFAULT_STORE_ID): ?> required-option<?php endif; ?>" type="text" name="frontend_label[<?= /* @escapeNotVerified */ $_store->getId() ?>]" value="<?= $block->escapeHtml($_labels[$_store->getId()]) ?>"<?php if ($block->getReadOnly()):?> disabled="disabled"<?php endif;?>/> + <input class="input-text<?php if ($_store->getId() == \Magento\Store\Model\Store::DEFAULT_STORE_ID) :?> required-option<?php endif; ?>" + type="text" + name="frontend_label[<?= $block->escapeHtmlAttr($_store->getId()) ?>]" + value="<?= $block->escapeHtmlAttr($_labels[$_store->getId()]) ?>" + <?php if ($block->getReadOnly()) :?> + disabled="disabled" + <?php endif;?>/> </td> <?php endforeach; ?> </tr> diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/options.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/options.phtml index f812a27f87ad9..e5f8a360c334c 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/options.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/options.phtml @@ -4,8 +4,6 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - /** @var $block \Magento\Eav\Block\Adminhtml\Attribute\Edit\Options\Options */ $stores = $block->getStoresSortedBySortOrder(); @@ -23,8 +21,8 @@ $stores = $block->getStoresSortedBySortOrder(); <span><?= $block->escapeHtml(__('Is Default')) ?></span> </th> <?php - foreach ($stores as $_store): ?> - <th<?php if ($_store->getId() == \Magento\Store\Model\Store::DEFAULT_STORE_ID): ?> class="_required"<?php endif; ?>> + foreach ($stores as $_store) :?> + <th<?php if ($_store->getId() == \Magento\Store\Model\Store::DEFAULT_STORE_ID) :?> class="_required"<?php endif; ?>> <span><?= $block->escapeHtml(__($_store->getName())) ?></span> </th> <?php endforeach; @@ -43,7 +41,7 @@ $stores = $block->getStoresSortedBySortOrder(); </tr> <tr> <th colspan="<?= (int) $storetotal ?>" class="col-actions-add"> - <?php if (!$block->getReadOnly() && !$block->canManageOptionDefaultOnly()):?> + <?php if (!$block->getReadOnly() && !$block->canManageOptionDefaultOnly()) :?> <button id="add_new_option_button" data-action="add_new_row" title="<?= $block->escapeHtml(__('Add Option')) ?>" type="button" class="action- scalable add"> @@ -59,22 +57,22 @@ $stores = $block->getStoresSortedBySortOrder(); <script id="row-template" type="text/x-magento-template"> <tr <% if (data.rowClasses) { %>class="<%- data.rowClasses %>"<% } %>> <td class="col-draggable"> - <?php if (!$block->getReadOnly() && !$block->canManageOptionDefaultOnly()): ?> + <?php if (!$block->getReadOnly() && !$block->canManageOptionDefaultOnly()) :?> <div data-role="draggable-handle" class="draggable-handle" title="<?= $block->escapeHtml(__('Sort Option')) ?>"> </div> <?php endif; ?> - <input data-role="order" type="hidden" name="option[order][<%- data.id %>]" value="<%- data.sort_order %>" <?php if ($block->getReadOnly() || $block->canManageOptionDefaultOnly()): ?> disabled="disabled"<?php endif; ?>/> + <input data-role="order" type="hidden" name="option[order][<%- data.id %>]" value="<%- data.sort_order %>" <?php if ($block->getReadOnly() || $block->canManageOptionDefaultOnly()) :?> disabled="disabled"<?php endif; ?>/> </td> <td class="col-default control-table-actions-cell"> - <input class="input-radio" type="<%- data.intype %>" name="default[]" value="<%- data.id %>" <%- data.checked %><?php if ($block->getReadOnly()):?>disabled="disabled"<?php endif;?>/> + <input class="input-radio" type="<%- data.intype %>" name="default[]" value="<%- data.id %>" <%- data.checked %><?php if ($block->getReadOnly()) :?>disabled="disabled"<?php endif;?>/> </td> - <?php foreach ($stores as $_store): ?> - <td class="col-<%- data.id %>"><input name="option[value][<%- data.id %>][<?= (int) $_store->getId() ?>]" value="<%- data.store<?= /* @noEscape */ (int) $_store->getId() ?> %>" class="input-text<?php if ($_store->getId() == \Magento\Store\Model\Store::DEFAULT_STORE_ID): ?> required-option required-unique<?php endif; ?>" type="text" <?php if ($block->getReadOnly() || $block->canManageOptionDefaultOnly()):?> disabled="disabled"<?php endif;?>/></td> + <?php foreach ($stores as $_store) :?> + <td class="col-<%- data.id %>"><input name="option[value][<%- data.id %>][<?= (int) $_store->getId() ?>]" value="<%- data.store<?= /* @noEscape */ (int) $_store->getId() ?> %>" class="input-text<?php if ($_store->getId() == \Magento\Store\Model\Store::DEFAULT_STORE_ID) :?> required-option required-unique<?php endif; ?>" type="text" <?php if ($block->getReadOnly() || $block->canManageOptionDefaultOnly()) :?> disabled="disabled"<?php endif;?>/></td> <?php endforeach; ?> <td id="delete_button_container_<%- data.id %>" class="col-delete"> <input type="hidden" class="delete-flag" name="option[delete][<%- data.id %>]" value="" /> - <?php if (!$block->getReadOnly() && !$block->canManageOptionDefaultOnly()):?> + <?php if (!$block->getReadOnly() && !$block->canManageOptionDefaultOnly()) :?> <button id="delete_button_<%- data.id %>" title="<?= $block->escapeHtml(__('Delete')) ?>" type="button" class="action- scalable delete delete-option" > @@ -86,9 +84,9 @@ $stores = $block->getStoresSortedBySortOrder(); </script> <?php $values = []; - foreach($block->getOptionValues() as $value) { + foreach ($block->getOptionValues() as $value) { $value = $value->getData(); - $values[] = is_array($value) ? array_map(function($str) { + $values[] = is_array($value) ? array_map(function ($str) { return htmlspecialchars_decode($str, ENT_QUOTES); }, $value) : $value; } diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/set/main.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/set/main.phtml index 9621b9a57168c..dd1009cc5e033 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/set/main.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/set/main.phtml @@ -4,8 +4,7 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - +/** @var $block Magento\Catalog\Block\Adminhtml\Product\Attribute\Set\Main */ ?> <div class="attribute-set"> @@ -31,11 +30,11 @@ </div> <div class="attribute-set-col fieldset-wrapper"> <div class="fieldset-wrapper-title"> - <span class="title"><?= /* @escapeNotVerified */ __('Groups') ?></span> + <span class="title"><?= $block->escapeHtml(__('Groups')) ?></span> </div> - <?php if (!$block->getIsReadOnly()): ?> - <?= /* @escapeNotVerified */ $block->getAddGroupButton() ?> <?= /* @escapeNotVerified */ $block->getDeleteGroupButton() ?> - <p class="note-block"><?= /* @escapeNotVerified */ __('Double click on a group to rename it.') ?></p> + <?php if (!$block->getIsReadOnly()) :?> + <?= /* @noEscape */ $block->getAddGroupButton() ?> <?= /* @noEscape */ $block->getDeleteGroupButton() ?> + <p class="note-block"><?= $block->escapeHtml(__('Double click on a group to rename it.')) ?></p> <?php endif; ?> <?= $block->getSetsFilterHtml() ?> @@ -43,7 +42,7 @@ </div> <div class="attribute-set-col fieldset-wrapper"> <div class="fieldset-wrapper-title"> - <span class="title"><?= /* @escapeNotVerified */ __('Unassigned Attributes') ?></span> + <span class="title"><?= $block->escapeHtml(__('Unassigned Attributes')) ?></span> </div> <div id="tree-div2" class="attribute-set-tree"></div> <script id="ie-deferred-loader" defer="defer" src="//:"></script> @@ -58,8 +57,8 @@ ], function(jQuery, prompt, alert){ //<![CDATA[ - var allowDragAndDrop = <?= /* @escapeNotVerified */ ($block->getIsReadOnly() ? 'false' : 'true') ?>; - var canEditGroups = <?= /* @escapeNotVerified */ ($block->getIsReadOnly() ? 'false' : 'true') ?>; + var allowDragAndDrop = <?= ($block->getIsReadOnly() ? 'false' : 'true') ?>; + var canEditGroups = <?= ($block->getIsReadOnly() ? 'false' : 'true') ?>; var TreePanels = function() { // shorthand @@ -86,7 +85,7 @@ }); tree.setRootNode(this.root); - buildCategoryTree(this.root, <?= /* @escapeNotVerified */ $block->getGroupTreeJson() ?>); + buildCategoryTree(this.root, <?= /* @noEscape */ $block->getGroupTreeJson() ?>); // render the tree tree.render(); this.root.expand(false, false); @@ -94,7 +93,7 @@ this.ge = new Ext.tree.TreeEditor(tree, { allowBlank:false, - blankText:'<?= /* @escapeNotVerified */ __('A name is required.') ?>', + blankText:'<?= $block->escapeJs(__('A name is required.')) ?>', selectOnFocus:true, cls:'folder' }); @@ -125,7 +124,7 @@ id:'free' }); tree2.setRootNode(this.root2); - buildCategoryTree(this.root2, <?= /* @escapeNotVerified */ $block->getAttributeTreeJson() ?>); + buildCategoryTree(this.root2, <?= /* @noEscape */ $block->getAttributeTreeJson() ?>); this.root2.addListener('beforeinsert', editSet.rightBeforeInsert); this.root2.addListener('beforeappend', editSet.rightBeforeAppend); @@ -196,14 +195,14 @@ } } } - } - node.appendChild(newNode); - newNode.addListener('click', editSet.unregister); } + node.appendChild(newNode); + newNode.addListener('click', editSet.unregister); } } } } + } editSet = function () { @@ -280,8 +279,8 @@ addGroup : function() { prompt({ - title: "<?= /* @escapeNotVerified */ __('Add New Group') ?>", - content: "<?= /* @escapeNotVerified */ __('Please enter a new group name.') ?>", + title: "<?= $block->escapeJs($block->escapeHtml(__('Add New Group'))) ?>", + content: "<?= $block->escapeJs($block->escapeHtml(__('Please enter a new group name.'))) ?>", value: "", validation: true, validationRules: ['required-entry'], @@ -346,7 +345,7 @@ } for (var i=0; i < TreePanels.root.childNodes.length; i++) { if (TreePanels.root.childNodes[i].text.toLowerCase() == name.toLowerCase() && TreePanels.root.childNodes[i].id != exceptNodeId) { - errorText = '<?= /* @escapeNotVerified */ __('An attribute group named "/name/" already exists.') ?>'; + errorText = '<?= $block->escapeJs(__('An attribute group named "/name/" already exists.')) ?>'; alert({ content: errorText.replace("/name/",name) }); @@ -374,7 +373,7 @@ editSet.req.form_key = FORM_KEY; } var req = {data : Ext.util.JSON.encode(editSet.req)}; - var con = new Ext.lib.Ajax.request('POST', '<?= /* @escapeNotVerified */ $block->getMoveUrl() ?>', {success:editSet.success,failure:editSet.failure}, req); + var con = new Ext.lib.Ajax.request('POST', '<?= $block->escapeJs($block->escapeUrl($block->getMoveUrl())) ?>', {success:editSet.success,failure:editSet.failure}, req); }, success : function(o) { @@ -449,7 +448,7 @@ rightRemove : function(tree, nodeThis, node) { if( nodeThis.firstChild == null && node.id != 'empty' ) { var newNode = new Ext.tree.TreeNode({ - text : '<?= /* @escapeNotVerified */ __('Empty') ?>', + text : '<?= $block->escapeJs(__('Empty')) ?>', id : 'empty', cls : 'folder', is_user_defined : 1, diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/set/toolbar/add.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/set/toolbar/add.phtml index c1af14389fe59..227ed4be81fae 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/set/toolbar/add.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/set/toolbar/add.phtml @@ -8,7 +8,7 @@ <script> require(['jquery', "mage/mage"], function(jQuery){ - jQuery('#<?= /* @escapeNotVerified */ $block->getFormId() ?>').mage('form').mage('validation'); + jQuery('#<?= $block->escapeJs($block->getFormId()) ?>').mage('form').mage('validation'); }); </script> diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/set/toolbar/main.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/set/toolbar/main.phtml index 902c6932f0ae1..c0928f4723b50 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/set/toolbar/main.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/attribute/set/toolbar/main.phtml @@ -3,8 +3,5 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <?= $block->getChildHtml('grid') ?> diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/composite/configure.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/composite/configure.phtml index 75027d5e043fb..32466a1dfa965 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/composite/configure.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/composite/configure.phtml @@ -3,10 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - - ?> +?> <div id="product_composite_configure" class="product-configure-popup" style="display:none;"> <iframe name="product_composite_configure_iframe" id="product_composite_configure_iframe" style="width:0; height:0; border:0px solid #fff; position:absolute; top:-1000px; left:-1000px" onload="window.productConfigure && productConfigure.onLoadIFrame()"></iframe> <form action="" method="post" id="product_composite_configure_form" enctype="multipart/form-data" onsubmit="productConfigure.onConfirmBtn(); return false;" target="product_composite_configure_iframe"> @@ -19,7 +16,7 @@ <div id="product_composite_configure_form_confirmed" style="display:none;"></div> </div> <input type="hidden" name="as_js_varname" value="iFrameResponse" /> - <input type="hidden" name="form_key" value="<?= /* @escapeNotVerified */ $block->getFormKey() ?>" /> + <input type="hidden" name="form_key" value="<?= $block->escapeHtmlAttr($block->getFormKey()) ?>" /> </form> <div id="product_composite_configure_confirmed" style="display:none;"></div> diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/composite/fieldset/options.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/composite/fieldset/options.phtml index acc80fa6ea6b0..6a83ece330441 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/composite/fieldset/options.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/composite/fieldset/options.phtml @@ -3,24 +3,22 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <?php /* @var $block \Magento\Catalog\Block\Adminhtml\Product\Composite\Fieldset\Options */ ?> <?php $options = $block->decorateArray($block->getOptions()); ?> -<?php if (count($options)): ?> +<?php if (count($options)) :?> -<?= $block->getChildHtml('options_js') ?> + <?= $block->getChildHtml('options_js') ?> -<fieldset id="product_composite_configure_fields_options" class="fieldset admin__fieldset <?= $block->getIsLastFieldset() ? 'last-fieldset' : '' ?>"> - <legend class="legend admin__legend"> - <span><?= /* @escapeNotVerified */ __('Custom Options') ?></span> - </legend><br> - <?php foreach ($options as $option): ?> - <?= $block->getOptionHtml($option) ?> - <?php endforeach;?> -</fieldset> + <fieldset id="product_composite_configure_fields_options" + class="fieldset admin__fieldset <?= $block->getIsLastFieldset() ? 'last-fieldset' : '' ?>"> + <legend class="legend admin__legend"> + <span><?= $block->escapeHtml(__('Custom Options')) ?></span> + </legend><br> + <?php foreach ($options as $option) :?> + <?= $block->getOptionHtml($option) ?> + <?php endforeach;?> + </fieldset> <?php endif; ?> diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/composite/fieldset/options/type/date.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/composite/fieldset/options/type/date.phtml index 30c05c2ec689b..8adffb752187b 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/composite/fieldset/options/type/date.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/composite/fieldset/options/type/date.phtml @@ -3,82 +3,82 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <?php /* @var $block \Magento\Catalog\Block\Product\View\Options\Type\Date */ ?> <?php $_option = $block->getOption(); ?> -<?php $_optionId = $_option->getId(); ?> -<div class="admin__field field<?php if ($_option->getIsRequire()) echo ' required _required' ?>"> - <label class="label admin__field-label"> - <?= $block->escapeHtml($_option->getTitle()) ?> - <?= /* @escapeNotVerified */ $block->getFormattedPrice() ?> - </label> - <div class="admin__field-control control"> +<?php $_optionId = (int)$_option->getId(); ?> +<div class="admin__field field<?= $_option->getIsRequire() ? ' required _required' : '' ?>"> + <label class="label admin__field-label"> + <?= $block->escapeHtml($_option->getTitle()) ?> + <?= /* @noEscape */ $block->getFormattedPrice() ?> + </label> + <div class="admin__field-control control"> - <?php if ($_option->getType() == \Magento\Catalog\Api\Data\ProductCustomOptionInterface::OPTION_TYPE_DATE_TIME - || $_option->getType() == \Magento\Catalog\Api\Data\ProductCustomOptionInterface::OPTION_TYPE_DATE): ?> + <?php if ($_option->getType() == \Magento\Catalog\Api\Data\ProductCustomOptionInterface::OPTION_TYPE_DATE_TIME + || $_option->getType() == \Magento\Catalog\Api\Data\ProductCustomOptionInterface::OPTION_TYPE_DATE) :?> - <?= $block->getDateHtml() ?> + <?= $block->getDateHtml() ?> - <?php if (!$block->useCalendar()): ?> - <script> -require([ - "prototype", - "Magento_Catalog/catalog/product/composite/configure" -], function(){ + <?php if (!$block->useCalendar()) :?> + <script> + require([ + "prototype", + "Magento_Catalog/catalog/product/composite/configure" + ], function(){ - window.dateOption = productConfigure.opConfig.dateOption; - Event.observe('options_<?= /* @escapeNotVerified */ $_optionId ?>_month', 'change', dateOption.reloadMonth.bind(dateOption)); - Event.observe('options_<?= /* @escapeNotVerified */ $_optionId ?>_year', 'change', dateOption.reloadMonth.bind(dateOption)); -}); -</script> - <?php endif; ?> + window.dateOption = productConfigure.opConfig.dateOption; + Event.observe('options_<?= /* @noEscape */ $_optionId ?>_month', 'change', dateOption.reloadMonth.bind(dateOption)); + Event.observe('options_<?= /* @noEscape */ $_optionId ?>_year', 'change', dateOption.reloadMonth.bind(dateOption)); + }); + </script> + <?php endif; ?> - <?php endif; ?> + <?php endif; ?> - <?php if ($_option->getType() == \Magento\Catalog\Api\Data\ProductCustomOptionInterface::OPTION_TYPE_DATE_TIME - || $_option->getType() == \Magento\Catalog\Api\Data\ProductCustomOptionInterface::OPTION_TYPE_TIME): ?> - <span class="time-picker"><?= $block->getTimeHtml() ?></span> - <?php endif; ?> + <?php if ($_option->getType() == \Magento\Catalog\Api\Data\ProductCustomOptionInterface::OPTION_TYPE_DATE_TIME + || $_option->getType() == \Magento\Catalog\Api\Data\ProductCustomOptionInterface::OPTION_TYPE_TIME) :?> + <span class="time-picker"><?= $block->getTimeHtml() ?></span> + <?php endif; ?> - <input type="hidden" name="validate_datetime_<?= /* @escapeNotVerified */ $_optionId ?>" class="validate-datetime-<?= /* @escapeNotVerified */ $_optionId ?>" value="" /> - <script> -require([ - "jquery", - "mage/backend/validation" -], function(jQuery){ + <input type="hidden" + name="validate_datetime_<?= /* @noEscape */ $_optionId ?>" + class="validate-datetime-<?= /* @noEscape */ $_optionId ?>" + value="" /> + <script> + require([ + "jquery", + "mage/backend/validation" + ], function(jQuery){ - //<![CDATA[ -<?php if ($_option->getIsRequire()): ?> - jQuery.validator.addMethod('validate-datetime-<?= /* @escapeNotVerified */ $_optionId ?>', function(v) { - var dateTimeParts = jQuery('.datetime-picker[id^="options_<?= /* @escapeNotVerified */ $_optionId ?>"]'); - for (var i=0; i < dateTimeParts.length; i++) { - if (dateTimeParts[i].value == "") return false; - } - return true; - }, '<?= $block->escapeJs(__('This is a required option.')) ?>'); -<?php else: ?> - jQuery.validator.addMethod('validate-datetime-<?= /* @escapeNotVerified */ $_optionId ?>', function(v) { - var dateTimeParts = jQuery('.datetime-picker[id^="options_<?= /* @escapeNotVerified */ $_optionId ?>"]'); - var hasWithValue = false, hasWithNoValue = false; - var pattern = /day_part$/i; - for (var i=0; i < dateTimeParts.length; i++) { - if (! pattern.test(dateTimeParts[i].id)) { - if (dateTimeParts[i].value === "") { - hasWithValue = true; - } else { - hasWithNoValue = true; + //<![CDATA[ + <?php if ($_option->getIsRequire()) :?> + jQuery.validator.addMethod('validate-datetime-<?= /* @noEscape */ $_optionId ?>', function(v) { + var dateTimeParts = jQuery('.datetime-picker[id^="options_<?= /* @noEscape */ $_optionId ?>"]'); + for (var i=0; i < dateTimeParts.length; i++) { + if (dateTimeParts[i].value == "") return false; } - } - } - return hasWithValue ^ hasWithNoValue; - }, '<?= $block->escapeJs(__('The field isn\'t complete.')) ?>'); -<?php endif; ?> - //]]> - -}); -</script> - </div> + return true; + }, '<?= $block->escapeJs(__('This is a required option.')) ?>'); + <?php else :?> + jQuery.validator.addMethod('validate-datetime-<?= /* @noEscape */ $_optionId ?>', function(v) { + var dateTimeParts = jQuery('.datetime-picker[id^="options_<?= /* @noEscape */ $_optionId ?>"]'); + var hasWithValue = false, hasWithNoValue = false; + var pattern = /day_part$/i; + for (var i=0; i < dateTimeParts.length; i++) { + if (! pattern.test(dateTimeParts[i].id)) { + if (dateTimeParts[i].value === "") { + hasWithValue = true; + } else { + hasWithNoValue = true; + } + } + } + return hasWithValue ^ hasWithNoValue; + }, '<?= $block->escapeJs(__('The field isn\'t complete.')) ?>'); + <?php endif; ?> + //]]> + + }); + </script> + </div> </div> diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/composite/fieldset/options/type/file.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/composite/fieldset/options/type/file.phtml index 4ad7a95c91980..da0b3b36d561e 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/composite/fieldset/options/type/file.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/composite/fieldset/options/type/file.phtml @@ -3,15 +3,12 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <?php /* @var $block \Magento\Catalog\Block\Product\View\Options\Type\File */ ?> <?php $_option = $block->getOption(); ?> <?php $_fileInfo = $block->getFileInfo(); ?> <?php $_fileExists = $_fileInfo->hasData() ? true : false; ?> -<?php $_fileName = 'options_' . $_option->getId() . '_file'; ?> +<?php $_fileName = 'options_' . (int)$_option->getId() . '_file'; ?> <?php $_fieldNameAction = $_fileName . '_action'; ?> <?php $_fieldValueAction = $_fileExists ? 'save_old' : 'save_new'; ?> <?php $_fileNamed = $_fileName . '_name'; ?> @@ -21,11 +18,11 @@ require(['prototype'], function(){ //<![CDATA[ - opFile<?= /* @escapeNotVerified */ $_rand ?> = { + opFile<?= /* @noEscape */ $_rand ?> = { initializeFile: function(inputBox) { - this.inputFile = inputBox.select('input[name="<?= /* @escapeNotVerified */ $_fileName ?>"]')[0]; - this.inputFileAction = inputBox.select('input[name="<?= /* @escapeNotVerified */ $_fieldNameAction ?>"]')[0]; - this.fileNameBox = inputBox.up('dd').select('.<?= /* @escapeNotVerified */ $_fileNamed ?>')[0]; + this.inputFile = inputBox.select('input[name="<?= /* @noEscape */ $_fileName ?>"]')[0]; + this.inputFileAction = inputBox.select('input[name="<?= /* @noEscape */ $_fieldNameAction ?>"]')[0]; + this.fileNameBox = inputBox.up('dd').select('.<?= /* @noEscape */ $_fileNamed ?>')[0]; }, toggleFileChange: function(inputBox) { @@ -62,42 +59,42 @@ require(['prototype'], function(){ }); </script> -<div class="admin__field <?php if ($_option->getIsRequire()) echo ' required _required' ?>"> +<div class="admin__field <?= $_option->getIsRequire() ? ' required _required' : '' ?>"> <label class="admin__field-label label"> <?= $block->escapeHtml($_option->getTitle()) ?> - <?= /* @escapeNotVerified */ $block->getFormattedPrice() ?> + <?= /* @noEscape */ $block->getFormattedPrice() ?> </label> <div class="admin__field-control control"> - <?php if ($_fileExists): ?> + <?php if ($_fileExists) :?> <span class="<?= /* @noEscape */ $_fileNamed ?>"><?= $block->escapeHtml($_fileInfo->getTitle()) ?></span> - <a href="javascript:void(0)" class="label" onclick="opFile<?= /* @escapeNotVerified */ $_rand ?>.toggleFileChange($(this).next('.input-box'))"> - <?= /* @escapeNotVerified */ __('Change') ?> + <a href="javascript:void(0)" class="label" onclick="opFile<?= /* @noEscape */ $_rand ?>.toggleFileChange($(this).next('.input-box'))"> + <?= $block->escapeHtml(__('Change')) ?> </a>  - <?php if (!$_option->getIsRequire()): ?> - <input type="checkbox" onclick="opFile<?= /* @escapeNotVerified */ $_rand ?>.toggleFileDelete($(this), $(this).next('.input-box'))" price="<?= /* @escapeNotVerified */ $block->getCurrencyPrice($_option->getPrice(true)) ?>"/> - <span class="label"><?= /* @escapeNotVerified */ __('Delete') ?></span> + <?php if (!$_option->getIsRequire()) :?> + <input type="checkbox" onclick="opFile<?= /* @noEscape */ $_rand ?>.toggleFileDelete($(this), $(this).next('.input-box'))" price="<?= $block->escapeHtmlAttr($block->getCurrencyPrice($_option->getPrice(true))) ?>"/> + <span class="label"><?= $block->escapeHtml(__('Delete')) ?></span> <?php endif; ?> <?php endif; ?> <div class="input-box" <?= $_fileExists ? 'style="display:none"' : '' ?>> <!-- ToDo UI: add appropriate file class when z-index issue in ui dialog will be resolved --> - <input type="file" name="<?= /* @noEscape */ $_fileName ?>" class="product-custom-option<?= $_option->getIsRequire() ? ' required-entry' : '' ?>" price="<?= /* @escapeNotVerified */ $block->getCurrencyPrice($_option->getPrice(true)) ?>" <?= $_fileExists ? 'disabled="disabled"' : '' ?>/> - <input type="hidden" name="<?= /* @escapeNotVerified */ $_fieldNameAction ?>" value="<?= /* @escapeNotVerified */ $_fieldValueAction ?>" /> + <input type="file" name="<?= /* @noEscape */ $_fileName ?>" class="product-custom-option<?= $_option->getIsRequire() ? ' required-entry' : '' ?>" price="<?= $block->escapeHtmlAttr($block->getCurrencyPrice($_option->getPrice(true))) ?>" <?= $_fileExists ? 'disabled="disabled"' : '' ?>/> + <input type="hidden" name="<?= /* @noEscape */ $_fieldNameAction ?>" value="<?= /* @noEscape */ $_fieldValueAction ?>" /> - <?php if ($_option->getFileExtension()): ?> + <?php if ($_option->getFileExtension()) :?> <div class="admin__field-note"> - <span><?= /* @escapeNotVerified */ __('Compatible file extensions to upload') ?>: <strong><?= /* @escapeNotVerified */ $_option->getFileExtension() ?></strong></span> + <span><?= $block->escapeHtml(__('Compatible file extensions to upload')) ?>: <strong><?= $block->escapeHtml($_option->getFileExtension()) ?></strong></span> </div> <?php endif; ?> - <?php if ($_option->getImageSizeX() > 0): ?> + <?php if ($_option->getImageSizeX() > 0) :?> <div class="admin__field-note"> - <span><?= /* @escapeNotVerified */ __('Maximum image width') ?>: <strong><?= /* @escapeNotVerified */ $_option->getImageSizeX() ?> <?= /* @escapeNotVerified */ __('px.') ?></strong></span> + <span><?= $block->escapeHtml(__('Maximum image width')) ?>: <strong><?= (int)$_option->getImageSizeX() ?> <?= $block->escapeHtml(__('px.')) ?></strong></span> </div> <?php endif; ?> - <?php if ($_option->getImageSizeY() > 0): ?> + <?php if ($_option->getImageSizeY() > 0) :?> <div class="admin__field-note"> - <span><?= /* @escapeNotVerified */ __('Maximum image height') ?>: <strong><?= /* @escapeNotVerified */ $_option->getImageSizeY() ?> <?= /* @escapeNotVerified */ __('px.') ?></strong></span> + <span><?= $block->escapeHtml(__('Maximum image height')) ?>: <strong><?= (int)$_option->getImageSizeY() ?> <?= $block->escapeHtml(__('px.')) ?></strong></span> </div> <?php endif; ?> </div> diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/composite/fieldset/options/type/select.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/composite/fieldset/options/type/select.phtml index af09bbe0acd9d..2218ce5d29671 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/composite/fieldset/options/type/select.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/composite/fieldset/options/type/select.phtml @@ -3,21 +3,18 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <?php /* @var $block \Magento\Catalog\Block\Product\View\Options\Type\Select */ ?> <?php $_option = $block->getOption(); ?> -<div class="admin__field field<?php if ($_option->getIsRequire()) echo ' required _required' ?>"> +<div class="admin__field field<?= $_option->getIsRequire() ? ' required _required' : '' ?>"> <label class="label admin__field-label"> <span><?= $block->escapeHtml($_option->getTitle()) ?></span> </label> <div class="control admin__field-control"> <?= $block->getValuesHtml() ?> - <?php if ($_option->getIsRequire()): ?> - <?php if ($_option->getType() == \Magento\Catalog\Api\Data\ProductCustomOptionInterface::OPTION_TYPE_RADIO || $_option->getType() == \Magento\Catalog\Api\Data\ProductCustomOptionInterface::OPTION_TYPE_CHECKBOX): ?> - <span id="options-<?= /* @escapeNotVerified */ $_option->getId() ?>-container"></span> + <?php if ($_option->getIsRequire()) :?> + <?php if ($_option->getType() == \Magento\Catalog\Api\Data\ProductCustomOptionInterface::OPTION_TYPE_RADIO || $_option->getType() == \Magento\Catalog\Api\Data\ProductCustomOptionInterface::OPTION_TYPE_CHECKBOX) :?> + <span id="options-<?= $block->escapeHtmlAttr($_option->getId()) ?>-container"></span> <?php endif; ?> <?php endif;?> </div> diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/composite/fieldset/options/type/text.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/composite/fieldset/options/type/text.phtml index 11fba22ea8139..d1a019911d581 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/composite/fieldset/options/type/text.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/composite/fieldset/options/type/text.phtml @@ -3,26 +3,33 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <?php /* @var $block \Magento\Catalog\Block\Product\View\Options\Type\Text */ ?> <?php $_option = $block->getOption(); ?> -<div class="field admin__field<?php if ($_option->getIsRequire()) echo ' required _required' ?>"> +<div class="field admin__field<?= $_option->getIsRequire() ? ' required _required' : '' ?>"> <label class="admin__field-label label"> <?= $block->escapeHtml($_option->getTitle()) ?> - <?= /* @escapeNotVerified */ $block->getFormattedPrice() ?> + <?= /* @noEscape */ $block->getFormattedPrice() ?> </label> <div class="control admin__field-control"> - <?php if ($_option->getType() == \Magento\Catalog\Api\Data\ProductCustomOptionInterface::OPTION_TYPE_FIELD): ?> - <input type="text" id="options_<?= /* @escapeNotVerified */ $_option->getId() ?>_text" class="input-text admin__control-text <?= $_option->getIsRequire() ? ' required-entry' : '' ?> <?= /* @escapeNotVerified */ $_option->getMaxCharacters() ? ' validate-length maximum-length-' . $_option->getMaxCharacters() : '' ?> product-custom-option" name="options[<?= /* @escapeNotVerified */ $_option->getId() ?>]" value="<?= $block->escapeHtml($block->getDefaultValue()) ?>" price="<?= /* @escapeNotVerified */ $block->getCurrencyPrice($_option->getPrice(true)) ?>" /> - <?php elseif ($_option->getType() == \Magento\Catalog\Api\Data\ProductCustomOptionInterface::OPTION_TYPE_AREA): ?> - <textarea id="options_<?= /* @escapeNotVerified */ $_option->getId() ?>_text" class="admin__control-textarea <?= $_option->getIsRequire() ? ' required-entry' : '' ?> <?= /* @escapeNotVerified */ $_option->getMaxCharacters() ? ' validate-length maximum-length-' . $_option->getMaxCharacters() : '' ?> product-custom-option" name="options[<?= /* @escapeNotVerified */ $_option->getId() ?>]" rows="5" cols="25" price="<?= /* @escapeNotVerified */ $block->getCurrencyPrice($_option->getPrice(true)) ?>"><?= $block->escapeHtml($block->getDefaultValue()) ?></textarea> + <?php if ($_option->getType() == \Magento\Catalog\Api\Data\ProductCustomOptionInterface::OPTION_TYPE_FIELD) :?> + <input type="text" + id="options_<?= $block->escapeHtmlAttr($_option->getId()) ?>_text" + class="input-text admin__control-text <?= $_option->getIsRequire() ? ' required-entry' : '' ?> <?= $_option->getMaxCharacters() ? ' validate-length maximum-length-' . (int) $_option->getMaxCharacters() : '' ?> product-custom-option" + name="options[<?= $block->escapeHtmlAttr($_option->getId()) ?>]" + value="<?= $block->escapeHtmlAttr($block->getDefaultValue()) ?>" + price="<?= $block->escapeHtmlAttr($block->getCurrencyPrice($_option->getPrice(true))) ?>" /> + <?php elseif ($_option->getType() == \Magento\Catalog\Api\Data\ProductCustomOptionInterface::OPTION_TYPE_AREA) :?> + <textarea id="options_<?= $block->escapeHtmlAttr($_option->getId()) ?>_text" + class="admin__control-textarea <?= $_option->getIsRequire() ? ' required-entry' : '' ?> <?= $_option->getMaxCharacters() ? ' validate-length maximum-length-' . (int) $_option->getMaxCharacters() : '' ?> product-custom-option" + name="options[<?= $block->escapeHtmlAttr($_option->getId()) ?>]" + rows="5" + cols="25" + price="<?= $block->escapeHtmlAttr($block->getCurrencyPrice($_option->getPrice(true))) ?>"><?= $block->escapeHtml($block->getDefaultValue()) ?></textarea> <?php endif;?> - <?php if ($_option->getMaxCharacters()): ?> - <p class="note"><?= /* @escapeNotVerified */ __('Maximum number of characters:') ?> <strong><?= /* @escapeNotVerified */ $_option->getMaxCharacters() ?></strong></p> + <?php if ($_option->getMaxCharacters()) :?> + <p class="note"><?= $block->escapeHtml(__('Maximum number of characters:')) ?> <strong><?= (int) $_option->getMaxCharacters() ?></strong></p> <?php endif; ?> </div> </div> diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/composite/fieldset/qty.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/composite/fieldset/qty.phtml index 487c9b8e8f2b7..4726bdc0930fd 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/composite/fieldset/qty.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/composite/fieldset/qty.phtml @@ -3,9 +3,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <?php /* @var $block \Magento\Catalog\Block\Adminhtml\Product\Composite\Fieldset\Qty */ ?> @@ -13,9 +10,9 @@ <fieldset id="product_composite_configure_fields_qty" class="fieldset product-composite-qty-block admin__fieldset <?= $block->getIsLastFieldset() ? 'last-fieldset' : '' ?>"> <div class="field admin__field"> - <label class="label admin__field-label"><span><?= /* @escapeNotVerified */ __('Quantity') ?></span></label> + <label class="label admin__field-label"><span><?= $block->escapeHtml(__('Quantity')) ?></span></label> <div class="control admin__field-control"> - <input id="product_composite_configure_input_qty" class="input-text admin__control-text qty" type="text" name="qty" value="<?= /* @escapeNotVerified */ $block->getQtyValue() * 1 ?>"> + <input id="product_composite_configure_input_qty" class="input-text admin__control-text qty" type="text" name="qty" value="<?= $block->getQtyValue() * 1 ?>"> </div> </div> </fieldset> diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/edit.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/edit.phtml index 7c25c3686eadc..66df098a194ae 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/edit.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/edit.phtml @@ -3,11 +3,10 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <?php +// phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis + /** * @var $block \Magento\Catalog\Block\Adminhtml\Product\Edit */ @@ -17,11 +16,11 @@ <div id="product-template-suggest-container" class="suggest-expandable"> <div class="action-dropdown"> <button type="button" class="action-toggle" data-mage-init='{"dropdown":{}}' data-toggle="dropdown"> - <span><?= /* @escapeNotVerified */ $block->getAttributeSetName() ?></span> + <span><?= $block->escapeHtml($block->getAttributeSetName()) ?></span> </button> <ul class="dropdown-menu"> <li><input type="text" id="product-template-suggest" class="search" - placeholder="<?= /* @noEscape */ __('start typing to search template') ?>"/></li> + placeholder="<?= $block->escapeHtmlAttr(__('start typing to search template')) ?>"/></li> </ul> </div> </div> @@ -30,32 +29,32 @@ <input type="checkbox" id="product-online-switcher" name="product-online-switcher" /> <label class="switcher-label" for="product-online-switcher" - data-text-on="<?= /* @escapeNotVerified */ __('Product online') ?>" - data-text-off="<?= /* @escapeNotVerified */ __('Product offline') ?>" - title="<?= /* @escapeNotVerified */ __('Product online status') ?>"></label> + data-text-on="<?= $block->escapeHtmlAttr(__('Product online')) ?>" + data-text-off="<?= $block->escapeHtmlAttr(__('Product offline')) ?>" + title="<?= $block->escapeHtmlAttr(__('Product online status')) ?>"></label> </div> - <?php if ($block->getProductId()): ?> + <?php if ($block->getProductId()) :?> <?= $block->getDeleteButtonHtml() ?> <?php endif; ?> - <?php if ($block->getProductSetId()): ?> + <?php if ($block->getProductSetId()) :?> <?= $block->getChangeAttributeSetButtonHtml() ?> <?= $block->getSaveSplitButtonHtml() ?> <?php endif; ?> <?= $block->getBackButtonHtml() ?> </div> </div> -<?php if ($block->getUseContainer()): ?> -<form action="<?= /* @escapeNotVerified */ $block->getSaveUrl() ?>" method="post" enctype="multipart/form-data" - data-form="edit-product" data-product-id="<?= /* @escapeNotVerified */ $block->getProduct()->getId() ?>"> +<?php if ($block->getUseContainer()) :?> +<form action="<?= $block->escapeUrl($block->getSaveUrl()) ?>" method="post" enctype="multipart/form-data" + data-form="edit-product" data-product-id="<?= $block->escapeHtmlAttr($block->getProduct()->getId()) ?>"> <?php endif; ?> <?= $block->getBlockHtml('formkey') ?> <div data-role="tabs" id="product-edit-form-tabs"></div> <?php /* @TODO: remove id after elimination of setDestElementId('product-edit-form-tabs') */?> <?= $block->getChildHtml('product-type-tabs') ?> - <input type="hidden" id="product_type_id" value="<?= /* @escapeNotVerified */ $block->getProduct()->getTypeId() ?>"/> - <input type="hidden" id="attribute_set_id" value="<?= /* @escapeNotVerified */ $block->getProduct()->getAttributeSetId() ?>"/> + <input type="hidden" id="product_type_id" value="<?= $block->escapeHtmlAttr($block->getProduct()->getTypeId()) ?>"/> + <input type="hidden" id="attribute_set_id" value="<?= $block->escapeHtmlAttr($block->getProduct()->getAttributeSetId()) ?>"/> <button type="submit" class="hidden"></button> -<?php if ($block->getUseContainer()): ?> +<?php if ($block->getUseContainer()) :?> </form> <?php endif; ?> <script> @@ -130,10 +129,10 @@ require([ } } }); - $form.mage('validation', {validationUrl: '<?= /* @escapeNotVerified */ $block->getValidationUrl() ?>'}); + $form.mage('validation', {validationUrl: '<?= $block->escapeJs($block->escapeUrl($block->getValidationUrl())) ?>'}); - var masks = <?= /* @escapeNotVerified */ $this->helper('Magento\Framework\Json\Helper\Data')->jsonEncode($block->getFieldsAutogenerationMasks()) ?>; - var availablePlaceholders = <?= /* @escapeNotVerified */ $this->helper('Magento\Framework\Json\Helper\Data')->jsonEncode($block->getAttributesAllowedForAutogeneration()) ?>; + var masks = <?= /* @noEscape */ $this->helper(Magento\Framework\Json\Helper\Data::class)->jsonEncode($block->getFieldsAutogenerationMasks()) ?>; + var availablePlaceholders = <?= /* @noEscape */ $this->helper(Magento\Framework\Json\Helper\Data::class)->jsonEncode($block->getAttributesAllowedForAutogeneration()) ?>; var Autogenerator = function(masks) { this._masks = masks || {}; this._fieldReverseIndex = this._buildReverseIndex(this._masks); diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/edit/action/attribute.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/edit/action/attribute.phtml index a3b0b32e4c29a..056cf014f769a 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/edit/action/attribute.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/edit/action/attribute.phtml @@ -4,18 +4,21 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - +/** @var $block Magento\Catalog\Block\Adminhtml\Product\Edit\Action\Attribute */ ?> -<form action="<?= /* @escapeNotVerified */ $block->getSaveUrl() ?>" method="post" id="attributes-edit-form" class="attributes-edit-form" enctype="multipart/form-data"> +<form action="<?= $block->escapeUrl($block->getSaveUrl()) ?>" + method="post" + id="attributes-edit-form" + class="attributes-edit-form" + enctype="multipart/form-data"> <?= $block->getBlockHtml('formkey') ?> </form> <script type="text/x-magento-init"> { "#attributes-edit-form": { "Magento_Catalog/catalog/product/edit/attribute": { - "validationUrl": "<?= /* @escapeNotVerified */ $block->getValidationUrl() ?>" + "validationUrl": "<?= $block->escapeJs($block->escapeUrl($block->getValidationUrl())) ?>" } } } diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/edit/action/inventory.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/edit/action/inventory.phtml index 64c8ba7dcf49f..792af12494af6 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/edit/action/inventory.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/edit/action/inventory.phtml @@ -4,7 +4,6 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile /** @var Magento\Catalog\Block\Adminhtml\Product\Edit\Tab\Inventory $block */ ?> <script> @@ -40,326 +39,363 @@ if (!is_numeric($defaultMinSaleQty)) { <div class="fieldset-wrapper form-inline advanced-inventory-edit"> <div class="fieldset-wrapper-title"> <strong class="title"> - <span><?= /* @escapeNotVerified */ __('Advanced Inventory') ?></span> + <span><?= $block->escapeHtml(__('Advanced Inventory')) ?></span> </strong> </div> <div class="fieldset-wrapper-content"> <fieldset class="fieldset" id="table_cataloginventory"> <div class="field"> <label class="label" for="inventory_manage_stock"> - <span><?= /* @escapeNotVerified */ __('Manage Stock') ?></span> + <span><?= $block->escapeHtml(__('Manage Stock')) ?></span> </label> <div class="control"> <div class="fields-group-2"> <div class="field"> - <select id="inventory_manage_stock" name="<?= /* @escapeNotVerified */ $block->getFieldSuffix() ?>[manage_stock]" + <select id="inventory_manage_stock" name="<?= /* @noEscape */ $block->getFieldSuffix() ?>[manage_stock]" class="select" disabled="disabled"> - <option value="1"><?= /* @escapeNotVerified */ __('Yes') ?></option> - <option - value="0"<?php if ($block->getFieldValue('manage_stock') == 0): ?> selected="selected"<?php endif; ?>><?= /* @escapeNotVerified */ __('No') ?></option> + <option value="1"><?= $block->escapeHtml(__('Yes')) ?></option> + <option value="0" + <?php if ($block->getFieldValue('manage_stock') == 0) :?> + selected="selected" + <?php endif; ?>><?= $block->escapeHtml(__('No')) ?></option> </select> </div> <div class="field choice"> - <input name="<?= /* @escapeNotVerified */ $block->getFieldSuffix() ?>[use_config_manage_stock]" type="checkbox" + <input name="<?= /* @noEscape */ $block->getFieldSuffix() ?>[use_config_manage_stock]" type="checkbox" id="inventory_use_config_manage_stock" data-role="toggle-editability" value="1" checked="checked" disabled="disabled"/> <label for="inventory_use_config_manage_stock" - class="label"><span><?= /* @escapeNotVerified */ __('Use Config Settings') ?></span></label> + class="label"><span><?= $block->escapeHtml(__('Use Config Settings')) ?></span></label> </div> <div class="field choice"> <input type="checkbox" id="inventory_manage_stock_checkbox" data-role="toggle-editability-all"/> <label for="inventory_manage_stock_checkbox" - class="label"><span><?= /* @escapeNotVerified */ __('Change') ?></span></label> + class="label"><span><?= $block->escapeHtml(__('Change')) ?></span></label> </div> </div> </div> - <div class="field-service" value-scope="<?= /* @escapeNotVerified */ __('[GLOBAL]') ?>"></div> + <div class="field-service" value-scope="<?= $block->escapeHtmlAttr(__('[GLOBAL]')) ?>"></div> </div> <div class="field required"> <label class="label" for="inventory_qty"> - <span><?= /* @escapeNotVerified */ __('Qty') ?></span> + <span><?= $block->escapeHtml(__('Qty')) ?></span> </label> <div class="control"> <div class="fields-group-2"> <div class="field"> <input type="text" class="input-text required-entry validate-number" id="inventory_qty" - name="<?= /* @escapeNotVerified */ $block->getFieldSuffix() ?>[qty]" - value="<?= /* @escapeNotVerified */ $block->getDefaultConfigValue('qty') * 1 ?>" disabled="disabled"/> + name="<?= /* @noEscape */ $block->getFieldSuffix() ?>[qty]" + value="<?= $block->getDefaultConfigValue('qty') * 1 ?>" disabled="disabled"/> </div> <div class="field choice"> <input type="checkbox" id="inventory_qty_checkbox" data-role="toggle-editability-all"/> <label for="inventory_qty_checkbox" - class="label"><span><?= /* @escapeNotVerified */ __('Change') ?></span></label> + class="label"><span><?= $block->escapeHtml(__('Change')) ?></span></label> </div> </div> </div> - <div class="field-service" value-scope="<?= /* @escapeNotVerified */ __('[GLOBAL]') ?>"></div> + <div class="field-service" value-scope="<?= $block->escapeHtmlAttr(__('[GLOBAL]')) ?>"></div> </div> <div class="field with-addon"> <label class="label" for="inventory_min_qty"> - <span><?= /* @escapeNotVerified */ __('Out-of-Stock Threshold') ?></span> + <span><?= $block->escapeHtml(__('Out-of-Stock Threshold')) ?></span> </label> <div class="control"> <div class="fields-group-2"> <div class="field"> <input type="text" class="input-text validate-number" id="inventory_min_qty" - name="<?= /* @escapeNotVerified */ $block->getFieldSuffix() ?>[min_qty]" - value="<?= /* @escapeNotVerified */ $block->getDefaultConfigValue('min_qty') * 1 ?>" disabled="disabled"/> + name="<?= /* @noEscape */ $block->getFieldSuffix() ?>[min_qty]" + value="<?= $block->getDefaultConfigValue('min_qty') * 1 ?>" disabled="disabled"/> </div> <div class="field choice"> <input type="checkbox" id="inventory_use_config_min_qty" - name="<?= /* @escapeNotVerified */ $block->getFieldSuffix() ?>[use_config_min_qty]" value="1" + name="<?= /* @noEscape */ $block->getFieldSuffix() ?>[use_config_min_qty]" value="1" data-role="toggle-editability" checked="checked" disabled="disabled"/> <label for="inventory_use_config_min_qty" class="label"> - <span><?= /* @escapeNotVerified */ __('Use Config Settings') ?></span> + <span><?= $block->escapeHtml(__('Use Config Settings')) ?></span> </label> </div> <div class="field choice"> <input type="checkbox" id="inventory_min_qty_checkbox" data-role="toggle-editability-all"/> <label for="inventory_min_qty_checkbox" - class="label"><span><?= /* @escapeNotVerified */ __('Change') ?></span></label> + class="label"><span><?= $block->escapeHtml(__('Change')) ?></span></label> </div> </div> </div> - <div class="field-service" value-scope="<?= /* @escapeNotVerified */ __('[GLOBAL]') ?>"></div> + <div class="field-service" value-scope="<?= $block->escapeHtmlAttr(__('[GLOBAL]')) ?>"></div> </div> <div class="field"> <label class="label" for="inventory_min_sale_qty"> - <span><?= /* @escapeNotVerified */ __('Minimum Qty Allowed in Shopping Cart') ?></span> + <span><?= $block->escapeHtml(__('Minimum Qty Allowed in Shopping Cart')) ?></span> </label> <div class="control"> <div class="fields-group-2"> <div class="field"> <input type="text" class="input-text validate-number" id="inventory_min_sale_qty" - name="<?= /* @escapeNotVerified */ $block->getFieldSuffix() ?>[min_sale_qty]" - value="<?= /* @escapeNotVerified */ $defaultMinSaleQty ?>" + name="<?= /* @noEscape */ $block->getFieldSuffix() ?>[min_sale_qty]" + value="<?= $defaultMinSaleQty * 1 ?>" disabled="disabled"/> </div> <div class="field choice"> <input type="checkbox" id="inventory_use_config_min_sale_qty" - name="<?= /* @escapeNotVerified */ $block->getFieldSuffix() ?>[use_config_min_sale_qty]" value="1" data-role="toggle-editability" checked="checked" disabled="disabled"/> + name="<?= /* @noEscape */ $block->getFieldSuffix() ?>[use_config_min_sale_qty]" + value="1" + data-role="toggle-editability" + checked="checked" + disabled="disabled"/> <label for="inventory_use_config_min_sale_qty" - class="label"><span><?= /* @escapeNotVerified */ __('Use Config Settings') ?></span></label> + class="label"><span><?= $block->escapeHtml(__('Use Config Settings')) ?></span></label> </div> <div class="field choice"> <input type="checkbox" id="inventory_min_sale_qty_checkbox" data-role="toggle-editability-all"/> <label for="inventory_min_sale_qty_checkbox" - class="label"><span><?= /* @escapeNotVerified */ __('Change') ?></span></label> + class="label"><span><?= $block->escapeHtml(__('Change')) ?></span></label> </div> </div> </div> - <div class="field-service" value-scope="<?= /* @escapeNotVerified */ __('[GLOBAL]') ?>"></div> + <div class="field-service" value-scope="<?= $block->escapeHtmlAttr(__('[GLOBAL]')) ?>"></div> </div> <div class="field"> <label class="label" for="inventory_max_sale_qty"> - <span><?= /* @escapeNotVerified */ __('Maximum Qty Allowed in Shopping Cart') ?></span> + <span><?= $block->escapeHtml(__('Maximum Qty Allowed in Shopping Cart')) ?></span> </label> <div class="control"> <div class="fields-group-2"> <div class="field"> <input type="text" class="input-text validate-number" id="inventory_max_sale_qty" - name="<?= /* @escapeNotVerified */ $block->getFieldSuffix() ?>[max_sale_qty]" - value="<?= /* @escapeNotVerified */ $block->getDefaultConfigValue('max_sale_qty') * 1 ?>" + name="<?= /* @noEscape */ $block->getFieldSuffix() ?>[max_sale_qty]" + value="<?= $block->getDefaultConfigValue('max_sale_qty') * 1 ?>" disabled="disabled"/> </div> <div class="field choice"> - <input type="checkbox" id="inventory_use_config_max_sale_qty" - name="<?= /* @escapeNotVerified */ $block->getFieldSuffix() ?>[use_config_max_sale_qty]" value="1" data-role="toggle-editability" checked="checked" disabled="disabled"/> + <input type="checkbox" + id="inventory_use_config_max_sale_qty" + name="<?= /* @noEscape */ $block->getFieldSuffix() ?>[use_config_max_sale_qty]" + value="1" + data-role="toggle-editability" + checked="checked" + disabled="disabled"/> <label for="inventory_use_config_max_sale_qty" - class="label"><span><?= /* @escapeNotVerified */ __('Use Config Settings') ?></span></label> + class="label"><span><?= $block->escapeHtml(__('Use Config Settings')) ?></span></label> </div> <div class="field choice"> <input type="checkbox" id="inventory_max_sale_checkbox" data-role="toggle-editability-all"/> <label for="inventory_max_sale_checkbox" - class="label"><span><?= /* @escapeNotVerified */ __('Change') ?></span></label> + class="label"><span><?= $block->escapeHtml(__('Change')) ?></span></label> </div> </div> </div> - <div class="field-service" value-scope="<?= /* @escapeNotVerified */ __('[GLOBAL]') ?>"></div> + <div class="field-service" value-scope="<?= $block->escapeHtmlAttr(__('[GLOBAL]')) ?>"></div> </div> <div class="field"> <label class="label" for="inventory_is_qty_decimal"> - <span><?= /* @escapeNotVerified */ __('Qty Uses Decimals') ?></span> + <span><?= $block->escapeHtml(__('Qty Uses Decimals')) ?></span> </label> <div class="control"> <div class="fields-group-2"> <div class="field"> <select id="inventory_is_qty_decimal" - name="<?= /* @escapeNotVerified */ $block->getFieldSuffix() ?>[is_qty_decimal]" class="select" + name="<?= /* @noEscape */ $block->getFieldSuffix() ?>[is_qty_decimal]" + class="select" disabled="disabled"> - <option value="0"><?= /* @escapeNotVerified */ __('No') ?></option> - <option - value="1"<?php if ($block->getDefaultConfigValue('is_qty_decimal') == 1): ?> selected="selected"<?php endif; ?>><?= /* @escapeNotVerified */ __('Yes') ?></option> + <option value="0"><?= $block->escapeHtml(__('No')) ?></option> + <option value="1" + <?php if ($block->getDefaultConfigValue('is_qty_decimal') == 1) :?> + selected="selected" + <?php endif; ?>><?= $block->escapeHtml(__('Yes')) ?></option> </select> </div> <div class="field choice"> <input type="checkbox" id="inventory_is_qty_decimal_checkbox" data-role="toggle-editability-all"/> <label for="inventory_is_qty_decimal_checkbox" - class="label"><span><?= /* @escapeNotVerified */ __('Change') ?></span></label> + class="label"><span><?= $block->escapeHtml(__('Change')) ?></span></label> </div> </div> </div> - <div class="field-service" value-scope="<?= /* @escapeNotVerified */ __('[GLOBAL]') ?>"></div> + <div class="field-service" value-scope="<?= $block->escapeHtmlAttr(__('[GLOBAL]')) ?>"></div> </div> <div class="field"> <label class="label" for="inventory_backorders"> - <span><?= /* @escapeNotVerified */ __('Backorders') ?></span> + <span><?= $block->escapeHtml(__('Backorders')) ?></span> </label> <div class="control"> <div class="fields-group-2"> <div class="field"> - <select id="inventory_backorders" name="<?= /* @escapeNotVerified */ $block->getFieldSuffix() ?>[backorders]" - class="select" disabled="disabled"> - <?php foreach ($block->getBackordersOption() as $option): ?> + <select id="inventory_backorders" + name="<?= /* @noEscape */ $block->getFieldSuffix() ?>[backorders]" + class="select" + disabled="disabled"> + <?php foreach ($block->getBackordersOption() as $option) :?> <?php $_selected = ($option['value'] == $block->getDefaultConfigValue('backorders')) ? ' selected="selected"' : '' ?> <option - value="<?= /* @escapeNotVerified */ $option['value'] ?>"<?= /* @escapeNotVerified */ $_selected ?>><?= /* @escapeNotVerified */ $option['label'] ?></option> + value="<?= $block->escapeHtmlAttr($option['value']) ?>"<?= /* @noEscape */ $_selected ?>><?= $block->escapeHtml($option['label']) ?></option> <?php endforeach; ?> </select> </div> <div class="field choice"> <input type="checkbox" id="inventory_use_config_backorders" - name="<?= /* @escapeNotVerified */ $block->getFieldSuffix() ?>[use_config_backorders]" value="1" data-role="toggle-editability" checked="checked" disabled="disabled"/> + name="<?= /* @noEscape */ $block->getFieldSuffix() ?>[use_config_backorders]" + value="1" + data-role="toggle-editability" + checked="checked" + disabled="disabled"/> <label for="inventory_use_config_backorders" - class="label"><span><?= /* @escapeNotVerified */ __('Use Config Settings') ?></span></label> + class="label"><span><?= $block->escapeHtml(__('Use Config Settings')) ?></span></label> </div> <div class="field choice"> <input type="checkbox" id="inventory_backorders_checkbox" data-role="toggle-editability-all"/> - <label for="inventory_backorders_checkbox" class="label"><span><?= /* @escapeNotVerified */ __('Change') ?></span></label> + <label for="inventory_backorders_checkbox" + class="label"><span><?= $block->escapeHtml(__('Change')) ?></span></label> </div> </div> </div> - <div class="field-service" value-scope="<?= /* @escapeNotVerified */ __('[GLOBAL]') ?>"></div> + <div class="field-service" value-scope="<?= $block->escapeHtmlAttr(__('[GLOBAL]')) ?>"></div> </div> <div class="field"> <label class="label" for="inventory_notify_stock_qty"> - <span><?= /* @escapeNotVerified */ __('Notify for Quantity Below') ?></span> + <span><?= $block->escapeHtml(__('Notify for Quantity Below')) ?></span> </label> <div class="control"> <div class="fields-group-2"> <div class="field"> <input type="text" class="input-text validate-number" id="inventory_notify_stock_qty" - name="<?= /* @escapeNotVerified */ $block->getFieldSuffix() ?>[notify_stock_qty]" - value="<?= /* @escapeNotVerified */ $block->getDefaultConfigValue('notify_stock_qty') * 1 ?>" + name="<?= /* @noEscape */ $block->getFieldSuffix() ?>[notify_stock_qty]" + value="<?= $block->getDefaultConfigValue('notify_stock_qty') * 1 ?>" disabled="disabled"/> </div> <div class="field choice"> - <input type="checkbox" id="inventory_use_config_notify_stock_qty" - name="<?= /* @escapeNotVerified */ $block->getFieldSuffix() ?>[use_config_notify_stock_qty]" value="1" data-role="toggle-editability" checked="checked" disabled="disabled"/> + <input type="checkbox" + id="inventory_use_config_notify_stock_qty" + name="<?= /* @noEscape */ $block->getFieldSuffix() ?>[use_config_notify_stock_qty]" + value="1" + data-role="toggle-editability" + checked="checked" + disabled="disabled"/> <label for="inventory_use_config_notify_stock_qty" - class="label"><span><?= /* @escapeNotVerified */ __('Use Config Settings') ?></span></label> + class="label"><span><?= $block->escapeHtml(__('Use Config Settings')) ?></span></label> </div> <div class="field choice"> <input type="checkbox" id="inventory_notify_stock_qty_checkbox" data-role="toggle-editability-all"/> <label for="inventory_notify_stock_qty_checkbox" - class="label"><span><?= /* @escapeNotVerified */ __('Change') ?></span></label> + class="label"><span><?= $block->escapeHtml(__('Change')) ?></span></label> </div> </div> </div> - <div class="field-service" value-scope="<?= /* @escapeNotVerified */ __('[GLOBAL]') ?>"></div> + <div class="field-service" value-scope="<?= $block->escapeHtmlAttr(__('[GLOBAL]')) ?>"></div> </div> <div class="field"> <label class="label" for="inventory_enable_qty_increments"> - <span><?= /* @escapeNotVerified */ __('Enable Qty Increments') ?></span> + <span><?= $block->escapeHtml(__('Enable Qty Increments')) ?></span> </label> <div class="control"> <div class="fields-group-2"> <div class="field"> <select id="inventory_enable_qty_increments" - name="<?= /* @escapeNotVerified */ $block->getFieldSuffix() ?>[enable_qty_increments]" class="select" + name="<?= /* @noEscape */ $block->getFieldSuffix() ?>[enable_qty_increments]" + class="select" disabled="disabled"> - <option value="1"><?= /* @escapeNotVerified */ __('Yes') ?></option> - <option - value="0"<?php if ($block->getDefaultConfigValue('enable_qty_increments') == 0): ?> selected="selected"<?php endif; ?>><?= /* @escapeNotVerified */ __('No') ?></option> + <option value="1"><?= $block->escapeHtml(__('Yes')) ?></option> + <option value="0" + <?php if ($block->getDefaultConfigValue('enable_qty_increments') == 0) :?> + selected="selected" + <?php endif; ?>><?= $block->escapeHtml(__('No')) ?></option> </select> </div> <div class="field choice"> <input type="checkbox" id="inventory_use_config_enable_qty_increments" - name="<?= /* @escapeNotVerified */ $block->getFieldSuffix() ?>[use_config_enable_qty_increments]" value="1" data-role="toggle-editability" checked="checked" disabled="disabled"/> + name="<?= /* @noEscape */ $block->getFieldSuffix() ?>[use_config_enable_qty_increments]" + value="1" + data-role="toggle-editability" + checked="checked" + disabled="disabled"/> <label for="inventory_use_config_enable_qty_increments" - class="label"><span><?= /* @escapeNotVerified */ __('Use Config Settings') ?></span></label> + class="label"><span><?= $block->escapeHtml(__('Use Config Settings')) ?></span></label> </div> <div class="field choice"> <input type="checkbox" id="inventory_enable_qty_increments_checkbox" data-role="toggle-editability-all"/> <label for="inventory_enable_qty_increments_checkbox" - class="label"><span><?= /* @escapeNotVerified */ __('Change') ?></span></label> + class="label"><span><?= $block->escapeHtml(__('Change')) ?></span></label> </div> </div> </div> - <div class="field-service" value-scope="<?= /* @escapeNotVerified */ __('[GLOBAL]') ?>"></div> + <div class="field-service" value-scope="<?= $block->escapeHtmlAttr(__('[GLOBAL]')) ?>"></div> </div> <div class="field"> <label class="label" for="inventory_qty_increments"> - <span><?= /* @escapeNotVerified */ __('Qty Increments') ?></span> + <span><?= $block->escapeHtml(__('Qty Increments')) ?></span> </label> <div class="control"> <div class="fields-group-2"> <div class="field"> <input type="text" class="input-text validate-number" id="inventory_qty_increments" - name="<?= /* @escapeNotVerified */ $block->getFieldSuffix() ?>[qty_increments]" - value="<?= /* @escapeNotVerified */ $block->getDefaultConfigValue('qty_increments') * 1 ?>" + name="<?= /* @noEscape */ $block->getFieldSuffix() ?>[qty_increments]" + value="<?= $block->getDefaultConfigValue('qty_increments') * 1 ?>" disabled="disabled"/> </div> <div class="field choice"> - <input type="checkbox" id="inventory_use_config_qty_increments" - name="<?= /* @escapeNotVerified */ $block->getFieldSuffix() ?>[use_config_qty_increments]" value="1" data-role="toggle-editability" checked="checked" disabled="disabled"/> + <input type="checkbox" + id="inventory_use_config_qty_increments" + name="<?= /* @noEscape */ $block->getFieldSuffix() ?>[use_config_qty_increments]" + value="1" + data-role="toggle-editability" + checked="checked" + disabled="disabled"/> <label for="inventory_use_config_qty_increments" - class="label"><span><?= /* @escapeNotVerified */ __('Use Config Settings') ?></span></label> + class="label"><span><?= $block->escapeHtml(__('Use Config Settings')) ?></span></label> </div> <div class="field choice"> <input type="checkbox" id="inventory_qty_increments_checkbox" data-role="toggle-editability-all"/> <label for="inventory_qty_increments_checkbox" - class="label"><span><?= /* @escapeNotVerified */ __('Change') ?></span></label> + class="label"><span><?= $block->escapeHtml(__('Change')) ?></span></label> </div> </div> </div> - <div class="field-service" value-scope="<?= /* @escapeNotVerified */ __('[GLOBAL]') ?>"></div> + <div class="field-service" value-scope="<?= $block->escapeHtmlAttr(__('[GLOBAL]')) ?>"></div> </div> <div class="field"> <label class="label" for="inventory_stock_availability"> - <span><?= /* @escapeNotVerified */ __('Stock Availability') ?></span> + <span><?= $block->escapeHtml(__('Stock Availability')) ?></span> </label> <div class="control"> <div class="fields-group-2"> <div class="field"> <select id="inventory_stock_availability" - name="<?= /* @escapeNotVerified */ $block->getFieldSuffix() ?>[is_in_stock]" class="select" + name="<?= /* @noEscape */ $block->getFieldSuffix() ?>[is_in_stock]" class="select" disabled="disabled"> - <option value="1"><?= /* @escapeNotVerified */ __('In Stock') ?></option> - <option - value="0"<?php if ($block->getDefaultConfigValue('is_in_stock') == 0): ?> selected<?php endif; ?>><?= /* @escapeNotVerified */ __('Out of Stock') ?></option> + <option value="1"><?= $block->escapeHtml(__('In Stock')) ?></option> + <option value="0"<?php if ($block->getDefaultConfigValue('is_in_stock') == 0) :?> selected<?php endif; ?>><?= $block->escapeHtml(__('Out of Stock')) ?></option> </select> </div> <div class="field choice"> <input type="checkbox" id="inventory_stock_availability_checkbox" data-role="toggle-editability-all"/> <label for="inventory_stock_availability_checkbox" - class="label"><span><?= /* @escapeNotVerified */ __('Change') ?></span></label> + class="label"><span><?= $block->escapeHtml(__('Change')) ?></span></label> </div> </div> </div> - <div class="field-service" value-scope="<?= /* @escapeNotVerified */ __('[GLOBAL]') ?>"></div> + <div class="field-service" value-scope="<?= $block->escapeHtmlAttr(__('[GLOBAL]')) ?>"></div> </div> </fieldset> </div> diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/edit/action/websites.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/edit/action/websites.phtml index cd297a7bbf27b..98b06050e0d1d 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/edit/action/websites.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/edit/action/websites.phtml @@ -4,29 +4,35 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - +/** @var $block Magento\Catalog\Block\Adminhtml\Product\Edit\Action\Attribute\Tab\Websites */ ?> <div class="fieldset-wrapper" id="add-products-to-website-wrapper"> <fieldset class="fieldset" id="grop_fields"> <legend class="legend"> - <span><?= /* @escapeNotVerified */ __('Add Product To Websites') ?></span> + <span><?= $block->escapeHtml(__('Add Product To Websites')) ?></span> </legend> <br> <div class="store-scope"> <div class="store-tree" id="add-products-to-website-content"> - <?php foreach ($block->getWebsiteCollection() as $_website): ?> + <?php foreach ($block->getWebsiteCollection() as $_website) :?> <div class="website-name"> - <input name="add_website_ids[]" value="<?= /* @escapeNotVerified */ $_website->getId() ?>" <?php if ($block->getWebsitesReadonly()): ?>disabled="disabled"<?php endif;?> class="checkbox website-checkbox" id="add_product_website_<?= /* @escapeNotVerified */ $_website->getId() ?>" type="checkbox" /> - <label for="add_product_website_<?= /* @escapeNotVerified */ $_website->getId() ?>"><?= $block->escapeHtml($_website->getName()) ?></label> + <input name="add_website_ids[]" + value="<?= $block->escapeHtmlAttr($_website->getId()) ?>" + <?php if ($block->getWebsitesReadonly()) :?> + disabled="disabled" + <?php endif;?> + class="checkbox website-checkbox" + id="add_product_website_<?= $block->escapeHtmlAttr($_website->getId()) ?>" + type="checkbox" /> + <label for="add_product_website_<?= $block->escapeHtmlAttr($_website->getId()) ?>"><?= $block->escapeHtml($_website->getName()) ?></label> </div> - <dl class="webiste-groups" id="add_product_website_<?= /* @escapeNotVerified */ $_website->getId() ?>_data"> - <?php foreach ($block->getGroupCollection($_website) as $_group): ?> + <dl class="webiste-groups" id="add_product_website_<?= $block->escapeHtmlAttr($_website->getId()) ?>_data"> + <?php foreach ($block->getGroupCollection($_website) as $_group) :?> <dt><?= $block->escapeHtml($_group->getName()) ?></dt> <dd class="group-stores"> <ul> - <?php foreach ($block->getStoreCollection($_group) as $_store): ?> + <?php foreach ($block->getStoreCollection($_group) as $_store) :?> <li> <?= $block->escapeHtml($_store->getName()) ?> </li> @@ -44,27 +50,35 @@ <div class="fieldset-wrapper" id="remove-products-to-website-wrapper"> <fieldset class="fieldset" id="grop_fields"> <legend class="legend"> - <span><?= /* @escapeNotVerified */ __('Remove Product From Websites') ?></span> + <span><?= $block->escapeHtml(__('Remove Product From Websites')) ?></span> </legend> <br> <div class="messages"> <div class="message message-notice"> - <div><?= /* @escapeNotVerified */ __('To hide an item in catalog or search results, set the status to "Disabled".') ?></div> + <div><?= $block->escapeHtml(__('To hide an item in catalog or search results, set the status to "Disabled".')) ?></div> </div> </div> <div class="store-scope"> <div class="store-tree" id="remove-products-to-website-content"> - <?php foreach ($block->getWebsiteCollection() as $_website): ?> + <?php foreach ($block->getWebsiteCollection() as $_website) :?> <div class="website-name"> - <input name="remove_website_ids[]" value="<?= /* @escapeNotVerified */ $_website->getId() ?>" <?php if ($block->getWebsitesReadonly()): ?>disabled="disabled"<?php endif;?> class="checkbox website-checkbox" id="remove_product_website_<?= /* @escapeNotVerified */ $_website->getId() ?>" type="checkbox" /> - <label for="remove_product_website_<?= /* @escapeNotVerified */ $_website->getId() ?>"><?= $block->escapeHtml($_website->getName()) ?></label> + <input name="remove_website_ids[]" + value="<?= $block->escapeHtmlAttr($_website->getId()) ?>" + <?php if ($block->getWebsitesReadonly()) :?> + disabled="disabled" + <?php endif;?> + class="checkbox website-checkbox" + id="remove_product_website_<?= $block->escapeHtmlAttr($_website->getId()) ?>" + type="checkbox" /> + <label for="remove_product_website_<?= $block->escapeHtmlAttr($_website->getId()) ?>"><?= $block->escapeHtml($_website->getName()) ?></label> </div> - <dl class="webiste-groups" id="remove_product_website_<?= /* @escapeNotVerified */ $_website->getId() ?>_data"> - <?php foreach ($block->getGroupCollection($_website) as $_group): ?> + <dl class="webiste-groups" + id="remove_product_website_<?= $block->escapeHtmlAttr($_website->getId()) ?>_data"> + <?php foreach ($block->getGroupCollection($_website) as $_group) :?> <dt><?= $block->escapeHtml($_group->getName()) ?></dt> <dd class="group-stores"> <ul> - <?php foreach ($block->getStoreCollection($_group) as $_store): ?> + <?php foreach ($block->getStoreCollection($_group) as $_store) :?> <li> <?= $block->escapeHtml($_store->getName()) ?> </li> diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/edit/attribute_set.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/edit/attribute_set.phtml index a7e8564e7a1d8..d073053e2f854 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/edit/attribute_set.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/edit/attribute_set.phtml @@ -4,9 +4,9 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile +// phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis - /* @var $block \Magento\Catalog\Block\Adminhtml\Product\Edit\AttributeSet */ +/* @var $block \Magento\Catalog\Block\Adminhtml\Product\Edit\AttributeSet */ ?> <script id="product-template-selector-template" type="text/x-magento-template"> <% if (!data.term && data.items.length && !data.allShown()) { %> @@ -32,7 +32,7 @@ } }); $suggest - .mage('suggest',<?= /* @escapeNotVerified */ $this->helper('Magento\Framework\Json\Helper\Data')->jsonEncode($block->getSelectorOptions()) ?>) + .mage('suggest',<?= /* @noEscape */ $this->helper(Magento\Framework\Json\Helper\Data::class)->jsonEncode($block->getSelectorOptions()) ?>) .on('suggestselect', function (e, ui) { if (ui.item.id) { $('[data-form=edit-product]').trigger('changeAttributeSet', ui.item); diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/edit/category/new/form.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/edit/category/new/form.phtml index 84c3257840259..f12a99e6c7843 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/edit/category/new/form.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/edit/category/new/form.phtml @@ -5,7 +5,7 @@ */ /* @var $block \Magento\Catalog\Block\Adminhtml\Product\Edit\NewCategory */ ?> -<div id="<?= /* @escapeNotVerified */ $block->getNameInLayout() ?>" style="display:none"> +<div id="<?= $block->escapeHtmlAttr($block->getNameInLayout()) ?>" style="display:none"> <?= $block->getFormHtml() ?> <?= $block->getAfterElementHtml() ?> </div> diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/edit/options.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/edit/options.phtml index 2570a5d712675..ad38d250a3345 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/edit/options.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/edit/options.phtml @@ -3,16 +3,13 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <?php /** @var $block \Magento\Catalog\Block\Adminhtml\Product\Edit\Tab\Options */ ?> <div class="fieldset-wrapper" id="product-custom-options-wrapper" data-block="product-custom-options"> <div class="fieldset-wrapper-title"> <strong class="title"> - <span><?= /* @escapeNotVerified */ __('Custom Options') ?></span> + <span><?= $block->escapeHtml(__('Custom Options')) ?></span> </strong> </div> <div class="fieldset-wrapper-content" id="product-custom-options-content" data-role="product-custom-options-content"> @@ -20,7 +17,7 @@ <div class="messages"> <div class="message message-error" id="dynamic-price-warning" style="display: none;"> <div class="message-inner"> - <div class="message-content"><?= /* @escapeNotVerified */ __('We can\'t save custom-defined options for bundles with dynamic pricing.') ?></div> + <div class="message-content"><?= $block->escapeHtml(__('We can\'t save custom-defined options for bundles with dynamic pricing.')) ?></div> </div> </div> </div> diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/edit/options/option.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/edit/options/option.phtml index d2bca5ce17321..713366e73aba5 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/edit/options/option.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/edit/options/option.phtml @@ -4,8 +4,7 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - +// phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis ?> <?php /** @var $block \Magento\Catalog\Block\Adminhtml\Product\Edit\Tab\Options\Option */ ?> <?= $block->getTemplatesHtml() ?> @@ -19,30 +18,52 @@ <span id="option_<%- data.id %>_header_title"><%- data.title %></span> </strong> <div class="actions"> - <button type="button" title="<?= /* @escapeNotVerified */ __('Delete Custom Option') ?>" class="action-delete" id="<?= /* @escapeNotVerified */ $block->getFieldId() ?>_<%- data.id %>_delete"> - <span><?= /* @escapeNotVerified */ __('Delete Custom Option') ?></span> + <button type="button" + title="<?= $block->escapeHtmlAttr(__('Delete Custom Option')) ?>" + class="action-delete" + id="<?= /* @noEscape */ $block->getFieldId() ?>_<%- data.id %>_delete"> + <span><?= $block->escapeHtml(__('Delete Custom Option')) ?></span> </button> </div> - <div id="<?= /* @escapeNotVerified */ $block->getFieldId() ?>_<%- data.id %>_move" data-role="draggable-handle" class="draggable-handle" - title="<?= /* @escapeNotVerified */ __('Sort Custom Options') ?>"></div> + <div id="<?= /* @noEscape */ $block->getFieldId() ?>_<%- data.id %>_move" + data-role="draggable-handle" + class="draggable-handle" + title="<?= $block->escapeHtmlAttr(__('Sort Custom Options')) ?>"></div> </div> <div class="fieldset-wrapper-content in collapse" id="<%- data.id %>-content"> <fieldset class="fieldset"> - <fieldset class="fieldset-alt" id="<?= /* @escapeNotVerified */ $block->getFieldId() ?>_<%- data.id %>"> - <input id="<?= /* @escapeNotVerified */ $block->getFieldId() ?>_<%- data.id %>_is_delete" name="<?= /* @escapeNotVerified */ $block->getFieldName() ?>[<%- data.id %>][is_delete]" type="hidden" value=""/> - <input id="<?= /* @escapeNotVerified */ $block->getFieldId() ?>_<%- data.id %>_previous_type" name="<?= /* @escapeNotVerified */ $block->getFieldName() ?>[<%- data.id %>][previous_type]" type="hidden" value="<%- data.type %>"/> - <input id="<?= /* @escapeNotVerified */ $block->getFieldId() ?>_<%- data.id %>_previous_group" name="<?= /* @escapeNotVerified */ $block->getFieldName() ?>[<%- data.id %>][previous_group]" type="hidden" value=""/> - <input id="<?= /* @escapeNotVerified */ $block->getFieldId() ?>_<%- data.id %>_id" name="<?= /* @escapeNotVerified */ $block->getFieldName() ?>[<%- data.id %>][id]" type="hidden" value="<%- data.id %>"/> - <input id="<?= /* @escapeNotVerified */ $block->getFieldId() ?>_<%- data.id %>_option_id" name="<?= /* @escapeNotVerified */ $block->getFieldName() ?>[<%- data.id %>][option_id]" type="hidden" value="<%- data.option_id %>"/> - <input name="<?= /* @escapeNotVerified */ $block->getFieldName() ?>[<%- data.id %>][sort_order]" type="hidden" value="<%- data.sort_order %>"/> + <fieldset class="fieldset-alt" id="<?= /* @noEscape */ $block->getFieldId() ?>_<%- data.id %>"> + <input id="<?= /* @noEscape */ $block->getFieldId() ?>_<%- data.id %>_is_delete" + name="<?= /* @noEscape */ $block->getFieldName() ?>[<%- data.id %>][is_delete]" + type="hidden" + value=""/> + <input id="<?= /* @noEscape */ $block->getFieldId() ?>_<%- data.id %>_previous_type" + name="<?= /* @noEscape */ $block->getFieldName() ?>[<%- data.id %>][previous_type]" + type="hidden" + value="<%- data.type %>"/> + <input id="<?= /* @noEscape */ $block->getFieldId() ?>_<%- data.id %>_previous_group" + name="<?= /* @noEscape */ $block->getFieldName() ?>[<%- data.id %>][previous_group]" + type="hidden" + value=""/> + <input id="<?= /* @noEscape */ $block->getFieldId() ?>_<%- data.id %>_id" + name="<?= /* @noEscape */ $block->getFieldName() ?>[<%- data.id %>][id]" + type="hidden" + value="<%- data.id %>"/> + <input id="<?= /* @noEscape */ $block->getFieldId() ?>_<%- data.id %>_option_id" + name="<?= /* @noEscape */ $block->getFieldName() ?>[<%- data.id %>][option_id]" + type="hidden" + value="<%- data.option_id %>"/> + <input name="<?= /* @noEscape */ $block->getFieldName() ?>[<%- data.id %>][sort_order]" + type="hidden" + value="<%- data.sort_order %>"/> <div class="field field-option-title required"> - <label class="label" for="<?= /* @escapeNotVerified */ $block->getFieldId() ?>_<%- data.id %>_title"> - <?= /* @escapeNotVerified */ __('Option Title') ?> + <label class="label" for="<?= /* @noEscape */ $block->getFieldId() ?>_<%- data.id %>_title"> + <?= $block->escapeHtml(__('Option Title')) ?> </label> <div class="control"> - <input id="<?= /* @escapeNotVerified */ $block->getFieldId() ?>_<%- data.id %>_title" - name="<?= /* @escapeNotVerified */ $block->getFieldName() ?>[<%- data.id %>][title]" + <input id="<?= /* @noEscape */ $block->getFieldId() ?>_<%- data.id %>_title" + name="<?= /* @noEscape */ $block->getFieldName() ?>[<%- data.id %>][title]" class="required-entry input-text" type="text" value="<%- data.title %>" @@ -54,8 +75,8 @@ </div> <div class="field field-option-input-type required"> - <label class="label" for="<?= /* @escapeNotVerified */ $block->getFieldId() ?>_<%- data.id %>_title"> - <?= /* @escapeNotVerified */ __('Input Type') ?> + <label class="label" for="<?= /* @noEscape */ $block->getFieldId() ?>_<%- data.id %>_title"> + <?= $block->escapeHtml(__('Input Type')) ?> </label> <div class="control opt-type"> <?= $block->getTypeSelectHtml() ?> @@ -64,9 +85,12 @@ <div class="field field-option-req"> <div class="control"> - <input id="<?= /* @escapeNotVerified */ $block->getFieldId() ?>_<%- data.id %>_required" class="is-required" type="checkbox" checked="checked"/> + <input id="<?= /* @noEscape */ $block->getFieldId() ?>_<%- data.id %>_required" + class="is-required" + type="checkbox" + checked="checked"/> <label for="field-option-req"> - <?= /* @escapeNotVerified */ __('Required') ?> + <?= $block->escapeHtml(__('Required')) ?> </label> <span style="display:none"><?= $block->getRequireSelectHtml() ?></span> </div> @@ -78,7 +102,7 @@ </script> <div id="import-container" style="display: none;"></div> -<?php if (!$block->isReadonly()): ?> +<?php if (!$block->isReadonly()) :?> <div><input type="hidden" name="affect_product_custom_options" value="1"/></div> <?php endif; ?> <script> @@ -89,21 +113,21 @@ require([ jQuery(function ($) { var fieldSet = $('[data-block=product-custom-options]'); - fieldSet.customOptions(<?php /* @escapeNotVerified */ echo $this->helper('Magento\Framework\Json\Helper\Data')->jsonEncode( + fieldSet.customOptions(<?= /* @noEscape */ $this->helper(Magento\Framework\Json\Helper\Data::class)->jsonEncode( [ 'fieldId' => $block->getFieldId(), - 'productGridUrl' => $block->getProductGridUrl(), + 'productGridUrl' => $block->escapeUrl($block->getProductGridUrl()), 'formKey' => $block->getFormKey(), - 'customOptionsUrl' => $block->getCustomOptionsUrl(), - 'isReadonly' => $block->isReadonly(), - 'itemCount' => $block->getItemCount(), - 'currentProductId' => $block->getCurrentProductId(), + 'customOptionsUrl' => $block->escapeUrl($block->getCustomOptionsUrl()), + 'isReadonly' => (bool) $block->isReadonly(), + 'itemCount' => (int) $block->getItemCount(), + 'currentProductId' => (int) $block->getCurrentProductId(), ] )?>); //adding data to templates <?php /** @var $_value \Magento\Framework\DataObject */ ?> - <?php foreach ($block->getOptionValues() as $_value): ?> - fieldSet.customOptions('addOption', <?= /* @escapeNotVerified */ $_value->toJson() ?>); + <?php foreach ($block->getOptionValues() as $_value) :?> + fieldSet.customOptions('addOption', <?= /* @noEscape */ $_value->toJson() ?>); <?php endforeach; ?> }); diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/edit/options/type/date.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/edit/options/type/date.phtml index 07ce6e5d86256..2063609bf0568 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/edit/options/type/date.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/edit/options/type/date.phtml @@ -4,8 +4,6 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - ?> <?php /** @var $block \Magento\Catalog\Block\Adminhtml\Product\Edit\Tab\Options\Type\Date */ ?> <script id="custom-option-date-type-template" type="text/x-magento-template"> @@ -14,10 +12,10 @@ <thead> <tr class="headings"> <?php if ($block->getCanReadPrice() !== false) : ?> - <th><?= /* @escapeNotVerified */ __('Price') ?></th> - <th><?= /* @escapeNotVerified */ __('Price Type') ?></th> + <th><?= $block->escapeHtml(__('Price')) ?></th> + <th><?= $block->escapeHtml(__('Price Type')) ?></th> <?php endif; ?> - <th><?= /* @escapeNotVerified */ __('SKU') ?></th> + <th><?= $block->escapeHtml(__('SKU')) ?></th> </tr> </thead> <tr> diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/edit/options/type/file.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/edit/options/type/file.phtml index 693c98fc02cab..c0e61c5de9988 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/edit/options/type/file.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/edit/options/type/file.phtml @@ -4,8 +4,6 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - ?> <?php /** @var $block \Magento\Catalog\Block\Adminhtml\Product\Edit\Tab\Options\Type\File */ ?> <script id="custom-option-file-type-template" type="text/x-magento-template"> @@ -14,27 +12,27 @@ <thead> <tr> <?php if ($block->getCanReadPrice() !== false) : ?> - <th><?= /* @escapeNotVerified */ __('Price') ?></th> - <th><?= /* @escapeNotVerified */ __('Price Type') ?></th> + <th><?= $block->escapeHtml(__('Price')) ?></th> + <th><?= $block->escapeHtml(__('Price Type')) ?></th> <?php endif; ?> - <th><?= /* @escapeNotVerified */ __('SKU') ?></th> - <th><?= /* @escapeNotVerified */ __('Compatible File Extensions') ?></th> - <th><?= /* @escapeNotVerified */ __('Maximum Image Size') ?></th> + <th><?= $block->escapeHtml(__('SKU')) ?></th> + <th><?= $block->escapeHtml(__('Compatible File Extensions')) ?></th> + <th><?= $block->escapeHtml(__('Maximum Image Size')) ?></th> </tr> </thead> <tr> <?php if ($block->getCanReadPrice() !== false) : ?> - <td class="opt-price"> - <input name="product[options][<%- data.option_id %>][price]" data-store-label="<%- data.price %>" - class="input-text validate-zero-or-greater" type="text" value="<%- data.price %>" - <?php if ($block->getCanEditPrice() === false) : ?> - disabled="disabled" - <?php endif; ?>> - </td> - <td class="opt-price-type"><?= $block->getPriceTypeSelectHtml('data-attr="price-type"') ?><%- data.checkboxScopePrice %></td> + <td class="opt-price"> + <input name="product[options][<%- data.option_id %>][price]" data-store-label="<%- data.price %>" + class="input-text validate-zero-or-greater" type="text" value="<%- data.price %>" + <?php if ($block->getCanEditPrice() === false) : ?> + disabled="disabled" + <?php endif; ?>> + </td> + <td class="opt-price-type"><?= $block->getPriceTypeSelectHtml('data-attr="price-type"') ?><%- data.checkboxScopePrice %></td> <?php else : ?> - <input name="product[options][<%- data.option_id %>][price]" type="hidden"> - <input id="product_option_<%- data.option_id %>_price_type" name="product[options][<%- data.option_id %>][price_type]" type="hidden"> + <input name="product[options][<%- data.option_id %>][price]" type="hidden"> + <input id="product_option_<%- data.option_id %>_price_type" name="product[options][<%- data.option_id %>][price_type]" type="hidden"> <?php endif; ?> <td> <input name="product[options][<%- data.option_id %>][sku]" class="input-text" type="text" value="<%- data.sku %>"> @@ -42,10 +40,15 @@ <td> <input name="product[options][<%- data.option_id %>][file_extension]" class="input-text" type="text" value="<%- data.file_extension %>"> </td> - <td class="col-file"><?php /* @escapeNotVerified */ echo __('%1 <span>x</span> %2 <span>px.</span>', - '<input class="input-text" type="text" name="product[options][<%- data.option_id %>][image_size_x]" value="<%- data.image_size_x %>">', - '<input class="input-text" type="text" name="product[options][<%- data.option_id %>][image_size_y]" value="<%- data.image_size_y %>">') ?> - <div class="note"><?= /* @escapeNotVerified */ __('Please leave blank if it is not an image.') ?></div> + <td class="col-file"><?= $block->escapeHtml( + __( + '%1 <span>x</span> %2 <span>px.</span>', + '<input class="input-text" type="text" name="product[options][<%- data.option_id %>][image_size_x]" value="<%- data.image_size_x %>">', + '<input class="input-text" type="text" name="product[options][<%- data.option_id %>][image_size_y]" value="<%- data.image_size_y %>">' + ), + ['span', 'input'] + ) ?> + <div class="note"><?= $block->escapeHtml(__('Please leave blank if it is not an image.')) ?></div> </td> </tr> </table> diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/edit/options/type/select.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/edit/options/type/select.phtml index e8c398228a469..c7ff03a08d954 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/edit/options/type/select.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/edit/options/type/select.phtml @@ -4,8 +4,6 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - ?> <?php /** @var $block \Magento\Catalog\Block\Adminhtml\Product\Edit\Tab\Options\Type\Select */ ?> <script id="custom-option-select-type-template" type="text/x-magento-template"> @@ -14,12 +12,12 @@ <thead> <tr> <th class="col-draggable"> </th> - <th class="col-name required"><?= /* @escapeNotVerified */ __('Title') ?><span class="required">*</span></th> + <th class="col-name required"><?= $block->escapeHtml(__('Title')) ?><span class="required">*</span></th> <?php if ($block->getCanReadPrice() !== false) : ?> - <th class="col-price"><?= /* @escapeNotVerified */ __('Price') ?></th> - <th class="col-price-type"><?= /* @escapeNotVerified */ __('Price Type') ?></th> + <th class="col-price"><?= $block->escapeHtml(__('Price')) ?></th> + <th class="col-price-type"><?= $block->escapeHtml(__('Price Type')) ?></th> <?php endif; ?> - <th class="col-sku"><?= /* @escapeNotVerified */ __('SKU') ?></th> + <th class="col-sku"><?= $block->escapeHtml(__('SKU')) ?></th> <th class="col-actions"> </th> </tr> </thead> @@ -38,7 +36,7 @@ <tr id="product_option_<%- data.id %>_select_<%- data.select_id %>"> <td class="col-draggable"> <div data-role="draggable-handle" class="draggable-handle" - title="<?= /* @escapeNotVerified */ __('Sort Custom Option') ?>"></div> + title="<?= $block->escapeHtmlAttr(__('Sort Custom Option')) ?>"></div> <input name="product[options][<%- data.id %>][values][<%- data.select_id %>][sort_order]" type="hidden" value="<%- data.sort_order %>"> </td> <td class="col-name select-opt-title"> @@ -58,7 +56,7 @@ <?php endif; ?>> </td> <td class="col-price-type select-opt-price-type"> - <?= /* @escapeNotVerified */ $block->getPriceTypeSelectHtml('data-attr="price-type" <% if (typeof data.scopePriceDisabled != "undefined" && data.scopePriceDisabled != null) { %> disabled="disabled" <% } %>') ?><%- data.checkboxScopePrice %> + <?= /* @noEscape */ $block->getPriceTypeSelectHtml('data-attr="price-type" <% if (typeof data.scopePriceDisabled != "undefined" && data.scopePriceDisabled != null) { %> disabled="disabled" <% } %>') ?><%- data.checkboxScopePrice %> </td> <?php else : ?> <input id="product_option_<%- data.id %>_select_<%- data.select_id %>_price" name="product[options][<%- data.id %>][values][<%- data.select_id %>][price]" type="hidden"> diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/edit/options/type/text.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/edit/options/type/text.phtml index c9d7190589ff5..89da5d633ef4d 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/edit/options/type/text.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/edit/options/type/text.phtml @@ -4,8 +4,6 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - ?> <?php /** @var $block \Magento\Catalog\Block\Adminhtml\Product\Edit\Tab\Options\Type\Text */ ?> <script id="custom-option-text-type-template" type="text/x-magento-template"> @@ -14,11 +12,11 @@ <thead> <tr> <?php if ($block->getCanReadPrice() !== false) : ?> - <th class="type-price"><?= /* @escapeNotVerified */ __('Price') ?></th> - <th class="type-type"><?= /* @escapeNotVerified */ __('Price Type') ?></th> + <th class="type-price"><?= $block->escapeHtml(__('Price')) ?></th> + <th class="type-type"><?= $block->escapeHtml(__('Price Type')) ?></th> <?php endif; ?> - <th class="type-sku"><?= /* @escapeNotVerified */ __('SKU') ?></th> - <th class="type-last last"><?= /* @escapeNotVerified */ __('Max Characters') ?></th> + <th class="type-sku"><?= $block->escapeHtml(__('SKU')) ?></th> + <th class="type-last last"><?= $block->escapeHtml(__('Max Characters')) ?></th> </tr> </thead> <tr> diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/edit/price/tier.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/edit/price/tier.phtml index 57715744823d6..e66a18c677cc3 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/edit/price/tier.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/edit/price/tier.phtml @@ -4,7 +4,7 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile +// phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis /* @var $block \Magento\Catalog\Block\Adminhtml\Product\Edit\Tab\Price\Tier */ $element = $block->getElement(); @@ -20,28 +20,28 @@ $element = $block->getElement(); <?php $_showWebsite = $block->isShowWebsiteColumn(); ?> <?php $_showWebsite = $block->isMultiWebsites(); ?> -<div class="field" id="attribute-<?= /* @escapeNotVerified */ $_htmlId ?>-container" data-attribute-code="<?= /* @escapeNotVerified */ $_htmlId ?>" +<div class="field" id="attribute-<?= /* @noEscape */ $_htmlId ?>-container" data-attribute-code="<?= /* @noEscape */ $_htmlId ?>" data-apply-to="<?= $block->escapeHtml( - $this->helper('Magento\Framework\Json\Helper\Data')->jsonEncode( + $this->helper(Magento\Framework\Json\Helper\Data::class)->jsonEncode( $element->hasEntityAttribute() ? $element->getEntityAttribute()->getApplyTo() : [] ) )?>"> - <label class="label"><span><?= /* @escapeNotVerified */ $block->getElement()->getLabel() ?></span></label> + <label class="label"><span><?= $block->escapeHtml($block->getElement()->getLabel()) ?></span></label> <div class="control"> <table class="admin__control-table tiers_table" id="tiers_table"> <thead> <tr> - <th class="col-websites" <?php if (!$_showWebsite): ?>style="display:none"<?php endif; ?>><?= /* @escapeNotVerified */ __('Web Site') ?></th> - <th class="col-customer-group"><?= /* @escapeNotVerified */ __('Customer Group') ?></th> - <th class="col-qty required"><?= /* @escapeNotVerified */ __('Quantity') ?></th> - <th class="col-price required"><?= /* @escapeNotVerified */ $block->getPriceColumnHeader(__('Item Price')) ?></th> - <th class="col-delete"><?= /* @escapeNotVerified */ __('Action') ?></th> + <th class="col-websites" <?php if (!$_showWebsite) :?>style="display:none"<?php endif; ?>><?= $block->escapeHtml(__('Web Site')) ?></th> + <th class="col-customer-group"><?= $block->escapeHtml(__('Customer Group')) ?></th> + <th class="col-qty required"><?= $block->escapeHtml(__('Quantity')) ?></th> + <th class="col-price required"><?= $block->escapeHtml($block->getPriceColumnHeader(__('Item Price'))) ?></th> + <th class="col-delete"><?= $block->escapeHtml(__('Action')) ?></th> </tr> </thead> - <tbody id="<?= /* @escapeNotVerified */ $_htmlId ?>_container"></tbody> + <tbody id="<?= /* @noEscape */ $_htmlId ?>_container"></tbody> <tfoot> <tr> - <td colspan="<?php if (!$_showWebsite): ?>4<?php else: ?>5<?php endif; ?>" class="col-actions-add"><?= $block->getAddButtonHtml() ?></td> + <td colspan="<?php if (!$_showWebsite) :?>4<?php else :?>5<?php endif; ?>" class="col-actions-add"><?= $block->getAddButtonHtml() ?></td> </tr> </tfoot> </table> @@ -55,39 +55,39 @@ require([ //<![CDATA[ var tierPriceRowTemplate = '<tr>' - + '<td class="col-websites"<?php if (!$_showWebsite): ?> style="display:none"<?php endif; ?>>' - + '<select class="<?= /* @escapeNotVerified */ $_htmlClass ?> required-entry" name="<?= /* @escapeNotVerified */ $_htmlName ?>[<%- data.index %>][website_id]" id="tier_price_row_<%- data.index %>_website">' - <?php foreach ($block->getWebsites() as $_websiteId => $_info): ?> - + '<option value="<?= /* @escapeNotVerified */ $_websiteId ?>"><?= $block->escapeJs($_info['name']) ?><?php if (!empty($_info['currency'])): ?> [<?= $block->escapeHtml($_info['currency']) ?>]<?php endif; ?></option>' + + '<td class="col-websites"<?php if (!$_showWebsite) :?> style="display:none"<?php endif; ?>>' + + '<select class="<?= $block->escapeHtmlAttr($_htmlClass) ?> required-entry" name="<?= /* @noEscape */ $_htmlName ?>[<%- data.index %>][website_id]" id="tier_price_row_<%- data.index %>_website">' + <?php foreach ($block->getWebsites() as $_websiteId => $_info) :?> + + '<option value="<?= $block->escapeHtmlAttr($_websiteId) ?>"><?= $block->escapeHtml($_info['name']) ?><?php if (!empty($_info['currency'])) :?> [<?= $block->escapeHtml($_info['currency']) ?>]<?php endif; ?></option>' <?php endforeach ?> + '</select></td>' - + '<td class="col-customer-group"><select class="<?= /* @escapeNotVerified */ $_htmlClass ?> custgroup required-entry" name="<?= /* @escapeNotVerified */ $_htmlName ?>[<%- data.index %>][cust_group]" id="tier_price_row_<%- data.index %>_cust_group">' - <?php foreach ($block->getCustomerGroups() as $_groupId => $_groupName): ?> - + '<option value="<?= /* @escapeNotVerified */ $_groupId ?>"><?= $block->escapeJs($_groupName) ?></option>' + + '<td class="col-customer-group"><select class="<?= $block->escapeHtmlAttr($_htmlClass) ?> custgroup required-entry" name="<?= /* @noEscape */ $_htmlName ?>[<%- data.index %>][cust_group]" id="tier_price_row_<%- data.index %>_cust_group">' + <?php foreach ($block->getCustomerGroups() as $_groupId => $_groupName) :?> + + '<option value="<?= $block->escapeHtmlAttr($_groupId) ?>"><?= $block->escapeHtml($_groupName) ?></option>' <?php endforeach ?> + '</select></td>' + '<td class="col-qty">' - + '<input class="<?= /* @escapeNotVerified */ $_htmlClass ?> qty required-entry validate-greater-than-zero" type="text" name="<?= /* @escapeNotVerified */ $_htmlName ?>[<%- data.index %>][price_qty]" value="<%- data.qty %>" id="tier_price_row_<%- data.index %>_qty" />' - + '<span><?= /* @escapeNotVerified */ __("and above") ?></span>' + + '<input class="<?= $block->escapeHtmlAttr($_htmlClass) ?> qty required-entry validate-greater-than-zero" type="text" name="<?= /* @noEscape */ $_htmlName ?>[<%- data.index %>][price_qty]" value="<%- data.qty %>" id="tier_price_row_<%- data.index %>_qty" />' + + '<span><?= $block->escapeHtml(__("and above")) ?></span>' + '</td>' - + '<td class="col-price"><input class="<?= /* @escapeNotVerified */ $_htmlClass ?> required-entry <?= /* @escapeNotVerified */ $_priceValueValidation ?>" type="text" name="<?= /* @escapeNotVerified */ $_htmlName ?>[<%- data.index %>][price]" value="<%- data.price %>" id="tier_price_row_<%- data.index %>_price" /></td>' - + '<td class="col-delete"><input type="hidden" name="<?= /* @escapeNotVerified */ $_htmlName ?>[<%- data.index %>][delete]" class="delete" value="" id="tier_price_row_<%- data.index %>_delete" />' - + '<button title="<?= /* @escapeNotVerified */ $block->escapeHtml(__('Delete Tier')) ?>" type="button" class="action- scalable delete icon-btn delete-product-option" id="tier_price_row_<%- data.index %>_delete_button" onclick="return tierPriceControl.deleteItem(event);">' - + '<span><?= /* @escapeNotVerified */ __("Delete") ?></span></button></td>' + + '<td class="col-price"><input class="<?= $block->escapeHtmlAttr($_htmlClass) ?> required-entry <?= $block->escapeHtmlAttr($_priceValueValidation) ?>" type="text" name="<?= /* @noEscape */ $_htmlName ?>[<%- data.index %>][price]" value="<%- data.price %>" id="tier_price_row_<%- data.index %>_price" /></td>' + + '<td class="col-delete"><input type="hidden" name="<?= /* @noEscape */ $_htmlName ?>[<%- data.index %>][delete]" class="delete" value="" id="tier_price_row_<%- data.index %>_delete" />' + + '<button title="<?= $block->escapeHtml(__('Delete Tier')) ?>" type="button" class="action- scalable delete icon-btn delete-product-option" id="tier_price_row_<%- data.index %>_delete_button" onclick="return tierPriceControl.deleteItem(event);">' + + '<span><?= $block->escapeHtml(__("Delete")) ?></span></button></td>' + '</tr>'; var tierPriceControl = { template: mageTemplate(tierPriceRowTemplate), itemsCount: 0, addItem : function () { - <?php if ($_readonly): ?> + <?php if ($_readonly) :?> if (arguments.length < 4) { return; } <?php endif; ?> var data = { - website_id: '<?= /* @escapeNotVerified */ $block->getDefaultWebsite() ?>', - group: '<?= /* @escapeNotVerified */ $block->getDefaultCustomerGroup() ?>', + website_id: '<?= (int) $block->getDefaultWebsite() ?>', + group: '<?= (int) $block->getDefaultCustomerGroup() ?>', qty: '', price: '', readOnly: false, @@ -104,7 +104,7 @@ var tierPriceControl = { data.readOnly = arguments[4]; } - Element.insert($('<?= /* @escapeNotVerified */ $_htmlId ?>_container'), { + Element.insert($('<?= $block->escapeJs($_htmlId) ?>_container'), { bottom : this.template({ data: data }) @@ -113,7 +113,7 @@ var tierPriceControl = { $('tier_price_row_' + data.index + '_cust_group').value = data.group; $('tier_price_row_' + data.index + '_website').value = data.website_id; - <?php if ($block->isShowWebsiteColumn() && !$block->isAllowChangeWebsite()):?> + <?php if ($block->isShowWebsiteColumn() && !$block->isAllowChangeWebsite()) :?> var wss = $('tier_price_row_' + data.index + '_website'); var txt = wss.options[wss.selectedIndex].text; @@ -128,11 +128,11 @@ var tierPriceControl = { $('tier_price_row_'+data.index+'_delete_button').hide(); } - <?php if ($_readonly): ?> - $('<?= /* @escapeNotVerified */ $_htmlId ?>_container').select('input', 'select').each(this.disableElement); - $('<?= /* @escapeNotVerified */ $_htmlId ?>_container').up('table').select('button').each(this.disableElement); - <?php else: ?> - $('<?= /* @escapeNotVerified */ $_htmlId ?>_container').select('input', 'select').each(function(el){ Event.observe(el, 'change', el.setHasChanges.bind(el)); }); + <?php if ($_readonly) :?> + $('<?= $block->escapeJs($_htmlId) ?>_container').select('input', 'select').each(this.disableElement); + $('<?= $block->escapeJs($_htmlId) ?>_container').up('table').select('button').each(this.disableElement); + <?php else :?> + $('<?= $block->escapeJs($_htmlId) ?>_container').select('input', 'select').each(function(el){ Event.observe(el, 'change', el.setHasChanges.bind(el)); }); <?php endif; ?> }, disableElement: function(el) { @@ -150,11 +150,11 @@ var tierPriceControl = { return false; } }; -<?php foreach ($block->getValues() as $_item): ?> -tierPriceControl.addItem('<?= /* @escapeNotVerified */ $_item['website_id'] ?>', '<?= /* @escapeNotVerified */ $_item['cust_group'] ?>', '<?= /* @escapeNotVerified */ $_item['price_qty']*1 ?>', '<?= /* @escapeNotVerified */ $_item['price'] ?>', <?= (int)!empty($_item['readonly']) ?>); +<?php foreach ($block->getValues() as $_item) :?> +tierPriceControl.addItem('<?= $block->escapeJs($_item['website_id']) ?>', '<?= $block->escapeJs($_item['cust_group']) ?>', '<?= $_item['price_qty']*1 ?>', '<?= $block->escapeJs($_item['price']) ?>', <?= (int)!empty($_item['readonly']) ?>); <?php endforeach; ?> -<?php if ($_readonly): ?> -$('<?= /* @escapeNotVerified */ $_htmlId ?>_container').up('table').select('button') +<?php if ($_readonly) :?> +$('<?= $block->escapeJs($_htmlId) ?>_container').up('table').select('button') .each(tierPriceControl.disableElement); <?php endif; ?> diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/edit/serializer.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/edit/serializer.phtml index 44fdb75cdac21..0c1da98c7d85a 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/edit/serializer.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/edit/serializer.phtml @@ -4,9 +4,12 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - +/** @var $block \Magento\Catalog\Block\Adminhtml\Product\Edit\Tab\Ajax\Serializer */ ?> +// phpcs:disable Magento2.Security.InsecureFunction.DiscouragedWithAlternative <?php $_id = 'id_' . md5(microtime()) ?> -<input type="hidden" name="<?= /* @escapeNotVerified */ $block->getInputElementName() ?>" value="" id="<?= /* @escapeNotVerified */ $_id ?>" /> +<input type="hidden" + name="<?= $block->escapeHtmlAttr($block->getInputElementName()) ?>" + value="" + id="<?= /* @noEscape */ $_id ?>" /> diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/edit/websites.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/edit/websites.phtml index 8f7f20f32d982..0193d7764cbb5 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/edit/websites.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/edit/websites.phtml @@ -4,16 +4,15 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - +/** @var $block \Magento\Catalog\Block\Adminhtml\Product\Edit\Tab\Websites */ ?> <fieldset id="grop_fields" class="fieldset"> - <legend class="legend"><span><?= /* @escapeNotVerified */ __('Product In Websites') ?></span></legend> + <legend class="legend"><span><?= $block->escapeHtml(__('Product In Websites')) ?></span></legend> <br> - <?php if ($block->getProductId()): ?> + <?php if ($block->getProductId()) :?> <div class="messages"> <div class="message message-notice"> - <?= /* @escapeNotVerified */ __('To hide an item in catalog or search results, set the status to "Disabled".') ?> + <?= $block->escapeHtml(__('To hide an item in catalog or search results, set the status to "Disabled".')) ?> </div> </div> <?php endif; ?> @@ -21,22 +20,36 @@ <?= $block->getHintHtml() ?> <div class="store-tree"> <?php $_websites = $block->getWebsiteCollection() ?> - <?php foreach ($_websites as $_website): ?> + <?php foreach ($_websites as $_website) :?> <div class="website-name"> - <input name="product[website_ids][]" value="<?= /* @escapeNotVerified */ $_website->getId() ?>" <?php if ($block->isReadonly()): ?> disabled="disabled"<?php endif;?> class="checkbox website-checkbox" id="product_website_<?= /* @escapeNotVerified */ $_website->getId() ?>" type="checkbox"<?php if ($block->hasWebsite($_website->getId()) || !$block->getProductId() && count($_websites) === 1): ?> checked="checked"<?php endif; ?> /> - <label for="product_website_<?= /* @escapeNotVerified */ $_website->getId() ?>"><?= $block->escapeHtml($_website->getName()) ?></label> + <input name="product[website_ids][]" + value="<?= (int) $_website->getId() ?>" + <?php if ($block->isReadonly()) :?> + disabled="disabled" + <?php endif;?> + class="checkbox website-checkbox" + id="product_website_<?= (int) $_website->getId() ?>" + type="checkbox" + <?php if ($block->hasWebsite($_website->getId()) || !$block->getProductId() && count($_websites) === 1) :?> + checked="checked" + <?php endif; ?> + /> + <label for="product_website_<?= (int) $_website->getId() ?>"><?= $block->escapeHtml($_website->getName()) ?></label> </div> - <dl class="webiste-groups" id="product_website_<?= /* @escapeNotVerified */ $_website->getId() ?>_data"> - <?php foreach ($block->getGroupCollection($_website) as $_group): ?> + <dl class="webiste-groups" id="product_website_<?= (int) $_website->getId() ?>_data"> + <?php foreach ($block->getGroupCollection($_website) as $_group) :?> <dt><?= $block->escapeHtml($_group->getName()) ?></dt> <dd> <ul> - <?php foreach ($block->getStoreCollection($_group) as $_store): ?> + <?php foreach ($block->getStoreCollection($_group) as $_store) :?> <li> <?= $block->escapeHtml($_store->getName()) ?> - <?php if ($block->getWebsites() && !$block->hasWebsite($_website->getId())): ?> - <span class="website-<?= /* @escapeNotVerified */ $_website->getId() ?>-select" style="display:none"> - <?= __('(Copy data from: %1)', $block->getChooseFromStoreHtml($_store)) ?> + <?php if ($block->getWebsites() && !$block->hasWebsite($_website->getId())) :?> + <span class="website-<?= (int) $_website->getId() ?>-select" style="display:none"> + <?= $block->escapeHtml( + __('(Copy data from: %1)', $block->getChooseFromStoreHtml($_store)), + ['select', 'option', 'optgroup'] + ) ?> </span> <?php endif; ?> </li> diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/helper/gallery.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/helper/gallery.phtml index 574c9ee81af7d..befdce30fc8f0 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/helper/gallery.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/helper/gallery.phtml @@ -4,7 +4,7 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile +// phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis /** @var $block \Magento\Catalog\Block\Adminhtml\Product\Helper\Form\Gallery\Content */ $elementName = $block->getElement()->getName() . '[images]'; @@ -16,61 +16,60 @@ $formName = $block->getFormName(); data-parent-component="<?= $block->escapeHtml($block->getData('config/parentComponent')) ?>" data-images="<?= $block->escapeHtml($block->getImagesJson()) ?>" data-types="<?= $block->escapeHtml( - $this->helper('Magento\Framework\Json\Helper\Data')->jsonEncode($block->getImageTypes()) + $this->helper(Magento\Framework\Json\Helper\Data::class)->jsonEncode($block->getImageTypes()) ) ?>" - > +> <?php if (!$block->getElement()->getReadonly()) {?> <div class="image image-placeholder"> <?= $block->getUploaderHtml() ?> <div class="product-image-wrapper"> <p class="image-placeholder-text"> - <?= /* @escapeNotVerified */ __('Browse to find or drag image here') ?> + <?= $block->escapeHtml(__('Browse to find or drag image here')) ?> </p> </div> </div> <?php } ?> <?php foreach ($block->getImageTypes() as $typeData) { - ?> - <input name="<?= $block->escapeHtml($typeData['name']) ?>" - data-form-part="<?= /* @escapeNotVerified */ $formName ?>" - class="image-<?= $block->escapeHtml($typeData['code']) ?>" + ?> + <input name="<?= $block->escapeHtmlAttr($typeData['name']) ?>" + data-form-part="<?= $block->escapeHtmlAttr($formName) ?>" + class="image-<?= $block->escapeHtmlAttr($typeData['code']) ?>" type="hidden" - value="<?= $block->escapeHtml($typeData['value']) ?>"/> - <?php - + value="<?= $block->escapeHtmlAttr($typeData['value']) ?>"/> + <?php } ?> <script id="<?= $block->getHtmlId() ?>-template" type="text/x-magento-template"> <div class="image item<% if (data.disabled == 1) { %> hidden-for-front<% } %>" data-role="image"> <input type="hidden" - name="<?= /* @escapeNotVerified */ $elementName ?>[<%- data.file_id %>][position]" + name="<?= $block->escapeHtmlAttr($elementName) ?>[<%- data.file_id %>][position]" value="<%- data.position %>" - data-form-part="<?= /* @escapeNotVerified */ $formName ?>" + data-form-part="<?= $block->escapeHtmlAttr($formName) ?>" class="position"/> <input type="hidden" - name="<?= /* @escapeNotVerified */ $elementName ?>[<%- data.file_id %>][file]" - data-form-part="<?= /* @escapeNotVerified */ $formName ?>" + name="<?= $block->escapeHtmlAttr($elementName) ?>[<%- data.file_id %>][file]" + data-form-part="<?= $block->escapeHtmlAttr($formName) ?>" value="<%- data.file %>"/> <input type="hidden" - name="<?= /* @escapeNotVerified */ $elementName ?>[<%- data.file_id %>][value_id]" - data-form-part="<?= /* @escapeNotVerified */ $formName ?>" + name="<?= $block->escapeHtmlAttr($elementName) ?>[<%- data.file_id %>][value_id]" + data-form-part="<?= $block->escapeHtmlAttr($formName) ?>" value="<%- data.value_id %>"/> <input type="hidden" - name="<?= /* @escapeNotVerified */ $elementName ?>[<%- data.file_id %>][label]" - data-form-part="<?= /* @escapeNotVerified */ $formName ?>" + name="<?= $block->escapeHtmlAttr($elementName) ?>[<%- data.file_id %>][label]" + data-form-part="<?= $block->escapeHtmlAttr($formName) ?>" value="<%- data.label %>"/> <input type="hidden" - name="<?= /* @escapeNotVerified */ $elementName ?>[<%- data.file_id %>][disabled]" - data-form-part="<?= /* @escapeNotVerified */ $formName ?>" + name="<?= $block->escapeHtmlAttr($elementName) ?>[<%- data.file_id %>][disabled]" + data-form-part="<?= $block->escapeHtmlAttr(formName) ?>" value="<%- data.disabled %>"/> <input type="hidden" - name="<?= /* @escapeNotVerified */ $elementName ?>[<%- data.file_id %>][media_type]" - data-form-part="<?= /* @escapeNotVerified */ $formName ?>" + name="<?= $block->escapeHtmlAttr($elementName) ?>[<%- data.file_id %>][media_type]" + data-form-part="<?= $block->escapeHtmlAttr($formName) ?>" value="image"/> <input type="hidden" - name="<?= /* @escapeNotVerified */ $elementName ?>[<%- data.file_id %>][removed]" - data-form-part="<?= /* @escapeNotVerified */ $formName ?>" + name="<?= $block->escapeHtmlAttr($elementName) ?>[<%- data.file_id %>][removed]" + data-form-part="<?= $block->escapeHtmlAttr($formName) ?>" value="" class="is-removed"/> @@ -84,21 +83,21 @@ $formName = $block->getFormName(); <button type="button" class="action-remove" data-role="delete-button" - title="<?= /* @escapeNotVerified */ __('Delete image') ?>"> + title="<?= $block->escapeHtmlAttr(__('Delete image')) ?>"> <span> - <?= /* @escapeNotVerified */ __('Delete image') ?> + <?= $block->escapeHtml(__('Delete image')) ?> </span> </button> <div class="draggable-handle"></div> </div> - <div class="image-fade"><span><?= /* @escapeNotVerified */ __('Hidden') ?></span></div> + <div class="image-fade"><span><?= $block->escapeHtml(__('Hidden')) ?></span></div> </div> <div class="item-description"> <div class="item-title" data-role="img-title"><%- data.label %></div> <div class="item-size"> - <span data-role="image-dimens"></span>, <span data-role="image-size"><%- data.sizeLabel %></span> + <span data-role="image-dimens"></span>, <span data-role="image-size"><%- data.sizeLabel %></span> </div> </div> @@ -106,12 +105,9 @@ $formName = $block->getFormName(); <?php foreach ($block->getImageTypes() as $typeData) { ?> - <li data-role-code="<?php /* @escapeNotVerified */ echo $block->escapeHtml( - $typeData['code'] - ) ?>" class="item-role item-role-<?php /* @escapeNotVerified */ echo $block->escapeHtml( - $typeData['code'] - ) ?>"> - <?= /* @escapeNotVerified */ $block->escapeHtml($typeData['label']) ?> + <li data-role-code="<?= $block->escapeHtmlAttr($typeData['code']) ?>" + class="item-role item-role-<?= $block->escapeHtmlAttr($typeData['code']) ?>"> + <?= $block->escapeHtml($typeData['label']) ?> </li> <?php } @@ -121,98 +117,94 @@ $formName = $block->getFormName(); </script> <script data-role="img-dialog-container-tmpl" type="text/x-magento-template"> - <div class="image-panel" data-role="dialog"> - </div> + <div class="image-panel" data-role="dialog"> + </div> </script> <script data-role="img-dialog-tmpl" type="text/x-magento-template"> - <div class="image-panel-preview"> - <img src="<%- data.url %>" alt="<%- data.label %>" /> - </div> - <div class="image-panel-controls"> - <strong class="image-name"><%- data.label %></strong> + <div class="image-panel-preview"> + <img src="<%- data.url %>" alt="<%- data.label %>" /> + </div> + <div class="image-panel-controls"> + <strong class="image-name"><%- data.label %></strong> - <fieldset class="admin__fieldset fieldset-image-panel"> - <div class="admin__field field-image-description"> - <label class="admin__field-label" for="image-description"> - <span><?= /* @escapeNotVerified */ __('Alt Text') ?></span> - </label> + <fieldset class="admin__fieldset fieldset-image-panel"> + <div class="admin__field field-image-description"> + <label class="admin__field-label" for="image-description"> + <span><?= $block->escapeHtml(__('Alt Text')) ?></span> + </label> - <div class="admin__field-control"> + <div class="admin__field-control"> <textarea data-role="image-description" rows="3" class="admin__control-textarea" - name="<?php /* @escapeNotVerified */ - echo $elementName - ?>[<%- data.file_id %>][label]"><%- data.label %></textarea> - </div> - </div> + name="<?= $block->escapeHtmlAttr($elementName) ?>[<%- data.file_id %>][label]"><%- data.label %></textarea> + </div> + </div> - <div class="admin__field field-image-role"> - <label class="admin__field-label"> - <span><?= /* @escapeNotVerified */ __('Role') ?></span> - </label> - <div class="admin__field-control"> - <ul class="multiselect-alt"> - <?php - foreach ($block->getMediaAttributes() as $attribute) : - ?> - <li class="item"> - <label> - <input class="image-type" - data-role="type-selector" - data-form-part="<?= /* @escapeNotVerified */ $formName ?>" - type="checkbox" - value="<?php /* @escapeNotVerified */ echo $block->escapeHtml( - $attribute->getAttributeCode() - ) ?>" - /> - <?php /* @escapeNotVerified */ echo $block->escapeHtml( - $attribute->getFrontendLabel() - ) ?> - </label> - </li> + <div class="admin__field field-image-role"> + <label class="admin__field-label"> + <span><?= $block->escapeHtml(__('Role')) ?></span> + </label> + <div class="admin__field-control"> + <ul class="multiselect-alt"> + <?php + foreach ($block->getMediaAttributes() as $attribute) : + ?> + <li class="item"> + <label> + <input class="image-type" + data-role="type-selector" + data-form-part="<?= $block->escapeHtmlAttr($formName) ?>" + type="checkbox" + value="<?= $block->escapeHtmlAttr($attribute->getAttributeCode()) ?>" + /> + <?= $block->escapeHtml( + $attribute->getFrontendLabel() + ) ?> + </label> + </li> <?php endforeach; - ?> - </ul> - </div> + ?> + </ul> </div> + </div> - <div class="admin__field admin__field-inline field-image-size" data-role="size"> - <label class="admin__field-label"> - <span><?= /* @escapeNotVerified */ __('Image Size') ?></span> - </label> - <div class="admin__field-value" data-message="<?= /* @escapeNotVerified */ __('{size}') ?>"></div> - </div> + <div class="admin__field admin__field-inline field-image-size" data-role="size"> + <label class="admin__field-label"> + <span><?= $block->escapeHtml(__('Image Size')) ?></span> + </label> + <div class="admin__field-value" data-message="<?= $block->escapeHtmlAttr(_('{size}')) ?>"></div> + </div> - <div class="admin__field admin__field-inline field-image-resolution" data-role="resolution"> - <label class="admin__field-label"> - <span><?= /* @escapeNotVerified */ __('Image Resolution') ?></span> - </label> - <div class="admin__field-value" data-message="<?= /* @escapeNotVerified */ __('{width}^{height} px') ?>"></div> - </div> + <div class="admin__field admin__field-inline field-image-resolution" data-role="resolution"> + <label class="admin__field-label"> + <span><?= $block->escapeHtml(__('Image Resolution')) ?></span> + </label> + <div class="admin__field-value" data-message="<?= $block->escapeHtmlAttr(__('{width}^{height} px')) ?>"></div> + </div> - <div class="admin__field field-image-hide"> - <div class="admin__field-control"> - <div class="admin__field admin__field-option"> - <input type="checkbox" - id="hide-from-product-page" - data-role="visibility-trigger" - data-form-part="<?= /* @escapeNotVerified */ $formName ?>" - value="1" - class="admin__control-checkbox" - name="<?= /* @escapeNotVerified */ $elementName ?>[<%- data.file_id %>][disabled]" - <% if (data.disabled == 1) { %>checked="checked"<% } %> /> - - <label for="hide-from-product-page" class="admin__field-label"> - <?= /* @escapeNotVerified */ __('Hide from Product Page') ?> - </label> - </div> + <div class="admin__field field-image-hide"> + <div class="admin__field-control"> + <div class="admin__field admin__field-option"> + <input type="checkbox" + id="hide-from-product-page" + data-role="visibility-trigger" + data-form-part="<?= $block->escapeHtmlAttr($formName) ?>" + value="1" + class="admin__control-checkbox" + name="<?= $block->escapeHtmlAttr($elementName) ?>[<%- data.file_id %>][disabled]" + <% if (data.disabled == 1) { %>checked="checked"<% } %> /> + + <label for="hide-from-product-page" class="admin__field-label"> + <?= $block->escapeHtml(__('Hide from Product Page')) ?> + </label> </div> </div> - </fieldset> - </div> + </div> + </fieldset> + </div> </script> <?= $block->getChildHtml('new-video') ?> </div> diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/js.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/js.phtml index 4134392c0f52b..0a13aee5930ad 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/js.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/js.phtml @@ -4,7 +4,7 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile +// phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis /** @var \Magento\Catalog\Block\Adminhtml\Product\Edit\Js $block */ ?> @@ -30,8 +30,8 @@ function registerTaxRecalcs() { Event.observe($('tax_class_id'), 'change', recalculateTax); } -var priceFormat = <?= /* @escapeNotVerified */ $this->helper('Magento\Tax\Helper\Data')->getPriceFormat($block->getStore()) ?>; -var taxRates = <?= /* @escapeNotVerified */ $block->getAllRatesByProductClassJson() ?>; +var priceFormat = <?= /* @noEscape */ $this->helper(Magento\Tax\Helper\Data::class)->getPriceFormat($block->getStore()) ?>; +var taxRates = <?= /* @noEscape */ $block->getAllRatesByProductClassJson() ?>; function recalculateTax() { if (typeof dynamicTaxes == 'undefined') { @@ -75,10 +75,10 @@ function bindActiveProductTab(event, ui) { jQuery(document).on('tabsactivate', bindActiveProductTab); // bind active tab -<?php if ($tabsBlock = $block->getLayout()->getBlock('product_tabs')): ?> +<?php if ($tabsBlock = $block->getLayout()->getBlock('product_tabs')) :?> jQuery(function () { - if (jQuery('#<?= /* @escapeNotVerified */ $tabsBlock->getId() ?>').length && jQuery('#<?= /* @escapeNotVerified */ $tabsBlock->getId() ?>').is(':mage-tabs')) { - var activeAnchor = jQuery('#<?= /* @escapeNotVerified */ $tabsBlock->getId() ?>').tabs('activeAnchor'); + if (jQuery('#<?= $block->escapeJs($tabsBlock->getId()) ?>').length && jQuery('#<?= $block->escapeJs($tabsBlock->getId()) ?>').is(':mage-tabs')) { + var activeAnchor = jQuery('#<?= $block->escapeJs($tabsBlock->getId()) ?>').tabs('activeAnchor'); if (activeAnchor && $('store_switcher')) { $('store_switcher').switchParams = 'active_tab/' + activeAnchor.prop('name') + '/'; } diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/tab/alert.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/tab/alert.phtml index 5b07121de49dc..7c3bee3d4d2fc 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/tab/alert.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/tab/alert.phtml @@ -4,8 +4,6 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - ?> <?php /** @@ -14,7 +12,7 @@ ?> <div id="alert_messages_block"><?= $block->getMessageHtml() ?></div> <div> - <h4 class="icon-head head-edit-form"><?= /* @escapeNotVerified */ __('Product Alerts') ?></h4> + <h4 class="icon-head head-edit-form"><?= $block->escapeHtml(__('Product Alerts')) ?></h4> </div> <div class="clear"></div> <?= $block->getAccordionHtml() ?> diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/tab/inventory.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/tab/inventory.phtml index 2c62bbf8db3e9..5028d3c1e83d0 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/tab/inventory.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/catalog/product/tab/inventory.phtml @@ -4,382 +4,448 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - /** @var $block \Magento\Catalog\Block\Adminhtml\Product\Edit\Tab\Inventory */ ?> -<?php if ($block->isReadonly()): ?> -<?php $_readonly = ' disabled="disabled" '; ?> -<?php else: ?> -<?php $_readonly = ''; ?> +<?php if ($block->isReadonly()) :?> + <?php $_readonly = ' disabled="disabled" '; ?> +<?php else :?> + <?php $_readonly = ''; ?> <?php endif; ?> <fieldset class="fieldset form-inline"> -<legend class="legend"><span><?= /* @escapeNotVerified */ __('Advanced Inventory') ?></span></legend> -<br> -<div id="table_cataloginventory"> -<div class="field"> - <label class="label" for="inventory_manage_stock"> - <span><?= /* @escapeNotVerified */ __('Manage Stock') ?></span> - </label> - <div class="control"> - <select id="inventory_manage_stock" name="<?= /* @escapeNotVerified */ $block->getFieldSuffix() ?>[stock_data][manage_stock]" <?= /* @escapeNotVerified */ $_readonly ?>> - <option value="1"><?= /* @escapeNotVerified */ __('Yes') ?></option> - <option value="0"<?php if ($block->getFieldValue('manage_stock') == 0): ?> selected="selected"<?php endif; ?>><?= /* @escapeNotVerified */ __('No') ?></option> - </select> - <input type="hidden" id="inventory_manage_stock_default" value="<?= /* @escapeNotVerified */ $block->getDefaultConfigValue('manage_stock') ?>"> - <?php $_checked = ($block->getFieldValue('use_config_manage_stock') || $block->isNew()) ? 'checked="checked"' : '' ?> - <input type="checkbox" id="inventory_use_config_manage_stock" name="<?= /* @escapeNotVerified */ $block->getFieldSuffix() ?>[stock_data][use_config_manage_stock]" value="1" <?= /* @escapeNotVerified */ $_checked ?> onclick="toggleValueElements(this, this.parentNode);" <?= /* @escapeNotVerified */ $_readonly ?>> - <label for="inventory_use_config_manage_stock"><?= /* @escapeNotVerified */ __('Use Config Settings') ?></label> - <?php if (!$block->isReadonly()): ?> - <script> -require(['prototype'], function(){ -toggleValueElements($('inventory_use_config_manage_stock'), $('inventory_use_config_manage_stock').parentNode); -}); -</script> - <?php endif; ?> - </div> - <?php if (!$block->isSingleStoreMode()): ?> - <div class="field-service"><?= /* @escapeNotVerified */ __('[GLOBAL]') ?></div> - <?php endif; ?> -</div> + <legend class="legend"><span><?= $block->escapeHtml(__('Advanced Inventory')) ?></span></legend> + <br> + <div id="table_cataloginventory"> + <div class="field"> + <label class="label" for="inventory_manage_stock"> + <span><?= $block->escapeHtml(__('Manage Stock')) ?></span> + </label> + <div class="control"> + <select id="inventory_manage_stock" + name="<?= /* @noEscape */ $block->getFieldSuffix() ?>[stock_data][manage_stock]" <?= /* @noEscape */ $_readonly ?>> + <option value="1"><?= $block->escapeHtml(__('Yes')) ?></option> + <option value="0"<?php if ($block->getFieldValue('manage_stock') == 0) :?> selected="selected"<?php endif; ?>><?= $block->escapeHtml(__('No')) ?></option> + </select> + <input type="hidden" + id="inventory_manage_stock_default" + value="<?= $block->escapeHtmlAttr($block->getDefaultConfigValue('manage_stock')) ?>"> + <?php $_checked = ($block->getFieldValue('use_config_manage_stock') || $block->isNew()) ? 'checked="checked"' : '' ?> + <input type="checkbox" + id="inventory_use_config_manage_stock" + name="<?= /* @noEscape */ $block->getFieldSuffix() ?>[stock_data][use_config_manage_stock]" + value="1" <?= /* @noEscape */ $_checked ?> + onclick="toggleValueElements(this, this.parentNode);" <?= /* @noEscape */ $_readonly ?>> + <label for="inventory_use_config_manage_stock"><?= $block->escapeHtml(__('Use Config Settings')) ?></label> + <?php if (!$block->isReadonly()) :?> + <script> + require(['prototype'], function(){ + toggleValueElements($('inventory_use_config_manage_stock'), $('inventory_use_config_manage_stock').parentNode); + }); + </script> + <?php endif; ?> + </div> + <?php if (!$block->isSingleStoreMode()) :?> + <div class="field-service"><?= $block->escapeHtml(__('[GLOBAL]')) ?></div> + <?php endif; ?> + </div> -<?php if (!$block->getProduct()->isComposite()): ?> -<div class="field"> - <label class="label" for="inventory_qty"> - <span><?= /* @escapeNotVerified */ __('Qty') ?></span> - </label> - <div class="control"> - <?php if (!$_readonly): ?> - <input type="hidden" id="original_inventory_qty" name="<?= /* @escapeNotVerified */ $block->getFieldSuffix() ?>[stock_data][original_inventory_qty]" value="<?= /* @escapeNotVerified */ $block->getFieldValue('qty') * 1 ?>"> - <?php endif;?> - <input type="text" class="input-text validate-number" id="inventory_qty" name="<?= /* @escapeNotVerified */ $block->getFieldSuffix() ?>[stock_data][qty]" value="<?= /* @escapeNotVerified */ $block->getFieldValue('qty') * 1 ?>" <?= /* @escapeNotVerified */ $_readonly ?>> - </div> - <?php if (!$block->isSingleStoreMode()): ?> - <div class="field-service"><?= /* @escapeNotVerified */ __('[GLOBAL]') ?></div> - <?php endif; ?> -</div> + <?php if (!$block->getProduct()->isComposite()) :?> + <div class="field"> + <label class="label" for="inventory_qty"> + <span><?= $block->escapeHtml(__('Qty')) ?></span> + </label> + <div class="control"> + <?php if (!$_readonly) :?> + <input type="hidden" + id="original_inventory_qty" + name="<?= /* @noEscape */ $block->getFieldSuffix() ?>[stock_data][original_inventory_qty]" + value="<?= $block->getFieldValue('qty') * 1 ?>"> + <?php endif;?> + <input type="text" + class="input-text validate-number" + id="inventory_qty" + name="<?= /* @noEscape */ $block->getFieldSuffix() ?>[stock_data][qty]" + value="<?= $block->getFieldValue('qty') * 1 ?>" <?= /* @noEscape */ $_readonly ?>> + </div> + <?php if (!$block->isSingleStoreMode()) :?> + <div class="field-service"><?= $block->escapeHtml(__('[GLOBAL]')) ?></div> + <?php endif; ?> + </div> -<div class="field"> - <label class="label" for="inventory_min_qty"> - <span><?= /* @escapeNotVerified */ __('Out-of-Stock Threshold') ?></span> - </label> - <div class="control"> - <input type="text" class="input-text validate-number" id="inventory_min_qty" name="<?= /* @escapeNotVerified */ $block->getFieldSuffix() ?>[stock_data][min_qty]" value="<?= /* @escapeNotVerified */ $block->getFieldValue('min_qty') * 1 ?>" <?= /* @escapeNotVerified */ $_readonly ?>> + <div class="field"> + <label class="label" for="inventory_min_qty"> + <span><?= $block->escapeHtml(__('Out-of-Stock Threshold')) ?></span> + </label> + <div class="control"> + <input type="text" + class="input-text validate-number" + id="inventory_min_qty" + name="<?= /* @noEscape */ $block->getFieldSuffix() ?>[stock_data][min_qty]" + value="<?= $block->getFieldValue('min_qty') * 1 ?>" <?= /* @noEscape */ $_readonly ?>> - <div class="control-inner-wrap"> - <?php $_checked = ($block->getFieldValue('use_config_min_qty') || $block->isNew()) ? 'checked="checked"' : '' ?> - <input type="checkbox" id="inventory_use_config_min_qty" name="<?= /* @escapeNotVerified */ $block->getFieldSuffix() ?>[stock_data][use_config_min_qty]" value="1" <?= /* @escapeNotVerified */ $_checked ?> onclick="toggleValueElements(this, this.parentNode);" <?= /* @escapeNotVerified */ $_readonly ?>> - <label for="inventory_use_config_min_qty"><?= /* @escapeNotVerified */ __('Use Config Settings') ?></label> - </div> + <div class="control-inner-wrap"> + <?php $_checked = ($block->getFieldValue('use_config_min_qty') || $block->isNew()) ? 'checked="checked"' : '' ?> + <input type="checkbox" + id="inventory_use_config_min_qty" + name="<?= /* @noEscape */ $block->getFieldSuffix() ?>[stock_data][use_config_min_qty]" + value="1" <?= /* @noEscape */ $_checked ?> + onclick="toggleValueElements(this, this.parentNode);" <?= /* @noEscape */ $_readonly ?>> + <label for="inventory_use_config_min_qty"><?= $block->escapeHtml(__('Use Config Settings')) ?></label> + </div> - <?php if (!$block->isReadonly()): ?> - <script> -require(["prototype"], function(){ -toggleValueElements($('inventory_use_config_min_qty'), $('inventory_use_config_min_qty').parentNode); -}); -</script> - <?php endif; ?> - </div> - <?php if (!$block->isSingleStoreMode()): ?> - <div class="field-service"><?= /* @escapeNotVerified */ __('[GLOBAL]') ?></div> - <?php endif; ?> -</div> + <?php if (!$block->isReadonly()) :?> + <script> + require(["prototype"], function(){ + toggleValueElements($('inventory_use_config_min_qty'), $('inventory_use_config_min_qty').parentNode); + }); + </script> + <?php endif; ?> + </div> + <?php if (!$block->isSingleStoreMode()) :?> + <div class="field-service"><?= $block->escapeHtml(__('[GLOBAL]')) ?></div> + <?php endif; ?> + </div> -<div class="field"> - <label class="label" for="inventory_min_sale_qty"> - <span><?= /* @escapeNotVerified */ __('Minimum Qty Allowed in Shopping Cart') ?></span> - </label> - <div class="control"> - <input type="text" class="input-text validate-number" id="inventory_min_sale_qty" - name="<?= /* @escapeNotVerified */ $block->getFieldSuffix() ?>[stock_data][min_sale_qty]" - value="<?= /* @escapeNotVerified */ $block->getFieldValue('min_sale_qty') * 1 ?>" <?= /* @escapeNotVerified */ $_readonly ?>> - <div class="control-inner-wrap"> - <?php $_checked = ($block->getFieldValue('use_config_min_sale_qty') || $block->isNew()) ? 'checked="checked"' : '' ?> - <input type="checkbox" id="inventory_use_config_min_sale_qty" name="<?= /* @escapeNotVerified */ $block->getFieldSuffix() ?>[stock_data][use_config_min_sale_qty]" value="1" <?= /* @escapeNotVerified */ $_checked ?> onclick="toggleValueElements(this, this.parentNode);" class="checkbox" <?= /* @escapeNotVerified */ $_readonly ?>> - <label for="inventory_use_config_min_sale_qty"><?= /* @escapeNotVerified */ __('Use Config Settings') ?></label> - </div> - <?php if (!$block->isReadonly()): ?> - <script> -require(['prototype'], function(){ -toggleValueElements($('inventory_use_config_min_sale_qty'), $('inventory_use_config_min_sale_qty').parentNode); -}); -</script> - <?php endif; ?> - </div> - <?php if (!$block->isSingleStoreMode()): ?> - <div class="field-service"><?= /* @escapeNotVerified */ __('[GLOBAL]') ?></div> - <?php endif; ?> -</div> + <div class="field"> + <label class="label" for="inventory_min_sale_qty"> + <span><?= $block->escapeHtml(__('Minimum Qty Allowed in Shopping Cart')) ?></span> + </label> + <div class="control"> + <input type="text" class="input-text validate-number" id="inventory_min_sale_qty" + name="<?= /* @noEscape */ $block->getFieldSuffix() ?>[stock_data][min_sale_qty]" + value="<?= $block->getFieldValue('min_sale_qty') * 1 ?>" <?= /* @noEscape */ $_readonly ?>> + <div class="control-inner-wrap"> + <?php $_checked = ($block->getFieldValue('use_config_min_sale_qty') || $block->isNew()) ? 'checked="checked"' : '' ?> + <input type="checkbox" + id="inventory_use_config_min_sale_qty" + name="<?= /* @noEscape */ $block->getFieldSuffix() ?>[stock_data][use_config_min_sale_qty]" + value="1" <?= /* @noEscape */ $_checked ?> + onclick="toggleValueElements(this, this.parentNode);" + class="checkbox" <?= /* @noEscape */ $_readonly ?>> + <label for="inventory_use_config_min_sale_qty"><?= $block->escapeHtml(__('Use Config Settings')) ?></label> + </div> + <?php if (!$block->isReadonly()) :?> + <script> + require(['prototype'], function(){ + toggleValueElements($('inventory_use_config_min_sale_qty'), $('inventory_use_config_min_sale_qty').parentNode); + }); + </script> + <?php endif; ?> + </div> + <?php if (!$block->isSingleStoreMode()) :?> + <div class="field-service"><?= $block->escapeHtml(__('[GLOBAL]')) ?></div> + <?php endif; ?> + </div> -<div class="field"> - <label class="label" for="inventory_max_sale_qty"> - <span><?= /* @escapeNotVerified */ __('Maximum Qty Allowed in Shopping Cart') ?></span> - </label> - <div class="control"> - <input type="text" class="input-text validate-number" id="inventory_max_sale_qty" name="<?= /* @escapeNotVerified */ $block->getFieldSuffix() ?>[stock_data][max_sale_qty]" value="<?= /* @escapeNotVerified */ $block->getFieldValue('max_sale_qty') * 1 ?>" <?= /* @escapeNotVerified */ $_readonly ?>> - <?php $_checked = ($block->getFieldValue('use_config_max_sale_qty') || $block->isNew()) ? 'checked="checked"' : '' ?> - <div class="control-inner-wrap"> - <input type="checkbox" id="inventory_use_config_max_sale_qty" name="<?= /* @escapeNotVerified */ $block->getFieldSuffix() ?>[stock_data][use_config_max_sale_qty]" value="1" <?= /* @escapeNotVerified */ $_checked ?> onclick="toggleValueElements(this, this.parentNode);" class="checkbox" <?= /* @escapeNotVerified */ $_readonly ?>> - <label for="inventory_use_config_max_sale_qty"><?= /* @escapeNotVerified */ __('Use Config Settings') ?></label> - </div> - <?php if (!$block->isReadonly()): ?> - <script> -require(['prototype'], function(){ -toggleValueElements($('inventory_use_config_max_sale_qty'), $('inventory_use_config_max_sale_qty').parentNode); -}); -</script> - <?php endif; ?> - </div> - <?php if (!$block->isSingleStoreMode()): ?> - <div class="field-service"><?= /* @escapeNotVerified */ __('[GLOBAL]') ?></div> - <?php endif; ?> -</div> + <div class="field"> + <label class="label" for="inventory_max_sale_qty"> + <span><?= $block->escapeHtml(__('Maximum Qty Allowed in Shopping Cart')) ?></span> + </label> + <div class="control"> + <input type="text" + class="input-text validate-number" + id="inventory_max_sale_qty" + name="<?= /* @noEscape */ $block->getFieldSuffix() ?>[stock_data][max_sale_qty]" + value="<?= $block->getFieldValue('max_sale_qty') * 1 ?>" <?= /* @noEscape */ $_readonly ?>> + <?php $_checked = ($block->getFieldValue('use_config_max_sale_qty') || $block->isNew()) ? 'checked="checked"' : '' ?> + <div class="control-inner-wrap"> + <input type="checkbox" + id="inventory_use_config_max_sale_qty" + name="<?= /* @noEscape */ $block->getFieldSuffix() ?>[stock_data][use_config_max_sale_qty]" + value="1" <?= /* @noEscape */ $_checked ?> + onclick="toggleValueElements(this, this.parentNode);" + class="checkbox" <?= /* @noEscape */ $_readonly ?>> + <label for="inventory_use_config_max_sale_qty"><?= $block->escapeHtml(__('Use Config Settings')) ?></label> + </div> + <?php if (!$block->isReadonly()) :?> + <script> + require(['prototype'], function(){ + toggleValueElements($('inventory_use_config_max_sale_qty'), $('inventory_use_config_max_sale_qty').parentNode); + }); + </script> + <?php endif; ?> + </div> + <?php if (!$block->isSingleStoreMode()) :?> + <div class="field-service"><?= $block->escapeHtml(__('[GLOBAL]')) ?></div> + <?php endif; ?> + </div> - <?php if ($block->canUseQtyDecimals()): ?> - <div class="field"> - <label class="label" for="inventory_is_qty_decimal"> - <span><?= /* @escapeNotVerified */ __('Qty Uses Decimals') ?></span> - </label> - <div class="control"> - <select id="inventory_is_qty_decimal" name="<?= /* @escapeNotVerified */ $block->getFieldSuffix() ?>[stock_data][is_qty_decimal]" <?= /* @escapeNotVerified */ $_readonly ?>> - <option value="0"><?= /* @escapeNotVerified */ __('No') ?></option> - <option value="1"<?php if ($block->getFieldValue('is_qty_decimal') == 1): ?> selected="selected"<?php endif; ?>><?= /* @escapeNotVerified */ __('Yes') ?></option> - </select> - </div> - <?php if (!$block->isSingleStoreMode()): ?> - <div class="field-service"><?= /* @escapeNotVerified */ __('[GLOBAL]') ?></div> - <?php endif; ?> - </div> + <?php if ($block->canUseQtyDecimals()) :?> + <div class="field"> + <label class="label" for="inventory_is_qty_decimal"> + <span><?= $block->escapeHtml(__('Qty Uses Decimals')) ?></span> + </label> + <div class="control"> + <select id="inventory_is_qty_decimal" + name="<?= /* @noEscape */ $block->getFieldSuffix() ?>[stock_data][is_qty_decimal]" <?= /* @noEscape */ $_readonly ?>> + <option value="0"><?= $block->escapeHtml(__('No')) ?></option> + <option value="1"<?php if ($block->getFieldValue('is_qty_decimal') == 1) :?> selected="selected"<?php endif; ?>><?= $block->escapeHtml(__('Yes')) ?></option> + </select> + </div> + <?php if (!$block->isSingleStoreMode()) :?> + <div class="field-service"><?= $block->escapeHtml(__('[GLOBAL]')) ?></div> + <?php endif; ?> + </div> - <?php if (!$block->isVirtual()) : ?> - <div class="field"> - <label class="label" for="inventory_is_decimal_divided"> - <span><?= /* @escapeNotVerified */ __('Allow Multiple Boxes for Shipping') ?></span> - </label> - <div class="control"> - <select id="inventory_is_decimal_divided" name="<?= /* @escapeNotVerified */ $block->getFieldSuffix() ?>[stock_data][is_decimal_divided]" <?= /* @escapeNotVerified */ $_readonly ?>> - <option value="0"><?= /* @escapeNotVerified */ __('No') ?></option> - <option value="1"<?php if ($block->getFieldValue('is_decimal_divided') == 1): ?> selected="selected"<?php endif; ?>><?= /* @escapeNotVerified */ __('Yes') ?></option> - </select> - </div> - <?php if (!$block->isSingleStoreMode()): ?> - <div class="field-service"><?= /* @escapeNotVerified */ __('[GLOBAL]') ?></div> + <?php if (!$block->isVirtual()) :?> + <div class="field"> + <label class="label" for="inventory_is_decimal_divided"> + <span><?= $block->escapeHtml(__('Allow Multiple Boxes for Shipping')) ?></span> + </label> + <div class="control"> + <select id="inventory_is_decimal_divided" + name="<?= /* @noEscape */ $block->getFieldSuffix() ?>[stock_data][is_decimal_divided]" <?= /* @noEscape */ $_readonly ?>> + <option value="0"><?= $block->escapeHtml(__('No')) ?></option> + <option value="1"<?php if ($block->getFieldValue('is_decimal_divided') == 1) :?> + selected="selected"<?php endif; ?>><?= $block->escapeHtml(__('Yes')) ?></option> + </select> + </div> + <?php if (!$block->isSingleStoreMode()) :?> + <div class="field-service"><?= $block->escapeHtml(__('[GLOBAL]')) ?></div> + <?php endif; ?> + </div> + <?php endif; ?> <?php endif; ?> - </div> - <?php endif; ?> - <?php endif; ?> -<div class="field"> - <label class="label" for="inventory_backorders"> - <span><?= /* @escapeNotVerified */ __('Backorders') ?></span> - </label> - <div class="control"> - <select id="inventory_backorders" name="<?= /* @escapeNotVerified */ $block->getFieldSuffix() ?>[stock_data][backorders]" <?= /* @escapeNotVerified */ $_readonly ?>> - <?php foreach ($block->getBackordersOption() as $option): ?> - <?php $_selected = ($option['value'] == $block->getFieldValue('backorders')) ? 'selected="selected"' : '' ?> - <option value="<?= /* @escapeNotVerified */ $option['value'] ?>" <?= /* @escapeNotVerified */ $_selected ?>><?= /* @escapeNotVerified */ $option['label'] ?></option> - <?php endforeach; ?> - </select> + <div class="field"> + <label class="label" for="inventory_backorders"> + <span><?= $block->escapeHtml(__('Backorders')) ?></span> + </label> + <div class="control"> + <select id="inventory_backorders" + name="<?= /* @noEscape */ $block->getFieldSuffix() ?>[stock_data][backorders]" <?= /* @noEscape */ $_readonly ?>> + <?php foreach ($block->getBackordersOption() as $option) :?> + <?php $_selected = ($option['value'] == $block->getFieldValue('backorders')) ? 'selected="selected"' : '' ?> + <option value="<?= $block->escapeHtmlAttr($option['value']) ?>" <?= /* @noEscape */ $_selected ?>><?= $block->escapeHtml($option['label']) ?></option> + <?php endforeach; ?> + </select> - <div class="control-inner-wrap"> - <?php $_checked = ($block->getFieldValue('use_config_backorders') || $block->isNew()) ? 'checked="checked"' : '' ?> - <input type="checkbox" id="inventory_use_config_backorders" name="<?= /* @escapeNotVerified */ $block->getFieldSuffix() ?>[stock_data][use_config_backorders]" value="1" <?= /* @escapeNotVerified */ $_checked ?> onclick="toggleValueElements(this, this.parentNode);" <?= /* @escapeNotVerified */ $_readonly ?>> - <label for="inventory_use_config_backorders"><?= /* @escapeNotVerified */ __('Use Config Settings') ?></label> - </div> - <?php if (!$block->isReadonly()): ?> - <script> -require(['prototype'], function(){ -toggleValueElements($('inventory_use_config_backorders'), $('inventory_use_config_backorders').parentNode); -}); -</script> - <?php endif; ?> - </div> - <?php if (!$block->isSingleStoreMode()): ?> - <div class="field-service"><?= /* @escapeNotVerified */ __('[GLOBAL]') ?></div> - <?php endif; ?> -</div> + <div class="control-inner-wrap"> + <?php $_checked = ($block->getFieldValue('use_config_backorders') || $block->isNew()) ? 'checked="checked"' : '' ?> + <input type="checkbox" + id="inventory_use_config_backorders" + name="<?= /* @noEscape */ $block->getFieldSuffix() ?>[stock_data][use_config_backorders]" + value="1" <?= /* @noEscape */ $_checked ?> + onclick="toggleValueElements(this, this.parentNode);" <?= /* @noEscape */ $_readonly ?>> + <label for="inventory_use_config_backorders"><?= $block->escapeHtml(__('Use Config Settings')) ?></label> + </div> + <?php if (!$block->isReadonly()) :?> + <script> + require(['prototype'], function(){ + toggleValueElements($('inventory_use_config_backorders'), $('inventory_use_config_backorders').parentNode); + }); + </script> + <?php endif; ?> + </div> + <?php if (!$block->isSingleStoreMode()) :?> + <div class="field-service"><?= $block->escapeHtml(__('[GLOBAL]')) ?></div> + <?php endif; ?> + </div> -<div class="field"> - <label class="label" for="inventory_notify_stock_qty"> - <span><?= /* @escapeNotVerified */ __('Notify for Quantity Below') ?></span> - </label> - <div class="control"> - <input type="text" class="input-text validate-number" id="inventory_notify_stock_qty" name="<?= /* @escapeNotVerified */ $block->getFieldSuffix() ?>[stock_data][notify_stock_qty]" value="<?= /* @escapeNotVerified */ $block->getFieldValue('notify_stock_qty') * 1 ?>" <?= /* @escapeNotVerified */ $_readonly ?>> + <div class="field"> + <label class="label" for="inventory_notify_stock_qty"> + <span><?= $block->escapeHtml(__('Notify for Quantity Below')) ?></span> + </label> + <div class="control"> + <input type="text" + class="input-text validate-number" + id="inventory_notify_stock_qty" + name="<?= /* @noEscape */ $block->getFieldSuffix() ?>[stock_data][notify_stock_qty]" + value="<?= $block->getFieldValue('notify_stock_qty') * 1 ?>" <?= /* @noEscape */ $_readonly ?>> - <div class="control-inner-wrap"> - <?php $_checked = ($block->getFieldValue('use_config_notify_stock_qty') || $block->isNew()) ? 'checked="checked"' : '' ?> - <input type="checkbox" id="inventory_use_config_notify_stock_qty" name="<?= /* @escapeNotVerified */ $block->getFieldSuffix() ?>[stock_data][use_config_notify_stock_qty]" value="1" <?= /* @escapeNotVerified */ $_checked ?> onclick="toggleValueElements(this, this.parentNode);" <?= /* @escapeNotVerified */ $_readonly ?>> - <label for="inventory_use_config_notify_stock_qty"><?= /* @escapeNotVerified */ __('Use Config Settings') ?></label> - </div> - <?php if (!$block->isReadonly()): ?> - <script> -require(['prototype'], function(){ -toggleValueElements($('inventory_use_config_notify_stock_qty'), $('inventory_use_config_notify_stock_qty').parentNode); -}); -</script> - <?php endif; ?> - </div> - <?php if (!$block->isSingleStoreMode()): ?> - <div class="field-service"><?= /* @escapeNotVerified */ __('[GLOBAL]') ?></div> - <?php endif; ?> -</div> + <div class="control-inner-wrap"> + <?php $_checked = ($block->getFieldValue('use_config_notify_stock_qty') || $block->isNew()) ? 'checked="checked"' : '' ?> + <input type="checkbox" + id="inventory_use_config_notify_stock_qty" + name="<?= /* @noEscape */ $block->getFieldSuffix() ?>[stock_data][use_config_notify_stock_qty]" + value="1" <?= /* @noEscape */ $_checked ?> + onclick="toggleValueElements(this, this.parentNode);" <?= /* @noEscape */ $_readonly ?>> + <label for="inventory_use_config_notify_stock_qty"><?= $block->escapeHtml(__('Use Config Settings')) ?></label> + </div> + <?php if (!$block->isReadonly()) :?> + <script> + require(['prototype'], function(){ + toggleValueElements($('inventory_use_config_notify_stock_qty'), $('inventory_use_config_notify_stock_qty').parentNode); + }); + </script> + <?php endif; ?> + </div> + <?php if (!$block->isSingleStoreMode()) :?> + <div class="field-service"><?= $block->escapeHtml(__('[GLOBAL]')) ?></div> + <?php endif; ?> + </div> - <?php endif; ?> -<div class="field"> - <label class="label" for="inventory_enable_qty_increments"> - <span><?= /* @escapeNotVerified */ __('Enable Qty Increments') ?></span> - </label> - <div class="control"> - <?php $qtyIncrementsEnabled = $block->getFieldValue('enable_qty_increments'); ?> - <select id="inventory_enable_qty_increments" name="<?= /* @escapeNotVerified */ $block->getFieldSuffix() ?>[stock_data][enable_qty_increments]" <?= /* @escapeNotVerified */ $_readonly ?>> - <option value="1"<?php if ($qtyIncrementsEnabled): ?> selected="selected"<?php endif; ?>><?= /* @escapeNotVerified */ __('Yes') ?></option> - <option value="0"<?php if (!$qtyIncrementsEnabled): ?> selected="selected"<?php endif; ?>><?= /* @escapeNotVerified */ __('No') ?></option> - </select> - <input type="hidden" id="inventory_enable_qty_increments_default" value="<?= /* @escapeNotVerified */ $block->getDefaultConfigValue('enable_qty_increments') ?>"> + <?php endif; ?> + <div class="field"> + <label class="label" for="inventory_enable_qty_increments"> + <span><?= $block->escapeHtml(__('Enable Qty Increments')) ?></span> + </label> + <div class="control"> + <?php $qtyIncrementsEnabled = $block->getFieldValue('enable_qty_increments'); ?> + <select id="inventory_enable_qty_increments" + name="<?= /* @noEscape */ $block->getFieldSuffix() ?>[stock_data][enable_qty_increments]" <?= /* @noEscape */ $_readonly ?>> + <option value="1"<?php if ($qtyIncrementsEnabled) :?> selected="selected"<?php endif; ?>><?= $block->escapeHtml(__('Yes')) ?></option> + <option value="0"<?php if (!$qtyIncrementsEnabled) :?> selected="selected"<?php endif; ?>><?= $block->escapeHtml(__('No')) ?></option> + </select> + <input type="hidden" + id="inventory_enable_qty_increments_default" + value="<?= $block->escapeHtmlAttr($block->getDefaultConfigValue('enable_qty_increments')) ?>"> - <div class="control-inner-wrap"> - <?php $_checked = ($block->getFieldValue('use_config_enable_qty_inc') || $block->isNew()) ? 'checked="checked"' : '' ?> - <input type="checkbox" id="inventory_use_config_enable_qty_increments" name="<?= /* @escapeNotVerified */ $block->getFieldSuffix() ?>[stock_data][use_config_enable_qty_increments]" value="1" <?= /* @escapeNotVerified */ $_checked ?> onclick="toggleValueElements(this, this.parentNode);" <?= /* @escapeNotVerified */ $_readonly ?>> - <label for="inventory_use_config_enable_qty_increments"><?= /* @escapeNotVerified */ __('Use Config Settings') ?></label> + <div class="control-inner-wrap"> + <?php $_checked = ($block->getFieldValue('use_config_enable_qty_inc') || $block->isNew()) ? 'checked="checked"' : '' ?> + <input type="checkbox" + id="inventory_use_config_enable_qty_increments" + name="<?= /* @noEscape */ $block->getFieldSuffix() ?>[stock_data][use_config_enable_qty_increments]" + value="1" <?= /* @noEscape */ $_checked ?> + onclick="toggleValueElements(this, this.parentNode);" <?= /* @noEscape */ $_readonly ?>> + <label for="inventory_use_config_enable_qty_increments"><?= $block->escapeHtml(__('Use Config Settings')) ?></label> + </div> + <?php if (!$block->isReadonly()) :?> + <script> + require(['prototype'], function(){ + toggleValueElements($('inventory_use_config_enable_qty_increments'), $('inventory_use_config_enable_qty_increments').parentNode); + }); + </script> + <?php endif; ?> + </div> + <?php if (!$block->isSingleStoreMode()) :?> + <div class="field-service"><?= $block->escapeHtml(__('[GLOBAL]')) ?></div> + <?php endif; ?> </div> - <?php if (!$block->isReadonly()): ?> - <script> -require(['prototype'], function(){ -toggleValueElements($('inventory_use_config_enable_qty_increments'), $('inventory_use_config_enable_qty_increments').parentNode); -}); -</script> - <?php endif; ?> - </div> - <?php if (!$block->isSingleStoreMode()): ?> - <div class="field-service"><?= /* @escapeNotVerified */ __('[GLOBAL]') ?></div> - <?php endif; ?> -</div> -<div class="field"> - <label class="label" for="inventory_qty_increments"> - <span><?= /* @escapeNotVerified */ __('Qty Increments') ?></span> - </label> - <div class="control"> - <input type="text" class="input-text validate-digits" id="inventory_qty_increments" name="<?= /* @escapeNotVerified */ $block->getFieldSuffix() ?>[stock_data][qty_increments]" value="<?= /* @escapeNotVerified */ $block->getFieldValue('qty_increments') * 1 ?>" <?= /* @escapeNotVerified */ $_readonly ?>> - <div class="control-inner-wrap"> - <?php $_checked = ($block->getFieldValue('use_config_qty_increments') || $block->isNew()) ? 'checked="checked"' : '' ?> - <input type="checkbox" id="inventory_use_config_qty_increments" name="<?= /* @escapeNotVerified */ $block->getFieldSuffix() ?>[stock_data][use_config_qty_increments]" value="1" <?= /* @escapeNotVerified */ $_checked ?> onclick="toggleValueElements(this, this.parentNode);" <?= /* @escapeNotVerified */ $_readonly ?>> - <label for="inventory_use_config_qty_increments"><?= /* @escapeNotVerified */ __('Use Config Settings') ?></label> + <div class="field"> + <label class="label" for="inventory_qty_increments"> + <span><?= $block->escapeHtml(__('Qty Increments')) ?></span> + </label> + <div class="control"> + <input type="text" + class="input-text validate-digits" + id="inventory_qty_increments" + name="<?= /* @noEscape */ $block->getFieldSuffix() ?>[stock_data][qty_increments]" + value="<?= $block->getFieldValue('qty_increments') * 1 ?>" <?= /* @noEscape */ $_readonly ?>> + <div class="control-inner-wrap"> + <?php $_checked = ($block->getFieldValue('use_config_qty_increments') || $block->isNew()) ? 'checked="checked"' : '' ?> + <input type="checkbox" + id="inventory_use_config_qty_increments" + name="<?= /* @noEscape */ $block->getFieldSuffix() ?>[stock_data][use_config_qty_increments]" + value="1" <?= /* @noEscape */ $_checked ?> + onclick="toggleValueElements(this, this.parentNode);" <?= /* @noEscape */ $_readonly ?>> + <label for="inventory_use_config_qty_increments"><?= $block->escapeHtml(__('Use Config Settings')) ?></label> + </div> + <?php if (!$block->isReadonly()) :?> + <script> + require(['prototype'], function(){ + toggleValueElements($('inventory_use_config_qty_increments'), $('inventory_use_config_qty_increments').parentNode); + }); + </script> + <?php endif; ?> + </div> + <?php if (!$block->isSingleStoreMode()) :?> + <div class="field-service"><?= $block->escapeHtml(__('[GLOBAL]')) ?></div> + <?php endif; ?> </div> - <?php if (!$block->isReadonly()): ?> - <script> -require(['prototype'], function(){ -toggleValueElements($('inventory_use_config_qty_increments'), $('inventory_use_config_qty_increments').parentNode); -}); -</script> - <?php endif; ?> - </div> - <?php if (!$block->isSingleStoreMode()): ?> - <div class="field-service"><?= /* @escapeNotVerified */ __('[GLOBAL]') ?></div> - <?php endif; ?> -</div> -<div class="field"> - <label class="label" for="inventory_stock_availability"> - <span><?= /* @escapeNotVerified */ __('Stock Availability') ?></span> - </label> - <div class="control"> - <select id="inventory_stock_availability" name="<?= /* @escapeNotVerified */ $block->getFieldSuffix() ?>[stock_data][is_in_stock]" <?= /* @escapeNotVerified */ $_readonly ?>> - <?php foreach ($block->getStockOption() as $option): ?> - <?php $_selected = ($block->getFieldValue('is_in_stock') !== null && $option['value'] == $block->getFieldValue('is_in_stock')) ? 'selected="selected"' : '' ?> - <option value="<?= /* @escapeNotVerified */ $option['value'] ?>" <?= /* @escapeNotVerified */ $_selected ?>><?= /* @escapeNotVerified */ $option['label'] ?></option> - <?php endforeach; ?> - </select> + <div class="field"> + <label class="label" for="inventory_stock_availability"> + <span><?= $block->escapeHtml(__('Stock Availability')) ?></span> + </label> + <div class="control"> + <select id="inventory_stock_availability" + name="<?= /* @noEscape */ $block->getFieldSuffix() ?>[stock_data][is_in_stock]" <?= /* @noEscape */ $_readonly ?>> + <?php foreach ($block->getStockOption() as $option) :?> + <?php $_selected = ($block->getFieldValue('is_in_stock') !== null && $option['value'] == $block->getFieldValue('is_in_stock')) ? 'selected="selected"' : '' ?> + <option value="<?= $block->escapeHtmlAttr($option['value']) ?>" <?= /* @noEscape */ $_selected ?>><?= $block->escapeHtml($option['label']) ?></option> + <?php endforeach; ?> + </select> + </div> + <?php if (!$block->isSingleStoreMode()) :?> + <div class="field-service"><?= $block->escapeHtml(__('[GLOBAL]')) ?></div> + <?php endif; ?> + </div> </div> - <?php if (!$block->isSingleStoreMode()): ?> - <div class="field-service"><?= /* @escapeNotVerified */ __('[GLOBAL]') ?></div> - <?php endif; ?> -</div> -</div> </fieldset> <script> -require(["jquery","prototype"], function(jQuery){ + require(["jquery","prototype"], function(jQuery){ - //<![CDATA[ - function changeManageStockOption() - { - var manageStock = $('inventory_use_config_manage_stock').checked + //<![CDATA[ + function changeManageStockOption() + { + var manageStock = $('inventory_use_config_manage_stock').checked ? $('inventory_manage_stock_default').value : $('inventory_manage_stock').value; - var catalogInventoryNotManageStockFields = { - inventory_min_sale_qty: true, - inventory_max_sale_qty: true, - inventory_enable_qty_increments : true, - inventory_qty_increments: true - }; - - $$('#table_cataloginventory > div').each(function(el) { - if (el == $('inventory_manage_stock').up(1)) { - return; - } + var catalogInventoryNotManageStockFields = { + inventory_min_sale_qty: true, + inventory_max_sale_qty: true, + inventory_enable_qty_increments : true, + inventory_qty_increments: true + }; - for (field in catalogInventoryNotManageStockFields) { - if ($(field) && ($(field).up(1) == el)) { + $$('#table_cataloginventory > div').each(function(el) { + if (el == $('inventory_manage_stock').up(1)) { return; } - } - el[manageStock == 1 ? 'show' : 'hide'](); - }); + for (field in catalogInventoryNotManageStockFields) { + if ($(field) && ($(field).up(1) == el)) { + return; + } + } - return true; - } + el[manageStock == 1 ? 'show' : 'hide'](); + }); - function applyEnableQtyIncrements() { - var enableQtyIncrements = $('inventory_use_config_enable_qty_increments').checked - ? $('inventory_enable_qty_increments_default').value - : $('inventory_enable_qty_increments').value; + return true; + } - $('inventory_qty_increments').up('.field')[enableQtyIncrements == 1 ? 'show' : 'hide'](); - } + function applyEnableQtyIncrements() { + var enableQtyIncrements = $('inventory_use_config_enable_qty_increments').checked + ? $('inventory_enable_qty_increments_default').value + : $('inventory_enable_qty_increments').value; - function applyEnableDecimalDivided() { - <?php if (!$block->isVirtual()) : ?> - $('inventory_is_decimal_divided').up('.field').hide(); - <?php endif; ?> - $('inventory_qty_increments').removeClassName('validate-digits').removeClassName('validate-number'); - $('inventory_min_sale_qty').removeClassName('validate-digits').removeClassName('validate-number'); - if ($('inventory_is_qty_decimal').value == 1) { - <?php if (!$block->isVirtual()) : ?> - $('inventory_is_decimal_divided').up('.field').show(); - <?php endif; ?> - $('inventory_qty_increments').addClassName('validate-number'); - $('inventory_min_sale_qty').addClassName('validate-number'); - } else { - $('inventory_qty_increments').addClassName('validate-digits'); - $('inventory_min_sale_qty').addClassName('validate-digits'); + $('inventory_qty_increments').up('.field')[enableQtyIncrements == 1 ? 'show' : 'hide'](); } - } - Event.observe(window, 'load', function() { - if ($('inventory_manage_stock') && $('inventory_use_config_manage_stock')) { - Event.observe($('inventory_manage_stock'), 'change', changeManageStockOption); - Event.observe($('inventory_use_config_manage_stock'), 'change', changeManageStockOption); - changeManageStockOption(); + function applyEnableDecimalDivided() { + <?php if (!$block->isVirtual()) :?> + $('inventory_is_decimal_divided').up('.field').hide(); + <?php endif; ?> + $('inventory_qty_increments').removeClassName('validate-digits').removeClassName('validate-number'); + $('inventory_min_sale_qty').removeClassName('validate-digits').removeClassName('validate-number'); + if ($('inventory_is_qty_decimal').value == 1) { + <?php if (!$block->isVirtual()) :?> + $('inventory_is_decimal_divided').up('.field').show(); + <?php endif; ?> + $('inventory_qty_increments').addClassName('validate-number'); + $('inventory_min_sale_qty').addClassName('validate-number'); + } else { + $('inventory_qty_increments').addClassName('validate-digits'); + $('inventory_min_sale_qty').addClassName('validate-digits'); + } } - if ($('inventory_enable_qty_increments') && $('inventory_use_config_enable_qty_increments')) { - //Delegation is used because of events, which are not firing while the input is disabled - jQuery('#inventory_enable_qty_increments').parent() + + Event.observe(window, 'load', function() { + if ($('inventory_manage_stock') && $('inventory_use_config_manage_stock')) { + Event.observe($('inventory_manage_stock'), 'change', changeManageStockOption); + Event.observe($('inventory_use_config_manage_stock'), 'change', changeManageStockOption); + changeManageStockOption(); + } + if ($('inventory_enable_qty_increments') && $('inventory_use_config_enable_qty_increments')) { + //Delegation is used because of events, which are not firing while the input is disabled + jQuery('#inventory_enable_qty_increments').parent() .on('change', '#inventory_enable_qty_increments', applyEnableQtyIncrements); - Event.observe($('inventory_use_config_enable_qty_increments'), 'change', applyEnableQtyIncrements); - applyEnableQtyIncrements(); - } - if ($('inventory_is_qty_decimal') && $('inventory_qty_increments') && $('inventory_min_sale_qty')) { - Event.observe($('inventory_is_qty_decimal'), 'change', applyEnableDecimalDivided); - applyEnableDecimalDivided(); - } - }); + Event.observe($('inventory_use_config_enable_qty_increments'), 'change', applyEnableQtyIncrements); + applyEnableQtyIncrements(); + } + if ($('inventory_is_qty_decimal') && $('inventory_qty_increments') && $('inventory_min_sale_qty')) { + Event.observe($('inventory_is_qty_decimal'), 'change', applyEnableDecimalDivided); + applyEnableDecimalDivided(); + } + }); - window.applyEnableDecimalDivided = applyEnableDecimalDivided; - window.applyEnableQtyIncrements = applyEnableQtyIncrements; - window.changeManageStockOption = changeManageStockOption; - //]]> + window.applyEnableDecimalDivided = applyEnableDecimalDivided; + window.applyEnableQtyIncrements = applyEnableQtyIncrements; + window.changeManageStockOption = changeManageStockOption; + //]]> -}); + }); </script> diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/product/edit/attribute/search.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/product/edit/attribute/search.phtml index 04ccfb5aee8d0..17fb517b32547 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/product/edit/attribute/search.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/product/edit/attribute/search.phtml @@ -4,24 +4,24 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile +// phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis /** @var $block \Magento\Catalog\Block\Adminhtml\Product\Edit\Tab\Attributes\Search */ ?> <div id="product-attribute-search-container" class="suggest-expandable attribute-selector"> <div class="action-dropdown"> <button type="button" class="action-toggle action-choose" data-mage-init='{"dropdown":{}}' data-toggle="dropdown"> - <span><?= /* @escapeNotVerified */ __('Add Attribute') ?></span> + <span><?= $block->escapeHtml(__('Add Attribute')) ?></span> </button> <div class="dropdown-menu"> <input data-role="product-attribute-search" - data-group="<?= $block->escapeHtml($block->getGroupCode()) ?>" + data-group="<?= $block->escapeHtmlAttr($block->getGroupCode()) ?>" class="search" type="text" - placeholder="<?= /* @noEscape */ __('start typing to search attribute') ?>" /> + placeholder="<?= $block->escapeHtmlAttr(__('start typing to search attribute')) ?>" /> </div> </div> -<script data-template-for="product-attribute-search-<?= /* @escapeNotVerified */ $block->getGroupId() ?>" type="text/x-magento-template"> +<script data-template-for="product-attribute-search-<?= $block->escapeHtmlAttr($block->getGroupId()) ?>" type="text/x-magento-template"> <ul data-mage-init='{"menu":[]}'> <% if (data.items.length) { %> <% _.each(data.items, function(value){ %> @@ -29,7 +29,7 @@ <% }); %> <% } else { %><span class="mage-suggest-no-records"><%- data.noRecordsText %></span><% } %> </ul> - <div class="actions"><?= /* @escapeNotVerified */ $block->getAttributeCreate() ?></div> + <div class="actions"><?= $block->escapeHtml($block->getAttributeCreate()) ?></div> </script> <script> @@ -51,13 +51,13 @@ }); }); - $suggest.mage('suggest', <?= /* @escapeNotVerified */ $this->helper('Magento\Framework\Json\Helper\Data')->jsonEncode($block->getSelectorOptions()) ?>) + $suggest.mage('suggest', <?= /* @noEscape */ $this->helper(Magento\Framework\Json\Helper\Data::class)->jsonEncode($block->getSelectorOptions()) ?>) .on('suggestselect', function (e, ui) { $(this).val(''); var templateId = $('#attribute_set_id').val(); if (ui.item.id) { $.ajax({ - url: '<?= /* @escapeNotVerified */ $block->getAddAttributeUrl() ?>', + url: '<?= $block->escapeJs($block->escapeUrl($block->getAddAttributeUrl())) ?>', type: 'POST', dataType: 'json', data: {attribute_id: ui.item.id, template_id: templateId, group: $(this).data('group')}, diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/product/edit/tabs.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/product/edit/tabs.phtml index 6a62f01f97b65..360694fceb241 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/product/edit/tabs.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/product/edit/tabs.phtml @@ -4,40 +4,38 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - /** @var $block \Magento\Catalog\Block\Adminhtml\Product\Edit\Tabs */ ?> -<?php if (!empty($tabs)): ?> +<?php if (!empty($tabs)) :?> <?php $tabGroups = [ \Magento\Catalog\Block\Adminhtml\Product\Edit\Tabs::BASIC_TAB_GROUP_CODE, \Magento\Catalog\Block\Adminhtml\Product\Edit\Tabs::ADVANCED_TAB_GROUP_CODE, ];?> - <div id="<?= /* @escapeNotVerified */ $block->getId() ?>" + <div id="<?= $block->escapeHtmlAttr($block->getId()) ?>" data-mage-init='{"tabs":{ - "active": "<?= /* @escapeNotVerified */ $block->getActiveTabId() ?>", - "destination": "#<?= /* @escapeNotVerified */ $block->getDestElementId() ?>", - "shadowTabs": "<?= /* @escapeNotVerified */ $block->getAllShadowTabs() ?>", - "tabsBlockPrefix": "<?= /* @escapeNotVerified */ $block->getId() ?>_", + "active": "<?= $block->escapeHtmlAttr($block->getActiveTabId()) ?>", + "destination": "#<?= $block->escapeHtmlAttr($block->getDestElementId()) ?>", + "shadowTabs": "<?= /* @noEscape */ $block->getAllShadowTabs() ?>", + "tabsBlockPrefix": "<?= $block->escapeHtmlAttr($block->getId()) ?>_", "tabIdArgument": "active_tab", - "tabPanelClass": "<?= /* @escapeNotVerified */ $block->getPanelsClass() ?>", - "excludedPanel": "<?= /* @escapeNotVerified */ $block->getExcludedPanel() ?>", + "tabPanelClass": "<?= $block->escapeHtmlAttr($block->getPanelsClass()) ?>", + "excludedPanel": "<?= $block->escapeHtmlAttr($block->getExcludedPanel()) ?>", "groups": "ul.tabs" }}'> - <?php foreach ($tabGroups as $tabGroupCode): ?> + <?php foreach ($tabGroups as $tabGroupCode) :?> <?php $tabGroupId = $block->getId() . '-' . $tabGroupCode; $isBasic = $tabGroupCode == \Magento\Catalog\Block\Adminhtml\Product\Edit\Tabs::BASIC_TAB_GROUP_CODE; $activeCollapsible = $block->isAdvancedTabGroupActive() ? true : false; ?> - <div class="admin__page-nav <?php if (!$isBasic): ?> <?= '_collapsed' ?> <?php endif;?>" + <div class="admin__page-nav <?php if (!$isBasic) :?> <?= '_collapsed' ?> <?php endif;?>" data-role="container" - id="<?= /* @escapeNotVerified */ $tabGroupId ?>" - <?php if (!$isBasic): ?> + id="<?= $block->escapeHtmlAttr($tabGroupId) ?>" + <?php if (!$isBasic) :?> data-mage-init='{"collapsible":{ - "active": "<?= /* @escapeNotVerified */ $activeCollapsible ?>", + "active": "<?= /* @noEscape */ $activeCollapsible ?>", "openedState": "_show", "closedState": "_hide", "animate": 200, @@ -45,44 +43,45 @@ }}' <?php endif;?>> - <div class="admin__page-nav-title-wrap" <?= /* @escapeNotVerified */ $block->getUiId('title') ?> data-role="title"> - <div class="admin__page-nav-title <?php if (!$isBasic): ?> <?= '_collapsible' ?><?php endif;?>" + <div class="admin__page-nav-title-wrap" <?= /* @noEscape */ $block->getUiId('title') ?> data-role="title"> + <div class="admin__page-nav-title <?php if (!$isBasic) :?> <?= '_collapsible' ?><?php endif;?>" data-role="trigger"> <strong> - <?= /* @escapeNotVerified */ $isBasic ? __('Basic Settings') : __('Advanced Settings') ?> + <?= $block->escapeHtml($isBasic ? __('Basic Settings') : __('Advanced Settings')) ?> </strong> <span data-role="title-messages" class="admin__page-nav-title-messages"></span> </div> </div> - <ul <?= /* @escapeNotVerified */ $block->getUiId('tab', $tabGroupId) ?> class="tabs admin__page-nav-items" data-role="content"> - <?php foreach ($tabs as $_tab): ?> + <ul <?= /* @noEscape */ $block->getUiId('tab', $tabGroupId) ?> class="tabs admin__page-nav-items" data-role="content"> + <?php foreach ($tabs as $_tab) :?> <?php /** @var $_tab \Magento\Backend\Block\Widget\Tab\TabInterface */ ?> <?php if (!$block->canShowTab($_tab) || $_tab->getParentTab() || ($_tab->getGroupCode() && $_tab->getGroupCode() != $tabGroupCode) - || (!$_tab->getGroupCode() && $isBasic)): continue; endif;?> + || (!$_tab->getGroupCode() && $isBasic)) : continue; + endif;?> <?php $_tabClass = 'tab-item-link ' . $block->getTabClass($_tab) . ' ' . (preg_match('/\s?ajax\s?/', $_tab->getClass()) ? 'notloaded' : '') ?> <?php $_tabType = (!preg_match('/\s?ajax\s?/', $_tabClass) && $block->getTabUrl($_tab) != '#') ? 'link' : '' ?> <?php $_tabHref = $block->getTabUrl($_tab) == '#' ? '#' . $block->getTabId($_tab) . '_content' : $block->getTabUrl($_tab) ?> - <li class="admin__page-nav-item <?php if ($block->getTabIsHidden($_tab)): ?> <?= "no-display" ?> <?php endif; ?> " <?= /* @escapeNotVerified */ $block->getUiId('tab', 'item', $_tab->getId()) ?>> - <a href="<?= /* @escapeNotVerified */ $_tabHref ?>" id="<?= /* @escapeNotVerified */ $block->getTabId($_tab) ?>" - name="<?= /* @escapeNotVerified */ $block->getTabId($_tab, false) ?>" - title="<?= /* @escapeNotVerified */ $block->getTabTitle($_tab) ?>" - class="admin__page-nav-link <?= /* @escapeNotVerified */ $_tabClass ?>" - data-tab-type="<?= /* @escapeNotVerified */ $_tabType ?>" <?= /* @escapeNotVerified */ $block->getUiId('tab', 'link', $_tab->getId()) ?> + <li class="admin__page-nav-item <?php if ($block->getTabIsHidden($_tab)) :?> <?= "no-display" ?> <?php endif; ?> " <?= /* @noEscape */ $block->getUiId('tab', 'item', $_tab->getId()) ?>> + <a href="<?= $block->escapeUrl($_tabHref) ?>" id="<?= $block->escapeHtmlAttr($block->getTabId($_tab)) ?>" + name="<?= $block->escapeHtmlAttr($block->getTabId($_tab, false)) ?>" + title="<?= $block->escapeHtmlAttr($block->getTabTitle($_tab)) ?>" + class="admin__page-nav-link <?= $block->escapeHtmlAttr($_tabClass) ?>" + data-tab-type="<?= /* @noEscape */ $_tabType ?>" <?= /* @noEscape */ $block->getUiId('tab', 'link', $_tab->getId()) ?> > <span><?= $block->escapeHtml($block->getTabLabel($_tab)) ?></span> <span class="admin__page-nav-item-messages" data-role="item-messages"> <span class="admin__page-nav-item-message _changed"> <span class="admin__page-nav-item-message-icon"></span> <span class="admin__page-nav-item-message-tooltip"> - <?= /* @escapeNotVerified */ __('Changes have been made to this section that have not been saved.') ?> + <?= $block->escapeHtml(__('Changes have been made to this section that have not been saved.')) ?> </span> </span> <span class="admin__page-nav-item-message _error"> <span class="admin__page-nav-item-message-icon"></span> <span class="admin__page-nav-item-message-tooltip"> - <?= /* @escapeNotVerified */ __('This tab contains invalid data. Please resolve this before saving.') ?> + <?= $block->escapeHtml(__('This tab contains invalid data. Please resolve this before saving.')) ?> </span> </span> <span class="admin__page-nav-item-message-loader"> @@ -93,11 +92,11 @@ </span> </span> </a> - <div id="<?= /* @escapeNotVerified */ $block->getTabId($_tab) ?>_content" class="no-display" - data-tab-panel="<?= /* @escapeNotVerified */ $_tab->getTabId() ?>" - <?= /* @escapeNotVerified */ $block->getUiId('tab', 'content', $_tab->getId()) ?> + <div id="<?= $block->escapeHtmlAttr($block->getTabId($_tab)) ?>_content" class="no-display" + data-tab-panel="<?= $block->escapeHtmlAttr($_tab->getTabId()) ?>" + <?= /* @noEscape */ $block->getUiId('tab', 'content', $_tab->getId()) ?> > - <?= /* @escapeNotVerified */ $block->getTabContent($_tab) ?> + <?= /* @noEscape */ $block->getTabContent($_tab) ?> <?= /* @noEscape */ $block->getAccordion($_tab) ?> </div> </li> diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/product/edit/tabs/child_tab.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/product/edit/tabs/child_tab.phtml index 842ed17375f77..c4dc1ddc0b02b 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/product/edit/tabs/child_tab.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/product/edit/tabs/child_tab.phtml @@ -4,13 +4,11 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - /** @var $block \Magento\Catalog\Block\Adminhtml\Product\Edit\Tab\ChildTab */ ?> <div class="fieldset-wrapper admin__collapsible-block-wrapper" data-tab="<?= /* @noEscape */ $block->getTabId() ?>" id="<?= /* @noEscape */ $block->getTabId() ?>-wrapper" data-mage-init='{"collapsible":{ - "active": <?= /* @noEscape */ $block->isTabOpened() ? 'true' : 'false' ?>, + "active": <?= $block->isTabOpened() ? 'true' : 'false' ?>, "openedState": "_show", "closedState": "_hide", "animate": 200, diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/product/grid/massaction_extended.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/product/grid/massaction_extended.phtml index 8df3e32b0a2c3..c814298d1dbc5 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/product/grid/massaction_extended.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/product/grid/massaction_extended.phtml @@ -4,11 +4,10 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - +/** @var $block \Magento\Catalog\Block\Adminhtml\Product\Grid */ ?> <div id="<?= $block->getHtmlId() ?>" class="admin__grid-massaction"> - <?php if ($block->getHideFormElement() !== true):?> + <?php if ($block->getHideFormElement() !== true) :?> <form action="" id="<?= $block->getHtmlId() ?>-form" method="post"> <?php endif ?> <div class="admin__grid-massaction-form"> @@ -16,43 +15,43 @@ <select id="<?= $block->getHtmlId() ?>-select" class="local-validation admin__control-select"> - <option class="admin__control-select-placeholder" value="" selected><?= /* @escapeNotVerified */ __('Actions') ?></option> - <?php foreach ($block->getItems() as $_item): ?> - <option value="<?= /* @escapeNotVerified */ $_item->getId() ?>"<?= ($_item->getSelected() ? ' selected="selected"' : '') ?>><?= /* @escapeNotVerified */ $_item->getLabel() ?></option> + <option class="admin__control-select-placeholder" value="" selected><?= $block->escapeHtml(__('Actions')) ?></option> + <?php foreach ($block->getItems() as $_item) :?> + <option value="<?= $block->escapeHtmlAttr($_item->getId()) ?>"<?= ($_item->getSelected() ? ' selected="selected"' : '') ?>><?= $block->escapeHtml($_item->getLabel()) ?></option> <?php endforeach; ?> </select> <span class="outer-span" id="<?= $block->getHtmlId() ?>-form-hiddens"></span> <span class="outer-span" id="<?= $block->getHtmlId() ?>-form-additional"></span> <?= $block->getApplyButtonHtml() ?> </div> - <?php if ($block->getHideFormElement() !== true):?> + <?php if ($block->getHideFormElement() !== true) :?> </form> <?php endif ?> <div class="no-display"> - <?php foreach ($block->getItems() as $_item): ?> - <div id="<?= $block->getHtmlId() ?>-item-<?= /* @escapeNotVerified */ $_item->getId() ?>-block"> + <?php foreach ($block->getItems() as $_item) :?> + <div id="<?= $block->getHtmlId() ?>-item-<?= $block->escapeHtmlAttr($_item->getId()) ?>-block"> <?= $_item->getAdditionalActionBlockHtml() ?> </div> <?php endforeach; ?> </div> <div class="mass-select-wrap"> - <select id="<?= $block->getHtmlId() ?>-mass-select" data-menu="grid-mass-select"> - <optgroup label="<?= /* @escapeNotVerified */ __('Mass Actions') ?>"> + <select id="<?= $block->escapeHtmlAttr($block->getHtmlId()) ?>-mass-select" data-menu="grid-mass-select"> + <optgroup label="<?= $block->escapeHtmlAttr(__('Mass Actions')) ?>"> <option disabled selected></option> - <?php if ($block->getUseSelectAll()):?> + <?php if ($block->getUseSelectAll()) :?> <option value="selectAll"> - <?= /* @escapeNotVerified */ __('Select All') ?> + <?= $block->escapeHtml(__('Select All')) ?> </option> <option value="unselectAll"> - <?= /* @escapeNotVerified */ __('Unselect All') ?> + <?= $block->escapeHtml(__('Unselect All')) ?> </option> <?php endif; ?> <option value="selectVisible"> - <?= /* @escapeNotVerified */ __('Select Visible') ?> + <?= $block->escapeHtml(__('Select Visible')) ?> </option> <option value="unselectVisible"> - <?= /* @escapeNotVerified */ __('Unselect Visible') ?> + <?= $block->escapeHtml(__('Unselect Visible')) ?> </option> </optgroup> </select> @@ -65,19 +64,19 @@ $('#<?= $block->getHtmlId() ?>-mass-select').change(function () { var massAction = $('option:selected', this).val(); switch (massAction) { - <?php if ($block->getUseSelectAll()):?> + <?php if ($block->getUseSelectAll()) :?> case 'selectAll': - return <?= /* @escapeNotVerified */ $block->getJsObjectName() ?>.selectAll(); + return <?= $block->escapeJs($block->getJsObjectName()) ?>.selectAll(); break case 'unselectAll': - return <?= /* @escapeNotVerified */ $block->getJsObjectName() ?>.unselectAll(); + return <?= $block->escapeJs($block->getJsObjectName()) ?>.unselectAll(); break <?php endif; ?> case 'selectVisible': - return <?= /* @escapeNotVerified */ $block->getJsObjectName() ?>.selectVisible(); + return <?= $block->escapeJs($block->getJsObjectName()) ?>.selectVisible(); break case 'unselectVisible': - return <?= /* @escapeNotVerified */ $block->getJsObjectName() ?>.unselectVisible(); + return <?= $block->escapeJs($block->getJsObjectName()) ?>.unselectVisible(); break } this.blur(); @@ -85,8 +84,8 @@ }); - <?php if (!$block->getParentBlock()->canDisplayContainer()): ?> - <?= /* @escapeNotVerified */ $block->getJsObjectName() ?>.setGridIds('<?= /* @escapeNotVerified */ $block->getGridIdsJson() ?>'); + <?php if (!$block->getParentBlock()->canDisplayContainer()) :?> + <?= $block->escapeJs($block->getJsObjectName()) ?>.setGridIds('<?= /* @noEscape */ $block->getGridIdsJson() ?>'); <?php endif; ?> </script> </div> diff --git a/app/code/Magento/Catalog/view/adminhtml/templates/rss/grid/link.phtml b/app/code/Magento/Catalog/view/adminhtml/templates/rss/grid/link.phtml index fb450d19312fa..668dc4a28a6d9 100644 --- a/app/code/Magento/Catalog/view/adminhtml/templates/rss/grid/link.phtml +++ b/app/code/Magento/Catalog/view/adminhtml/templates/rss/grid/link.phtml @@ -4,10 +4,8 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - /** @var $block \Magento\Catalog\Block\Adminhtml\Rss\Grid\Link */ ?> -<?php if ($block->isRssAllowed() && $block->getLink()): ?> -<a href="<?= /* @escapeNotVerified */ $block->getLink() ?>" class="link-feed"><?= /* @escapeNotVerified */ $block->getLabel() ?></a> +<?php if ($block->isRssAllowed() && $block->getLink()) :?> +<a href="<?= $block->escapeUrl($block->getLink()) ?>" class="link-feed"><?= $block->escapeHtml($block->getLabel()) ?></a> <?php endif; ?> diff --git a/app/code/Magento/Catalog/view/adminhtml/ui_component/product_listing.xml b/app/code/Magento/Catalog/view/adminhtml/ui_component/product_listing.xml index d689daef4bcab..d2d6f098125ce 100644 --- a/app/code/Magento/Catalog/view/adminhtml/ui_component/product_listing.xml +++ b/app/code/Magento/Catalog/view/adminhtml/ui_component/product_listing.xml @@ -192,13 +192,6 @@ <label translate="true">Websites</label> </settings> </column> - <column name="cost" class="Magento\Catalog\Ui\Component\Listing\Columns\Price" sortOrder="120"> - <settings> - <addField>true</addField> - <filter>textRange</filter> - <label translate="true">Cost</label> - </settings> - </column> <actionsColumn name="actions" class="Magento\Catalog\Ui\Component\Listing\Columns\ProductActions" sortOrder="200"> <settings> <indexField>entity_id</indexField> diff --git a/app/code/Magento/Catalog/view/base/templates/js/components.phtml b/app/code/Magento/Catalog/view/base/templates/js/components.phtml index bad5acc209b5f..5902a9f25cc4b 100644 --- a/app/code/Magento/Catalog/view/base/templates/js/components.phtml +++ b/app/code/Magento/Catalog/view/base/templates/js/components.phtml @@ -3,8 +3,5 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <?= $block->getChildHtml() ?> diff --git a/app/code/Magento/Catalog/view/base/templates/product/composite/fieldset/options/view/checkable.phtml b/app/code/Magento/Catalog/view/base/templates/product/composite/fieldset/options/view/checkable.phtml index 0f3b4f481a288..dbc064665d3fe 100644 --- a/app/code/Magento/Catalog/view/base/templates/product/composite/fieldset/options/view/checkable.phtml +++ b/app/code/Magento/Catalog/view/base/templates/product/composite/fieldset/options/view/checkable.phtml @@ -4,11 +4,10 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile use Magento\Catalog\Model\Product\Option; /** - * @var \Magento\Catalog\Block\Product\View\Options\View\Checkable $block + * @var $block \Magento\Catalog\Block\Product\View\Options\Type\Select\Checkable */ $option = $block->getOption(); if ($option) : ?> @@ -19,27 +18,21 @@ if ($option) : ?> $count = 1; ?> -<div class="options-list nested" id="options-<?php echo /* @noEscape */ -$option->getId() ?>-list"> - <?php if ($optionType === Option::OPTION_TYPE_RADIO && !$option->getIsRequire()): ?> +<div class="options-list nested" id="options-<?= $block->escapeHtmlAttr($option->getId()) ?>-list"> + <?php if ($optionType === Option::OPTION_TYPE_RADIO && !$option->getIsRequire()) :?> <div class="field choice admin__field admin__field-option"> <input type="radio" - id="options_<?php echo /* @noEscape */ - $option->getId() ?>" + id="options_<?= $block->escapeHtmlAttr($option->getId()) ?>" class="radio admin__control-radio product-custom-option" - name="options[<?php echo /* @noEscape */ - $option->getId() ?>]" - data-selector="options[<?php echo /* @noEscape */ - $option->getId() ?>]" - onclick="<?php echo $block->getSkipJsReloadPrice() ? '' : 'opConfig.reloadPrice()' ?>" + name="options[<?= $block->escapeHtmlAttr($option->getId()) ?>]" + data-selector="options[<?= $block->escapeHtmlAttr($option->getId()) ?>]" + onclick="<?= $block->getSkipJsReloadPrice() ? '' : 'opConfig.reloadPrice()' ?>" value="" checked="checked" /> - <label class="label admin__field-label" for="options_<?php echo /* @noEscape */ - $option->getId() ?>"> + <label class="label admin__field-label" for="options_<?= $block->escapeHtmlAttr($option->getId()) ?>"> <span> - <?php echo /* @noEscape */ - __('None') ?> + <?= $block->escapeHtml(__('None')) ?> </span> </label> </div> @@ -60,41 +53,29 @@ $option->getId() ?>-list"> } ?> - <div class="field choice admin__field admin__field-option <?php echo /* @noEscape */ - $option->getIsRequire() ? 'required': '' ?>"> - <input type="<?php echo /* @noEscape */ - $optionType ?>" - class="<?php /** @noinspection DisconnectedForeachInstructionInspection */ - echo /* @noEscape */ - $optionType === Option::OPTION_TYPE_RADIO ? - 'radio admin__control-radio' : - 'checkbox admin__control-checkbox' ?> <?php echo /* @noEscape */ - $option->getIsRequire() ? 'required': '' ?> + <div class="field choice admin__field admin__field-option <?= /* @noEscape */ $option->getIsRequire() ? 'required': '' ?>"> + <input type="<?= $block->escapeHtmlAttr($optionType) ?>" + class="<?= $optionType === Option::OPTION_TYPE_RADIO + ? 'radio admin__control-radio' + : 'checkbox admin__control-checkbox' ?> <?= $option->getIsRequire() + ? 'required': '' ?> product-custom-option - <?php echo $block->getSkipJsReloadPrice() ? '' : 'opConfig.reloadPrice()' ?>" - name="options[<?php echo $option->getId() ?>]<?php echo /* @noEscape */ - $arraySign ?>" - id="options_<?php echo /* @noEscape */ - $option->getId() . '_' . $count ?>" - value="<?php echo /* @noEscape */ - $value->getOptionTypeId() ?>" - <?php echo /* @noEscape */ - $checked ?> - data-selector="<?php echo /* @noEscape */ - $dataSelector ?>" - price="<?php echo /* @noEscape */ - $block->getCurrencyByStore($value) ?>" + <?= $block->getSkipJsReloadPrice() ? '' : 'opConfig.reloadPrice()' ?>" + name="options[<?= $block->escapeHtmlAttr($option->getId()) ?>]<?= /* @noEscape */ $arraySign ?>" + id="options_<?= $block->escapeHtmlAttr($option->getId() . '_' . $count) ?>" + value="<?= $block->escapeHtmlAttr($value->getOptionTypeId()) ?>" + <?= $block->escapeHtml($checked) ?> + data-selector="<?= $block->escapeHtmlAttr($dataSelector) ?>" + price="<?= $block->escapeHtmlAttr($block->getCurrencyByStore($value)) ?>" /> <label class="label admin__field-label" - for="options_<?php echo /* @noEscape */ - $option->getId() . '_' . $count ?>"> + for="options_<?= $block->escapeHtmlAttr($option->getId() . '_' . $count) ?>"> <span> - <?php echo $block->escapeHtml($value->getTitle()) ?> + <?= $block->escapeHtml($value->getTitle()) ?> </span> - <?php echo /* @noEscape */ - $block->formatPrice($value) ?> + <?= /* @noEscape */ $block->formatPrice($value) ?> </label> </div> <?php endforeach; ?> </div> -<?php endif; ?> \ No newline at end of file +<?php endif; ?> diff --git a/app/code/Magento/Catalog/view/base/templates/product/price/amount/default.phtml b/app/code/Magento/Catalog/view/base/templates/product/price/amount/default.phtml index ce1561e382eed..b2c2acb7419bd 100644 --- a/app/code/Magento/Catalog/view/base/templates/product/price/amount/default.phtml +++ b/app/code/Magento/Catalog/view/base/templates/product/price/amount/default.phtml @@ -3,29 +3,26 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> -<?php /** @var \Magento\Framework\Pricing\Render\Amount $block */ ?> +<?php /** @var $block \Magento\Framework\Pricing\Render\Amount */ ?> -<span class="price-container <?= /* @escapeNotVerified */ $block->getAdjustmentCssClasses() ?>" +<span class="price-container <?= $block->escapeHtmlAttr($block->getAdjustmentCssClasses()) ?>" <?= $block->getSchema() ? ' itemprop="offers" itemscope itemtype="http://schema.org/Offer"' : '' ?>> - <?php if ($block->getDisplayLabel()): ?> - <span class="price-label"><?= /* @escapeNotVerified */ $block->getDisplayLabel() ?></span> + <?php if ($block->getDisplayLabel()) :?> + <span class="price-label"><?= $block->escapeHtml($block->getDisplayLabel()) ?></span> <?php endif; ?> - <span <?php if ($block->getPriceId()): ?> id="<?= /* @escapeNotVerified */ $block->getPriceId() ?>"<?php endif;?> - <?= ($block->getPriceDisplayLabel()) ? 'data-label="' . $block->getPriceDisplayLabel() . $block->getPriceDisplayInclExclTaxes() . '"' : '' ?> - data-price-amount="<?= /* @escapeNotVerified */ $block->getDisplayValue() ?>" - data-price-type="<?= /* @escapeNotVerified */ $block->getPriceType() ?>" - class="price-wrapper <?= /* @escapeNotVerified */ $block->getPriceWrapperCss() ?>" - ><?= /* @escapeNotVerified */ $block->formatCurrency($block->getDisplayValue(), (bool)$block->getIncludeContainer()) ?></span> - <?php if ($block->hasAdjustmentsHtml()): ?> + <span <?php if ($block->getPriceId()) :?> id="<?= $block->escapeHtmlAttr($block->getPriceId()) ?>"<?php endif;?> + <?= ($block->getPriceDisplayLabel()) ? 'data-label="' . $block->escapeHtmlAttr($block->getPriceDisplayLabel() . $block->getPriceDisplayInclExclTaxes()) . '"' : '' ?> + data-price-amount="<?= $block->escapeHtmlAttr($block->getDisplayValue()) ?>" + data-price-type="<?= $block->escapeHtmlAttr($block->getPriceType()) ?>" + class="price-wrapper <?= $block->escapeHtmlAttr($block->getPriceWrapperCss()) ?>" + ><?= $block->escapeHtml($block->formatCurrency($block->getDisplayValue(), (bool)$block->getIncludeContainer()), ['span']) ?></span> + <?php if ($block->hasAdjustmentsHtml()) :?> <?= $block->getAdjustmentsHtml() ?> <?php endif; ?> - <?php if ($block->getSchema()): ?> - <meta itemprop="price" content="<?= /* @escapeNotVerified */ $block->getDisplayValue() ?>" /> - <meta itemprop="priceCurrency" content="<?= /* @escapeNotVerified */ $block->getDisplayCurrencyCode() ?>" /> + <?php if ($block->getSchema()) :?> + <meta itemprop="price" content="<?= $block->escapeHtmlAttr($block->getDisplayValue()) ?>" /> + <meta itemprop="priceCurrency" content="<?= $block->escapeHtmlAttr($block->getDisplayCurrencyCode()) ?>" /> <?php endif; ?> </span> diff --git a/app/code/Magento/Catalog/view/base/templates/product/price/default.phtml b/app/code/Magento/Catalog/view/base/templates/product/price/default.phtml index b414f02a3d6fb..7005e65bcca80 100644 --- a/app/code/Magento/Catalog/view/base/templates/product/price/default.phtml +++ b/app/code/Magento/Catalog/view/base/templates/product/price/default.phtml @@ -3,9 +3,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <?php @@ -15,7 +12,7 @@ /** @var \Magento\Framework\Pricing\Price\PriceInterface $priceModel */ $priceModel = $block->getPriceType('regular_price'); -/* @escapeNotVerified */ echo $block->renderAmount($priceModel->getAmount(), [ +/* @noEscape */ echo $block->renderAmount($priceModel->getAmount(), [ 'price_id' => $block->getPriceId('product-price-'), 'include_container' => true ]); diff --git a/app/code/Magento/Catalog/view/base/templates/product/price/final_price.phtml b/app/code/Magento/Catalog/view/base/templates/product/price/final_price.phtml index 6e281bdef7afb..e56804a06de22 100644 --- a/app/code/Magento/Catalog/view/base/templates/product/price/final_price.phtml +++ b/app/code/Magento/Catalog/view/base/templates/product/price/final_price.phtml @@ -3,9 +3,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <?php @@ -21,9 +18,9 @@ $finalPriceModel = $block->getPriceType('final_price'); $idSuffix = $block->getIdSuffix() ? $block->getIdSuffix() : ''; $schema = ($block->getZone() == 'item_view') ? true : false; ?> -<?php if ($block->hasSpecialPrice()): ?> +<?php if ($block->hasSpecialPrice()) :?> <span class="special-price"> - <?php /* @escapeNotVerified */ echo $block->renderAmount($finalPriceModel->getAmount(), [ + <?= /* @noEscape */ $block->renderAmount($finalPriceModel->getAmount(), [ 'display_label' => __('Special Price'), 'price_id' => $block->getPriceId('product-price-' . $idSuffix), 'price_type' => 'finalPrice', @@ -32,7 +29,7 @@ $schema = ($block->getZone() == 'item_view') ? true : false; ]); ?> </span> <span class="old-price"> - <?php /* @escapeNotVerified */ echo $block->renderAmount($priceModel->getAmount(), [ + <?= /* @noEscape */ $block->renderAmount($priceModel->getAmount(), [ 'display_label' => __('Regular Price'), 'price_id' => $block->getPriceId('old-price-' . $idSuffix), 'price_type' => 'oldPrice', @@ -40,8 +37,8 @@ $schema = ($block->getZone() == 'item_view') ? true : false; 'skip_adjustments' => true ]); ?> </span> -<?php else: ?> - <?php /* @escapeNotVerified */ echo $block->renderAmount($finalPriceModel->getAmount(), [ +<?php else :?> + <?= /* @noEscape */ $block->renderAmount($finalPriceModel->getAmount(), [ 'price_id' => $block->getPriceId('product-price-' . $idSuffix), 'price_type' => 'finalPrice', 'include_container' => true, @@ -49,14 +46,14 @@ $schema = ($block->getZone() == 'item_view') ? true : false; ]); ?> <?php endif; ?> -<?php if ($block->showMinimalPrice()): ?> - <?php if ($block->getUseLinkForAsLowAs()):?> - <a href="<?= /* @escapeNotVerified */ $block->getSaleableItem()->getProductUrl() ?>" class="minimal-price-link"> - <?= /* @escapeNotVerified */ $block->renderAmountMinimal() ?> +<?php if ($block->showMinimalPrice()) :?> + <?php if ($block->getUseLinkForAsLowAs()) :?> + <a href="<?= $block->escapeUrl($block->getSaleableItem()->getProductUrl()) ?>" class="minimal-price-link"> + <?= /* @noEscape */ $block->renderAmountMinimal() ?> </a> - <?php else:?> + <?php else :?> <span class="minimal-price-link"> - <?= /* @escapeNotVerified */ $block->renderAmountMinimal() ?> + <?= /* @noEscape */ $block->renderAmountMinimal() ?> </span> <?php endif?> <?php endif; ?> diff --git a/app/code/Magento/Catalog/view/base/templates/product/price/tier_prices.phtml b/app/code/Magento/Catalog/view/base/templates/product/price/tier_prices.phtml index f5cffb99d75dd..5949b54268a62 100644 --- a/app/code/Magento/Catalog/view/base/templates/product/price/tier_prices.phtml +++ b/app/code/Magento/Catalog/view/base/templates/product/price/tier_prices.phtml @@ -3,12 +3,12 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <?php +// phpcs:disable Magento2.Templates.ThisInTemplate +// phpcs:disable Generic.WhiteSpace.ScopeIndent + /** @var \Magento\Catalog\Pricing\Render\PriceBox $block */ /** @var \Magento\Catalog\Pricing\Price\TierPrice $tierPriceModel */ @@ -18,17 +18,17 @@ $msrpShowOnGesture = $block->getPriceType('msrp_price')->isShowPriceOnGesture(); $product = $block->getSaleableItem(); ?> <?php if (count($tierPrices)) : ?> - <ul class="<?= /* @escapeNotVerified */ ($block->hasListClass() ? $block->getListClass() : 'prices-tier items') ?>"> - <?php foreach ($tierPrices as $index => $price) : ?> - <li class="item"> - <?php + <ul class="<?= $block->escapeHtmlAttr(($block->hasListClass() ? $block->getListClass() : 'prices-tier items')) ?>"> + <?php foreach ($tierPrices as $index => $price) : ?> + <li class="item"> + <?php $productId = $product->getId(); $isSaleable = $product->isSaleable(); $popupId = 'msrp-popup-' . $productId . $block->getRandomString(20); - if ($msrpShowOnGesture && $price['price']->getValue() < $product->getMsrp()): + if ($msrpShowOnGesture && $price['price']->getValue() < $product->getMsrp()) : $addToCartUrl = ''; if ($isSaleable) { - $addToCartUrl = $this->helper('\Magento\Checkout\Helper\Cart') + $addToCartUrl = $this->helper(\Magento\Checkout\Helper\Cart::class) ->getAddUrl($product, ['qty' => $price['price_qty']]); } $tierPriceData = [ @@ -54,13 +54,13 @@ $product = $block->getSaleableItem(); if ($block->getCanDisplayQty($product)) { $tierPriceData['qty'] = $price['price_qty']; } - ?> - <?= /* @escapeNotVerified */ __('Buy %1 for: ', $price['price_qty']) ?> - <a href="javascript:void(0);" - id="<?= /* @escapeNotVerified */ ($popupId) ?>" - data-tier-price="<?= $block->escapeHtml($block->jsonEncode($tierPriceData)) ?>"> - <?= /* @escapeNotVerified */ __('Click for price') ?></a> - <?php else: + ?> + <?= $block->escapeHtml(__('Buy %1 for: ', $price['price_qty'])) ?> + <a href="javascript:void(0);" + id="<?= $block->escapeHtmlAttr($popupId) ?>" + data-tier-price="<?= $block->escapeHtml($block->jsonEncode($tierPriceData)) ?>"> + <?= $block->escapeHtml(__('Click for price')) ?></a> + <?php else : $priceAmountBlock = $block->renderAmount( $price['price'], [ @@ -70,22 +70,22 @@ $product = $block->getSaleableItem(); 'zone' => \Magento\Framework\Pricing\Render::ZONE_ITEM_OPTION ] ); - ?> - <?php /* @escapeNotVerified */ echo ($block->getShowDetailedPrice() !== false) - ? __( - 'Buy %1 for %2 each and <strong class="benefit">save<span class="percent tier-%3"> %4</span>%</strong>', - $price['price_qty'], - $priceAmountBlock, - $index, - $tierPriceModel->getSavePercent($price['price']) - ) - : __('Buy %1 for %2 each', $price['price_qty'], $priceAmountBlock); - ?> - <?php endif; ?> - </li> - <?php endforeach; ?> + ?> + <?= /* @noEscape */ ($block->getShowDetailedPrice() !== false) + ? __( + 'Buy %1 for %2 each and <strong class="benefit">save<span class="percent tier-%3"> %4</span>%</strong>', + $price['price_qty'], + $priceAmountBlock, + $index, + $block->formatPercent($price['percentage_value'] ?? $tierPriceModel->getSavePercent($price['price'])) + ) + : __('Buy %1 for %2 each', $price['price_qty'], $priceAmountBlock); + ?> + <?php endif; ?> + </li> + <?php endforeach; ?> </ul> - <?php if ($msrpShowOnGesture):?> + <?php if ($msrpShowOnGesture) :?> <script type="text/x-magento-init"> { ".product-info-main": { @@ -95,9 +95,9 @@ $product = $block->getSaleableItem(); "inputQty": "#qty", "attr": "[data-tier-price]", "productForm": "#product_addtocart_form", - "productId": "<?= /* @escapeNotVerified */ $productId ?>", + "productId": "<?= (int) $productId ?>", "productIdInput": "input[type=hidden][name=product]", - "isSaleable": "<?= /* @escapeNotVerified */ $isSaleable ?>" + "isSaleable": "<?= (bool) $isSaleable ?>" } } } diff --git a/app/code/Magento/Catalog/view/frontend/templates/category/cms.phtml b/app/code/Magento/Catalog/view/frontend/templates/category/cms.phtml index 3619ce94031c2..b50095e91d999 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/category/cms.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/category/cms.phtml @@ -3,9 +3,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <?php /** @@ -14,7 +11,7 @@ * @var $block \Magento\Catalog\Block\Category\View */ ?> -<?php if ($block->isContentMode() || $block->isMixedMode()): ?> +<?php if ($block->isContentMode() || $block->isMixedMode()) :?> <div class="category-cms"> <?= $block->getCmsBlockHtml() ?> </div> diff --git a/app/code/Magento/Catalog/view/frontend/templates/category/description.phtml b/app/code/Magento/Catalog/view/frontend/templates/category/description.phtml index 0efce1014f9c2..2f5b852575c78 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/category/description.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/category/description.phtml @@ -3,19 +3,22 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <?php +// phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis + /** * Category view template * * @var $block \Magento\Catalog\Block\Category\View */ ?> -<?php if ($_description = $block->getCurrentCategory()->getDescription()): ?> +<?php if ($_description = $block->getCurrentCategory()->getDescription()) :?> <div class="category-description"> - <?= /* @escapeNotVerified */ $this->helper('Magento\Catalog\Helper\Output')->categoryAttribute($block->getCurrentCategory(), $_description, 'description') ?> + <?= /* @noEscape */ $this->helper(Magento\Catalog\Helper\Output::class)->categoryAttribute( + $block->getCurrentCategory(), + $_description, + 'description' + ) ?> </div> <?php endif; ?> diff --git a/app/code/Magento/Catalog/view/frontend/templates/category/image.phtml b/app/code/Magento/Catalog/view/frontend/templates/category/image.phtml index edff2147ad14b..02593d3b541a1 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/category/image.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/category/image.phtml @@ -3,9 +3,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <?php /** @@ -13,14 +10,24 @@ * * @var $block \Magento\Catalog\Block\Category\View */ + +// phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis +// phpcs:disable Generic.WhiteSpace.ScopeIndent.IncorrectExact +// phpcs:disable Magento2.Security.LanguageConstruct.DirectOutput ?> <?php - $_helper = $this->helper('Magento\Catalog\Helper\Output'); + $_helper = $this->helper(Magento\Catalog\Helper\Output::class); $_category = $block->getCurrentCategory(); $_imgHtml = ''; if ($_imgUrl = $_category->getImageUrl()) { - $_imgHtml = '<div class="category-image"><img src="' . $_imgUrl . '" alt="' . $block->escapeHtml($_category->getName()) . '" title="' . $block->escapeHtml($_category->getName()) . '" class="image" /></div>'; + $_imgHtml = '<div class="category-image"><img src="' + . $block->escapeUrl($_imgUrl) + . '" alt="' + . $block->escapeHtmlAttr($_category->getName()) + . '" title="' + . $block->escapeHtmlAttr($_category->getName()) + . '" class="image" /></div>'; $_imgHtml = $_helper->categoryAttribute($_category, $_imgHtml, 'image'); - /* @escapeNotVerified */ echo $_imgHtml; + /* @noEscape */ echo $_imgHtml; } ?> diff --git a/app/code/Magento/Catalog/view/frontend/templates/category/products.phtml b/app/code/Magento/Catalog/view/frontend/templates/category/products.phtml index c521cf03ad156..80a9ae0a03e66 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/category/products.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/category/products.phtml @@ -3,9 +3,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <?php /** @@ -14,6 +11,6 @@ * @var $block \Magento\Catalog\Block\Category\View */ ?> -<?php if (!$block->isContentMode() || $block->isMixedMode()): ?> +<?php if (!$block->isContentMode() || $block->isMixedMode()) :?> <?= $block->getProductListHtml() ?> <?php endif; ?> diff --git a/app/code/Magento/Catalog/view/frontend/templates/category/rss.phtml b/app/code/Magento/Catalog/view/frontend/templates/category/rss.phtml index 774aa8d839e87..65ee7ea789e46 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/category/rss.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/category/rss.phtml @@ -4,9 +4,9 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - +/** @var $block \Magento\Catalog\Block\Category\Rss\Link */ ?> -<?php if ($block->isRssAllowed() && $block->getLink() && $block->isTopCategory()): ?> - <a href="<?= /* @escapeNotVerified */ $block->getLink() ?>" class="action link rss"><span><?= /* @escapeNotVerified */ $block->getLabel() ?></span></a> +<?php if ($block->isRssAllowed() && $block->getLink() && $block->isTopCategory()) :?> + <a href="<?= $block->escapeUrl($block->getLink()) ?>" + class="action link rss"><span><?= $block->escapeHtml($block->getLabel()) ?></span></a> <?php endif; ?> diff --git a/app/code/Magento/Catalog/view/frontend/templates/category/widget/link/link_block.phtml b/app/code/Magento/Catalog/view/frontend/templates/category/widget/link/link_block.phtml index 2b0098be6545b..15fdd30c2d93f 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/category/widget/link/link_block.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/category/widget/link/link_block.phtml @@ -3,9 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile ?> <div class="widget block block-category-link"> - <a <?= /* @escapeNotVerified */ $block->getLinkAttributes() ?>><span><?= $block->escapeHtml($block->getLabel()) ?></span></a> + <a <?= /* @noEscape */ $block->getLinkAttributes() ?>><span><?= $block->escapeHtml($block->getLabel()) ?></span></a> </div> diff --git a/app/code/Magento/Catalog/view/frontend/templates/category/widget/link/link_href.phtml b/app/code/Magento/Catalog/view/frontend/templates/category/widget/link/link_href.phtml index 91ab70b03769f..18ffee4b5f701 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/category/widget/link/link_href.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/category/widget/link/link_href.phtml @@ -4,7 +4,6 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile /** @var Magento\Catalog\Block\Widget\Link $block */ ?> <?= $block->escapeHtml($block->getHref()) ?> diff --git a/app/code/Magento/Catalog/view/frontend/templates/category/widget/link/link_inline.phtml b/app/code/Magento/Catalog/view/frontend/templates/category/widget/link/link_inline.phtml index f53c1c1ed90d7..8f3b2ae613731 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/category/widget/link/link_inline.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/category/widget/link/link_inline.phtml @@ -3,9 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile ?> <span class="widget block block-category-link-inline"> - <a <?= /* @escapeNotVerified */ $block->getLinkAttributes() ?>><span><?= $block->escapeHtml($block->getLabel()) ?></span></a> + <a <?= /* @noEscape */ $block->getLinkAttributes() ?>><span><?= $block->escapeHtml($block->getLabel()) ?></span></a> </span> diff --git a/app/code/Magento/Catalog/view/frontend/templates/frontend_storage_manager.phtml b/app/code/Magento/Catalog/view/frontend/templates/frontend_storage_manager.phtml index 4c103b40ba28c..52bec7858a919 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/frontend_storage_manager.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/frontend_storage_manager.phtml @@ -3,7 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile + +/** @var $block Magento\Catalog\Block\FrontendStorageManager */ ?> <script type="text/x-magento-init"> { @@ -12,9 +13,8 @@ "components": { "storage-manager": { "component": "Magento_Catalog/js/storage-manager", - "appendTo": "<?= /* @escapeNotVerified */ $block->getParentComponentName() ?>", - "storagesConfiguration" : - <?= /* @escapeNotVerified */ $block->getConfigurationJson() ?> + "appendTo": "<?= $block->escapeJs($block->getParentComponentName()) ?>", + "storagesConfiguration" : <?= /* @noEscape */ $block->getConfigurationJson() ?> } } } diff --git a/app/code/Magento/Catalog/view/frontend/templates/messages/addCompareSuccessMessage.phtml b/app/code/Magento/Catalog/view/frontend/templates/messages/addCompareSuccessMessage.phtml index 5f44c42e17c57..f5dca566abfed 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/messages/addCompareSuccessMessage.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/messages/addCompareSuccessMessage.phtml @@ -3,12 +3,14 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile + /** @var \Magento\Framework\View\Element\Template $block */ ?> -<?= $block->escapeHtml(__( - 'You added product %1 to the <a href="%2">comparison list</a>.', - $block->getData('product_name'), - $block->getData('compare_list_url')), +<?= $block->escapeHtml( + __( + 'You added product %1 to the <a href="%2">comparison list</a>.', + $block->getData('product_name'), + $block->getData('compare_list_url') + ), ['a'] ); diff --git a/app/code/Magento/Catalog/view/frontend/templates/navigation/left.phtml b/app/code/Magento/Catalog/view/frontend/templates/navigation/left.phtml index 01820361744e0..6d5ddb95ab178 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/navigation/left.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/navigation/left.phtml @@ -4,8 +4,6 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - /** * Category left navigation * @@ -15,25 +13,29 @@ <?php if (!$block->getCategory()) { return; } ?> -<?php $_categories = $block->getCurrentChildCategories(); ?> +<?php $_categories = $block->getCurrentChildCategories() ;?> <?php $_count = is_array($_categories) ? count($_categories) : $_categories->count(); ?> -<?php if ($_count): ?> +<?php if ($_count) :?> <div class="block filter"> <div class="title"> - <strong><?= /* @escapeNotVerified */ __('Shop By') ?></strong> + <strong><?= $block->escapeHtml(__('Shop By')) ?></strong> </div> <div class="content"> - <strong class="subtitle"><?= /* @escapeNotVerified */ __('Shopping Options') ?></strong> + <strong class="subtitle"><?= $block->escapeHtml(__('Shopping Options')) ?></strong> <dl class="options" id="narrow-by-list2"> - <dt><?= /* @escapeNotVerified */ __('Category') ?></dt> + <dt><?= $block->escapeHtml(__('Category')) ?></dt> <dd> <ol class="items"> <?php /** @var \Magento\Catalog\Model\Category $_category */ ?> - <?php foreach ($_categories as $_category): ?> - <?php if ($_category->getIsActive()): ?> + <?php foreach ($_categories as $_category) :?> + <?php if ($_category->getIsActive()) :?> <li class="item"> - <a href="<?= /* @escapeNotVerified */ $block->getCategoryUrl($_category) ?>"<?php if ($block->isCategoryActive($_category)): ?> class="current"<?php endif; ?>><?= $block->escapeHtml($_category->getName()) ?></a> - <span class="count"><?= /* @escapeNotVerified */ $_category->getProductCount() ?></span> + <a href="<?= $block->escapeUrl($block->getCategoryUrl($_category)) ?>" + <?php if ($block->isCategoryActive($_category)) :?> + class="current" + <?php endif; ?> + ><?= $block->escapeHtml($_category->getName()) ?></a> + <span class="count"><?= $block->escapeHtml($_category->getProductCount()) ?></span> </li> <?php endif; ?> <?php endforeach ?> diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/compare/link.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/compare/link.phtml index b8595aae9d993..05a5649135ef5 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/compare/link.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/compare/link.phtml @@ -4,16 +4,16 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile +/** @var $block Magento\Framework\View\Element\Template */ ?> <li class="item link compare" data-bind="scope: 'compareProducts'" data-role="compare-products-link"> - <a class="action compare no-display" title="<?= /* @escapeNotVerified */ __('Compare Products') ?>" + <a class="action compare no-display" title="<?= $block->escapeHtmlAttr(__('Compare Products')) ?>" data-bind="attr: {'href': compareProducts().listUrl}, css: {'no-display': !compareProducts().count}" > - <?= /* @escapeNotVerified */ __('Compare Products') ?> + <?= $block->escapeHtml(__('Compare Products')) ?> <span class="counter qty" data-bind="text: compareProducts().countCaption"></span> </a> </li> <script type="text/x-magento-init"> -{"[data-role=compare-products-link]": {"Magento_Ui/js/core/app": <?= /* @escapeNotVerified */ $block->getJsLayout() ?>}} +{"[data-role=compare-products-link]": {"Magento_Ui/js/core/app": <?= /* @noEscape */ $block->getJsLayout() ?>}} </script> diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/compare/list.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/compare/list.phtml index 7daf049980362..55772388d44bf 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/compare/list.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/compare/list.phtml @@ -4,14 +4,16 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile +// phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis +// phpcs:disable PSR2.ControlStructures.SwitchDeclaration +// phpcs:disable Generic.WhiteSpace.ScopeIndent /* @var $block \Magento\Catalog\Block\Product\Compare\ListCompare */ ?> <?php $total = $block->getItems()->getSize() ?> -<?php if ($total): ?> - <a href="#" class="action print hidden-print" title="<?= /* @escapeNotVerified */ __('Print This Page') ?>"> - <span><?= /* @escapeNotVerified */ __('Print This Page') ?></span> +<?php if ($total) :?> + <a href="#" class="action print hidden-print" title="<?= $block->escapeHtmlAttr(__('Print This Page')) ?>"> + <span><?= $block->escapeHtml(__('Print This Page')) ?></span> </a> <div class="table-wrapper comparison"> <table class="data table table-comparison" id="product-comparison" @@ -21,19 +23,19 @@ "selectors":{ "productAddToCartSelector":"button.action.tocart"} }}'> - <caption class="table-caption"><?= /* @escapeNotVerified */ __('Compare Products') ?></caption> + <caption class="table-caption"><?= $block->escapeHtml(__('Compare Products')) ?></caption> <thead> <tr> <?php $index = 0 ?> - <?php foreach ($block->getItems() as $item): ?> - <?php if ($index++ == 0): ?> - <th scope="row" class="cell label remove"><span><?= /* @escapeNotVerified */ __('Remove Product') ?></span></th> + <?php foreach ($block->getItems() as $item) :?> + <?php if ($index++ == 0) :?> + <th scope="row" class="cell label remove"><span><?= $block->escapeHtml(__('Remove Product')) ?></span></th> <?php endif; ?> <td class="cell remove product hidden-print"> - <?php $compareHelper = $this->helper('Magento\Catalog\Helper\Product\Compare');?> - <a href="#" data-post='<?= /* @escapeNotVerified */ $compareHelper->getPostDataRemove($item) ?>' - class="action delete" title="<?= /* @escapeNotVerified */ __('Remove Product') ?>"> - <span><?= /* @escapeNotVerified */ __('Remove Product') ?></span> + <?php $compareHelper = $this->helper(Magento\Catalog\Helper\Product\Compare::class);?> + <a href="#" data-post='<?= /* @noEscape */ $compareHelper->getPostDataRemove($item) ?>' + class="action delete" title="<?= $block->escapeHtmlAttr(__('Remove Product')) ?>"> + <span><?= $block->escapeHtml(__('Remove Product')) ?></span> </a> </td> <?php endforeach; ?> @@ -42,44 +44,54 @@ <tbody> <tr> <?php $index = 0; ?> - <?php $helper = $this->helper('Magento\Catalog\Helper\Output'); ?> + <?php $helper = $this->helper(Magento\Catalog\Helper\Output::class); ?> <?php /** @var $item \Magento\Catalog\Model\Product */ ?> - <?php foreach ($block->getItems() as $item): ?> - <?php if ($index++ == 0): ?> - <th scope="row" class="cell label product"><span><?= /* @escapeNotVerified */ __('Product') ?></span></th> + <?php foreach ($block->getItems() as $item) :?> + <?php if ($index++ == 0) :?> + <th scope="row" class="cell label product"> + <span><?= $block->escapeHtml(__('Product')) ?></span> + </th> <?php endif; ?> - <td data-th="<?= $block->escapeHtml(__('Product')) ?>" class="cell product info"> - <a class="product-item-photo" href="<?= /* @escapeNotVerified */ $block->getProductUrl($item) ?>" title="<?= /* @escapeNotVerified */ $block->stripTags($item->getName(), null, true) ?>"> + <td data-th="<?= $block->escapeHtmlAttr(__('Product')) ?>" class="cell product info"> + <a class="product-item-photo" + href="<?= $block->escapeUrl($block->getProductUrl($item)) ?>" + title="<?= /* @noEscape */ $block->stripTags($item->getName(), null, true) ?>"> <?= $block->getImage($item, 'product_comparison_list')->toHtml() ?> </a> <strong class="product-item-name"> - <a href="<?= /* @escapeNotVerified */ $block->getProductUrl($item) ?>" title="<?= /* @escapeNotVerified */ $block->stripTags($item->getName(), null, true) ?>"> - <?= /* @escapeNotVerified */ $helper->productAttribute($item, $item->getName(), 'name') ?> + <a href="<?= $block->escapeUrl($block->getProductUrl($item)) ?>" + title="<?= /* @noEscape */ $block->stripTags($item->getName(), null, true) ?>"> + <?= /* @noEscape */ $helper->productAttribute($item, $item->getName(), 'name') ?> </a> </strong> <?= $block->getReviewsSummaryHtml($item, 'short') ?> - <?= /* @escapeNotVerified */ $block->getProductPrice($item, '-compare-list-top') ?> + <?= /* @noEscape */ $block->getProductPrice($item, '-compare-list-top') ?> <div class="product-item-actions hidden-print"> <div class="actions-primary"> - <?php if ($item->isSaleable()): ?> - <form data-role="tocart-form" action="<?= /* @escapeNotVerified */ $this->helper('Magento\Catalog\Helper\Product\Compare')->getAddToCartUrl($item) ?>" method="post"> + <?php if ($item->isSaleable()) :?> + <form data-role="tocart-form" + action="<?= $block->escapeUrl($this->helper(Magento\Catalog\Helper\Product\Compare::class)->getAddToCartUrl($item)) ?>" + method="post"> <?= $block->getBlockHtml('formkey') ?> <button type="submit" class="action tocart primary"> - <span><?= /* @escapeNotVerified */ __('Add to Cart') ?></span> + <span><?= $block->escapeHtml(__('Add to Cart')) ?></span> </button> </form> - <?php else: ?> - <?php if ($item->getIsSalable()): ?> - <div class="stock available"><span><?= /* @escapeNotVerified */ __('In stock') ?></span></div> - <?php else: ?> - <div class="stock unavailable"><span><?= /* @escapeNotVerified */ __('Out of stock') ?></span></div> + <?php else :?> + <?php if ($item->getIsSalable()) :?> + <div class="stock available"><span><?= $block->escapeHtml(__('In stock')) ?></span></div> + <?php else :?> + <div class="stock unavailable"><span><?= $block->escapeHtml(__('Out of stock')) ?></span></div> <?php endif; ?> <?php endif; ?> </div> - <?php if ($this->helper('Magento\Wishlist\Helper\Data')->isAllow()) : ?> + <?php if ($this->helper(Magento\Wishlist\Helper\Data::class)->isAllow()) :?> <div class="secondary-addto-links actions-secondary" data-role="add-to-links"> - <a href="#" data-post='<?= /* @escapeNotVerified */ $block->getAddToWishlistParams($item) ?>' class="action towishlist" data-action="add-to-wishlist"> - <span><?= /* @escapeNotVerified */ __('Add to Wish List') ?></span> + <a href="#" + data-post='<?= /* @noEscape */ $block->getAddToWishlistParams($item) ?>' + class="action towishlist" + data-action="add-to-wishlist"> + <span><?= $block->escapeHtml(__('Add to Wish List')) ?></span> </a> </div> <?php endif; ?> @@ -89,12 +101,12 @@ </tr> </tbody> <tbody> - <?php foreach ($block->getAttributes() as $attribute): ?> + <?php foreach ($block->getAttributes() as $attribute) :?> <?php $index = 0; ?> - <?php if ($block->hasAttributeValueForProducts($attribute)): ?> + <?php if ($block->hasAttributeValueForProducts($attribute)) :?> <tr> - <?php foreach ($block->getItems() as $item): ?> - <?php if ($index++ == 0): ?> + <?php foreach ($block->getItems() as $item) :?> + <?php if ($index++ == 0) :?> <th scope="row" class="cell label"> <span class="attribute label"> <?= $block->escapeHtml($attribute->getStoreLabel() ? $attribute->getStoreLabel() : __($attribute->getFrontendLabel())) ?> @@ -105,21 +117,21 @@ <div class="attribute value"> <?php switch ($attribute->getAttributeCode()) { case "price": ?> - <?php - /* @escapeNotVerified */ echo $block->getProductPrice( - $item, - '-compare-list-' . $attribute->getCode() - ) + <?= + /* @noEscape */ $block->getProductPrice( + $item, + '-compare-list-' . $attribute->getCode() + ) ?> <?php break; case "small_image": ?> <?php $block->getImage($item, 'product_small_image')->toHtml(); ?> <?php break; - default: ?> - <?php if (is_string($block->getProductAttributeValue($item, $attribute))): ?> - <?= /* @escapeNotVerified */ $helper->productAttribute($item, $block->getProductAttributeValue($item, $attribute), $attribute->getAttributeCode()) ?> + default :?> + <?php if (is_string($block->getProductAttributeValue($item, $attribute))) :?> + <?= /* @noEscape */ $helper->productAttribute($item, $block->getProductAttributeValue($item, $attribute), $attribute->getAttributeCode()) ?> <?php endif; ?> - <?php break; + <?php break; } ?> </div> </td> @@ -130,7 +142,7 @@ </tbody> </table> </div> - <?php if (!$block->isRedirectToCartEnabled()) : ?> + <?php if (!$block->isRedirectToCartEnabled()) :?> <script type="text/x-magento-init"> { "[data-role=tocart-form]": { @@ -139,6 +151,6 @@ } </script> <?php endif; ?> -<?php else: ?> - <div class="message info empty"><div><?= /* @escapeNotVerified */ __('You have no items to compare.') ?></div></div> +<?php else :?> + <div class="message info empty"><div><?= $block->escapeHtml(__('You have no items to compare.')) ?></div></div> <?php endif; ?> diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/compare/sidebar.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/compare/sidebar.phtml index 8daa342454445..809ddc5c61701 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/compare/sidebar.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/compare/sidebar.phtml @@ -4,12 +4,13 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile +// phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis + /* @var $block \Magento\Framework\View\Element\Template */ ?> <div class="block block-compare" data-bind="scope: 'compareProducts'" data-role="compare-products-sidebar"> <div class="block-title"> - <strong id="block-compare-heading" role="heading" aria-level="2"><?= /* @escapeNotVerified */ __('Compare Products') ?></strong> + <strong id="block-compare-heading" role="heading" aria-level="2"><?= $block->escapeHtml(__('Compare Products')) ?></strong> <span class="counter qty no-display" data-bind="text: compareProducts().countCaption, css: {'no-display': !compareProducts().count}"></span> </div> <!-- ko if: compareProducts().count --> @@ -20,29 +21,32 @@ <strong class="product-item-name"> <a data-bind="attr: {href: product_url}, html: name" class="product-item-link"></a> </strong> - <a href="#" data-bind="attr: {'data-post': remove_url}" title="<?= /* @escapeNotVerified */ __('Remove This Item') ?>" class="action delete"> - <span><?= /* @escapeNotVerified */ __('Remove This Item') ?></span> + <a href="#" + data-bind="attr: {'data-post': remove_url}" + title="<?= $block->escapeHtmlAttr(__('Remove This Item')) ?>" + class="action delete"> + <span><?= $block->escapeHtml(__('Remove This Item')) ?></span> </a> </li> </ol> <div class="actions-toolbar"> <div class="primary"> - <a data-bind="attr: {'href': compareProducts().listUrl}" class="action compare primary"><span><?= /* @escapeNotVerified */ __('Compare') ?></span></a> + <a data-bind="attr: {'href': compareProducts().listUrl}" class="action compare primary"><span><?= $block->escapeHtml(__('Compare')) ?></span></a> </div> <div class="secondary"> <a id="compare-clear-all" href="#" class="action clear" data-post="<?=$block->escapeHtml( - $this->helper('Magento\Catalog\Helper\Product\Compare')->getPostDataClearList() + $this->helper(Magento\Catalog\Helper\Product\Compare::class)->getPostDataClearList() ) ?>"> - <span><?= /* @escapeNotVerified */ __('Clear All') ?></span> + <span><?= $block->escapeHtml(__('Clear All')) ?></span> </a> </div> </div> </div> <!-- /ko --> <!-- ko ifnot: compareProducts().count --> - <div class="empty"><?= /* @escapeNotVerified */ __('You have no items to compare.') ?></div> + <div class="empty"><?= $block->escapeHtml(__('You have no items to compare.')) ?></div> <!-- /ko --> </div> <script type="text/x-magento-init"> -{"[data-role=compare-products-sidebar]": {"Magento_Ui/js/core/app": <?= /* @escapeNotVerified */ $block->getJsLayout() ?>}} +{"[data-role=compare-products-sidebar]": {"Magento_Ui/js/core/app": <?= /* @noEscape */ $block->getJsLayout() ?>}} </script> diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/gallery.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/gallery.phtml index c7abb0525b302..e9551793c86f5 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/gallery.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/gallery.phtml @@ -4,39 +4,45 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - /** @var \Magento\Catalog\Block\Product\Gallery $block */ ?> <?php $_width = $block->getImageWidth(); ?> -<div class="product-image-popup" style="width:<?= /* @escapeNotVerified */ $_width ?>px;"> - <div class="buttons-set"><a href="#" class="button" role="close-window"><span><?= /* @escapeNotVerified */ __('Close Window') ?></span></a></div> - <?php if ($block->getPreviousImageUrl() || $block->getNextImageUrl()): ?> +<div class="product-image-popup" style="width:<?= /* @noEscape */ $_width ?>px;"> + <div class="buttons-set"><a href="#" class="button" role="close-window"><span><?= $block->escapeHtml(__('Close Window')) ?></span></a></div> + <?php if ($block->getPreviousImageUrl() || $block->getNextImageUrl()) :?> <div class="nav"> - <?php if ($_prevUrl = $block->getPreviousImageUrl()): ?> - <a href="<?= /* @escapeNotVerified */ $_prevUrl ?>" class="prev">« <?= /* @escapeNotVerified */ __('Prev') ?></a> - <?php endif; ?> - <?php if ($_nextUrl = $block->getNextImageUrl()): ?> - <a href="<?= /* @escapeNotVerified */ $_nextUrl ?>" class="next"><?= /* @escapeNotVerified */ __('Next') ?> »</a> - <?php endif; ?> + <?php if ($_prevUrl = $block->getPreviousImageUrl()) :?> + <a href="<?= $block->escapeUrl($_prevUrl) ?>" class="prev">« <?= $block->escapeHtml(__('Prev')) ?></a> + <?php endif; ?> + <?php if ($_nextUrl = $block->getNextImageUrl()) :?> + <a href="<?= $block->escapeUrl($_nextUrl) ?>" class="next"><?= $block->escapeHtml(__('Next')) ?> »</a> + <?php endif; ?> </div> <?php endif; ?> - <?php if ($_imageTitle = $block->escapeHtml($block->getCurrentImage()->getLabel())): ?> - <h1 class="image-label"><?= /* @escapeNotVerified */ $_imageTitle ?></h1> + <?php if ($_imageTitle = $block->escapeHtml($block->getCurrentImage()->getLabel())) :?> + <h1 class="image-label"><?= /* @noEscape */ $_imageTitle ?></h1> <?php endif; ?> <?php - $imageUrl = $block->getImageUrl(); + $imageUrl = $block->getImageUrl(); ?> - <img src="<?= /* @escapeNotVerified */ $imageUrl ?>"<?php if ($_width): ?> width="<?= /* @escapeNotVerified */ $_width ?>"<?php endif; ?> alt="<?= $block->escapeHtml($block->getCurrentImage()->getLabel()) ?>" title="<?= $block->escapeHtml($block->getCurrentImage()->getLabel()) ?>" id="product-gallery-image" class="image" data-mage-init='{"catalogGallery":{}}'/> - <div class="buttons-set"><a href="#" class="button" role="close-window"><span><?= /* @escapeNotVerified */ __('Close Window') ?></span></a></div> - <?php if ($block->getPreviousImageUrl() || $block->getNextImageUrl()): ?> + <img src="<?= $block->escapeUrl($imageUrl) ?>" + <?php if ($_width) :?> + width="<?= /* @noEscape */ $_width ?>" + <?php endif; ?> + alt="<?= $block->escapeHtmlAttr($block->getCurrentImage()->getLabel()) ?>" + title="<?= $block->escapeHtmlAttr($block->getCurrentImage()->getLabel()) ?>" + id="product-gallery-image" + class="image" + data-mage-init='{"catalogGallery":{}}'/> + <div class="buttons-set"><a href="#" class="button" role="close-window"><span><?= /* @noEscape */ __('Close Window') ?></span></a></div> + <?php if ($block->getPreviousImageUrl() || $block->getNextImageUrl()) :?> <div class="nav"> - <?php if ($_prevUrl = $block->getPreviousImageUrl()): ?> - <a href="<?= /* @escapeNotVerified */ $_prevUrl ?>" class="prev">« <?= /* @escapeNotVerified */ __('Prev') ?></a> - <?php endif; ?> - <?php if ($_nextUrl = $block->getNextImageUrl()): ?> - <a href="<?= /* @escapeNotVerified */ $_nextUrl ?>" class="next"><?= /* @escapeNotVerified */ __('Next') ?> »</a> - <?php endif; ?> + <?php if ($_prevUrl = $block->getPreviousImageUrl()) :?> + <a href="<?= $block->escapeUrl($_prevUrl) ?>" class="prev">« <?= $block->escapeHtml(__('Prev')) ?></a> + <?php endif; ?> + <?php if ($_nextUrl = $block->getNextImageUrl()) :?> + <a href="<?= $block->escapeUrl($_nextUrl) ?>" class="next"><?= $block->escapeHtml(__('Next')) ?> »</a> + <?php endif; ?> </div> <?php endif; ?> </div> diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/image.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/image.phtml index 94b829eb92137..5a1b102ff6362 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/image.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/image.phtml @@ -7,8 +7,8 @@ <?php /** @var $block \Magento\Catalog\Block\Product\Image */ ?> <img class="photo image" - <?= /* @escapeNotVerified */ $block->getCustomAttributes() ?> - src="<?= /* @escapeNotVerified */ $block->getImageUrl() ?>" - width="<?= /* @escapeNotVerified */ $block->getWidth() ?>" - height="<?= /* @escapeNotVerified */ $block->getHeight() ?>" - alt="<?= /* @escapeNotVerified */ $block->stripTags($block->getLabel(), null, true) ?>" /> + <?= $block->escapeHtml($block->getCustomAttributes()) ?> + src="<?= $block->escapeUrl($block->getImageUrl()) ?>" + width="<?= $block->escapeHtmlAttr($block->getWidth()) ?>" + height="<?= $block->escapeHtmlAttr($block->getHeight()) ?>" + alt="<?= /* @noEscape */ $block->stripTags($block->getLabel(), null, true) ?>" /> diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/image_with_borders.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/image_with_borders.phtml index 8a907bd54aa6a..33f7620f1a1f5 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/image_with_borders.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/image_with_borders.phtml @@ -7,13 +7,13 @@ <?php /** @var $block \Magento\Catalog\Block\Product\Image */ ?> <span class="product-image-container" - style="width:<?= /* @escapeNotVerified */ $block->getWidth() ?>px;"> + style="width:<?= $block->escapeHtmlAttr($block->getWidth()) ?>px;"> <span class="product-image-wrapper" - style="padding-bottom: <?= /* @escapeNotVerified */ ($block->getRatio() * 100) ?>%;"> - <img class="<?= /* @escapeNotVerified */ $block->getClass() ?>" - <?= /* @escapeNotVerified */ $block->getCustomAttributes() ?> - src="<?= /* @escapeNotVerified */ $block->getImageUrl() ?>" - max-width="<?= /* @escapeNotVerified */ $block->getWidth() ?>" - max-height="<?= /* @escapeNotVerified */ $block->getHeight() ?>" - alt="<?= /* @escapeNotVerified */ $block->stripTags($block->getLabel(), null, true) ?>"/></span> + style="padding-bottom: <?= ($block->getRatio() * 100) ?>%;"> + <img class="<?= $block->escapeHtmlAttr($block->getClass()) ?>" + <?= $block->escapeHtmlAttr($block->getCustomAttributes()) ?> + src="<?= $block->escapeUrl($block->getImageUrl()) ?>" + max-width="<?= $block->escapeHtmlAttr($block->getWidth()) ?>" + max-height="<?= $block->escapeHtmlAttr($block->getHeight()) ?>" + alt="<?= /* @noEscape */ $block->stripTags($block->getLabel(), null, true) ?>"/></span> </span> diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/list.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/list.phtml index e970ade6cee96..ce44884a575b8 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/list.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/list.phtml @@ -5,10 +5,10 @@ */ use Magento\Framework\App\Action\Action; -// @codingStandardsIgnoreFile - ?> <?php +// phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis + /** * Product list template * @@ -17,11 +17,11 @@ use Magento\Framework\App\Action\Action; ?> <?php $_productCollection = $block->getLoadedProductCollection(); -$_helper = $this->helper('Magento\Catalog\Helper\Output'); +$_helper = $this->helper(Magento\Catalog\Helper\Output::class); ?> -<?php if (!$_productCollection->count()): ?> - <div class="message info empty"><div><?= /* @escapeNotVerified */ __('We can\'t find products matching the selection.') ?></div></div> -<?php else: ?> +<?php if (!$_productCollection->count()) :?> + <div class="message info empty"><div><?= $block->escapeHtml(__('We can\'t find products matching the selection.')) ?></div></div> +<?php else :?> <?= $block->getToolbarHtml() ?> <?= $block->getAdditionalHtml() ?> <?php @@ -41,12 +41,12 @@ $_helper = $this->helper('Magento\Catalog\Helper\Output'); */ $pos = $block->getPositioned(); ?> - <div class="products wrapper <?= /* @escapeNotVerified */ $viewMode ?> products-<?= /* @escapeNotVerified */ $viewMode ?>"> + <div class="products wrapper <?= /* @noEscape */ $viewMode ?> products-<?= /* @noEscape */ $viewMode ?>"> <ol class="products list items product-items"> <?php /** @var $_product \Magento\Catalog\Model\Product */ ?> - <?php foreach ($_productCollection as $_product): ?> + <?php foreach ($_productCollection as $_product) :?> <li class="item product product-item"> - <div class="product-item-info" data-container="product-<?= /* @escapeNotVerified */ $viewMode ?>"> + <div class="product-item-info" data-container="product-<?= /* @noEscape */ $viewMode ?>"> <?php $productImage = $block->getImage($_product, $imageDisplayArea); if ($pos != null) { @@ -55,7 +55,9 @@ $_helper = $this->helper('Magento\Catalog\Helper\Output'); } ?> <?php // Product Image ?> - <a href="<?= /* @escapeNotVerified */ $_product->getProductUrl() ?>" class="product photo product-item-photo" tabindex="-1"> + <a href="<?= $block->escapeUrl($_product->getProductUrl()) ?>" + class="product photo product-item-photo" + tabindex="-1"> <?= $productImage->toHtml() ?> </a> <div class="product details product-item-details"> @@ -64,48 +66,55 @@ $_helper = $this->helper('Magento\Catalog\Helper\Output'); ?> <strong class="product name product-item-name"> <a class="product-item-link" - href="<?= /* @escapeNotVerified */ $_product->getProductUrl() ?>"> - <?= /* @escapeNotVerified */ $_helper->productAttribute($_product, $_product->getName(), 'name') ?> + href="<?= $block->escapeUrl($_product->getProductUrl()) ?>"> + <?= /* @noEscape */ $_helper->productAttribute($_product, $_product->getName(), 'name') ?> </a> </strong> <?= $block->getReviewsSummaryHtml($_product, $templateType) ?> - <?= /* @escapeNotVerified */ $block->getProductPrice($_product) ?> + <?= /* @noEscape */ $block->getProductPrice($_product) ?> <?= $block->getProductDetailsHtml($_product) ?> <div class="product-item-inner"> - <div class="product actions product-item-actions"<?= strpos($pos, $viewMode . '-actions') ? $position : '' ?>> - <div class="actions-primary"<?= strpos($pos, $viewMode . '-primary') ? $position : '' ?>> - <?php if ($_product->isSaleable()): ?> + <div class="product actions product-item-actions"<?= strpos($pos, $viewMode . '-actions') ? $block->escapeHtmlAttr($position) : '' ?>> + <div class="actions-primary"<?= strpos($pos, $viewMode . '-primary') ? $block->escapeHtmlAttr($position) : '' ?>> + <?php if ($_product->isSaleable()) :?> <?php $postParams = $block->getAddToCartPostParams($_product); ?> - <form data-role="tocart-form" data-product-sku="<?= $block->escapeHtml($_product->getSku()) ?>" action="<?= /* @NoEscape */ $postParams['action'] ?>" method="post"> - <input type="hidden" name="product" value="<?= /* @escapeNotVerified */ $postParams['data']['product'] ?>"> - <input type="hidden" name="<?= /* @escapeNotVerified */ Action::PARAM_NAME_URL_ENCODED ?>" value="<?= /* @escapeNotVerified */ $postParams['data'][Action::PARAM_NAME_URL_ENCODED] ?>"> + <form data-role="tocart-form" + data-product-sku="<?= $block->escapeHtml($_product->getSku()) ?>" + action="<?= $block->escapeUrl($postParams['action']) ?>" + method="post"> + <input type="hidden" + name="product" + value="<?= /* @noEscape */ $postParams['data']['product'] ?>"> + <input type="hidden" name="<?= /* @noEscape */ Action::PARAM_NAME_URL_ENCODED ?>" + value="<?= /* @noEscape */ $postParams['data'][Action::PARAM_NAME_URL_ENCODED] ?>"> <?= $block->getBlockHtml('formkey') ?> <button type="submit" - title="<?= $block->escapeHtml(__('Add to Cart')) ?>" + title="<?= $block->escapeHtmlAttr(__('Add to Cart')) ?>" class="action tocart primary"> - <span><?= /* @escapeNotVerified */ __('Add to Cart') ?></span> + <span><?= $block->escapeHtml(__('Add to Cart')) ?></span> </button> </form> - <?php else: ?> - <?php if ($_product->isAvailable()): ?> - <div class="stock available"><span><?= /* @escapeNotVerified */ __('In stock') ?></span></div> - <?php else: ?> - <div class="stock unavailable"><span><?= /* @escapeNotVerified */ __('Out of stock') ?></span></div> + <?php else :?> + <?php if ($_product->isAvailable()) :?> + <div class="stock available"><span><?= $block->escapeHtml(__('In stock')) ?></span></div> + <?php else :?> + <div class="stock unavailable"><span><?= $block->escapeHtml(__('Out of stock')) ?></span></div> <?php endif; ?> <?php endif; ?> </div> - <div data-role="add-to-links" class="actions-secondary"<?= strpos($pos, $viewMode . '-secondary') ? $position : '' ?>> - <?php if ($addToBlock = $block->getChildBlock('addto')): ?> + <div data-role="add-to-links" class="actions-secondary"<?= strpos($pos, $viewMode . '-secondary') ? $block->escapeHtmlAttr($position) : '' ?>> + <?php if ($addToBlock = $block->getChildBlock('addto')) :?> <?= $addToBlock->setProduct($_product)->getChildHtml() ?> <?php endif; ?> </div> </div> - <?php if ($showDescription):?> + <?php if ($showDescription) :?> <div class="product description product-item-description"> - <?= /* @escapeNotVerified */ $_helper->productAttribute($_product, $_product->getShortDescription(), 'short_description') ?> - <a href="<?= /* @escapeNotVerified */ $_product->getProductUrl() ?>" title="<?= /* @escapeNotVerified */ $_productNameStripped ?>" - class="action more"><?= /* @escapeNotVerified */ __('Learn More') ?></a> + <?= /* @noEscape */ $_helper->productAttribute($_product, $_product->getShortDescription(), 'short_description') ?> + <a href="<?= $block->escapeUrl($_product->getProductUrl()) ?>" + title="<?= /* @noEscape */ $_productNameStripped ?>" + class="action more"><?= $block->escapeHtml(__('Learn More')) ?></a> </div> <?php endif; ?> </div> @@ -116,12 +125,12 @@ $_helper = $this->helper('Magento\Catalog\Helper\Output'); </ol> </div> <?= $block->getToolbarHtml() ?> - <?php if (!$block->isRedirectToCartEnabled()) : ?> + <?php if (!$block->isRedirectToCartEnabled()) :?> <script type="text/x-magento-init"> { "[data-role=tocart-form], .form.map.checkout": { "catalogAddToCart": { - "product_sku": "<?= /* @NoEscape */ $_product->getSku() ?>" + "product_sku": "<?= $block->escapeJs($_product->getSku()) ?>" } } } diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/list/addto/compare.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/list/addto/compare.phtml index 8798170e8c0b0..c23ee021ca3a8 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/list/addto/compare.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/list/addto/compare.phtml @@ -4,14 +4,13 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile /** @var $block Magento\Catalog\Block\Product\ProductList\Item\AddTo\Compare */ ?> <a href="#" class="action tocompare" title="<?= $block->escapeHtml(__('Add to Compare')) ?>" aria-label="<?= $block->escapeHtml(__('Add to Compare')) ?>" - data-post='<?= /* @escapeNotVerified */ $block->getCompareHelper()->getPostDataParams($block->getProduct()) ?>' + data-post='<?= /* @noEscape */ $block->getCompareHelper()->getPostDataParams($block->getProduct()) ?>' role="button"> - <span><?= /* @escapeNotVerified */ __('Add to Compare') ?></span> + <span><?= $block->escapeHtml(__('Add to Compare')) ?></span> </a> diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/list/items.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/list/items.phtml index ecc9700802d27..91e261900aef2 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/list/items.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/list/items.phtml @@ -4,7 +4,8 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile +// phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis +// phpcs:disable Generic.WhiteSpace.ScopeIndent.Incorrect /* @var $block \Magento\Catalog\Block\Product\AbstractProduct */ ?> @@ -29,7 +30,7 @@ switch ($type = $block->getType()) { $templateType = null; $description = false; } - break; + break; case 'related': /** @var \Magento\Catalog\Block\Product\ProductList\Related $block */ @@ -49,7 +50,7 @@ switch ($type = $block->getType()) { $templateType = null; $description = false; } - break; + break; case 'upsell-rule': if ($exist = $block->hasItems()) { @@ -68,7 +69,7 @@ switch ($type = $block->getType()) { $description = false; $canItemsAddToCart = false; } - break; + break; case 'upsell': /** @var \Magento\Catalog\Block\Product\ProductList\Upsell $block */ @@ -88,7 +89,7 @@ switch ($type = $block->getType()) { $description = false; $canItemsAddToCart = false; } - break; + break; case 'crosssell-rule': /** @var \Magento\Catalog\Block\Product\ProductList\Crosssell $block */ @@ -106,7 +107,7 @@ switch ($type = $block->getType()) { $description = false; $canItemsAddToCart = false; } - break; + break; case 'crosssell': /** @var \Magento\Catalog\Block\Product\ProductList\Crosssell $block */ @@ -124,7 +125,7 @@ switch ($type = $block->getType()) { $description = false; $canItemsAddToCart = false; } - break; + break; case 'new': if ($exist = $block->getProductCollection()) { @@ -144,117 +145,117 @@ switch ($type = $block->getType()) { $description = ($mode == 'list') ? true : false; $canItemsAddToCart = false; } - break; + break; default: $exist = null; } ?> -<?php if ($exist):?> +<?php if ($exist) :?> - <?php if ($type == 'related' || $type == 'upsell'): ?> - <?php if ($type == 'related'): ?> - <div class="block <?= /* @escapeNotVerified */ $class ?>" data-mage-init='{"relatedProducts":{"relatedCheckbox":".related.checkbox"}}' data-limit="<?= /* @escapeNotVerified */ $limit ?>" data-shuffle="<?= /* @escapeNotVerified */ $shuffle ?>"> - <?php else: ?> - <div class="block <?= /* @escapeNotVerified */ $class ?>" data-mage-init='{"upsellProducts":{}}' data-limit="<?= /* @escapeNotVerified */ $limit ?>" data-shuffle="<?= /* @escapeNotVerified */ $shuffle ?>"> +<?php if ($type == 'related' || $type == 'upsell') :?> +<?php if ($type == 'related') :?> +<div class="block <?= $block->escapeHtmlAttr($class) ?>" data-mage-init='{"relatedProducts":{"relatedCheckbox":".related.checkbox"}}' data-limit="<?= $block->escapeHtmlAttr($limit) ?>" data-shuffle="<?= /* @noEscape */ $shuffle ?>"> + <?php else :?> + <div class="block <?= $block->escapeHtmlAttr($class) ?>" data-mage-init='{"upsellProducts":{}}' data-limit="<?= $block->escapeHtmlAttr($limit) ?>" data-shuffle="<?= /* @noEscape */ $shuffle ?>"> <?php endif; ?> - <?php else: ?> - <div class="block <?= /* @escapeNotVerified */ $class ?>"> - <?php endif; ?> - <div class="block-title title"> - <strong id="block-<?= /* @escapeNotVerified */ $class ?>-heading" role="heading" aria-level="2"><?= /* @escapeNotVerified */ $title ?></strong> - </div> - <div class="block-content content" aria-labelledby="block-<?= /* @escapeNotVerified */ $class ?>-heading"> - <?php if ($type == 'related' && $canItemsAddToCart): ?> - <div class="block-actions"> - <?= /* @escapeNotVerified */ __('Check items to add to the cart or') ?> - <button type="button" class="action select" role="button"><span><?= /* @escapeNotVerified */ __('select all') ?></span></button> - </div> - <?php endif; ?> - <div class="products wrapper grid products-grid products-<?= /* @escapeNotVerified */ $type ?>"> - <ol class="products list items product-items"> - <?php foreach ($items as $_item): ?> - <?php $available = ''; ?> - <?php if (!$_item->isComposite() && $_item->isSaleable() && $type == 'related'): ?> - <?php if (!$_item->getRequiredOptions()): ?> - <?php $available = 'related-available'; ?> - <?php endif; ?> - <?php endif; ?> - <?php if ($type == 'related' || $type == 'upsell'): ?> - <li class="item product product-item" style="display: none;"> - <?php else: ?> - <li class="item product product-item"> + <?php else :?> + <div class="block <?= $block->escapeHtmlAttr($class) ?>"> + <?php endif; ?> + <div class="block-title title"> + <strong id="block-<?= $block->escapeHtmlAttr($class) ?>-heading" role="heading" aria-level="2"><?= $block->escapeHtml($title) ?></strong> + </div> + <div class="block-content content" aria-labelledby="block-<?= $block->escapeHtmlAttr($class) ?>-heading"> + <?php if ($type == 'related' && $canItemsAddToCart) :?> + <div class="block-actions"> + <?= $block->escapeHtml(__('Check items to add to the cart or')) ?> + <button type="button" class="action select" role="button"><span><?= $block->escapeHtml(__('select all')) ?></span></button> + </div> <?php endif; ?> - <div class="product-item-info <?= /* @escapeNotVerified */ $available ?>"> - <?= /* @escapeNotVerified */ '<!-- ' . $image . '-->' ?> - <a href="<?= /* @escapeNotVerified */ $block->getProductUrl($_item) ?>" class="product photo product-item-photo"> - <?= $block->getImage($_item, $image)->toHtml() ?> - </a> - <div class="product details product-item-details"> - <strong class="product name product-item-name"><a class="product-item-link" title="<?= $block->escapeHtml($_item->getName()) ?>" href="<?= /* @escapeNotVerified */ $block->getProductUrl($_item) ?>"> - <?= $block->escapeHtml($_item->getName()) ?></a> - </strong> - - <?= /* @escapeNotVerified */ $block->getProductPrice($_item) ?> - - <?php if ($templateType): ?> - <?= $block->getReviewsSummaryHtml($_item, $templateType) ?> - <?php endif; ?> - - <?php if ($canItemsAddToCart && !$_item->isComposite() && $_item->isSaleable() && $type == 'related'): ?> - <?php if (!$_item->getRequiredOptions()): ?> - <div class="field choice related"> - <input type="checkbox" class="checkbox related" id="related-checkbox<?= /* @escapeNotVerified */ $_item->getId() ?>" name="related_products[]" value="<?= /* @escapeNotVerified */ $_item->getId() ?>" /> - <label class="label" for="related-checkbox<?= /* @escapeNotVerified */ $_item->getId() ?>"><span><?= /* @escapeNotVerified */ __('Add to Cart') ?></span></label> - </div> + <div class="products wrapper grid products-grid products-<?= $block->escapeHtmlAttr($type) ?>"> + <ol class="products list items product-items"> + <?php foreach ($items as $_item) :?> + <?php $available = ''; ?> + <?php if (!$_item->isComposite() && $_item->isSaleable() && $type == 'related') :?> + <?php if (!$_item->getRequiredOptions()) :?> + <?php $available = 'related-available'; ?> <?php endif; ?> <?php endif; ?> + <?php if ($type == 'related' || $type == 'upsell') :?> + <li class="item product product-item" style="display: none;"> + <?php else :?> + <li class="item product product-item"> + <?php endif; ?> + <div class="product-item-info <?= /* @noEscape */ $available ?>"> + <?= /* @noEscape */ '<!-- ' . $image . '-->' ?> + <a href="<?= $block->escapeUrl($block->getProductUrl($_item)) ?>" class="product photo product-item-photo"> + <?= $block->getImage($_item, $image)->toHtml() ?> + </a> + <div class="product details product-item-details"> + <strong class="product name product-item-name"><a class="product-item-link" title="<?= $block->escapeHtml($_item->getName()) ?>" href="<?= $block->escapeUrl($block->getProductUrl($_item)) ?>"> + <?= $block->escapeHtml($_item->getName()) ?></a> + </strong> + + <?= /* @noEscape */ $block->getProductPrice($_item) ?> + + <?php if ($templateType) :?> + <?= $block->getReviewsSummaryHtml($_item, $templateType) ?> + <?php endif; ?> - <?php if ($showAddTo || $showCart): ?> - <div class="product actions product-item-actions"> - <?php if ($showCart): ?> - <div class="actions-primary"> - <?php if ($_item->isSaleable()): ?> - <?php if ($_item->getTypeInstance()->hasRequiredOptions($_item)): ?> - <button class="action tocart primary" data-mage-init='{"redirectUrl": {"url": "<?= /* @escapeNotVerified */ $block->getAddToCartUrl($_item) ?>"}}' type="button" title="<?= /* @escapeNotVerified */ __('Add to Cart') ?>"> - <span><?= /* @escapeNotVerified */ __('Add to Cart') ?></span> - </button> - <?php else: ?> - <?php $postDataHelper = $this->helper('Magento\Framework\Data\Helper\PostHelper'); - $postData = $postDataHelper->getPostData($block->getAddToCartUrl($_item), ['product' => $_item->getEntityId()]) - ?> - <button class="action tocart primary" - data-post='<?= /* @escapeNotVerified */ $postData ?>' - type="button" title="<?= /* @escapeNotVerified */ __('Add to Cart') ?>"> - <span><?= /* @escapeNotVerified */ __('Add to Cart') ?></span> - </button> - <?php endif; ?> - <?php else: ?> - <?php if ($_item->getIsSalable()): ?> - <div class="stock available"><span><?= /* @escapeNotVerified */ __('In stock') ?></span></div> - <?php else: ?> - <div class="stock unavailable"><span><?= /* @escapeNotVerified */ __('Out of stock') ?></span></div> - <?php endif; ?> - <?php endif; ?> - </div> + <?php if ($canItemsAddToCart && !$_item->isComposite() && $_item->isSaleable() && $type == 'related') :?> + <?php if (!$_item->getRequiredOptions()) :?> + <div class="field choice related"> + <input type="checkbox" class="checkbox related" id="related-checkbox<?= $block->escapeHtmlAttr($_item->getId()) ?>" name="related_products[]" value="<?= $block->escapeHtmlAttr($_item->getId()) ?>" /> + <label class="label" for="related-checkbox<?= $block->escapeHtmlAttr($_item->getId()) ?>"><span><?= $block->escapeHtml(__('Add to Cart')) ?></span></label> + </div> + <?php endif; ?> <?php endif; ?> - <?php if ($showAddTo): ?> - <div class="secondary-addto-links actions-secondary" data-role="add-to-links"> - <?php if ($addToBlock = $block->getChildBlock('addto')): ?> - <?= $addToBlock->setProduct($_item)->getChildHtml() ?> + <?php if ($showAddTo || $showCart) :?> + <div class="product actions product-item-actions"> + <?php if ($showCart) :?> + <div class="actions-primary"> + <?php if ($_item->isSaleable()) :?> + <?php if ($_item->getTypeInstance()->hasRequiredOptions($_item)) :?> + <button class="action tocart primary" data-mage-init='{"redirectUrl": {"url": "<?= $block->escapeUrl($block->getAddToCartUrl($_item)) ?>"}}' type="button" title="<?= $block->escapeHtmlAttr(__('Add to Cart')) ?>"> + <span><?= $block->escapeHtml(__('Add to Cart')) ?></span> + </button> + <?php else :?> + <?php $postDataHelper = $this->helper(Magento\Framework\Data\Helper\PostHelper::class); + $postData = $postDataHelper->getPostData($block->escapeUrl($block->getAddToCartUrl($_item)), ['product' => $_item->getEntityId()]) + ?> + <button class="action tocart primary" + data-post='<?= /* @noEscape */ $postData ?>' + type="button" title="<?= $block->escapeHtmlAttr(__('Add to Cart')) ?>"> + <span><?= $block->escapeHtml(__('Add to Cart')) ?></span> + </button> + <?php endif; ?> + <?php else :?> + <?php if ($_item->getIsSalable()) :?> + <div class="stock available"><span><?= $block->escapeHtml(__('In stock')) ?></span></div> + <?php else :?> + <div class="stock unavailable"><span><?= $block->escapeHtml(__('Out of stock')) ?></span></div> + <?php endif; ?> + <?php endif; ?> + </div> + <?php endif; ?> + + <?php if ($showAddTo) :?> + <div class="secondary-addto-links actions-secondary" data-role="add-to-links"> + <?php if ($addToBlock = $block->getChildBlock('addto')) :?> + <?= $addToBlock->setProduct($_item)->getChildHtml() ?> + <?php endif; ?> + </div> <?php endif; ?> </div> <?php endif; ?> </div> - <?php endif; ?> - </div> - </div> - </li> - <?php endforeach ?> - </ol> + </div> + </li> + <?php endforeach ?> + </ol> + </div> + </div> </div> - </div> -</div> -<?php endif;?> + <?php endif;?> diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/list/toolbar.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/list/toolbar.phtml index 02a6e999ad51f..b2ae8b9f7ab13 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/list/toolbar.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/list/toolbar.phtml @@ -3,9 +3,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <?php /** @@ -13,11 +10,13 @@ * * @var $block \Magento\Catalog\Block\Product\ProductList\Toolbar */ -use Magento\Catalog\Model\Product\ProductList\Toolbar; + +// phpcs:disable Magento2.Security.IncludeFile.FoundIncludeFile +// phpcs:disable PSR2.Methods.FunctionCallSignature.SpaceBeforeOpenBracket ?> -<?php if ($block->getCollection()->getSize()): ?> - <div class="toolbar toolbar-products" data-mage-init='<?= /* @escapeNotVerified */ $block->getWidgetOptionsJson() ?>'> - <?php if ($block->isExpanded()): ?> +<?php if ($block->getCollection()->getSize()) :?> + <div class="toolbar toolbar-products" data-mage-init='<?= /* @noEscape */ $block->getWidgetOptionsJson() ?>'> + <?php if ($block->isExpanded()) :?> <?php include ($block->getTemplateFile('Magento_Catalog::product/list/toolbar/viewmode.phtml')) ?> <?php endif; ?> @@ -27,7 +26,7 @@ use Magento\Catalog\Model\Product\ProductList\Toolbar; <?php include ($block->getTemplateFile('Magento_Catalog::product/list/toolbar/limiter.phtml')) ?> - <?php if ($block->isExpanded()): ?> + <?php if ($block->isExpanded()) :?> <?php include ($block->getTemplateFile('Magento_Catalog::product/list/toolbar/sorter.phtml')) ?> <?php endif; ?> </div> diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/list/toolbar/amount.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/list/toolbar/amount.phtml index b4ff1afa1c606..a8f504d6a4f17 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/list/toolbar/amount.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/list/toolbar/amount.phtml @@ -3,9 +3,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <?php /** @@ -13,19 +10,27 @@ * * @var $block \Magento\Catalog\Block\Product\ProductList\Toolbar */ -use Magento\Catalog\Model\Product\ProductList\Toolbar; ?> <p class="toolbar-amount" id="toolbar-amount"> - <?php if ($block->getLastPageNum() > 1): ?> - <?php /* @escapeNotVerified */ echo __('Items %1-%2 of %3', - '<span class="toolbar-number">' . $block->getFirstNum() . '</span>', - '<span class="toolbar-number">' . $block->getLastNum() . '</span>', - '<span class="toolbar-number">' . $block->getTotalNum() . '</span>') ?> - <?php elseif ($block->getTotalNum() == 1): ?> - <?php /* @escapeNotVerified */ echo __('%1 Item', - '<span class="toolbar-number">' . $block->getTotalNum() . '</span>') ?> - <?php else: ?> - <?php /* @escapeNotVerified */ echo __('%1 Items', - '<span class="toolbar-number">' . $block->getTotalNum() . '</span>') ?> + <?php if ($block->getLastPageNum() > 1) :?> + <?= $block->escapeHtml( + __( + 'Items %1-%2 of %3', + '<span class="toolbar-number">' . $block->getFirstNum() . '</span>', + '<span class="toolbar-number">' . $block->getLastNum() . '</span>', + '<span class="toolbar-number">' . $block->getTotalNum() . '</span>' + ), + ['span'] + ) ?> + <?php elseif ($block->getTotalNum() == 1) :?> + <?= $block->escapeHtml( + __('%1 Item', '<span class="toolbar-number">' . $block->getTotalNum() . '</span>'), + ['span'] + ) ?> + <?php else :?> + <?= $block->escapeHtml( + __('%1 Items', '<span class="toolbar-number">' . $block->getTotalNum() . '</span>'), + ['span'] + ) ?> <?php endif; ?> </p> diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/list/toolbar/limiter.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/list/toolbar/limiter.phtml index ec4541bde5ca6..4ded219748c64 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/list/toolbar/limiter.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/list/toolbar/limiter.phtml @@ -3,9 +3,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <?php /** @@ -13,21 +10,22 @@ * * @var $block \Magento\Catalog\Block\Product\ProductList\Toolbar */ -use Magento\Catalog\Model\Product\ProductList\Toolbar; ?> <div class="field limiter"> <label class="label" for="limiter"> - <span><?= /* @escapeNotVerified */ __('Show') ?></span> + <span><?= $block->escapeHtml(__('Show')) ?></span> </label> <div class="control"> <select id="limiter" data-role="limiter" class="limiter-options"> - <?php foreach ($block->getAvailableLimit() as $_key => $_limit): ?> - <option value="<?= /* @escapeNotVerified */ $_key ?>"<?php if ($block->isLimitCurrent($_key)): ?> - selected="selected"<?php endif ?>> - <?= /* @escapeNotVerified */ $_limit ?> + <?php foreach ($block->getAvailableLimit() as $_key => $_limit) :?> + <option value="<?= $block->escapeHtmlAttr($_key) ?>" + <?php if ($block->isLimitCurrent($_key)) :?> + selected="selected" + <?php endif ?>> + <?= $block->escapeHtml($_limit) ?> </option> <?php endforeach; ?> </select> </div> - <span class="limiter-text"><?= /* @escapeNotVerified */ __('per page') ?></span> + <span class="limiter-text"><?= $block->escapeHtml(__('per page')) ?></span> </div> diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/list/toolbar/sorter.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/list/toolbar/sorter.phtml index 92514c5b8ea50..58dde199998bc 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/list/toolbar/sorter.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/list/toolbar/sorter.phtml @@ -3,9 +3,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <?php /** @@ -13,14 +10,13 @@ * * @var $block \Magento\Catalog\Block\Product\ProductList\Toolbar */ -use Magento\Catalog\Model\Product\ProductList\Toolbar; ?> <div class="toolbar-sorter sorter"> - <label class="sorter-label" for="sorter"><?= /* @escapeNotVerified */ __('Sort By') ?></label> + <label class="sorter-label" for="sorter"><?= $block->escapeHtml(__('Sort By')) ?></label> <select id="sorter" data-role="sorter" class="sorter-options"> - <?php foreach ($block->getAvailableOrders() as $_key => $_order): ?> - <option value="<?= /* @escapeNotVerified */ $_key ?>" - <?php if ($block->isOrderCurrent($_key)): ?> + <?php foreach ($block->getAvailableOrders() as $_key => $_order) :?> + <option value="<?= $block->escapeHtmlAttr($_key) ?>" + <?php if ($block->isOrderCurrent($_key)) :?> selected="selected" <?php endif; ?> > @@ -28,13 +24,21 @@ use Magento\Catalog\Model\Product\ProductList\Toolbar; </option> <?php endforeach; ?> </select> - <?php if ($block->getCurrentDirection() == 'desc'): ?> - <a title="<?= /* @escapeNotVerified */ __('Set Ascending Direction') ?>" href="#" class="action sorter-action sort-desc" data-role="direction-switcher" data-value="asc"> - <span><?= /* @escapeNotVerified */ __('Set Ascending Direction') ?></span> + <?php if ($block->getCurrentDirection() == 'desc') :?> + <a title="<?= $block->escapeHtmlAttr(__('Set Ascending Direction')) ?>" + href="#" + class="action sorter-action sort-desc" + data-role="direction-switcher" + data-value="asc"> + <span><?= $block->escapeHtml(__('Set Ascending Direction')) ?></span> </a> - <?php else: ?> - <a title="<?= /* @escapeNotVerified */ __('Set Descending Direction') ?>" href="#" class="action sorter-action sort-asc" data-role="direction-switcher" data-value="desc"> - <span><?= /* @escapeNotVerified */ __('Set Descending Direction') ?></span> + <?php else :?> + <a title="<?= $block->escapeHtmlAttr(__('Set Descending Direction')) ?>" + href="#" + class="action sorter-action sort-asc" + data-role="direction-switcher" + data-value="desc"> + <span><?= $block->escapeHtml(__('Set Descending Direction')) ?></span> </a> <?php endif; ?> </div> diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/list/toolbar/viewmode.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/list/toolbar/viewmode.phtml index 366dfba71b0d1..955897f315d6f 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/list/toolbar/viewmode.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/list/toolbar/viewmode.phtml @@ -3,9 +3,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <?php /** @@ -13,32 +10,31 @@ * * @var $block \Magento\Catalog\Block\Product\ProductList\Toolbar */ -use Magento\Catalog\Model\Product\ProductList\Toolbar; ?> -<?php if ($block->isEnabledViewSwitcher()): ?> -<div class="modes"> - <?php $_modes = $block->getModes(); ?> - <?php if ($_modes && count($_modes) > 1): ?> - <strong class="modes-label" id="modes-label"><?= /* @escapeNotVerified */ __('View as') ?></strong> - <?php foreach ($block->getModes() as $_code => $_label): ?> - <?php if ($block->isModeActive($_code)): ?> - <strong title="<?= /* @escapeNotVerified */ $_label ?>" - class="modes-mode active mode-<?= /* @escapeNotVerified */ strtolower($_code) ?>" - data-value="<?= /* @escapeNotVerified */ strtolower($_code) ?>"> - <span><?= /* @escapeNotVerified */ $_label ?></span> - </strong> - <?php else: ?> - <a class="modes-mode mode-<?= /* @escapeNotVerified */ strtolower($_code) ?>" - title="<?= /* @escapeNotVerified */ $_label ?>" - href="#" - data-role="mode-switcher" - data-value="<?= /* @escapeNotVerified */ strtolower($_code) ?>" - id="mode-<?= /* @escapeNotVerified */ strtolower($_code) ?>" - aria-labelledby="modes-label mode-<?= /* @escapeNotVerified */ strtolower($_code) ?>"> - <span><?= /* @escapeNotVerified */ $_label ?></span> - </a> - <?php endif; ?> - <?php endforeach; ?> - <?php endif; ?> -</div> +<?php if ($block->isEnabledViewSwitcher()) :?> + <div class="modes"> + <?php $_modes = $block->getModes(); ?> + <?php if ($_modes && count($_modes) > 1) :?> + <strong class="modes-label" id="modes-label"><?= $block->escapeHtml(__('View as')) ?></strong> + <?php foreach ($block->getModes() as $_code => $_label) :?> + <?php if ($block->isModeActive($_code)) :?> + <strong title="<?= $block->escapeHtmlAttr($_label) ?>" + class="modes-mode active mode-<?= $block->escapeHtmlAttr(strtolower($_code)) ?>" + data-value="<?= $block->escapeHtmlAttr(strtolower($_code)) ?>"> + <span><?= $block->escapeHtml($_label) ?></span> + </strong> + <?php else :?> + <a class="modes-mode mode-<?= $block->escapeHtmlAttr(strtolower($_code)) ?>" + title="<?= $block->escapeHtmlAttr($_label) ?>" + href="#" + data-role="mode-switcher" + data-value="<?= $block->escapeHtmlAttr(strtolower($_code)) ?>" + id="mode-<?= $block->escapeHtmlAttr(strtolower($_code)) ?>" + aria-labelledby="modes-label mode-<?= $block->escapeHtmlAttr(strtolower($_code)) ?>"> + <span><?= $block->escapeHtml($_label) ?></span> + </a> + <?php endif; ?> + <?php endforeach; ?> + <?php endif; ?> + </div> <?php endif; ?> diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/listing.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/listing.phtml index f2d5e40cca4e5..b776fd4f7e193 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/listing.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/listing.phtml @@ -3,28 +3,29 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <?php +// phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis +// phpcs:disable Magento2.Files.LineLength.MaxExceeded +// phpcs:disable Magento2.Security.LanguageConstruct.DirectOutput + /** * Product list template * - * @see \Magento\Catalog\Block\Product\ListProduct + * @var $block \Magento\Catalog\Block\Product\ListProduct */ ?> <?php $start = microtime(true); $_productCollection = $block->getLoadedProductCollection(); -$_helper = $this->helper('Magento\Catalog\Helper\Output'); +$_helper = $this->helper(Magento\Catalog\Helper\Output::class); ?> -<?php if (!$_productCollection->count()): ?> -<p class="message note"><?= /* @escapeNotVerified */ __('We can\'t find products matching the selection.') ?></p> -<?php else: ?> -<?= $block->getToolbarHtml() ?> -<?= $block->getAdditionalHtml() ?> -<?php +<?php if (!$_productCollection->count()) :?> + <p class="message note"><?= $block->escapeHtml(__('We can\'t find products matching the selection.')) ?></p> +<?php else :?> + <?= $block->getToolbarHtml() ?> + <?= $block->getAdditionalHtml() ?> + <?php if ($block->getMode() == 'grid') { $viewMode = 'grid'; $image = 'category_page_grid'; @@ -36,65 +37,65 @@ $_helper = $this->helper('Magento\Catalog\Helper\Output'); $showDescription = true; $templateType = \Magento\Catalog\Block\Product\ReviewRendererInterface::FULL_VIEW; } -?> -<div class="products wrapper <?= /* @escapeNotVerified */ $viewMode ?>"> - <ol class="products list items"> - <?php foreach ($_productCollection as $_product): ?> - <li class="item product"> - <div class="product"> - <?php // Product Image ?> - <a href="<?= /* @escapeNotVerified */ $_product->getProductUrl() ?>" class="product photo"> - <?= $block->getImage($_product, $image)->toHtml() ?> - </a> - <div class="product details"> - <?php + ?> + <div class="products wrapper <?= /* @noEscape */ $viewMode ?>"> + <ol class="products list items"> + <?php foreach ($_productCollection as $_product) :?> + <li class="item product"> + <div class="product"> + <?php // Product Image ?> + <a href="<?= $block->escapeUrl($_product->getProductUrl()) ?>" class="product photo"> + <?= $block->getImage($_product, $image)->toHtml() ?> + </a> + <div class="product details"> + <?php - $info = []; - $info['name'] = '<strong class="product name">' - . ' <a href="' . $_product->getProductUrl() . '" title="' - . $block->stripTags($_product->getName(), null, true) . '">' - . $_helper->productAttribute($_product, $_product->getName(), 'name') - . '</a></strong>'; - $info['price'] = $block->getProductPrice($_product); - $info['review'] = $block->getReviewsSummaryHtml($_product, $templateType); + $info = []; + $info['name'] = '<strong class="product name">' + . ' <a href="' . $block->escapeUrl($_product->getProductUrl()) . '" title="' + . $block->stripTags($_product->getName(), null, true) . '">' + . $_helper->productAttribute($_product, $_product->getName(), 'name') + . '</a></strong>'; + $info['price'] = $block->getProductPrice($_product); + $info['review'] = $block->getReviewsSummaryHtml($_product, $templateType); - if ($_product->isSaleable()) { - $info['button'] = '<button type="button" title="' . __('Add to Cart') . '" class="action tocart"' - . ' data-mage-init=\'{ "redirectUrl": { "event": "click", url: "' . $block->getAddToCartUrl($_product) . '"} }\'>' - . '<span>' . __('Add to Cart') . '</span></button>'; - } else { - $info['button'] = $_product->getIsSalable() ? '<div class="stock available"><span>' . __('In stock') . '</span></div>' : - '<div class="stock unavailable"><span>' . __('Out of stock') . '</span></div>'; - } + if ($_product->isSaleable()) { + $info['button'] = '<button type="button" title="' . $block->escapeHtmlAttr(__('Add to Cart')) . '" class="action tocart"' + . ' data-mage-init=\'{ "redirectUrl": { "event": "click", url: "' . $block->escapeUrl($block->getAddToCartUrl($_product)) . '"} }\'>' + . '<span>' . $block->escapeHtml(__('Add to Cart')) . '</span></button>'; + } else { + $info['button'] = $_product->getIsSalable() ? '<div class="stock available"><span>' . $block->escapeHtml(__('In stock')) . '</span></div>' : + '<div class="stock unavailable"><span>' . $block->escapeHtml(__('Out of stock')) . '</span></div>'; + } - $info['links'] = '<div class="product links" data-role="add-to-links">' - . '<a href="#" data-post=\'' . $this->helper('Magento\Wishlist\Helper\Data')->getAddParams($_product) . '\' class="action towishlist" data-action="add-to-wishlist">' - . '<span>' . __('Add to Wish List') . '</span></a>' - . '<a href="' . $block->getAddToCompareUrl($_product) . '" class="action tocompare">' - . '<span>' . __('Add to Compare') . '</span></a></div>'; - $info['actions'] = '<div class="product action">' . $info['button'] . $info['links'] . '</div>'; + $info['links'] = '<div class="product links" data-role="add-to-links">' + . '<a href="#" data-post=\'' . $this->helper(Magento\Wishlist\Helper\Data::class)->getAddParams($_product) . '\' class="action towishlist" data-action="add-to-wishlist">' + . '<span>' . $block->escapeHtml(__('Add to Wish List')) . '</span></a>' + . '<a href="' . $block->escapeUrl($block->getAddToCompareUrl($_product)) . '" class="action tocompare">' + . '<span>' . $block->escapeHtml(__('Add to Compare')) . '</span></a></div>'; + $info['actions'] = '<div class="product action">' . $info['button'] . $info['links'] . '</div>'; - if ($showDescription) { - $info['description'] = '<div class="product description">' - . $_helper->productAttribute($_product, $_product->getShortDescription(), 'short_description') - . ' <a href="' . $_product->getProductUrl() . '" class="action more">' - . __('Learn More') . '</a></div>'; - } else { - $info['description'] = ''; - } + if ($showDescription) { + $info['description'] = '<div class="product description">' + . $_helper->productAttribute($_product, $_product->getShortDescription(), 'short_description') + . ' <a href="' . $block->escapeUrl($_product->getProductUrl()) . '" class="action more">' + . $block->escapeHtml(__('Learn More')) . '</a></div>'; + } else { + $info['description'] = ''; + } - $details = $block->getInfoOrder() ?: ['name','price','review','description','actions']; - foreach ($details as $detail) { - /* @escapeNotVerified */ echo $info[$detail]; - } - ?> + $details = $block->getInfoOrder() ?: ['name','price','review','description','actions']; + foreach ($details as $detail) { + /* @noEscape */ echo $info[$detail]; + } + ?> + </div> </div> - </div> - </li> - <?php endforeach; ?> - </ol> -</div> -<?= $block->getToolbarHtml() ?> + </li> + <?php endforeach; ?> + </ol> + </div> + <?= $block->getToolbarHtml() ?> <?php endif; ?> -<?= /* @escapeNotVerified */ $time_taken = microtime(true) - $start ?> +<?= $time_taken = microtime(true) - $start ?> diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/view/additional.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/view/additional.phtml index 2d89e24cc7aac..316fdb06592e2 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/view/additional.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/view/additional.phtml @@ -4,9 +4,8 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - +/** @var $block \Magento\Catalog\Block\Product\View\Additional */ ?> -<?php foreach ($block->getChildHtmlList() as $_html): ?> - <?= /* @escapeNotVerified */ $_html ?> +<?php foreach ($block->getChildHtmlList() as $_html) :?> + <?= /* @noEscape */ $_html ?> <?php endforeach; ?> diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/view/addto.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/view/addto.phtml index 0893cfab0bbf8..1924175764555 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/view/addto.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/view/addto.phtml @@ -4,8 +4,6 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - /** @var $block \Magento\Catalog\Block\Product\View*/ ?> <div class="product-addto-links" data-role="add-to-links"> diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/view/addto/compare.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/view/addto/compare.phtml index 194a472d81d58..9183e65181c48 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/view/addto/compare.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/view/addto/compare.phtml @@ -4,15 +4,13 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - /** @var $block \Magento\Catalog\Block\Product\View\Addto\Compare */ ?> <?php $viewModel = $block->getData('addToCompareViewModel'); ?> -<?php if ($viewModel->isAvailableForCompare($block->getProduct())): ?> -<a href="#" data-post='<?= /* @escapeNotVerified */ $block->getPostDataParams() ?>' +<?php if ($viewModel->isAvailableForCompare($block->getProduct())) :?> +<a href="#" data-post='<?= /* @noEscape */ $block->getPostDataParams() ?>' data-role="add-to-links" - class="action tocompare"><span><?= /* @escapeNotVerified */ __('Add to Compare') ?></span></a> + class="action tocompare"><span><?= $block->escapeHtml(__('Add to Compare')) ?></span></a> <?php endif; ?> diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/view/addtocart.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/view/addtocart.phtml index 71452a2d65e97..f15824595f0ba 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/view/addtocart.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/view/addtocart.phtml @@ -4,25 +4,23 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - /** @var $block \Magento\Catalog\Block\Product\View */ ?> <?php $_product = $block->getProduct(); ?> <?php $buttonTitle = __('Add to Cart'); ?> -<?php if ($_product->isSaleable()): ?> +<?php if ($_product->isSaleable()) :?> <div class="box-tocart"> <div class="fieldset"> - <?php if ($block->shouldRenderQuantity()): ?> + <?php if ($block->shouldRenderQuantity()) :?> <div class="field qty"> - <label class="label" for="qty"><span><?= /* @escapeNotVerified */ __('Qty') ?></span></label> + <label class="label" for="qty"><span><?= $block->escapeHtml(__('Qty')) ?></span></label> <div class="control"> <input type="number" name="qty" id="qty" min="0" - value="<?= /* @escapeNotVerified */ $block->getProductDefaultQty() * 1 ?>" - title="<?= /* @escapeNotVerified */ __('Qty') ?>" + value="<?= $block->getProductDefaultQty() * 1 ?>" + title="<?= $block->escapeHtmlAttr(__('Qty')) ?>" class="input-text qty" data-validate="<?= $block->escapeHtml(json_encode($block->getQuantityValidators())) ?>" /> @@ -31,10 +29,10 @@ <?php endif; ?> <div class="actions"> <button type="submit" - title="<?= /* @escapeNotVerified */ $buttonTitle ?>" + title="<?= $block->escapeHtmlAttr($buttonTitle) ?>" class="action primary tocart" id="product-addtocart-button" disabled> - <span><?= /* @escapeNotVerified */ $buttonTitle ?></span> + <span><?= $block->escapeHtml($buttonTitle) ?></span> </button> <?= $block->getChildHtml('', true) ?> </div> diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/view/attribute.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/view/attribute.phtml index 86f97cf6f6aaf..2e022a5df14ed 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/view/attribute.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/view/attribute.phtml @@ -4,16 +4,16 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile +// phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis /** * Product view template * - * @see \Magento\Catalog\Block\Product\View\Description + * @var $block \Magento\Catalog\Block\Product\View\Description */ ?> <?php -$_helper = $this->helper('Magento\Catalog\Helper\Output'); +$_helper = $this->helper(Magento\Catalog\Helper\Output::class); $_product = $block->getProduct(); $_call = $block->getAtCall(); $_code = $block->getAtCode(); @@ -32,15 +32,19 @@ if ($_attributeLabel && $_attributeLabel == 'default') { $_attributeLabel = $_product->getResource()->getAttribute($_code)->getStoreLabel(); } if ($_attributeType && $_attributeType == 'text') { - $_attributeValue = ($_helper->productAttribute($_product, $_product->$_call(), $_code)) ? $_product->getAttributeText($_code) : ''; + $_attributeValue = ($_helper->productAttribute($_product, $_product->$_call(), $_code)) + ? $_product->getAttributeText($_code) + : ''; } else { $_attributeValue = $_helper->productAttribute($_product, $_product->$_call(), $_code); } ?> -<?php if ($_attributeValue): ?> -<div class="product attribute <?= /* @escapeNotVerified */ $_className ?>"> - <?php if ($renderLabel): ?><strong class="type"><?= /* @escapeNotVerified */ $_attributeLabel ?></strong><?php endif; ?> - <div class="value" <?= /* @escapeNotVerified */ $_attributeAddAttribute ?>><?= /* @escapeNotVerified */ $_attributeValue ?></div> +<?php if ($_attributeValue) :?> +<div class="product attribute <?= $block->escapeHtmlAttr($_className) ?>"> + <?php if ($renderLabel) :?> + <strong class="type"><?= $block->escapeHtml($_attributeLabel) ?></strong> + <?php endif; ?> + <div class="value" <?= /* @noEscape */ $_attributeAddAttribute ?>><?= /* @noEscape */ $_attributeValue ?></div> </div> <?php endif; ?> diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/view/attributes.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/view/attributes.phtml index 1c4a37fedebe3..a4f0fb3efab9e 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/view/attributes.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/view/attributes.phtml @@ -4,7 +4,7 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile +// phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis /** * Product additional attributes template @@ -13,18 +13,18 @@ */ ?> <?php - $_helper = $this->helper('Magento\Catalog\Helper\Output'); + $_helper = $this->helper(Magento\Catalog\Helper\Output::class); $_product = $block->getProduct(); ?> -<?php if ($_additional = $block->getAdditionalData()): ?> +<?php if ($_additional = $block->getAdditionalData()) :?> <div class="additional-attributes-wrapper table-wrapper"> <table class="data table additional-attributes" id="product-attribute-specs-table"> - <caption class="table-caption"><?= /* @escapeNotVerified */ __('More Information') ?></caption> + <caption class="table-caption"><?= $block->escapeHtml(__('More Information')) ?></caption> <tbody> - <?php foreach ($_additional as $_data): ?> + <?php foreach ($_additional as $_data) :?> <tr> <th class="col label" scope="row"><?= $block->escapeHtml($_data['label']) ?></th> - <td class="col data" data-th="<?= $block->escapeHtml($_data['label']) ?>"><?= /* @escapeNotVerified */ $_helper->productAttribute($_product, $_data['value'], $_data['code']) ?></td> + <td class="col data" data-th="<?= $block->escapeHtmlAttr($_data['label']) ?>"><?= /* @noEscape */ $_helper->productAttribute($_product, $_data['value'], $_data['code']) ?></td> </tr> <?php endforeach; ?> </tbody> diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/view/counter.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/view/counter.phtml index 4414214f99a6e..a4aa675b2c346 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/view/counter.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/view/counter.phtml @@ -13,7 +13,7 @@ { "*": { "Magento_Catalog/js/product/view/provider": { - "data": <?= /* @escapeNotVerified */ $block->getCurrentProductData() ?> + "data": <?= /* @noEscape */ $block->getCurrentProductData() ?> } } } diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/view/description.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/view/description.phtml index b5cdd1a2a31ba..c08c4d771b34a 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/view/description.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/view/description.phtml @@ -4,7 +4,7 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile +// phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis /** * Product description template @@ -12,4 +12,8 @@ * @var $block \Magento\Catalog\Block\Product\View\Description */ ?> -<?= /* @escapeNotVerified */ $this->helper('Magento\Catalog\Helper\Output')->productAttribute($block->getProduct(), $block->getProduct()->getDescription(), 'description') ?> +<?= /* @noEscape */ $this->helper(Magento\Catalog\Helper\Output::class)->productAttribute( + $block->getProduct(), + $block->getProduct()->getDescription(), + 'description' +) ?> diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/view/details.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/view/details.phtml index 57eabbf1d8c8a..d25b19ee217f0 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/view/details.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/view/details.phtml @@ -4,36 +4,34 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - /** @var \Magento\Catalog\Block\Product\View\Details $block */ ?> -<?php if ($detailedInfoGroup = $block->getGroupSortedChildNames('detailed_info', 'getChildHtml')):?> +<?php if ($detailedInfoGroup = $block->getGroupSortedChildNames('detailed_info', 'getChildHtml')) :?> <div class="product info detailed"> <?php $layout = $block->getLayout(); ?> <div class="product data items" data-mage-init='{"tabs":{"openedState":"active"}}'> - <?php foreach ($detailedInfoGroup as $name):?> + <?php foreach ($detailedInfoGroup as $name) :?> <?php - $html = $layout->renderElement($name); - if (!trim($html)) { - continue; - } - $alias = $layout->getElementAlias($name); - $label = $block->getChildData($alias, 'title'); + $html = $layout->renderElement($name); + if (!trim($html)) { + continue; + } + $alias = $layout->getElementAlias($name); + $label = $block->getChildData($alias, 'title'); ?> <div class="data item title" - data-role="collapsible" id="tab-label-<?= /* @escapeNotVerified */ $alias ?>"> + data-role="collapsible" id="tab-label-<?= $block->escapeHtmlAttr($alias) ?>"> <a class="data switch" tabindex="-1" data-toggle="trigger" - href="#<?= /* @escapeNotVerified */ $alias ?>" - id="tab-label-<?= /* @escapeNotVerified */ $alias ?>-title"> - <?= /* @escapeNotVerified */ $label ?> + href="#<?= $block->escapeUrl($alias) ?>" + id="tab-label-<?= $block->escapeHtmlAttr($alias) ?>-title"> + <?= $block->escapeHtml($label) ?> </a> </div> - <div class="data item content" - aria-labelledby="tab-label-<?= /* @escapeNotVerified */ $alias ?>-title" id="<?= /* @escapeNotVerified */ $alias ?>" data-role="content"> - <?= /* @escapeNotVerified */ $html ?> + <div class="data item content" + aria-labelledby="tab-label-<?= $block->escapeHtmlAttr($alias) ?>-title" id="<?= $block->escapeHtmlAttr($alias) ?>" data-role="content"> + <?= /* @noEscape */ $html ?> </div> <?php endforeach;?> </div> diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/view/form.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/view/form.phtml index 9c5cce7865532..8d298aec9f1cb 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/view/form.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/view/form.phtml @@ -4,7 +4,7 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile +// phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis /** * Product view template @@ -12,28 +12,28 @@ * @var $block \Magento\Catalog\Block\Product\View */ ?> -<?php $_helper = $this->helper('Magento\Catalog\Helper\Output'); ?> +<?php $_helper = $this->helper(Magento\Catalog\Helper\Output::class); ?> <?php $_product = $block->getProduct(); ?> <div class="product-add-form"> <form data-product-sku="<?= $block->escapeHtml($_product->getSku()) ?>" - action="<?= /* @NoEscape */ $block->getSubmitUrl($_product) ?>" method="post" - id="product_addtocart_form"<?php if ($_product->getOptions()): ?> enctype="multipart/form-data"<?php endif; ?>> - <input type="hidden" name="product" value="<?= /* @escapeNotVerified */ $_product->getId() ?>" /> + action="<?= $block->escapeUrl($block->getSubmitUrl($_product)) ?>" method="post" + id="product_addtocart_form"<?php if ($_product->getOptions()) :?> enctype="multipart/form-data"<?php endif; ?>> + <input type="hidden" name="product" value="<?= (int)$_product->getId() ?>" /> <input type="hidden" name="selected_configurable_option" value="" /> <input type="hidden" name="related_product" id="related-products-field" value="" /> - <input type="hidden" name="item" value="<?= /* @noEscape */ $block->getRequest()->getParam('id') ?>" /> + <input type="hidden" name="item" value="<?= $block->escapeHtmlAttr($block->getRequest()->getParam('id')) ?>" /> <?= $block->getBlockHtml('formkey') ?> <?= $block->getChildHtml('form_top') ?> - <?php if (!$block->hasOptions()):?> + <?php if (!$block->hasOptions()) :?> <?= $block->getChildHtml('product_info_form_content') ?> - <?php else:?> - <?php if ($_product->isSaleable() && $block->getOptionsContainer() == 'container1'):?> + <?php else :?> + <?php if ($_product->isSaleable() && $block->getOptionsContainer() == 'container1') :?> <?= $block->getChildChildHtml('options_container') ?> <?php endif;?> <?php endif; ?> - <?php if ($_product->isSaleable() && $block->hasOptions() && $block->getOptionsContainer() == 'container2'):?> + <?php if ($_product->isSaleable() && $block->hasOptions() && $block->getOptionsContainer() == 'container2') :?> <?= $block->getChildChildHtml('options_container') ?> <?php endif;?> <?= $block->getChildHtml('form_bottom') ?> @@ -52,6 +52,6 @@ return !$(elem).find('.price-from').length; }); - priceBoxes.priceBox({'priceConfig': <?= /* @escapeNotVerified */ $block->getJsonConfig() ?>}); + priceBoxes.priceBox({'priceConfig': <?= /* @noEscape */ $block->getJsonConfig() ?>}); }); </script> diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/view/gallery.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/view/gallery.phtml index 1f06b90758d0b..4b33864aef47a 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/view/gallery.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/view/gallery.phtml @@ -4,8 +4,6 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - /** * Product media data template * @@ -14,19 +12,19 @@ ?> <?php - $images = $block->getGalleryImages()->getItems(); - $mainImage = current(array_filter($images, function ($img) use ($block) { - return $block->isMainImage($img); - })); +$images = $block->getGalleryImages()->getItems(); +$mainImage = current(array_filter($images, function ($img) use ($block) { + return $block->isMainImage($img); +})); - if (!empty($images) && empty($mainImage)) { - $mainImage = $block->getGalleryImages()->getFirstItem(); - } +if (!empty($images) && empty($mainImage)) { + $mainImage = $block->getGalleryImages()->getFirstItem(); +} - $helper = $block->getData('imageHelper'); - $mainImageData = $mainImage ? - $mainImage->getData('medium_image_url') : - $helper->getDefaultPlaceholderUrl('image'); +$helper = $block->getData('imageHelper'); +$mainImageData = $mainImage ? + $mainImage->getData('medium_image_url') : + $helper->getDefaultPlaceholderUrl('image'); ?> @@ -43,11 +41,11 @@ "[data-gallery-role=gallery-placeholder]": { "mage/gallery/gallery": { "mixins":["magnifier/magnify"], - "magnifierOpts": <?= /* @escapeNotVerified */ $block->getMagnifier() ?>, - "data": <?= /* @escapeNotVerified */ $block->getGalleryImagesJson() ?>, + "magnifierOpts": <?= /* @noEscape */ $block->getMagnifier() ?>, + "data": <?= /* @noEscape */ $block->getGalleryImagesJson() ?>, "options": <?= /* @noEscape */ $block->getGalleryOptions()->getOptionsJson() ?>, "fullscreen": <?= /* @noEscape */ $block->getGalleryOptions()->getFSOptionsJson() ?>, - "breakpoints": <?= /* @escapeNotVerified */ $block->getBreakpoints() ?> + "breakpoints": <?= /* @noEscape */ $block->getBreakpoints() ?> } } } diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/view/mailto.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/view/mailto.phtml index d52b594ededdf..f57c9b68ddbd2 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/view/mailto.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/view/mailto.phtml @@ -4,11 +4,10 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - +// phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis ?> <?php $_product = $block->getProduct() ?> -<?php if ($block->canEmailToFriend()): ?> - <a href="<?= /* @escapeNotVerified */ $this->helper('Magento\Catalog\Helper\Product')->getEmailToFriendUrl($_product) ?>" - class="action mailto friend"><span><?= /* @escapeNotVerified */ __('Email') ?></span></a> +<?php if ($block->canEmailToFriend()) :?> + <a href="<?= $block->escapeUrl($this->helper(Magento\Catalog\Helper\Product::class)->getEmailToFriendUrl($_product)) ?>" + class="action mailto friend"><span><?= $block->escapeHtml(__('Email')) ?></span></a> <?php endif; ?> diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/view/opengraph/currency.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/view/opengraph/currency.phtml index 87655797f40e5..7f14b71a60c7a 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/view/opengraph/currency.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/view/opengraph/currency.phtml @@ -4,8 +4,7 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - /** @var $block \Magento\Directory\Block\Currency */ ?> -<meta property="product:price:currency" content="<?= /* @escapeNotVerified */ $block->stripTags($block->getCurrentCurrencyCode()) ?>"/> +<meta property="product:price:currency" + content="<?= /* @noEscape */ $block->stripTags($block->getCurrentCurrencyCode()) ?>"/> diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/view/opengraph/general.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/view/opengraph/general.phtml index 40f86c7e68d6c..eb2bde647f9b1 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/view/opengraph/general.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/view/opengraph/general.phtml @@ -4,17 +4,18 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - /** @var $block \Magento\Catalog\Block\Product\View */ ?> <meta property="og:type" content="product" /> -<meta property="og:title" content="<?= $block->escapeHtmlAttr($block->stripTags($block->getProduct()->getName())) ?>" /> -<meta property="og:image" content="<?= $block->escapeUrl($block->getImage($block->getProduct(), 'product_base_image')->getImageUrl()) ?>" /> -<meta property="og:description" content="<?= $block->escapeHtmlAttr($block->stripTags($block->getProduct()->getShortDescription())) ?>" /> +<meta property="og:title" + content="<?= /* @noEscape */ $block->stripTags($block->getProduct()->getName()) ?>" /> +<meta property="og:image" + content="<?= $block->escapeUrl($block->getImage($block->getProduct(), 'product_base_image')->getImageUrl()) ?>" /> +<meta property="og:description" + content="<?= /* @noEscape */ $block->stripTags($block->getProduct()->getShortDescription()) ?>" /> <meta property="og:url" content="<?= $block->escapeUrl($block->getProduct()->getProductUrl()) ?>" /> -<?php if ($priceAmount = $block->getProduct()->getPriceInfo()->getPrice(\Magento\Catalog\Pricing\Price\FinalPrice::PRICE_CODE)->getAmount()):?> - <meta property="product:price:amount" content="<?= /* @escapeNotVerified */ $priceAmount ?>"/> +<?php if ($priceAmount = $block->getProduct()->getPriceInfo()->getPrice(\Magento\Catalog\Pricing\Price\FinalPrice::PRICE_CODE)->getAmount()) :?> + <meta property="product:price:amount" content="<?= $block->escapeHtmlAttr($priceAmount) ?>"/> <?= $block->getChildHtml('meta.currency') ?> <?php endif;?> diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/view/options.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/view/options.phtml index 3ebfa76860950..d9a0c845b9f83 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/view/options.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/view/options.phtml @@ -4,26 +4,24 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - /* @var $block \Magento\Catalog\Block\Product\View\Options */ ?> <?php $_options = $block->decorateArray($block->getOptions()) ?> <?php $_productId = $block->getProduct()->getId() ?> -<?php if (count($_options)):?> +<?php if (count($_options)) :?> <script type="text/x-magento-init"> { "#product_addtocart_form": { "priceOptions": { - "optionConfig": <?= /* @escapeNotVerified */ $block->getJsonConfig() ?>, + "optionConfig": <?= /* @noEscape */ $block->getJsonConfig() ?>, "controlContainer": ".field", "priceHolderSelector": "[data-product-id='<?= $block->escapeHtml($_productId) ?>'][data-role=priceBox]" } } } </script> - <?php foreach ($_options as $_option): ?> + <?php foreach ($_options as $_option) :?> <?= $block->getOptionHtml($_option) ?> <?php endforeach; ?> <?php endif; ?> diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/view/options/type/date.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/view/options/type/date.phtml index 66895fa1eabf9..b7cd64277fe40 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/view/options/type/date.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/view/options/type/date.phtml @@ -3,46 +3,43 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <?php /* @var $block \Magento\Catalog\Block\Product\View\Options\Type\Date */ ?> <?php $_option = $block->getOption() ?> -<?php $_optionId = $_option->getId() ?> +<?php $_optionId = $block->escapeHtmlAttr($_option->getId()) ?> <?php $class = ($_option->getIsRequire()) ? ' required' : ''; ?> -<div class="field date<?= /* @escapeNotVerified */ $class ?>" +<div class="field date<?= /* @noEscape */ $class ?>" data-mage-init='{"priceOptionDate":{"fromSelector":"#product_addtocart_form"}}'> - <fieldset class="fieldset fieldset-product-options-inner<?= /* @escapeNotVerified */ $class ?>"> + <fieldset class="fieldset fieldset-product-options-inner<?= /* @noEscape */ $class ?>"> <legend class="legend"> <span><?= $block->escapeHtml($_option->getTitle()) ?></span> - <?= /* @escapeNotVerified */ $block->getFormattedPrice() ?> + <?= /* @noEscape */ $block->getFormattedPrice() ?> </legend> <div class="control"> <?php if ($_option->getType() == \Magento\Catalog\Api\Data\ProductCustomOptionInterface::OPTION_TYPE_DATE_TIME - || $_option->getType() == \Magento\Catalog\Api\Data\ProductCustomOptionInterface::OPTION_TYPE_DATE): ?> + || $_option->getType() == \Magento\Catalog\Api\Data\ProductCustomOptionInterface::OPTION_TYPE_DATE) :?> <?= $block->getDateHtml() ?> <?php endif; ?> <?php if ($_option->getType() == \Magento\Catalog\Api\Data\ProductCustomOptionInterface::OPTION_TYPE_DATE_TIME - || $_option->getType() == \Magento\Catalog\Api\Data\ProductCustomOptionInterface::OPTION_TYPE_TIME): ?> + || $_option->getType() == \Magento\Catalog\Api\Data\ProductCustomOptionInterface::OPTION_TYPE_TIME) :?> <?= $block->getTimeHtml() ?> <?php endif; ?> - <?php if ($_option->getIsRequire()): ?> + <?php if ($_option->getIsRequire()) :?> <input type="hidden" - name="validate_datetime_<?= /* @escapeNotVerified */ $_optionId ?>" - class="validate-datetime-<?= /* @escapeNotVerified */ $_optionId ?>" + name="validate_datetime_<?= /* @noEscape */ $_optionId ?>" + class="validate-datetime-<?= /* @noEscape */ $_optionId ?>" value="" - data-validate="{'validate-required-datetime':<?= /* @escapeNotVerified */ $_optionId ?>}"/> - <?php else: ?> + data-validate="{'validate-required-datetime':<?= /* @noEscape */ $_optionId ?>}"/> + <?php else :?> <input type="hidden" - name="validate_datetime_<?= /* @escapeNotVerified */ $_optionId ?>" - class="validate-datetime-<?= /* @escapeNotVerified */ $_optionId ?>" + name="validate_datetime_<?= /* @noEscape */ $_optionId ?>" + class="validate-datetime-<?= /* @noEscape */ $_optionId ?>" value="" - data-validate="{'validate-optional-datetime':<?= /* @escapeNotVerified */ $_optionId ?>}"/> + data-validate="{'validate-optional-datetime':<?= /* @noEscape */ $_optionId ?>}"/> <?php endif; ?> <script type="text/x-magento-init"> { diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/view/options/type/default.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/view/options/type/default.phtml index 2006bf6e9f414..c25dab8b70a5c 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/view/options/type/default.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/view/options/type/default.phtml @@ -3,9 +3,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <?php $_option = $block->getOption() ?> <div class="field"> diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/view/options/type/file.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/view/options/type/file.phtml index adb729c6d86ec..e83e55ad2a03c 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/view/options/type/file.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/view/options/type/file.phtml @@ -3,65 +3,62 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <?php /* @var $block \Magento\Catalog\Block\Product\View\Options\Type\File */ ?> <?php $_option = $block->getOption(); ?> <?php $_fileInfo = $block->getFileInfo(); ?> <?php $_fileExists = $_fileInfo->hasData(); ?> -<?php $_fileName = 'options_' . $_option->getId() . '_file'; ?> +<?php $_fileName = 'options_' . $block->escapeHtmlAttr($_option->getId()) . '_file'; ?> <?php $_fieldNameAction = $_fileName . '_action'; ?> <?php $_fieldValueAction = $_fileExists ? 'save_old' : 'save_new'; ?> <?php $_fileNamed = $_fileName . '_name'; ?> <?php $class = ($_option->getIsRequire()) ? ' required' : ''; ?> -<div class="field file<?= /* @escapeNotVerified */ $class ?>"> +<div class="field file<?= /* @noEscape */ $class ?>"> <label class="label" for="<?= /* @noEscape */ $_fileName ?>" id="<?= /* @noEscape */ $_fileName ?>-label"> <span><?= $block->escapeHtml($_option->getTitle()) ?></span> - <?= /* @escapeNotVerified */ $block->getFormattedPrice() ?> + <?= /* @noEscape */ $block->getFormattedPrice() ?> </label> - <?php if ($_fileExists): ?> + <?php if ($_fileExists) :?> <div class="control"> <span class="<?= /* @noEscape */ $_fileNamed ?>"><?= $block->escapeHtml($_fileInfo->getTitle()) ?></span> <a href="javascript:void(0)" class="label" id="change-<?= /* @noEscape */ $_fileName ?>" > - <?= /* @escapeNotVerified */ __('Change') ?> + <?= $block->escapeHtml(__('Change')) ?> </a> - <?php if (!$_option->getIsRequire()): ?> - <input type="checkbox" id="delete-<?= /* @escapeNotVerified */ $_fileName ?>" /> - <span class="label"><?= /* @escapeNotVerified */ __('Delete') ?></span> + <?php if (!$_option->getIsRequire()) :?> + <input type="checkbox" id="delete-<?= /* @noEscape */ $_fileName ?>" /> + <span class="label"><?= $block->escapeHtml(__('Delete')) ?></span> <?php endif; ?> </div> <?php endif; ?> - <div class="control" id="input-box-<?= /* @escapeNotVerified */ $_fileName ?>" + <div class="control" id="input-box-<?= /* @noEscape */ $_fileName ?>" data-mage-init='{"priceOptionFile":{ "fileName":"<?= /* @noEscape */ $_fileName ?>", "fileNamed":"<?= /* @noEscape */ $_fileNamed ?>", - "fieldNameAction":"<?= /* @escapeNotVerified */ $_fieldNameAction ?>", - "changeFileSelector":"#change-<?= /* @escapeNotVerified */ $_fileName ?>", - "deleteFileSelector":"#delete-<?= /* @escapeNotVerified */ $_fileName ?>"} + "fieldNameAction":"<?= /* @noEscape */ $_fieldNameAction ?>", + "changeFileSelector":"#change-<?= /* @noEscape */ $_fileName ?>", + "deleteFileSelector":"#delete-<?= /* @noEscape */ $_fileName ?>"} }' <?= $_fileExists ? 'style="display:none"' : '' ?>> <input type="file" - name="<?= /* @escapeNotVerified */ $_fileName ?>" - id="<?= /* @escapeNotVerified */ $_fileName ?>" + name="<?= /* @noEscape */ $_fileName ?>" + id="<?= /* @noEscape */ $_fileName ?>" class="product-custom-option<?= $_option->getIsRequire() ? ' required' : '' ?>" - <?= $_fileExists ? 'disabled="disabled"' : '' ?> /> - <input type="hidden" name="<?= /* @escapeNotVerified */ $_fieldNameAction ?>" value="<?= /* @escapeNotVerified */ $_fieldValueAction ?>" /> - <?php if ($_option->getFileExtension()): ?> + <?= $_fileExists ? 'disabled="disabled"' : '' ?> /> + <input type="hidden" name="<?= /* @noEscape */ $_fieldNameAction ?>" value="<?= /* @noEscape */ $_fieldValueAction ?>" /> + <?php if ($_option->getFileExtension()) :?> <p class="note"> - <?= /* @escapeNotVerified */ __('Compatible file extensions to upload') ?>: <strong><?= /* @escapeNotVerified */ $_option->getFileExtension() ?></strong> + <?= $block->escapeHtml(__('Compatible file extensions to upload')) ?>: <strong><?= $block->escapeHtml($_option->getFileExtension()) ?></strong> </p> <?php endif; ?> - <?php if ($_option->getImageSizeX() > 0): ?> + <?php if ($_option->getImageSizeX() > 0) :?> <p class="note"> - <?= /* @escapeNotVerified */ __('Maximum image width') ?>: <strong><?= /* @escapeNotVerified */ $_option->getImageSizeX() ?> <?= /* @escapeNotVerified */ __('px.') ?></strong> + <?= $block->escapeHtml(__('Maximum image width')) ?>: <strong><?= (int)$_option->getImageSizeX() ?> <?= $block->escapeHtml(__('px.')) ?></strong> </p> <?php endif; ?> - <?php if ($_option->getImageSizeY() > 0): ?> + <?php if ($_option->getImageSizeY() > 0) :?> <p class="note"> - <?= /* @escapeNotVerified */ __('Maximum image height') ?>: <strong><?= /* @escapeNotVerified */ $_option->getImageSizeY() ?> <?= /* @escapeNotVerified */ __('px.') ?></strong> + <?= $block->escapeHtml(__('Maximum image height')) ?>: <strong><?= (int)$_option->getImageSizeY() ?> <?= $block->escapeHtml(__('px.')) ?></strong> </p> <?php endif; ?> </div> diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/view/options/type/select.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/view/options/type/select.phtml index 980b78f917cf2..c4c1d24423bb0 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/view/options/type/select.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/view/options/type/select.phtml @@ -3,9 +3,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <?php /* @var $block \Magento\Catalog\Block\Product\View\Options\Type\Select */ ?> @@ -13,15 +10,15 @@ $_option = $block->getOption(); $class = ($_option->getIsRequire()) ? ' required' : ''; ?> -<div class="field<?= /* @escapeNotVerified */ $class ?>"> - <label class="label" for="select_<?= /* @escapeNotVerified */ $_option->getId() ?>"> +<div class="field<?= /* @noEscape */ $class ?>"> + <label class="label" for="select_<?= $block->escapeHtmlAttr($_option->getId()) ?>"> <span><?= $block->escapeHtml($_option->getTitle()) ?></span> </label> <div class="control"> <?= $block->getValuesHtml() ?> - <?php if ($_option->getIsRequire()): ?> - <?php if ($_option->getType() == \Magento\Catalog\Api\Data\ProductCustomOptionInterface::OPTION_TYPE_RADIO || $_option->getType() == \Magento\Catalog\Api\Data\ProductCustomOptionInterface::OPTION_TYPE_CHECKBOX): ?> - <span id="options-<?= /* @escapeNotVerified */ $_option->getId() ?>-container"></span> + <?php if ($_option->getIsRequire()) :?> + <?php if ($_option->getType() == \Magento\Catalog\Api\Data\ProductCustomOptionInterface::OPTION_TYPE_RADIO || $_option->getType() == \Magento\Catalog\Api\Data\ProductCustomOptionInterface::OPTION_TYPE_CHECKBOX) :?> + <span id="options-<?= $block->escapeHtmlAttr($_option->getId()) ?>-container"></span> <?php endif; ?> <?php endif;?> </div> diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/view/options/type/text.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/view/options/type/text.phtml index a04e366a43a2d..dd4c000d1f338 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/view/options/type/text.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/view/options/type/text.phtml @@ -3,9 +3,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <?php /* @var $block \Magento\Catalog\Block\Product\View\Options\Type\Text */ ?> <?php @@ -15,14 +12,14 @@ $class = ($_option->getIsRequire()) ? ' required' : ''; <div class="field<?php if ($_option->getType() == \Magento\Catalog\Api\Data\ProductCustomOptionInterface::OPTION_TYPE_AREA) { echo ' textarea'; -} ?><?= /* @escapeNotVerified */ $class ?>"> - <label class="label" for="options_<?= /* @escapeNotVerified */ $_option->getId() ?>_text"> +} ?><?= /* @noEscape */ $class ?>"> + <label class="label" for="options_<?= $block->escapeHtmlAttr($_option->getId()) ?>_text"> <span><?= $block->escapeHtml($_option->getTitle()) ?></span> - <?= /* @escapeNotVerified */ $block->getFormattedPrice() ?> + <?= /* @noEscape */ $block->getFormattedPrice() ?> </label> <div class="control"> - <?php if ($_option->getType() == \Magento\Catalog\Api\Data\ProductCustomOptionInterface::OPTION_TYPE_FIELD): ?> + <?php if ($_option->getType() == \Magento\Catalog\Api\Data\ProductCustomOptionInterface::OPTION_TYPE_FIELD) :?> <?php $_textValidate = null; if ($_option->getIsRequire()) { $_textValidate['required'] = true; @@ -33,15 +30,15 @@ $class = ($_option->getIsRequire()) ? ' required' : ''; $_textValidate['validate-no-utf8mb4-characters'] = true; ?> <input type="text" - id="options_<?= /* @escapeNotVerified */ $_option->getId() ?>_text" + id="options_<?= $block->escapeHtmlAttr($_option->getId()) ?>_text" class="input-text product-custom-option" - <?php if (!empty($_textValidate)) {?> - data-validate="<?= $block->escapeHtml(json_encode($_textValidate)) ?>" - <?php } ?> - name="options[<?= /* @escapeNotVerified */ $_option->getId() ?>]" - data-selector="options[<?= /* @escapeNotVerified */ $_option->getId() ?>]" + <?php if (!empty($_textValidate)) {?> + data-validate="<?= $block->escapeHtml(json_encode($_textValidate)) ?>" + <?php } ?> + name="options[<?= $block->escapeHtmlAttr($_option->getId()) ?>]" + data-selector="options[<?= $block->escapeHtmlAttr($_option->getId()) ?>]" value="<?= $block->escapeHtml($block->getDefaultValue()) ?>"/> - <?php elseif ($_option->getType() == \Magento\Catalog\Api\Data\ProductCustomOptionInterface::OPTION_TYPE_AREA): ?> + <?php elseif ($_option->getType() == \Magento\Catalog\Api\Data\ProductCustomOptionInterface::OPTION_TYPE_AREA) :?> <?php $_textAreaValidate = null; if ($_option->getIsRequire()) { $_textAreaValidate['required'] = true; @@ -51,31 +48,31 @@ $class = ($_option->getIsRequire()) ? ' required' : ''; } $_textAreaValidate['validate-no-utf8mb4-characters'] = true; ?> - <textarea id="options_<?= /* @escapeNotVerified */ $_option->getId() ?>_text" + <textarea id="options_<?= $block->escapeHtmlAttr($_option->getId()) ?>_text" class="product-custom-option" <?php if (!empty($_textAreaValidate)) {?> data-validate="<?= $block->escapeHtml(json_encode($_textAreaValidate)) ?>" <?php } ?> - name="options[<?= /* @escapeNotVerified */ $_option->getId() ?>]" - data-selector="options[<?= /* @escapeNotVerified */ $_option->getId() ?>]" + name="options[<?= $block->escapeHtmlAttr($_option->getId()) ?>]" + data-selector="options[<?= $block->escapeHtmlAttr($_option->getId()) ?>]" rows="5" cols="25"><?= $block->escapeHtml($block->getDefaultValue()) ?></textarea> <?php endif; ?> - <?php if ($_option->getMaxCharacters()): ?> - <p class="note note_<?= /* @escapeNotVerified */ $_option->getId() ?>"> - <?= /* @escapeNotVerified */ __('Maximum %1 characters', $_option->getMaxCharacters()) ?> + <?php if ($_option->getMaxCharacters()) :?> + <p class="note note_<?= $block->escapeHtmlAttr($_option->getId()) ?>"> + <?= $block->escapeHtml(__('Maximum %1 characters', $_option->getMaxCharacters())) ?> <span class="character-counter no-display"></span> </p> <?php endif; ?> </div> - <?php if ($_option->getMaxCharacters()): ?> + <?php if ($_option->getMaxCharacters()) :?> <script type="text/x-magento-init"> { - "[data-selector='options[<?= /* @escapeNotVerified */ $_option->getId() ?>]']": { + "[data-selector='options[<?= $block->escapeJs($_option->getId()) ?>]']": { "Magento_Catalog/js/product/remaining-characters": { - "maxLength": "<?= /* @escapeNotVerified */ $_option->getMaxCharacters() ?>", - "noteSelector": ".note_<?= /* @escapeNotVerified */ $_option->getId() ?>", - "counterSelector": ".note_<?= /* @escapeNotVerified */ $_option->getId() ?> .character-counter" + "maxLength": "<?= (int)$_option->getMaxCharacters() ?>", + "noteSelector": ".note_<?= $block->escapeJs($_option->getId()) ?>", + "counterSelector": ".note_<?= $block->escapeJs($_option->getId()) ?> .character-counter" } } } diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/view/options/wrapper.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/view/options/wrapper.phtml index ca6960a215a7a..88ee45bafe731 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/view/options/wrapper.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/view/options/wrapper.phtml @@ -3,14 +3,16 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + +/** @var $block Magento\Catalog\Block\Product\View */ ?> <?php $required = ''; if ($block->hasRequiredOptions()) { - $required = ' data-hasrequired="' . __('* Required Fields') . '"'; + $required = ' data-hasrequired="' . $block->escapeHtmlAttr(__('* Required Fields')) . '"'; } ?> -<div class="product-options-wrapper" id="product-options-wrapper"<?= /* @escapeNotVerified */ $required ?>> +<div class="product-options-wrapper" id="product-options-wrapper"<?= /* @noEscape */ $required ?>> <div class="fieldset" tabindex="0"> <?= $block->getChildHtml('', true) ?> </div> diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/view/price_clone.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/view/price_clone.phtml index e8c0b32fd7692..979bab167c344 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/view/price_clone.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/view/price_clone.phtml @@ -3,9 +3,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <?php /** @var \Magento\Catalog\Block\Product\AbstractProduct $block */ ?> <?php $_product = $block->getProduct() ?> diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/view/review.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/view/review.phtml index 5575d00df7457..5250673436648 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/view/review.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/view/review.phtml @@ -3,9 +3,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <?php /** @var $block \Magento\Catalog\Block\Product\AbstractProduct */ ?> <?= $block->getReviewsSummaryHtml($block->getProduct(), false, true) ?> diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/view/type/default.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/view/type/default.phtml index 7e522b4f88306..30edb2df03754 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/view/type/default.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/view/type/default.phtml @@ -3,21 +3,18 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <?php /* @var $block \Magento\Catalog\Block\Product\View\AbstractView */?> <?php $_product = $block->getProduct() ?> -<?php if ($block->displayProductStockStatus()): ?> - <?php if ($_product->isAvailable()): ?> - <div class="stock available" title="<?= /* @escapeNotVerified */ __('Availability') ?>"> - <span><?= /* @escapeNotVerified */ __('In stock') ?></span> +<?php if ($block->displayProductStockStatus()) :?> + <?php if ($_product->isAvailable()) :?> + <div class="stock available" title="<?= $block->escapeHtmlAttr(__('Availability')) ?>"> + <span><?= $block->escapeHtml(__('In stock')) ?></span> </div> - <?php else: ?> - <div class="stock unavailable" title="<?= /* @escapeNotVerified */ __('Availability') ?>"> - <span><?= /* @escapeNotVerified */ __('Out of stock') ?></span> + <?php else :?> + <div class="stock unavailable" title="<?= $block->escapeHtmlAttr(__('Availability')) ?>"> + <span><?= $block->escapeHtml(__('Out of stock')) ?></span> </div> <?php endif; ?> <?php endif; ?> diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/widget/compared/grid.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/widget/compared/grid.phtml index e0550cc7d4414..a2187b685ca0e 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/widget/compared/grid.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/widget/compared/grid.phtml @@ -1,18 +1,16 @@ <?php /** - * Copyright © Magento, Inc. All rights reserved. + * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - - //@codingStandardsIgnoreFile ?> <?php -/** - * @var $block \Magento\Ui\Block\Wrapper - */ +// phpcs:disable Magento2.PHP.ShortEchoSyntax.ShortEchoTag + +/** @var $block \Magento\Ui\Block\Wrapper */ ?> -<?php /* @escapeNotVerified */ echo $block->renderApp( +<?php /* @noEscape */ echo $block->renderApp( [ 'widget_columns' => [ 'displayMode' => 'grid' diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/widget/compared/list.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/widget/compared/list.phtml index 3a4f81d946bfd..5c5f6493c3163 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/widget/compared/list.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/widget/compared/list.phtml @@ -3,16 +3,14 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - - //@codingStandardsIgnoreFile ?> <?php -/** - * @var $block \Magento\Ui\Block\Wrapper - */ +// phpcs:disable Magento2.PHP.ShortEchoSyntax.ShortEchoTag + +/** @var $block \Magento\Ui\Block\Wrapper */ ?> -<?php /* @escapeNotVerified */ echo $block->renderApp( +<?php /* @noEscape */ echo $block->renderApp( [ 'widget_columns' => [ 'displayMode' => 'list' diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/widget/compared/sidebar.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/widget/compared/sidebar.phtml index 2d2c91aadd473..8db3cc80368e2 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/widget/compared/sidebar.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/widget/compared/sidebar.phtml @@ -3,16 +3,14 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - - //@codingStandardsIgnoreFile ?> <?php -/** - * @var $block \Magento\Ui\Block\Wrapper - */ +// phpcs:disable Magento2.PHP.ShortEchoSyntax.ShortEchoTag + +/** @var $block \Magento\Ui\Block\Wrapper */ ?> -<?php /* @escapeNotVerified */ echo $block->renderApp( +<?php /* @noEscape */ echo $block->renderApp( [ 'listing' => [ 'displayMode' => 'grid' diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/widget/link/link_block.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/widget/link/link_block.phtml index 2ec671b8de3ab..69f0319134ea0 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/widget/link/link_block.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/widget/link/link_block.phtml @@ -3,9 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile ?> <div class="widget block block-product-link"> - <a <?= /* @escapeNotVerified */ $block->getLinkAttributes() ?>><span><?= $block->escapeHtml($block->getLabel()) ?></span></a> + <a <?= /* @noEscape */ $block->getLinkAttributes() ?>><span><?= $block->escapeHtml($block->getLabel()) ?></span></a> </div> diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/widget/link/link_inline.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/widget/link/link_inline.phtml index 373eda1117455..8d9f6500894b4 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/widget/link/link_inline.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/widget/link/link_inline.phtml @@ -3,9 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile ?> <span class="widget block block-product-link-inline"> - <a <?= /* @escapeNotVerified */ $block->getLinkAttributes() ?>><span><?= $block->escapeHtml($block->getLabel()) ?></span></a> + <a <?= /* @noEscape */ $block->getLinkAttributes() ?>><span><?= $block->escapeHtml($block->getLabel()) ?></span></a> </span> diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/widget/new/column/new_default_list.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/widget/new/column/new_default_list.phtml index 45a206f3f92bf..53a0682311b1f 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/widget/new/column/new_default_list.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/widget/new/column/new_default_list.phtml @@ -4,60 +4,61 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - +// phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis +// phpcs:disable Magento2.Files.LineLength.MaxExceeded ?> -<?php if (($_products = $block->getProductCollection()) && $_products->getSize()): ?> +<?php if (($_products = $block->getProductCollection()) && $_products->getSize()) :?> <div class="block widget block-new-products-list"> <div class="block-title"> - <strong><?= /* @escapeNotVerified */ __('New Products') ?></strong> + <strong><?= $block->escapeHtml(__('New Products')) ?></strong> </div> <div class="block-content"> <?php $suffix = $block->getNameInLayout(); ?> - <ol class="product-items" id="widget-new-products-<?= /* @escapeNotVerified */ $suffix ?>"> - <?php foreach ($_products->getItems() as $_product): ?> + <ol class="product-items" id="widget-new-products-<?= $block->escapeHtmlAttr($suffix) ?>"> + <?php foreach ($_products->getItems() as $_product) :?> <li class="product-item"> <div class="product-item-info"> - <a class="product-item-photo" href="<?= /* @escapeNotVerified */ $_product->getProductUrl() ?>" - title="<?= /* @escapeNotVerified */ $block->stripTags($_product->getName(), null, true) ?>"> + <a class="product-item-photo" href="<?= $block->escapeUrl($_product->getProductUrl()) ?>" + title="<?= /* @noEscape */ $block->stripTags($_product->getName(), null, true) ?>"> <?= $block->getImage($_product, 'side_column_widget_product_thumbnail')->toHtml() ?> </a> <div class="product-item-details"> <strong class="product-item-name"> - <a href="<?= /* @escapeNotVerified */ $_product->getProductUrl() ?>" - title="<?= /* @escapeNotVerified */ $block->stripTags($_product->getName(), null, true) ?>)" class="product-item-link"> - <?= /* @escapeNotVerified */ $this->helper('Magento\Catalog\Helper\Output')->productAttribute($_product, $_product->getName(), 'name') ?> + <a href="<?= $block->escapeUrl($_product->getProductUrl()) ?>" + title="<?= /* @noEscape */ $block->stripTags($_product->getName(), null, true) ?>)" + class="product-item-link"> + <?= /* @noEscape */ $this->helper(Magento\Catalog\Helper\Output::class)->productAttribute($_product, $_product->getName(), 'name') ?> </a> </strong> - <?= /* @escapeNotVerified */ $block->getProductPriceHtml($_product, '-widget-new-' . $suffix) ?> + <?= $block->getProductPriceHtml($_product, '-widget-new-' . $suffix) ?> <div class="product-item-actions"> <div class="actions-primary"> - <?php if ($_product->isSaleable()): ?> - <?php if (!$_product->getTypeInstance()->isPossibleBuyFromList($_product)): ?> - <button type="button" title="<?= /* @escapeNotVerified */ __('Add to Cart') ?>" + <?php if ($_product->isSaleable()) :?> + <?php if (!$_product->getTypeInstance()->isPossibleBuyFromList($_product)) :?> + <button type="button" title="<?= $block->escapeHtmlAttr(__('Add to Cart')) ?>" class="action tocart primary" - data-mage-init='{"redirectUrl":{"url":"<?= /* @escapeNotVerified */ $block->getAddToCartUrl($_product) ?>"}}'> - <span><?= /* @escapeNotVerified */ __('Add to Cart') ?></span> + data-mage-init='{"redirectUrl":{"url":"<?= $block->escapeUrl($block->getAddToCartUrl($_product)) ?>"}}'> + <span><?= $block->escapeHtml(__('Add to Cart')) ?></span> </button> - <?php else: ?> + <?php else :?> <?php - $postDataHelper = $this->helper('Magento\Framework\Data\Helper\PostHelper'); - $postData = $postDataHelper->getPostData($block->getAddToCartUrl($_product), ['product' => $_product->getEntityId()]); - ?> - <button type="button" title="<?= /* @escapeNotVerified */ __('Add to Cart') ?>" + $postDataHelper = $this->helper(Magento\Framework\Data\Helper\PostHelper::class); + $postData = $postDataHelper->getPostData($block->escapeUrl($block->getAddToCartUrl($_product)), ['product' => $_product->getEntityId()]); + ?> + <button type="button" title="<?= $block->escapeHtmlAttr(__('Add to Cart')) ?>" class="action tocart primary" - data-post='<?= /* @escapeNotVerified */ $postData ?>'> - <span><?= /* @escapeNotVerified */ __('Add to Cart') ?></span> + data-post='<?= /* @noEscape */ $postData ?>'> + <span><?= $block->escapeHtml(__('Add to Cart')) ?></span> </button> <?php endif; ?> - <?php else: ?> - <?php if ($_product->getIsSalable()): ?> - <div class="stock available" title="<?= /* @escapeNotVerified */ __('Availability') ?>"> - <span><?= /* @escapeNotVerified */ __('In stock') ?></span> + <?php else :?> + <?php if ($_product->getIsSalable()) :?> + <div class="stock available" title="<?= $block->escapeHtmlAttr(__('Availability')) ?>"> + <span><?= $block->escapeHtml(__('In stock')) ?></span> </div> - <?php else: ?> - <div class="stock unavailable" title="<?= /* @escapeNotVerified */ __('Availability') ?>"> - <span><?= /* @escapeNotVerified */ __('Out of stock') ?></span> + <?php else :?> + <div class="stock unavailable" title="<?= $block->escapeHtmlAttr(__('Availability')) ?>"> + <span><?= $block->escapeHtml(__('Out of stock')) ?></span> </div> <?php endif; ?> <?php endif; ?> diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/widget/new/column/new_images_list.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/widget/new/column/new_images_list.phtml index 2c40f9f7d63dc..8a776adc95018 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/widget/new/column/new_images_list.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/widget/new/column/new_images_list.phtml @@ -3,22 +3,20 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> -<?php if (($_products = $block->getProductCollection()) && $_products->getSize()): ?> +<?php if (($_products = $block->getProductCollection()) && $_products->getSize()) :?> <div class="block widget block-new-products-images"> <div class="block-title"> - <strong><?= /* @escapeNotVerified */ __('New Products') ?></strong> + <strong><?= $block->escapeHtml(__('New Products')) ?></strong> </div> <div class="block-content"> <?php $suffix = $block->getNameInLayout(); ?> - <ol id="widget-new-products-<?= /* @escapeNotVerified */ $suffix ?>" class="product-items product-items-images"> - <?php foreach ($_products->getItems() as $_product): ?> + <ol id="widget-new-products-<?= $block->escapeHtmlAttr($suffix) ?>" + class="product-items product-items-images"> + <?php foreach ($_products->getItems() as $_product) :?> <li class="product-item"> - <a class="product-item-photo" href="<?= /* @escapeNotVerified */ $_product->getProductUrl() ?>" - title="<?= /* @escapeNotVerified */ $block->stripTags($_product->getName(), null, true) ?>"> + <a class="product-item-photo" href="<?= $block->escapeUrl($_product->getProductUrl()) ?>" + title="<?= /* @noEscape */ $block->stripTags($_product->getName(), null, true) ?>"> <?php /* new_products_images_only_widget */ ?> <?= $block->getImage($_product, 'new_products_images_only_widget')->toHtml() ?> </a> diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/widget/new/column/new_names_list.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/widget/new/column/new_names_list.phtml index c0fb12df91137..371d4df7c0206 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/widget/new/column/new_names_list.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/widget/new/column/new_names_list.phtml @@ -4,24 +4,26 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - +// phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis ?> -<?php if (($_products = $block->getProductCollection()) && $_products->getSize()): ?> +<?php if (($_products = $block->getProductCollection()) && $_products->getSize()) :?> <div class="block widget block-new-products-names"> <div class="block-title"> - <strong><?= /* @escapeNotVerified */ __('New Products') ?></strong> + <strong><?= $block->escapeHtml(__('New Products')) ?></strong> </div> <div class="block-content"> <?php $suffix = $block->getNameInLayout(); ?> - <ol id="widget-new-products-<?= /* @escapeNotVerified */ $suffix ?>" class="product-items product-items-names"> - <?php foreach ($_products->getItems() as $_product): ?> + <ol id="widget-new-products-<?= $block->escapeHtmlAttr($suffix) ?>" + class="product-items product-items-names"> + <?php foreach ($_products->getItems() as $_product) :?> <li class="product-item"> <strong class="product-item-name"> - <a href="<?= /* @escapeNotVerified */ $_product->getProductUrl() ?>" - title="<?= /* @escapeNotVerified */ $block->stripTags($_product->getName(), null, true) ?>)" + <a href="<?= $block->escapeUrl($_product->getProductUrl()) ?>" + title="<?= /* @noEscape */ $block->stripTags($_product->getName(), null, true) ?>)" class="product-item-link"> - <?= /* @escapeNotVerified */ $this->helper('Magento\Catalog\Helper\Output')->productAttribute($_product, $_product->getName(), 'name') ?> + <?= /* @noEscape */ $this->helper( + Magento\Catalog\Helper\Output::class + )->productAttribute($_product, $_product->getName(), 'name') ?> </a> </strong> </li> diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/widget/new/content/new_grid.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/widget/new/content/new_grid.phtml index 93542c4c9095c..5108c488aec19 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/widget/new/content/new_grid.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/widget/new/content/new_grid.phtml @@ -3,9 +3,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <?php /** @@ -13,6 +10,10 @@ * * @var $block \Magento\Catalog\Block\Product\Widget\NewWidget */ + +// phpcs:disable Magento2.Files.LineLength.MaxExceeded +// phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis + if ($exist = ($block->getProductCollection() && $block->getProductCollection()->getSize())) { $type = 'widget-new-grid'; @@ -30,84 +31,93 @@ if ($exist = ($block->getProductCollection() && $block->getProductCollection()-> } ?> -<?php if ($exist):?> - <div class="block widget block-new-products <?= /* @escapeNotVerified */ $mode ?>"> +<?php if ($exist) :?> + <div class="block widget block-new-products <?= /* @noEscape */ $mode ?>"> <div class="block-title"> - <strong role="heading" aria-level="2"><?= /* @escapeNotVerified */ $title ?></strong> + <strong role="heading" aria-level="2"><?= $block->escapeHtml($title) ?></strong> </div> <div class="block-content"> - <?= /* @escapeNotVerified */ '<!-- ' . $image . '-->' ?> - <div class="products-<?= /* @escapeNotVerified */ $mode ?> <?= /* @escapeNotVerified */ $mode ?>"> - <ol class="product-items <?= /* @escapeNotVerified */ $type ?>"> - <?php foreach ($items as $_item): ?> + <?= /* @noEscape */ '<!-- ' . $image . '-->' ?> + <div class="products-<?= /* @noEscape */ $mode ?> <?= /* @noEscape */ $mode ?>"> + <ol class="product-items <?= /* @noEscape */ $type ?>"> + <?php foreach ($items as $_item) :?> <li class="product-item"> <div class="product-item-info"> - <a href="<?= /* @escapeNotVerified */ $block->getProductUrl($_item) ?>" class="product-item-photo"> + <a href="<?= $block->escapeUrl($block->getProductUrl($_item)) ?>" + class="product-item-photo"> <?= $block->getImage($_item, $image)->toHtml() ?> </a> <div class="product-item-details"> <strong class="product-item-name"> <a title="<?= $block->escapeHtml($_item->getName()) ?>" - href="<?= /* @escapeNotVerified */ $block->getProductUrl($_item) ?>" + href="<?= $block->escapeUrl($block->getProductUrl($_item)) ?>" class="product-item-link"> <?= $block->escapeHtml($_item->getName()) ?> </a> </strong> - <?php - echo $block->getProductPriceHtml($_item, $type); - ?> + <?= $block->getProductPriceHtml($_item, $type); ?> - <?php if ($templateType): ?> + <?php if ($templateType) :?> <?= $block->getReviewsSummaryHtml($_item, $templateType) ?> <?php endif; ?> - <?php if ($showWishlist || $showCompare || $showCart): ?> + <?php if ($showWishlist || $showCompare || $showCart) :?> <div class="product-item-actions"> - <?php if ($showCart): ?> + <?php if ($showCart) :?> <div class="actions-primary"> - <?php if ($_item->isSaleable()): ?> - <?php if (!$_item->getTypeInstance()->isPossibleBuyFromList($_item)): ?> + <?php if ($_item->isSaleable()) :?> + <?php if (!$_item->getTypeInstance()->isPossibleBuyFromList($_item)) :?> <button class="action tocart primary" - data-mage-init='{"redirectUrl":{"url":"<?= /* @escapeNotVerified */ $block->getAddToCartUrl($_item) ?>"}}' - type="button" title="<?= /* @escapeNotVerified */ __('Add to Cart') ?>"> - <span><?= /* @escapeNotVerified */ __('Add to Cart') ?></span> + data-mage-init='{"redirectUrl":{"url":"<?= $block->escapeUrl($block->getAddToCartUrl($_item)) ?>"}}' + type="button" + title="<?= $block->escapeHtmlAttr(__('Add to Cart')) ?>"> + <span><?= $block->escapeHtml(__('Add to Cart')) ?></span> </button> - <?php else: ?> + <?php else :?> <?php - $postDataHelper = $this->helper('Magento\Framework\Data\Helper\PostHelper'); - $postData = $postDataHelper->getPostData($block->getAddToCartUrl($_item), ['product' => $_item->getEntityId()]) + $postDataHelper = $this->helper(Magento\Framework\Data\Helper\PostHelper::class); + $postData = $postDataHelper->getPostData( + $block->escapeUrl($block->getAddToCartUrl($_item)), + ['product' => (int) $_item->getEntityId()] + ) ?> <button class="action tocart primary" - data-post='<?= /* @escapeNotVerified */ $postData ?>' - type="button" title="<?= /* @escapeNotVerified */ __('Add to Cart') ?>"> - <span><?= /* @escapeNotVerified */ __('Add to Cart') ?></span> + data-post='<?= /* @noEscape */ $postData ?>' + type="button" + title="<?= $block->escapeHtmlAttr(__('Add to Cart')) ?>"> + <span><?= $block->escapeHtml(__('Add to Cart')) ?></span> </button> <?php endif; ?> - <?php else: ?> - <?php if ($_item->getIsSalable()): ?> - <div class="stock available"><span><?= /* @escapeNotVerified */ __('In stock') ?></span></div> - <?php else: ?> - <div class="stock unavailable"><span><?= /* @escapeNotVerified */ __('Out of stock') ?></span></div> + <?php else :?> + <?php if ($_item->getIsSalable()) :?> + <div class="stock available"> + <span><?= $block->escapeHtml(__('In stock')) ?></span> + </div> + <?php else :?> + <div class="stock unavailable"> + <span><?= $block->escapeHtml(__('Out of stock')) ?></span> + </div> <?php endif; ?> <?php endif; ?> </div> <?php endif; ?> - <?php if ($showWishlist || $showCompare): ?> + <?php if ($showWishlist || $showCompare) :?> <div class="actions-secondary" data-role="add-to-links"> - <?php if ($this->helper('Magento\Wishlist\Helper\Data')->isAllow() && $showWishlist): ?> + <?php if ($this->helper(Magento\Wishlist\Helper\Data::class)->isAllow() && $showWishlist) :?> <a href="#" - data-post='<?= /* @escapeNotVerified */ $block->getAddToWishlistParams($_item) ?>' - class="action towishlist" data-action="add-to-wishlist" - title="<?= /* @escapeNotVerified */ __('Add to Wish List') ?>"> - <span><?= /* @escapeNotVerified */ __('Add to Wish List') ?></span> + data-post='<?= /* @noEscape */ $block->getAddToWishlistParams($_item) ?>' + class="action towishlist" + data-action="add-to-wishlist" + title="<?= $block->escapeHtmlAttr(__('Add to Wish List')) ?>"> + <span><?= $block->escapeHtml(__('Add to Wish List')) ?></span> </a> <?php endif; ?> - <?php if ($block->getAddToCompareUrl() && $showCompare): ?> - <?php $compareHelper = $this->helper('Magento\Catalog\Helper\Product\Compare');?> + <?php if ($block->getAddToCompareUrl() && $showCompare) :?> + <?php $compareHelper = $this->helper(Magento\Catalog\Helper\Product\Compare::class);?> <a href="#" class="action tocompare" - data-post='<?= /* @escapeNotVerified */ $compareHelper->getPostDataParams($_item) ?>' - title="<?= /* @escapeNotVerified */ __('Add to Compare') ?>"> - <span><?= /* @escapeNotVerified */ __('Add to Compare') ?></span> + data-post='<?= /* @noEscape */ $compareHelper->getPostDataParams($_item) ?>' + title="<?= $block->escapeHtmlAttr(__('Add to Compare')) ?>"> + <span><?= $block->escapeHtml(__('Add to Compare')) ?></span> </a> <?php endif; ?> </div> diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/widget/new/content/new_list.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/widget/new/content/new_list.phtml index ad75a3a6f0743..378cd49493a6e 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/widget/new/content/new_list.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/widget/new/content/new_list.phtml @@ -3,11 +3,12 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <?php + +// phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis +// phpcs:disable Magento2.Files.LineLength.MaxExceeded + /** * Template for displaying new products widget * @@ -21,7 +22,8 @@ if ($exist = ($block->getProductCollection() && $block->getProductCollection()-> $image = 'new_products_content_widget_list'; $title = __('New Products'); $items = $block->getProductCollection()->getItems(); - $_helper = $this->helper('Magento\Catalog\Helper\Output'); + /** @var Magento\Catalog\Helper\Output $_helper */ + $_helper = $this->helper(Magento\Catalog\Helper\Output::class); $showWishlist = true; $showCompare = true; @@ -31,94 +33,102 @@ if ($exist = ($block->getProductCollection() && $block->getProductCollection()-> } ?> -<?php if ($exist):?> - <div class="block widget block-new-products <?= /* @escapeNotVerified */ $mode ?>"> +<?php if ($exist) :?> + <div class="block widget block-new-products <?= /* @noEscape */ $mode ?>"> <div class="block-title"> - <strong role="heading" aria-level="2"><?= /* @escapeNotVerified */ $title ?></strong> + <strong role="heading" aria-level="2"><?= $block->escapeHtml($title) ?></strong> </div> <div class="block-content"> - <?= /* @escapeNotVerified */ '<!-- ' . $image . '-->' ?> - <div class="products-<?= /* @escapeNotVerified */ $mode ?> <?= /* @escapeNotVerified */ $mode ?>"> - <ol class="product-items <?= /* @escapeNotVerified */ $type ?>"> - <?php foreach ($items as $_item): ?> + <?= /* @noEscape */ '<!-- ' . $image . '-->' ?> + <div class="products-<?= /* @noEscape */ $mode ?> <?= /* @noEscape */ $mode ?>"> + <ol class="product-items <?= /* @noEscape */ $type ?>"> + <?php foreach ($items as $_item) :?> <li class="product-item"> <div class="product-item-info"> - <a href="<?= /* @escapeNotVerified */ $block->getProductUrl($_item) ?>" class="product-item-photo"> + <a href="<?= $block->escapeUrl($block->getProductUrl($_item)) ?>" + class="product-item-photo"> <?= $block->getImage($_item, $image)->toHtml() ?> </a> <div class="product-item-details"> <strong class="product-item-name"> - <a title="<?= $block->escapeHtml($_item->getName()) ?>" - href="<?= /* @escapeNotVerified */ $block->getProductUrl($_item) ?>" + <a title="<?= $block->escapeHtmlAttr($_item->getName()) ?>" + href="<?= $block->escapeUrl($block->getProductUrl($_item)) ?>" class="product-item-link"> <?= $block->escapeHtml($_item->getName()) ?> </a> </strong> <?= $block->getProductPriceHtml($_item, $type) ?> - <?php if ($templateType): ?> + <?php if ($templateType) :?> <?= $block->getReviewsSummaryHtml($_item, $templateType) ?> <?php endif; ?> - <?php if ($showWishlist || $showCompare || $showCart): ?> + <?php if ($showWishlist || $showCompare || $showCart) :?> <div class="product-item-actions"> - <?php if ($showCart): ?> + <?php if ($showCart) :?> <div class="actions-primary"> - <?php if ($_item->isSaleable()): ?> - <?php if (!$_item->getTypeInstance()->isPossibleBuyFromList($_item)): ?> + <?php if ($_item->isSaleable()) :?> + <?php if (!$_item->getTypeInstance()->isPossibleBuyFromList($_item) + ) :?> <button class="action tocart primary" - data-mage-init='{"redirectUrl":{"url":"<?= /* @escapeNotVerified */ $block->getAddToCartUrl($_item) ?>"}}' - type="button" title="<?= /* @escapeNotVerified */ __('Add to Cart') ?>"> - <span><?= /* @escapeNotVerified */ __('Add to Cart') ?></span> + data-mage-init='{"redirectUrl":{"url":"<?= $block->escapeUrl($block->getAddToCartUrl($_item)) ?>"}}' + type="button" + title="<?= $block->escapeHtmlAttr(__('Add to Cart')) ?>"> + <span><?= $block->escapeHtml(__('Add to Cart')) ?></span> </button> - <?php else: ?> + <?php else :?> <?php - $postDataHelper = $this->helper('Magento\Framework\Data\Helper\PostHelper'); + $postDataHelper = $this->helper(Magento\Framework\Data\Helper\PostHelper::class); $postData = $postDataHelper->getPostData($block->getAddToCartUrl($_item), ['product' => $_item->getEntityId()]) ?> <button class="action tocart primary" - data-post='<?= /* @escapeNotVerified */ $postData ?>' - type="button" title="<?= /* @escapeNotVerified */ __('Add to Cart') ?>"> - <span><?= /* @escapeNotVerified */ __('Add to Cart') ?></span> + data-post='<?= /* @noEscape */ $postData ?>' + type="button" title="<?= $block->escapeHtmlAttr(__('Add to Cart')) ?>"> + <span><?= $block->escapeHtml(__('Add to Cart')) ?></span> </button> <?php endif; ?> - <?php else: ?> - <?php if ($_item->getIsSalable()): ?> - <div class="stock available"><span><?= /* @escapeNotVerified */ __('In stock') ?></span></div> - <?php else: ?> - <div class="stock unavailable"><span><?= /* @escapeNotVerified */ __('Out of stock') ?></span></div> + <?php else :?> + <?php if ($_item->getIsSalable()) :?> + <div class="stock available"><span><?= $block->escapeHtml(__('In stock')) ?></span></div> + <?php else :?> + <div class="stock unavailable"><span><?= $block->escapeHtml(__('Out of stock')) ?></span></div> <?php endif; ?> <?php endif; ?> </div> <?php endif; ?> - <?php if ($showWishlist || $showCompare): ?> + <?php if ($showWishlist || $showCompare) :?> <div class="actions-secondary" data-role="add-to-links"> - <?php if ($this->helper('Magento\Wishlist\Helper\Data')->isAllow() && $showWishlist): ?> + <?php if ($this->helper(Magento\Wishlist\Helper\Data::class)->isAllow() && $showWishlist) :?> <a href="#" - data-post='<?= /* @escapeNotVerified */ $block->getAddToWishlistParams($_item) ?>' + data-post='<?= /* @noEscape */ $block->getAddToWishlistParams($_item) ?>' class="action towishlist" data-action="add-to-wishlist" - title="<?= /* @escapeNotVerified */ __('Add to Wish List') ?>"> - <span><?= /* @escapeNotVerified */ __('Add to Wish List') ?></span> + title="<?= $block->escapeHtmlAttr(__('Add to Wish List')) ?>"> + <span><?= $block->escapeHtml(__('Add to Wish List')) ?></span> </a> <?php endif; ?> - <?php if ($block->getAddToCompareUrl() && $showCompare): ?> - <?php $compareHelper = $this->helper('Magento\Catalog\Helper\Product\Compare'); ?> + <?php if ($block->getAddToCompareUrl() && $showCompare) :?> + <?php $compareHelper = $this->helper(Magento\Catalog\Helper\Product\Compare::class); ?> <a href="#" class="action tocompare" - title="<?= /* @escapeNotVerified */ __('Add to Compare') ?>" - data-post='<?= /* @escapeNotVerified */ $compareHelper->getPostDataParams($_item) ?>'> - <span><?= /* @escapeNotVerified */ __('Add to Compare') ?></span> + title="<?= $block->escapeHtmlAttr(__('Add to Compare')) ?>" + data-post='<?= /* @noEscape */ $compareHelper->getPostDataParams($_item) ?>' + > + <span><?= $block->escapeHtml(__('Add to Compare')) ?></span> </a> <?php endif; ?> </div> <?php endif; ?> </div> <?php endif; ?> - <?php if ($description):?> + <?php if ($description) :?> <div class="product-item-description"> - <?= /* @escapeNotVerified */ $_helper->productAttribute($_item, $_item->getShortDescription(), 'short_description') ?> - <a title="<?= $block->escapeHtml($_item->getName()) ?>" - href="<?= /* @escapeNotVerified */ $block->getProductUrl($_item) ?>" - class="action more"><?= /* @escapeNotVerified */ __('Learn More') ?></a> + <?= /* @noEscape */ $_helper->productAttribute( + $_item, + $_item->getShortDescription(), + 'short_description' + ) ?> + <a title="<?= $block->escapeHtmlAttr($_item->getName()) ?>" + href="<?= $block->escapeUrl($block->getProductUrl($_item))?>" + class="action more"><?= $block->escapeHtml(__('Learn More')) ?></a> </div> <?php endif; ?> </div> diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/widget/viewed/grid.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/widget/viewed/grid.phtml index 578630a11e930..d4db174dbe5e7 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/widget/viewed/grid.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/widget/viewed/grid.phtml @@ -5,12 +5,13 @@ */ ?> <?php -/** - * @var $block \Magento\Ui\Block\Wrapper - */ +// phpcs:disable Magento2.Security.LanguageConstruct.DirectOutput +// phpcs:disable Magento2.PHP.ShortEchoSyntax.ShortEchoTag + +/** @var $block \Magento\Ui\Block\Wrapper */ ?> -<?= /* @escapeNotVerified */ $block->renderApp([ +<?php /* @noEscape */ echo $block->renderApp([ 'widget_columns' => [ 'displayMode' => 'grid' ], diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/widget/viewed/list.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/widget/viewed/list.phtml index 3770c330ad73e..e03ac9ca692cc 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/widget/viewed/list.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/widget/viewed/list.phtml @@ -5,12 +5,13 @@ */ ?> <?php -/** - * @var $block \Magento\Ui\Block\Wrapper - */ +// phpcs:disable Magento2.Security.LanguageConstruct.DirectOutput +// phpcs:disable Magento2.PHP.ShortEchoSyntax.ShortEchoTag + +/** @var $block \Magento\Ui\Block\Wrapper */ ?> -<?= /* @escapeNotVerified */ $block->renderApp([ +<?php /* @noEscape */ echo $block->renderApp([ 'widget_columns' => [ 'displayMode' => 'list' ], diff --git a/app/code/Magento/Catalog/view/frontend/templates/product/widget/viewed/sidebar.phtml b/app/code/Magento/Catalog/view/frontend/templates/product/widget/viewed/sidebar.phtml index 1c4ad3105a2b5..a42b46a5238f9 100644 --- a/app/code/Magento/Catalog/view/frontend/templates/product/widget/viewed/sidebar.phtml +++ b/app/code/Magento/Catalog/view/frontend/templates/product/widget/viewed/sidebar.phtml @@ -3,16 +3,14 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - - //@codingStandardsIgnoreFile ?> <?php -/** - * @var $block \Magento\Ui\Block\Wrapper - */ +// phpcs:disable Magento2.PHP.ShortEchoSyntax.ShortEchoTag + +/** @var $block \Magento\Ui\Block\Wrapper */ ?> -<?php /* @escapeNotVerified */ echo $block->renderApp( +<?php /* @noEscape */ echo $block->renderApp( [ 'listing' => [ 'displayMode' => 'grid' diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product.php index 40aa54fd93873..86137990cc57d 100644 --- a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product.php +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product.php @@ -7,14 +7,13 @@ namespace Magento\CatalogGraphQl\Model\Resolver; -use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +use Magento\CatalogGraphQl\Model\Resolver\Product\ProductFieldsSelector; use Magento\CatalogGraphQl\Model\Resolver\Products\DataProvider\Deferred\Product as ProductDataProvider; use Magento\Framework\GraphQl\Config\Element\Field; use Magento\Framework\GraphQl\Exception\GraphQlInputException; -use Magento\Framework\GraphQl\Query\FieldTranslator; -use Magento\Framework\GraphQl\Query\Resolver\Value; use Magento\Framework\GraphQl\Query\Resolver\ValueFactory; use Magento\Framework\GraphQl\Query\ResolverInterface; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; /** * @inheritdoc @@ -32,23 +31,23 @@ class Product implements ResolverInterface private $valueFactory; /** - * @var FieldTranslator + * @var ProductFieldsSelector */ - private $fieldTranslator; + private $productFieldsSelector; /** * @param ProductDataProvider $productDataProvider * @param ValueFactory $valueFactory - * @param FieldTranslator $fieldTranslator + * @param ProductFieldsSelector $productFieldsSelector */ public function __construct( ProductDataProvider $productDataProvider, ValueFactory $valueFactory, - FieldTranslator $fieldTranslator + ProductFieldsSelector $productFieldsSelector ) { $this->productDataProvider = $productDataProvider; $this->valueFactory = $valueFactory; - $this->fieldTranslator = $fieldTranslator; + $this->productFieldsSelector = $productFieldsSelector; } /** @@ -60,7 +59,7 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value throw new GraphQlInputException(__('No child sku found for product link.')); } $this->productDataProvider->addProductSku($value['sku']); - $fields = $this->getProductFields($info); + $fields = $this->productFieldsSelector->getProductFieldsFromInfo($info); $this->productDataProvider->addEavAttributes($fields); $result = function () use ($value) { @@ -86,34 +85,4 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value return $this->valueFactory->create($result); } - - /** - * Return field names for all requested product fields. - * - * @param ResolveInfo $info - * @return string[] - */ - private function getProductFields(ResolveInfo $info) : array - { - $fieldNames = []; - foreach ($info->fieldNodes as $node) { - if ($node->name->value !== 'product') { - continue; - } - foreach ($node->selectionSet->selections as $selectionNode) { - if ($selectionNode->kind === 'InlineFragment') { - foreach ($selectionNode->selectionSet->selections as $inlineSelection) { - if ($inlineSelection->kind === 'InlineFragment') { - continue; - } - $fieldNames[] = $this->fieldTranslator->translate($inlineSelection->name->value); - } - continue; - } - $fieldNames[] = $this->fieldTranslator->translate($selectionNode->name->value); - } - } - - return $fieldNames; - } } diff --git a/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductFieldsSelector.php b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductFieldsSelector.php new file mode 100644 index 0000000000000..9ddad4e6451fa --- /dev/null +++ b/app/code/Magento/CatalogGraphQl/Model/Resolver/Product/ProductFieldsSelector.php @@ -0,0 +1,61 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CatalogGraphQl\Model\Resolver\Product; + +use Magento\Framework\GraphQl\Query\FieldTranslator; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; + +/** + * Select Product Fields From Resolve Info + */ +class ProductFieldsSelector +{ + /** + * @var FieldTranslator + */ + private $fieldTranslator; + + /** + * @param FieldTranslator $fieldTranslator + */ + public function __construct(FieldTranslator $fieldTranslator) + { + $this->fieldTranslator = $fieldTranslator; + } + + /** + * Return field names for all requested product fields. + * + * @param ResolveInfo $info + * @param string $productNodeName + * @return string[] + */ + public function getProductFieldsFromInfo(ResolveInfo $info, string $productNodeName = 'product') : array + { + $fieldNames = []; + foreach ($info->fieldNodes as $node) { + if ($node->name->value !== $productNodeName) { + continue; + } + foreach ($node->selectionSet->selections as $selectionNode) { + if ($selectionNode->kind === 'InlineFragment') { + foreach ($selectionNode->selectionSet->selections as $inlineSelection) { + if ($inlineSelection->kind === 'InlineFragment') { + continue; + } + $fieldNames[] = $this->fieldTranslator->translate($inlineSelection->name->value); + } + continue; + } + $fieldNames[] = $this->fieldTranslator->translate($selectionNode->name->value); + } + } + + return $fieldNames; + } +} diff --git a/app/code/Magento/CatalogGraphQl/etc/graphql/di.xml b/app/code/Magento/CatalogGraphQl/etc/graphql/di.xml index a5bd42860ded0..2292004f3cf01 100644 --- a/app/code/Magento/CatalogGraphQl/etc/graphql/di.xml +++ b/app/code/Magento/CatalogGraphQl/etc/graphql/di.xml @@ -80,4 +80,19 @@ </virtualType> <preference for="Magento\Framework\Search\Adapter\Mysql\Query\Builder\Match" type="Magento\CatalogGraphQl\Model\Search\Adapter\Mysql\Query\Builder\Match" /> + <type name="Magento\StoreGraphQl\Model\Resolver\Store\StoreConfigDataProvider"> + <arguments> + <argument name="extendedConfigData" xsi:type="array"> + <item name="product_url_suffix" xsi:type="string">catalog/seo/product_url_suffix</item> + <item name="category_url_suffix" xsi:type="string">catalog/seo/category_url_suffix</item> + <item name="title_separator" xsi:type="string">catalog/seo/title_separator</item> + <item name="list_mode" xsi:type="string">catalog/frontend/list_mode</item> + <item name="grid_per_page_values" xsi:type="string">catalog/frontend/grid_per_page_values</item> + <item name="list_per_page_values" xsi:type="string">catalog/frontend/list_per_page_values</item> + <item name="grid_per_page" xsi:type="string">catalog/frontend/grid_per_page</item> + <item name="list_per_page" xsi:type="string">catalog/frontend/list_per_page</item> + <item name="catalog_default_sort_by" xsi:type="string">catalog/frontend/default_sort_by</item> + </argument> + </arguments> + </type> </config> diff --git a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls index 9f102a1c6a150..27df5eccf323c 100644 --- a/app/code/Magento/CatalogGraphQl/etc/schema.graphqls +++ b/app/code/Magento/CatalogGraphQl/etc/schema.graphqls @@ -9,22 +9,22 @@ type Query { currentPage: Int = 1 @doc(description: "Specifies which page of results to return. The default value is 1."), sort: ProductSortInput @doc(description: "Specifies which attribute to sort on, and whether to return the results in ascending or descending order.") ): Products - @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Products") @doc(description: "The products query searches for products that match the criteria specified in the search and filter attributes") @cache(cacheTag: "cat_p", cacheIdentity: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\Identity") + @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Products") @doc(description: "The products query searches for products that match the criteria specified in the search and filter attributes.") @cache(cacheTag: "cat_p", cacheIdentity: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\Identity") category ( - id: Int @doc(description: "Id of the category") + id: Int @doc(description: "Id of the category.") ): CategoryTree - @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\CategoryTree") @doc(description: "The category query searches for categories that match the criteria specified in the search and filter attributes") @cache(cacheTag: "cat_c", cacheIdentity: "Magento\\CatalogGraphQl\\Model\\Resolver\\Category\\CategoryTreeIdentity") + @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\CategoryTree") @doc(description: "The category query searches for categories that match the criteria specified in the search and filter attributes.") @cache(cacheTag: "cat_c", cacheIdentity: "Magento\\CatalogGraphQl\\Model\\Resolver\\Category\\CategoryTreeIdentity") } type Price @doc(description: "The Price object defines the price of a product as well as any tax-related adjustments.") { - amount: Money @doc(description: "The price of a product plus a three-letter currency code") - adjustments: [PriceAdjustment] @doc(description: "An array that provides information about tax, weee, or weee_tax adjustments") + amount: Money @doc(description: "The price of a product plus a three-letter currency code.") + adjustments: [PriceAdjustment] @doc(description: "An array that provides information about tax, weee, or weee_tax adjustments.") } type PriceAdjustment @doc(description: "The PricedAdjustment object defines the amount of money to apply as an adjustment, the type of adjustment to apply, and whether the item is included or excluded from the adjustment.") { - amount: Money @doc(description: "The amount of the price adjustment and its currency code") - code: PriceAdjustmentCodesEnum @doc(description: "Indicates whether the adjustment involves tax, weee, or weee_tax") - description: PriceAdjustmentDescriptionEnum @doc(description: "Indicates whether the entity described by the code attribute is included or excluded from the adjustment") + amount: Money @doc(description: "The amount of the price adjustment and its currency code.") + code: PriceAdjustmentCodesEnum @doc(description: "Indicates whether the adjustment involves tax, weee, or weee_tax.") + description: PriceAdjustmentDescriptionEnum @doc(description: "Indicates whether the entity described by the code attribute is included or excluded from the adjustment.") } enum PriceAdjustmentCodesEnum @doc(description: "Note: This enumeration contains values defined in modules other than the Catalog module.") { @@ -51,341 +51,341 @@ type ProductLinks implements ProductLinksInterface @doc(description: "ProductLin } interface ProductLinksInterface @typeResolver(class: "Magento\\CatalogGraphQl\\Model\\ProductLinkTypeResolverComposite") @doc(description:"ProductLinks contains information about linked products, including the link type and product type of each item.") { - sku: String @doc(description: "The identifier of the linked product") - link_type: String @doc(description: "One of related, associated, upsell, or crosssell") - linked_product_sku: String @doc(description: "The SKU of the linked product") - linked_product_type: String @doc(description: "The type of linked product (simple, virtual, bundle, downloadable, grouped, configurable)") - position: Int @doc(description: "The position within the list of product links") + sku: String @doc(description: "The identifier of the linked product.") + link_type: String @doc(description: "One of related, associated, upsell, or crosssell.") + linked_product_sku: String @doc(description: "The SKU of the linked product.") + linked_product_type: String @doc(description: "The type of linked product (simple, virtual, bundle, downloadable, grouped, configurable).") + position: Int @doc(description: "The position within the list of product links.") } type ProductTierPrices @doc(description: "The ProductTierPrices object defines a tier price, which is a quantity discount offered to a specific customer group.") { - customer_group_id: String @doc(description: "The ID of the customer group") - qty: Float @doc(description: "The number of items that must be purchased to qualify for tier pricing") - value: Float @doc(description: "The price of the fixed price item") - percentage_value: Float @doc(description: "The percentage discount of the item") - website_id: Float @doc(description: "The ID assigned to the website") + customer_group_id: String @doc(description: "The ID of the customer group.") + qty: Float @doc(description: "The number of items that must be purchased to qualify for tier pricing.") + value: Float @doc(description: "The price of the fixed price item.") + percentage_value: Float @doc(description: "The percentage discount of the item.") + website_id: Float @doc(description: "The ID assigned to the website.") } interface ProductInterface @typeResolver(class: "Magento\\CatalogGraphQl\\Model\\ProductInterfaceTypeResolverComposite") @doc(description: "The ProductInterface contains attributes that are common to all types of products. Note that descriptions may not be available for custom and EAV attributes.") { - id: Int @doc(description: "The ID number assigned to the product") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\EntityIdToId") + id: Int @doc(description: "The ID number assigned to the product.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\EntityIdToId") name: String @doc(description: "The product name. Customers use this name to identify the product.") - sku: String @doc(description: "A number or code assigned to a product to identify the product, options, price, and manufacturer") + sku: String @doc(description: "A number or code assigned to a product to identify the product, options, price, and manufacturer.") description: ComplexTextValue @doc(description: "Detailed information about the product. The value can include simple HTML tags.") @resolver(class: "\\Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\ProductComplexTextAttribute") short_description: ComplexTextValue @doc(description: "A short description of the product. Its use depends on the theme.") @resolver(class: "\\Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\ProductComplexTextAttribute") - special_price: Float @doc(description: "The discounted price of the product") - special_from_date: String @doc(description: "The beginning date that a product has a special price") - special_to_date: String @doc(description: "The end date that a product has a special price") - attribute_set_id: Int @doc(description: "The attribute set assigned to the product") - meta_title: String @doc(description: "A string that is displayed in the title bar and tab of the browser and in search results lists") - meta_keyword: String @doc(description: "A comma-separated list of keywords that are visible only to search engines") - meta_description: String @doc(description: "A brief overview of the product for search results listings, maximum 255 characters") - image: ProductImage @doc(description: "The relative path to the main image on the product page") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\ProductImage") - small_image: ProductImage @doc(description: "The relative path to the small image, which is used on catalog pages") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\ProductImage") - thumbnail: ProductImage @doc(description: "The relative path to the product's thumbnail image") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\ProductImage") - new_from_date: String @doc(description: "The beginning date for new product listings, and determines if the product is featured as a new product") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\NewFromTo") - new_to_date: String @doc(description: "The end date for new product listings") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\NewFromTo") - tier_price: Float @doc(description: "The price when tier pricing is in effect and the items purchased threshold has been reached") - options_container: String @doc(description: "If the product has multiple options, determines where they appear on the product page") - created_at: String @doc(description: "Timestamp indicating when the product was created") - updated_at: String @doc(description: "Timestamp indicating when the product was updated") - country_of_manufacture: String @doc(description: "The product's country of origin") - type_id: String @doc(description: "One of simple, virtual, bundle, downloadable, grouped, or configurable") - websites: [Website] @doc(description: "An array of websites in which the product is available") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\Websites") - product_links: [ProductLinksInterface] @doc(description: "An array of ProductLinks objects") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\ProductLinks") - media_gallery_entries: [MediaGalleryEntry] @doc(description: "An array of MediaGalleryEntry objects") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\MediaGalleryEntries") - tier_prices: [ProductTierPrices] @doc(description: "An array of ProductTierPrices objects") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\TierPrices") - price: ProductPrices @doc(description: "A ProductPrices object, indicating the price of an item") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\Price") - gift_message_available: String @doc(description: "Indicates whether a gift message is available") - manufacturer: Int @doc(description: "A number representing the product's manufacturer") - categories: [CategoryInterface] @doc(description: "The categories assigned to a product") @cache(cacheTag: "cat_c", cacheIdentity: "Magento\\CatalogGraphQl\\Model\\Resolver\\Category\\CategoriesIdentity") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Categories") - canonical_url: String @doc(description: "Canonical URL") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\CanonicalUrl") -} - -interface PhysicalProductInterface @typeResolver(class: "Magento\\CatalogGraphQl\\Model\\ProductInterfaceTypeResolverComposite") @doc(description: "PhysicalProductInterface contains attributes specific to tangible products") { - weight: Float @doc(description: "The weight of the item, in units defined by the store") -} - -type CustomizableAreaOption implements CustomizableOptionInterface @doc(description: "CustomizableAreaOption contains information about a text area that is defined as part of a customizable option") { - value: CustomizableAreaValue @doc(description: "An object that defines a text area") - product_sku: String @doc(description: "The Stock Keeping Unit of the base product") -} - -type CustomizableAreaValue @doc(description: "CustomizableAreaValue defines the price and sku of a product whose page contains a customized text area") { - price: Float @doc(description: "The price assigned to this option") - price_type: PriceTypeEnum @doc(description: "FIXED, PERCENT, or DYNAMIC") - sku: String @doc(description: "The Stock Keeping Unit for this option") - max_characters: Int @doc(description: "The maximum number of characters that can be entered for this customizable option") -} - -type CategoryTree implements CategoryInterface @doc(description: "Category Tree implementation") { - children: [CategoryTree] @doc(description: "Child categories tree") @resolve(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\CategoryTree") -} - -type CustomizableDateOption implements CustomizableOptionInterface @doc(description: "CustomizableDateOption contains information about a date picker that is defined as part of a customizable option") { + special_price: Float @doc(description: "The discounted price of the product.") + special_from_date: String @doc(description: "The beginning date that a product has a special price.") + special_to_date: String @doc(description: "The end date that a product has a special price.") + attribute_set_id: Int @doc(description: "The attribute set assigned to the product.") + meta_title: String @doc(description: "A string that is displayed in the title bar and tab of the browser and in search results lists.") + meta_keyword: String @doc(description: "A comma-separated list of keywords that are visible only to search engines.") + meta_description: String @doc(description: "A brief overview of the product for search results listings, maximum 255 characters.") + image: ProductImage @doc(description: "The relative path to the main image on the product page.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\ProductImage") + small_image: ProductImage @doc(description: "The relative path to the small image, which is used on catalog pages.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\ProductImage") + thumbnail: ProductImage @doc(description: "The relative path to the product's thumbnail image.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\ProductImage") + new_from_date: String @doc(description: "The beginning date for new product listings, and determines if the product is featured as a new product.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\NewFromTo") + new_to_date: String @doc(description: "The end date for new product listings.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\NewFromTo") + tier_price: Float @doc(description: "The price when tier pricing is in effect and the items purchased threshold has been reached.") + options_container: String @doc(description: "If the product has multiple options, determines where they appear on the product page.") + created_at: String @doc(description: "Timestamp indicating when the product was created.") + updated_at: String @doc(description: "Timestamp indicating when the product was updated.") + country_of_manufacture: String @doc(description: "The product's country of origin.") + type_id: String @doc(description: "One of simple, virtual, bundle, downloadable, grouped, or configurable.") + websites: [Website] @doc(description: "An array of websites in which the product is available.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\Websites") + product_links: [ProductLinksInterface] @doc(description: "An array of ProductLinks objects.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\ProductLinks") + media_gallery_entries: [MediaGalleryEntry] @doc(description: "An array of MediaGalleryEntry objects.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\MediaGalleryEntries") + tier_prices: [ProductTierPrices] @doc(description: "An array of ProductTierPrices objects.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\TierPrices") + price: ProductPrices @doc(description: "A ProductPrices object, indicating the price of an item.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\Price") + gift_message_available: String @doc(description: "Indicates whether a gift message is available.") + manufacturer: Int @doc(description: "A number representing the product's manufacturer.") + categories: [CategoryInterface] @doc(description: "The categories assigned to a product.") @cache(cacheTag: "cat_c", cacheIdentity: "Magento\\CatalogGraphQl\\Model\\Resolver\\Category\\CategoriesIdentity") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Categories") + canonical_url: String @doc(description: "Canonical URL.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\CanonicalUrl") +} + +interface PhysicalProductInterface @typeResolver(class: "Magento\\CatalogGraphQl\\Model\\ProductInterfaceTypeResolverComposite") @doc(description: "PhysicalProductInterface contains attributes specific to tangible products.") { + weight: Float @doc(description: "The weight of the item, in units defined by the store.") +} + +type CustomizableAreaOption implements CustomizableOptionInterface @doc(description: "CustomizableAreaOption contains information about a text area that is defined as part of a customizable option.") { + value: CustomizableAreaValue @doc(description: "An object that defines a text area.") + product_sku: String @doc(description: "The Stock Keeping Unit of the base product.") +} + +type CustomizableAreaValue @doc(description: "CustomizableAreaValue defines the price and sku of a product whose page contains a customized text area.") { + price: Float @doc(description: "The price assigned to this option.") + price_type: PriceTypeEnum @doc(description: "FIXED, PERCENT, or DYNAMIC.") + sku: String @doc(description: "The Stock Keeping Unit for this option.") + max_characters: Int @doc(description: "The maximum number of characters that can be entered for this customizable option.") +} + +type CategoryTree implements CategoryInterface @doc(description: "Category Tree implementation.") { + children: [CategoryTree] @doc(description: "Child categories tree.") @resolve(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\CategoryTree") +} + +type CustomizableDateOption implements CustomizableOptionInterface @doc(description: "CustomizableDateOption contains information about a date picker that is defined as part of a customizable option.") { value: CustomizableDateValue @doc(description: "An object that defines a date field in a customizable option.") - product_sku: String @doc(description: "The Stock Keeping Unit of the base product") + product_sku: String @doc(description: "The Stock Keeping Unit of the base product.") } -type CustomizableDateValue @doc(description: "CustomizableDateValue defines the price and sku of a product whose page contains a customized date picker") { - price: Float @doc(description: "The price assigned to this option") - price_type: PriceTypeEnum @doc(description: "FIXED, PERCENT, or DYNAMIC") - sku: String @doc(description: "The Stock Keeping Unit for this option") +type CustomizableDateValue @doc(description: "CustomizableDateValue defines the price and sku of a product whose page contains a customized date picker.") { + price: Float @doc(description: "The price assigned to this option.") + price_type: PriceTypeEnum @doc(description: "FIXED, PERCENT, or DYNAMIC.") + sku: String @doc(description: "The Stock Keeping Unit for this option.") } -type CustomizableDropDownOption implements CustomizableOptionInterface @doc(description: "CustomizableDropDownOption contains information about a drop down menu that is defined as part of a customizable option") { - value: [CustomizableDropDownValue] @doc(description: "An array that defines the set of options for a drop down menu") +type CustomizableDropDownOption implements CustomizableOptionInterface @doc(description: "CustomizableDropDownOption contains information about a drop down menu that is defined as part of a customizable option.") { + value: [CustomizableDropDownValue] @doc(description: "An array that defines the set of options for a drop down menu.") } -type CustomizableDropDownValue @doc(description: "CustomizableDropDownValue defines the price and sku of a product whose page contains a customized drop down menu") { - option_type_id: Int @doc(description: "The ID assigned to the value") - price: Float @doc(description: "The price assigned to this option") - price_type: PriceTypeEnum @doc(description: "FIXED, PERCENT, or DYNAMIC") - sku: String @doc(description: "The Stock Keeping Unit for this option") - title: String @doc(description: "The display name for this option") - sort_order: Int @doc(description: "The order in which the option is displayed") +type CustomizableDropDownValue @doc(description: "CustomizableDropDownValue defines the price and sku of a product whose page contains a customized drop down menu.") { + option_type_id: Int @doc(description: "The ID assigned to the value.") + price: Float @doc(description: "The price assigned to this option.") + price_type: PriceTypeEnum @doc(description: "FIXED, PERCENT, or DYNAMIC.") + sku: String @doc(description: "The Stock Keeping Unit for this option.") + title: String @doc(description: "The display name for this option.") + sort_order: Int @doc(description: "The order in which the option is displayed.") } -type CustomizableMultipleOption implements CustomizableOptionInterface @doc(description: "CustomizableMultipleOption contains information about a multiselect that is defined as part of a customizable option") { - value: [CustomizableMultipleValue] @doc(description: "An array that defines the set of options for a multiselect") +type CustomizableMultipleOption implements CustomizableOptionInterface @doc(description: "CustomizableMultipleOption contains information about a multiselect that is defined as part of a customizable option.") { + value: [CustomizableMultipleValue] @doc(description: "An array that defines the set of options for a multiselect.") } -type CustomizableMultipleValue @doc(description: "CustomizableMultipleValue defines the price and sku of a product whose page contains a customized multiselect") { - option_type_id: Int @doc(description: "The ID assigned to the value") - price: Float @doc(description: "The price assigned to this option") - price_type: PriceTypeEnum @doc(description: "FIXED, PERCENT, or DYNAMIC") - sku: String @doc(description: "The Stock Keeping Unit for this option") - title: String @doc(description: "The display name for this option") - sort_order: Int @doc(description: "The order in which the option is displayed") +type CustomizableMultipleValue @doc(description: "CustomizableMultipleValue defines the price and sku of a product whose page contains a customized multiselect.") { + option_type_id: Int @doc(description: "The ID assigned to the value.") + price: Float @doc(description: "The price assigned to this option.") + price_type: PriceTypeEnum @doc(description: "FIXED, PERCENT, or DYNAMIC.") + sku: String @doc(description: "The Stock Keeping Unit for this option.") + title: String @doc(description: "The display name for this option.") + sort_order: Int @doc(description: "The order in which the option is displayed.") } -type CustomizableFieldOption implements CustomizableOptionInterface @doc(description: "CustomizableFieldOption contains information about a text field that is defined as part of a customizable option") { - value: CustomizableFieldValue @doc(description: "An object that defines a text field") - product_sku: String @doc(description: "The Stock Keeping Unit of the base product") +type CustomizableFieldOption implements CustomizableOptionInterface @doc(description: "CustomizableFieldOption contains information about a text field that is defined as part of a customizable option.") { + value: CustomizableFieldValue @doc(description: "An object that defines a text field.") + product_sku: String @doc(description: "The Stock Keeping Unit of the base product.") } -type CustomizableFieldValue @doc(description: "CustomizableFieldValue defines the price and sku of a product whose page contains a customized text field") { - price: Float @doc(description: "The price of the custom value") - price_type: PriceTypeEnum @doc(description: "FIXED, PERCENT, or DYNAMIC") - sku: String @doc(description: "The Stock Keeping Unit for this option") - max_characters: Int @doc(description: "The maximum number of characters that can be entered for this customizable option") +type CustomizableFieldValue @doc(description: "CustomizableFieldValue defines the price and sku of a product whose page contains a customized text field.") { + price: Float @doc(description: "The price of the custom value.") + price_type: PriceTypeEnum @doc(description: "FIXED, PERCENT, or DYNAMIC.") + sku: String @doc(description: "The Stock Keeping Unit for this option.") + max_characters: Int @doc(description: "The maximum number of characters that can be entered for this customizable option.") } -type CustomizableFileOption implements CustomizableOptionInterface @doc(description: "CustomizableFileOption contains information about a file picker that is defined as part of a customizable option") { - value: CustomizableFileValue @doc(description: "An object that defines a file value") - product_sku: String @doc(description: "The Stock Keeping Unit of the base product") +type CustomizableFileOption implements CustomizableOptionInterface @doc(description: "CustomizableFileOption contains information about a file picker that is defined as part of a customizable option.") { + value: CustomizableFileValue @doc(description: "An object that defines a file value.") + product_sku: String @doc(description: "The Stock Keeping Unit of the base product.") } -type CustomizableFileValue @doc(description: "CustomizableFileValue defines the price and sku of a product whose page contains a customized file picker") { - price: Float @doc(description: "The price assigned to this option") - price_type: PriceTypeEnum @doc(description: "FIXED, PERCENT, or DYNAMIC") - sku: String @doc(description: "The Stock Keeping Unit for this option") - file_extension: String @doc(description: "The file extension to accept") - image_size_x: Int @doc(description: "The maximum width of an image") - image_size_y: Int @doc(description: "The maximum height of an image") +type CustomizableFileValue @doc(description: "CustomizableFileValue defines the price and sku of a product whose page contains a customized file picker.") { + price: Float @doc(description: "The price assigned to this option.") + price_type: PriceTypeEnum @doc(description: "FIXED, PERCENT, or DYNAMIC.") + sku: String @doc(description: "The Stock Keeping Unit for this option.") + file_extension: String @doc(description: "The file extension to accept.") + image_size_x: Int @doc(description: "The maximum width of an image.") + image_size_y: Int @doc(description: "The maximum height of an image.") } -type ProductImage @doc(description: "Product image information. Contains image relative path, URL and label") { +type ProductImage @doc(description: "Product image information. Contains image relative path, URL and label.") { url: String @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\ProductImage\\Url") label: String @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\ProductImage\\Label") } interface CustomizableOptionInterface @typeResolver(class: "Magento\\CatalogGraphQl\\Model\\CustomizableOptionTypeResolver") @doc(description: "The CustomizableOptionInterface contains basic information about a customizable option. It can be implemented by several types of configurable options.") { - title: String @doc(description: "The display name for this option") - required: Boolean @doc(description: "Indicates whether the option is required") - sort_order: Int @doc(description: "The order in which the option is displayed") - option_id: Int @doc(description: "Option ID") + title: String @doc(description: "The display name for this option.") + required: Boolean @doc(description: "Indicates whether the option is required.") + sort_order: Int @doc(description: "The order in which the option is displayed.") + option_id: Int @doc(description: "Option ID.") } interface CustomizableProductInterface @typeResolver(class: "Magento\\CatalogGraphQl\\Model\\ProductInterfaceTypeResolverComposite") @doc(description: "CustomizableProductInterface contains information about customizable product options.") { - options: [CustomizableOptionInterface] @doc(description: "An array of options for a customizable product") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\Options") -} - -interface CategoryInterface @typeResolver(class: "Magento\\CatalogGraphQl\\Model\\CategoryInterfaceTypeResolver") @doc(description: "CategoryInterface contains the full set of attributes that can be returned in a category search") { - id: Int @doc(description: "An ID that uniquely identifies the category") - description: String @doc(description: "An optional description of the category") @resolver(class: "\\Magento\\CatalogGraphQl\\Model\\Resolver\\Category\\CategoryHtmlAttribute") - name: String @doc(description: "The display name of the category") - path: String @doc(description: "Category Path") - path_in_store: String @doc(description: "Category path in store") - url_key: String @doc(description: "The url key assigned to the category") - url_path: String @doc(description: "The url path assigned to the category") - position: Int @doc(description: "The position of the category relative to other categories at the same level in tree") - level: Int @doc(description: "Indicates the depth of the category within the tree") - created_at: String @doc(description: "Timestamp indicating when the category was created") - updated_at: String @doc(description: "Timestamp indicating when the category was updated") - product_count: Int @doc(description: "The number of products in the category") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Category\\ProductsCount") - default_sort_by: String @doc(description: "The attribute to use for sorting") + options: [CustomizableOptionInterface] @doc(description: "An array of options for a customizable product.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\Options") +} + +interface CategoryInterface @typeResolver(class: "Magento\\CatalogGraphQl\\Model\\CategoryInterfaceTypeResolver") @doc(description: "CategoryInterface contains the full set of attributes that can be returned in a category search.") { + id: Int @doc(description: "An ID that uniquely identifies the category.") + description: String @doc(description: "An optional description of the category.") @resolver(class: "\\Magento\\CatalogGraphQl\\Model\\Resolver\\Category\\CategoryHtmlAttribute") + name: String @doc(description: "The display name of the category.") + path: String @doc(description: "Category Path.") + path_in_store: String @doc(description: "Category path in store.") + url_key: String @doc(description: "The url key assigned to the category.") + url_path: String @doc(description: "The url path assigned to the category.") + position: Int @doc(description: "The position of the category relative to other categories at the same level in tree.") + level: Int @doc(description: "Indicates the depth of the category within the tree.") + created_at: String @doc(description: "Timestamp indicating when the category was created.") + updated_at: String @doc(description: "Timestamp indicating when the category was updated.") + product_count: Int @doc(description: "The number of products in the category.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Category\\ProductsCount") + default_sort_by: String @doc(description: "The attribute to use for sorting.") products( pageSize: Int = 20 @doc(description: "Specifies the maximum number of results to return at once. This attribute is optional."), currentPage: Int = 1 @doc(description: "Specifies which page of results to return. The default value is 1."), sort: ProductSortInput @doc(description: "Specifies which attribute to sort on, and whether to return the results in ascending or descending order.") - ): CategoryProducts @doc(description: "The list of products assigned to the category") @cache(cacheTag: "cat_p", cacheIdentity: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\Identity") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Category\\Products") - breadcrumbs: [Breadcrumb] @doc(description: "Breadcrumbs, parent categories info") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Category\\Breadcrumbs") + ): CategoryProducts @doc(description: "The list of products assigned to the category.") @cache(cacheTag: "cat_p", cacheIdentity: "Magento\\CatalogGraphQl\\Model\\Resolver\\Product\\Identity") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Category\\Products") + breadcrumbs: [Breadcrumb] @doc(description: "Breadcrumbs, parent categories info.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Category\\Breadcrumbs") } -type Breadcrumb @doc(description: "Breadcrumb item"){ - category_id: Int @doc(description: "Category ID") - category_name: String @doc(description: "Category name") - category_level: Int @doc(description: "Category level") - category_url_key: String @doc(description: "Category URL key") +type Breadcrumb @doc(description: "Breadcrumb item."){ + category_id: Int @doc(description: "Category ID.") + category_name: String @doc(description: "Category name.") + category_level: Int @doc(description: "Category level.") + category_url_key: String @doc(description: "Category URL key.") } -type CustomizableRadioOption implements CustomizableOptionInterface @doc(description: "CustomizableRadioOption contains information about a set of radio buttons that are defined as part of a customizable option") { - value: [CustomizableRadioValue] @doc(description: "An array that defines a set of radio buttons") +type CustomizableRadioOption implements CustomizableOptionInterface @doc(description: "CustomizableRadioOption contains information about a set of radio buttons that are defined as part of a customizable option.") { + value: [CustomizableRadioValue] @doc(description: "An array that defines a set of radio buttons.") } -type CustomizableRadioValue @doc(description: "CustomizableRadioValue defines the price and sku of a product whose page contains a customized set of radio buttons") { - option_type_id: Int @doc(description: "The ID assigned to the value") - price: Float @doc(description: "The price assigned to this option") - price_type: PriceTypeEnum @doc(description: "FIXED, PERCENT, or DYNAMIC") - sku: String @doc(description: "The Stock Keeping Unit for this option") - title: String @doc(description: "The display name for this option") - sort_order: Int @doc(description: "The order in which the radio button is displayed") +type CustomizableRadioValue @doc(description: "CustomizableRadioValue defines the price and sku of a product whose page contains a customized set of radio buttons.") { + option_type_id: Int @doc(description: "The ID assigned to the value.") + price: Float @doc(description: "The price assigned to this option.") + price_type: PriceTypeEnum @doc(description: "FIXED, PERCENT, or DYNAMIC.") + sku: String @doc(description: "The Stock Keeping Unit for this option.") + title: String @doc(description: "The display name for this option.") + sort_order: Int @doc(description: "The order in which the radio button is displayed.") } -type CustomizableCheckboxOption implements CustomizableOptionInterface @doc(description: "CustomizableCheckbbixOption contains information about a set of checkbox values that are defined as part of a customizable option") { - value: [CustomizableCheckboxValue] @doc(description: "An array that defines a set of checkbox values") +type CustomizableCheckboxOption implements CustomizableOptionInterface @doc(description: "CustomizableCheckbbixOption contains information about a set of checkbox values that are defined as part of a customizable option.") { + value: [CustomizableCheckboxValue] @doc(description: "An array that defines a set of checkbox values.") } -type CustomizableCheckboxValue @doc(description: "CustomizableCheckboxValue defines the price and sku of a product whose page contains a customized set of checkbox values") { - option_type_id: Int @doc(description: "The ID assigned to the value") - price: Float @doc(description: "The price assigned to this option") - price_type: PriceTypeEnum @doc(description: "FIXED, PERCENT, or DYNAMIC") - sku: String @doc(description: "The Stock Keeping Unit for this option") - title: String @doc(description: "The display name for this option") - sort_order: Int @doc(description: "The order in which the checkbox value is displayed") +type CustomizableCheckboxValue @doc(description: "CustomizableCheckboxValue defines the price and sku of a product whose page contains a customized set of checkbox values.") { + option_type_id: Int @doc(description: "The ID assigned to the value.") + price: Float @doc(description: "The price assigned to this option.") + price_type: PriceTypeEnum @doc(description: "FIXED, PERCENT, or DYNAMIC.") + sku: String @doc(description: "The Stock Keeping Unit for this option.") + title: String @doc(description: "The display name for this option.") + sort_order: Int @doc(description: "The order in which the checkbox value is displayed.") } -type VirtualProduct implements ProductInterface, CustomizableProductInterface @doc(description: "A virtual product is non-tangible product that does not require shipping and is not kept in inventory") { +type VirtualProduct implements ProductInterface, CustomizableProductInterface @doc(description: "A virtual product is non-tangible product that does not require shipping and is not kept in inventory.") { } -type SimpleProduct implements ProductInterface, PhysicalProductInterface, CustomizableProductInterface @doc(description: "A simple product is tangible and are usually sold as single units or in fixed quantities") +type SimpleProduct implements ProductInterface, PhysicalProductInterface, CustomizableProductInterface @doc(description: "A simple product is tangible and are usually sold as single units or in fixed quantities.") { } -type Products @doc(description: "The Products object is the top-level object returned in a product search") { - items: [ProductInterface] @doc(description: "An array of products that match the specified search criteria") - page_info: SearchResultPageInfo @doc(description: "An object that includes the page_info and currentPage values specified in the query") - total_count: Int @doc(description: "The number of products returned") - filters: [LayerFilter] @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\LayerFilters") @doc(description: "Layered navigation filters array") - sort_fields: SortFields @doc(description: "An object that includes the default sort field and all available sort fields") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Category\\SortFields") +type Products @doc(description: "The Products object is the top-level object returned in a product search.") { + items: [ProductInterface] @doc(description: "An array of products that match the specified search criteria.") + page_info: SearchResultPageInfo @doc(description: "An object that includes the page_info and currentPage values specified in the query.") + total_count: Int @doc(description: "The number of products returned.") + filters: [LayerFilter] @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\LayerFilters") @doc(description: "Layered navigation filters array.") + sort_fields: SortFields @doc(description: "An object that includes the default sort field and all available sort fields.") @resolver(class: "Magento\\CatalogGraphQl\\Model\\Resolver\\Category\\SortFields") } -type CategoryProducts @doc(description: "The category products object returned in the Category query") { - items: [ProductInterface] @doc(description: "An array of products that are assigned to the category") - page_info: SearchResultPageInfo @doc(description: "An object that includes the page_info and currentPage values specified in the query") - total_count: Int @doc(description: "The number of products returned") +type CategoryProducts @doc(description: "The category products object returned in the Category query.") { + items: [ProductInterface] @doc(description: "An array of products that are assigned to the category.") + page_info: SearchResultPageInfo @doc(description: "An object that includes the page_info and currentPage values specified in the query.") + total_count: Int @doc(description: "The number of products returned.") } input ProductFilterInput @doc(description: "ProductFilterInput defines the filters to be used in the search. A filter contains at least one attribute, a comparison operator, and the value that is being searched for.") { name: FilterTypeInput @doc(description: "The product name. Customers use this name to identify the product.") - sku: FilterTypeInput @doc(description: "A number or code assigned to a product to identify the product, options, price, and manufacturer") + sku: FilterTypeInput @doc(description: "A number or code assigned to a product to identify the product, options, price, and manufacturer.") description: FilterTypeInput @doc(description: "Detailed information about the product. The value can include simple HTML tags.") short_description: FilterTypeInput @doc(description: "A short description of the product. Its use depends on the theme.") - price: FilterTypeInput @doc(description: "The price of an item") + price: FilterTypeInput @doc(description: "The price of an item.") special_price: FilterTypeInput @doc(description: "The discounted price of the product. Do not include the currency code.") - special_from_date: FilterTypeInput @doc(description: "The beginning date that a product has a special price") - special_to_date: FilterTypeInput @doc(description: "The end date that a product has a special price") - weight: FilterTypeInput @doc(description: "The weight of the item, in units defined by the store") - manufacturer: FilterTypeInput @doc(description: "A number representing the product's manufacturer") - meta_title: FilterTypeInput @doc(description: "A string that is displayed in the title bar and tab of the browser and in search results lists") - meta_keyword: FilterTypeInput @doc(description: "A comma-separated list of keywords that are visible only to search engines") - meta_description: FilterTypeInput @doc(description: "A brief overview of the product for search results listings, maximum 255 characters") - image: FilterTypeInput @doc(description: "The relative path to the main image on the product page") - small_image: FilterTypeInput @doc(description: "The relative path to the small image, which is used on catalog pages") - thumbnail: FilterTypeInput @doc(description: "The relative path to the product's thumbnail image") - tier_price: FilterTypeInput @doc(description: "The price when tier pricing is in effect and the items purchased threshold has been reached") - news_from_date: FilterTypeInput @doc(description: "The beginning date for new product listings, and determines if the product is featured as a new product") - news_to_date: FilterTypeInput @doc(description: "The end date for new product listings") - custom_layout_update: FilterTypeInput @doc(description: "XML code that is applied as a layout update to the product page") + special_from_date: FilterTypeInput @doc(description: "The beginning date that a product has a special price.") + special_to_date: FilterTypeInput @doc(description: "The end date that a product has a special price.") + weight: FilterTypeInput @doc(description: "The weight of the item, in units defined by the store.") + manufacturer: FilterTypeInput @doc(description: "A number representing the product's manufacturer.") + meta_title: FilterTypeInput @doc(description: "A string that is displayed in the title bar and tab of the browser and in search results lists.") + meta_keyword: FilterTypeInput @doc(description: "A comma-separated list of keywords that are visible only to search engines.") + meta_description: FilterTypeInput @doc(description: "A brief overview of the product for search results listings, maximum 255 characters.") + image: FilterTypeInput @doc(description: "The relative path to the main image on the product page.") + small_image: FilterTypeInput @doc(description: "The relative path to the small image, which is used on catalog pages.") + thumbnail: FilterTypeInput @doc(description: "The relative path to the product's thumbnail image.") + tier_price: FilterTypeInput @doc(description: "The price when tier pricing is in effect and the items purchased threshold has been reached.") + news_from_date: FilterTypeInput @doc(description: "The beginning date for new product listings, and determines if the product is featured as a new product.") + news_to_date: FilterTypeInput @doc(description: "The end date for new product listings.") + custom_layout_update: FilterTypeInput @doc(description: "XML code that is applied as a layout update to the product page.") min_price: FilterTypeInput @doc(description:"The numeric minimal price of the product. Do not include the currency code.") max_price: FilterTypeInput @doc(description:"The numeric maximal price of the product. Do not include the currency code.") - category_id: FilterTypeInput @doc(description: "Category ID the product belongs to") - options_container: FilterTypeInput @doc(description: "If the product has multiple options, determines where they appear on the product page") - required_options: FilterTypeInput @doc(description: "Indicates whether the product has required options") - has_options: FilterTypeInput @doc(description: "Indicates whether additional attributes have been created for the product") - image_label: FilterTypeInput @doc(description: "The label assigned to a product image") - small_image_label: FilterTypeInput @doc(description: "The label assigned to a product's small image") - thumbnail_label: FilterTypeInput @doc(description: "The label assigned to a product's thumbnail image") - created_at: FilterTypeInput @doc(description: "Timestamp indicating when the product was created") - updated_at: FilterTypeInput @doc(description: "Timestamp indicating when the product was updated") - country_of_manufacture: FilterTypeInput @doc(description: "The product's country of origin") - custom_layout: FilterTypeInput @doc(description: "The name of a custom layout") - gift_message_available: FilterTypeInput @doc(description: "Indicates whether a gift message is available") - or: ProductFilterInput @doc(description: "The keyword required to perform a logical OR comparison") -} - -type ProductMediaGalleryEntriesContent @doc(description: "ProductMediaGalleryEntriesContent contains an image in base64 format and basic information about the image") { - base64_encoded_data: String @doc(description: "The image in base64 format") - type: String @doc(description: "The MIME type of the file, such as image/png") - name: String @doc(description: "The file name of the image") -} - -type ProductMediaGalleryEntriesVideoContent @doc(description: "ProductMediaGalleryEntriesVideoContent contains a link to a video file and basic information about the video") { - media_type: String @doc(description: "Must be external-video") - video_provider: String @doc(description: "Describes the video source") - video_url: String @doc(description: "The URL to the video") - video_title: String @doc(description: "The title of the video") - video_description: String @doc(description: "A description of the video") - video_metadata: String @doc(description: "Optional data about the video") -} - -input ProductSortInput @doc(description: "ProductSortInput specifies the attribute to use for sorting search results and indicates whether the results are sorted in ascending or descending order") { + category_id: FilterTypeInput @doc(description: "Category ID the product belongs to.") + options_container: FilterTypeInput @doc(description: "If the product has multiple options, determines where they appear on the product page.") + required_options: FilterTypeInput @doc(description: "Indicates whether the product has required options.") + has_options: FilterTypeInput @doc(description: "Indicates whether additional attributes have been created for the product.") + image_label: FilterTypeInput @doc(description: "The label assigned to a product image.") + small_image_label: FilterTypeInput @doc(description: "The label assigned to a product's small image.") + thumbnail_label: FilterTypeInput @doc(description: "The label assigned to a product's thumbnail image.") + created_at: FilterTypeInput @doc(description: "Timestamp indicating when the product was created.") + updated_at: FilterTypeInput @doc(description: "Timestamp indicating when the product was updated.") + country_of_manufacture: FilterTypeInput @doc(description: "The product's country of origin.") + custom_layout: FilterTypeInput @doc(description: "The name of a custom layout.") + gift_message_available: FilterTypeInput @doc(description: "Indicates whether a gift message is available.") + or: ProductFilterInput @doc(description: "The keyword required to perform a logical OR comparison.") +} + +type ProductMediaGalleryEntriesContent @doc(description: "ProductMediaGalleryEntriesContent contains an image in base64 format and basic information about the image.") { + base64_encoded_data: String @doc(description: "The image in base64 format.") + type: String @doc(description: "The MIME type of the file, such as image/png.") + name: String @doc(description: "The file name of the image.") +} + +type ProductMediaGalleryEntriesVideoContent @doc(description: "ProductMediaGalleryEntriesVideoContent contains a link to a video file and basic information about the video.") { + media_type: String @doc(description: "Must be external-video.") + video_provider: String @doc(description: "Describes the video source.") + video_url: String @doc(description: "The URL to the video.") + video_title: String @doc(description: "The title of the video.") + video_description: String @doc(description: "A description of the video.") + video_metadata: String @doc(description: "Optional data about the video.") +} + +input ProductSortInput @doc(description: "ProductSortInput specifies the attribute to use for sorting search results and indicates whether the results are sorted in ascending or descending order.") { name: SortEnum @doc(description: "The product name. Customers use this name to identify the product.") - sku: SortEnum @doc(description: "A number or code assigned to a product to identify the product, options, price, and manufacturer") + sku: SortEnum @doc(description: "A number or code assigned to a product to identify the product, options, price, and manufacturer.") description: SortEnum @doc(description: "Detailed information about the product. The value can include simple HTML tags.") short_description: SortEnum @doc(description: "A short description of the product. Its use depends on the theme.") - price: SortEnum @doc(description: "The price of the item") - special_price: SortEnum @doc(description: "The discounted price of the product") - special_from_date: SortEnum @doc(description: "The beginning date that a product has a special price") - special_to_date: SortEnum @doc(description: "The end date that a product has a special price") - weight: SortEnum @doc(description: "The weight of the item, in units defined by the store") - manufacturer: SortEnum @doc(description: "A number representing the product's manufacturer") - meta_title: SortEnum @doc(description: "A string that is displayed in the title bar and tab of the browser and in search results lists") - meta_keyword: SortEnum @doc(description: "A comma-separated list of keywords that are visible only to search engines") - meta_description: SortEnum @doc(description: "A brief overview of the product for search results listings, maximum 255 characters") - image: SortEnum @doc(description: "The relative path to the main image on the product page") - small_image: SortEnum @doc(description: "The relative path to the small image, which is used on catalog pages") - thumbnail: SortEnum @doc(description: "The relative path to the product's thumbnail image") - tier_price: SortEnum @doc(description: "The price when tier pricing is in effect and the items purchased threshold has been reached") - news_from_date: SortEnum @doc(description: "The beginning date for new product listings, and determines if the product is featured as a new product") - news_to_date: SortEnum @doc(description: "The end date for new product listings") - custom_layout_update: SortEnum @doc(description: "XML code that is applied as a layout update to the product page") - options_container: SortEnum @doc(description: "If the product has multiple options, determines where they appear on the product page") - required_options: SortEnum @doc(description: "Indicates whether the product has required options") - has_options: SortEnum @doc(description: "Indicates whether additional attributes have been created for the product") - image_label: SortEnum @doc(description: "The label assigned to a product image") - small_image_label: SortEnum @doc(description: "The label assigned to a product's small image") - thumbnail_label: SortEnum @doc(description: "The label assigned to a product's thumbnail image") - created_at: SortEnum @doc(description: "Timestamp indicating when the product was created") - updated_at: SortEnum @doc(description: "Timestamp indicating when the product was updated") - country_of_manufacture: SortEnum @doc(description: "The product's country of origin") - custom_layout: SortEnum @doc(description: "The name of a custom layout") - gift_message_available: SortEnum @doc(description: "Indicates whether a gift message is available") -} - -type MediaGalleryEntry @doc(description: "MediaGalleryEntry defines characteristics about images and videos associated with a specific product") { - id: Int @doc(description: "The identifier assigned to the object") - media_type: String @doc(description: "image or video") - label: String @doc(description: "The alt text displayed on the UI when the user points to the image") - position: Int @doc(description: "The media item's position after it has been sorted") - disabled: Boolean @doc(description: "Whether the image is hidden from vie") - types: [String] @doc(description: "Array of image types. It can have the following values: image, small_image, thumbnail") - file: String @doc(description: "The path of the image on the server") - content: ProductMediaGalleryEntriesContent @doc(description: "Contains a ProductMediaGalleryEntriesContent object") - video_content: ProductMediaGalleryEntriesVideoContent @doc(description: "Contains a ProductMediaGalleryEntriesVideoContent object") + price: SortEnum @doc(description: "The price of the item.") + special_price: SortEnum @doc(description: "The discounted price of the product.") + special_from_date: SortEnum @doc(description: "The beginning date that a product has a special price.") + special_to_date: SortEnum @doc(description: "The end date that a product has a special price.") + weight: SortEnum @doc(description: "The weight of the item, in units defined by the store.") + manufacturer: SortEnum @doc(description: "A number representing the product's manufacturer.") + meta_title: SortEnum @doc(description: "A string that is displayed in the title bar and tab of the browser and in search results lists.") + meta_keyword: SortEnum @doc(description: "A comma-separated list of keywords that are visible only to search engines.") + meta_description: SortEnum @doc(description: "A brief overview of the product for search results listings, maximum 255 characters.") + image: SortEnum @doc(description: "The relative path to the main image on the product page.") + small_image: SortEnum @doc(description: "The relative path to the small image, which is used on catalog pages.") + thumbnail: SortEnum @doc(description: "The relative path to the product's thumbnail image.") + tier_price: SortEnum @doc(description: "The price when tier pricing is in effect and the items purchased threshold has been reached.") + news_from_date: SortEnum @doc(description: "The beginning date for new product listings, and determines if the product is featured as a new product.") + news_to_date: SortEnum @doc(description: "The end date for new product listings.") + custom_layout_update: SortEnum @doc(description: "XML code that is applied as a layout update to the product page.") + options_container: SortEnum @doc(description: "If the product has multiple options, determines where they appear on the product page.") + required_options: SortEnum @doc(description: "Indicates whether the product has required options.") + has_options: SortEnum @doc(description: "Indicates whether additional attributes have been created for the product.") + image_label: SortEnum @doc(description: "The label assigned to a product image.") + small_image_label: SortEnum @doc(description: "The label assigned to a product's small image.") + thumbnail_label: SortEnum @doc(description: "The label assigned to a product's thumbnail image.") + created_at: SortEnum @doc(description: "Timestamp indicating when the product was created.") + updated_at: SortEnum @doc(description: "Timestamp indicating when the product was updated.") + country_of_manufacture: SortEnum @doc(description: "The product's country of origin.") + custom_layout: SortEnum @doc(description: "The name of a custom layout.") + gift_message_available: SortEnum @doc(description: "Indicates whether a gift message is available.") +} + +type MediaGalleryEntry @doc(description: "MediaGalleryEntry defines characteristics about images and videos associated with a specific product.") { + id: Int @doc(description: "The identifier assigned to the object.") + media_type: String @doc(description: "image or video.") + label: String @doc(description: "The alt text displayed on the UI when the user points to the image.") + position: Int @doc(description: "The media item's position after it has been sorted.") + disabled: Boolean @doc(description: "Whether the image is hidden from vie.") + types: [String] @doc(description: "Array of image types. It can have the following values: image, small_image, thumbnail.") + file: String @doc(description: "The path of the image on the server.") + content: ProductMediaGalleryEntriesContent @doc(description: "Contains a ProductMediaGalleryEntriesContent object.") + video_content: ProductMediaGalleryEntriesVideoContent @doc(description: "Contains a ProductMediaGalleryEntriesVideoContent object.") } type LayerFilter { - name: String @doc(description: "Layered navigation filter name") - request_var: String @doc(description: "Request variable name for filter query") - filter_items_count: Int @doc(description: "Count of filter items in filter group") - filter_items: [LayerFilterItemInterface] @doc(description: "Array of filter items") + name: String @doc(description: "Layered navigation filter name.") + request_var: String @doc(description: "Request variable name for filter query.") + filter_items_count: Int @doc(description: "Count of filter items in filter group.") + filter_items: [LayerFilterItemInterface] @doc(description: "Array of filter items.") } interface LayerFilterItemInterface @typeResolver(class: "Magento\\CatalogGraphQl\\Model\\LayerFilterItemTypeResolverComposite") { - label: String @doc(description: "Filter label") - value_string: String @doc(description: "Value for filter request variable to be used in query") - items_count: Int @doc(description: "Count of items by filter") + label: String @doc(description: "Filter label.") + value_string: String @doc(description: "Value for filter request variable to be used in query.") + items_count: Int @doc(description: "Count of items by filter.") } type LayerFilterItem implements LayerFilterItemInterface { @@ -393,11 +393,23 @@ type LayerFilterItem implements LayerFilterItemInterface { } type SortField { - value: String @doc(description: "Attribute code of sort field") - label: String @doc(description: "Label of sort field") + value: String @doc(description: "Attribute code of sort field.") + label: String @doc(description: "Label of sort field.") } -type SortFields @doc(description: "SortFields contains a default value for sort fields and all available sort fields") { - default: String @doc(description: "Default value of sort fields") - options: [SortField] @doc(description: "Available sort fields") +type SortFields @doc(description: "SortFields contains a default value for sort fields and all available sort fields.") { + default: String @doc(description: "Default value of sort fields.") + options: [SortField] @doc(description: "Available sort fields.") +} + +type StoreConfig @doc(description: "The type contains information about a store config.") { + product_url_suffix : String @doc(description: "Product URL Suffix.") + category_url_suffix : String @doc(description: "Category URL Suffix.") + title_separator : String @doc(description: "Page Title Separator.") + list_mode : String @doc(description: "List Mode.") + grid_per_page_values : String @doc(description: "Products per Page on Grid Allowed Values.") + list_per_page_values : String @doc(description: "Products per Page on List Allowed Values.") + grid_per_page : Int @doc(description: "Products per Page on Grid Default Value.") + list_per_page : Int @doc(description: "Products per Page on List Default Value.") + catalog_default_sort_by : String @doc(description: "Default Sort By.") } diff --git a/app/code/Magento/CatalogImportExport/Model/Import/Product.php b/app/code/Magento/CatalogImportExport/Model/Import/Product.php index edeb955b19c9b..0b7fbaf86826b 100644 --- a/app/code/Magento/CatalogImportExport/Model/Import/Product.php +++ b/app/code/Magento/CatalogImportExport/Model/Import/Product.php @@ -9,6 +9,7 @@ use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Catalog\Model\Config as CatalogConfig; use Magento\Catalog\Model\Product\Visibility; +use Magento\Catalog\Model\ResourceModel\Product\Link; use Magento\CatalogImportExport\Model\Import\Product\ImageTypeProcessor; use Magento\CatalogImportExport\Model\Import\Product\MediaGalleryProcessor; use Magento\CatalogImportExport\Model\Import\Product\RowValidatorInterface as ValidatorInterface; @@ -999,10 +1000,12 @@ public function setParameters(array $params) */ public function deleteProductsForReplacement() { - $this->setParameters(array_merge( - $this->getParameters(), - ['behavior' => Import::BEHAVIOR_DELETE] - )); + $this->setParameters( + array_merge( + $this->getParameters(), + ['behavior' => Import::BEHAVIOR_DELETE] + ) + ); $this->_deleteProducts(); return $this; @@ -1091,10 +1094,12 @@ protected function _replaceProducts() $this->deleteProductsForReplacement(); $this->_oldSku = $this->skuProcessor->reloadOldSkus()->getOldSkus(); $this->_validatedRows = null; - $this->setParameters(array_merge( - $this->getParameters(), - ['behavior' => Import::BEHAVIOR_APPEND] - )); + $this->setParameters( + array_merge( + $this->getParameters(), + ['behavior' => Import::BEHAVIOR_APPEND] + ) + ); $this->_saveProductsData(); return $this; @@ -1244,13 +1249,10 @@ protected function _prepareRowForDb(array $rowData) * Must be called after ALL products saving done. * * @return $this - * @SuppressWarnings(PHPMD.CyclomaticComplexity) - * @SuppressWarnings(PHPMD.NPathComplexity) - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) - * phpcs:disable Generic.Metrics.NestingLevel */ protected function _saveLinks() { + /** @var Link $resource */ $resource = $this->_linkFactory->create(); $mainTable = $resource->getMainTable(); $positionAttrId = []; @@ -1268,114 +1270,10 @@ protected function _saveLinks() $positionAttrId[$linkId] = $this->_connection->fetchOne($select, $bind); } while ($bunch = $this->_dataSourceModel->getNextBunch()) { - $productIds = []; - $linkRows = []; - $positionRows = []; - - foreach ($bunch as $rowNum => $rowData) { - if (!$this->isRowAllowedToImport($rowData, $rowNum)) { - continue; - } - - $sku = $rowData[self::COL_SKU]; - - $productId = $this->skuProcessor->getNewSku($sku)[$this->getProductEntityLinkField()]; - $productLinkKeys = []; - $select = $this->_connection->select()->from( - $resource->getTable('catalog_product_link'), - ['id' => 'link_id', 'linked_id' => 'linked_product_id', 'link_type_id' => 'link_type_id'] - )->where( - 'product_id = :product_id' - ); - $bind = [':product_id' => $productId]; - foreach ($this->_connection->fetchAll($select, $bind) as $linkData) { - $linkKey = "{$productId}-{$linkData['linked_id']}-{$linkData['link_type_id']}"; - $productLinkKeys[$linkKey] = $linkData['id']; - } - foreach ($this->_linkNameToId as $linkName => $linkId) { - $productIds[] = $productId; - if (isset($rowData[$linkName . 'sku'])) { - $linkSkus = explode($this->getMultipleValueSeparator(), $rowData[$linkName . 'sku']); - $linkPositions = !empty($rowData[$linkName . 'position']) - ? explode($this->getMultipleValueSeparator(), $rowData[$linkName . 'position']) - : []; - foreach ($linkSkus as $linkedKey => $linkedSku) { - $linkedSku = trim($linkedSku); - if (($this->skuProcessor->getNewSku($linkedSku) !== null || $this->isSkuExist($linkedSku)) - && strcasecmp($linkedSku, $sku) !== 0 - ) { - $newSku = $this->skuProcessor->getNewSku($linkedSku); - if (!empty($newSku)) { - $linkedId = $newSku['entity_id']; - } else { - $linkedId = $this->getExistingSku($linkedSku)['entity_id']; - } - - if ($linkedId == null) { - // Import file links to a SKU which is skipped for some reason, - // which leads to a "NULL" - // link causing fatal errors. - $this->_logger->critical( - new \Exception( - sprintf( - 'WARNING: Orphaned link skipped: From SKU %s (ID %d) to SKU %s, ' . - 'Link type id: %d', - $sku, - $productId, - $linkedSku, - $linkId - ) - ) - ); - continue; - } - - $linkKey = "{$productId}-{$linkedId}-{$linkId}"; - if (empty($productLinkKeys[$linkKey])) { - $productLinkKeys[$linkKey] = $nextLinkId; - } - if (!isset($linkRows[$linkKey])) { - $linkRows[$linkKey] = [ - 'link_id' => $productLinkKeys[$linkKey], - 'product_id' => $productId, - 'linked_product_id' => $linkedId, - 'link_type_id' => $linkId, - ]; - } - if (!empty($linkPositions[$linkedKey])) { - $positionRows[] = [ - 'link_id' => $productLinkKeys[$linkKey], - 'product_link_attribute_id' => $positionAttrId[$linkId], - 'value' => $linkPositions[$linkedKey], - ]; - } - $nextLinkId++; - } - } - } - } - } - if (Import::BEHAVIOR_APPEND != $this->getBehavior() && $productIds) { - $this->_connection->delete( - $mainTable, - $this->_connection->quoteInto('product_id IN (?)', array_unique($productIds)) - ); - } - if ($linkRows) { - $this->_connection->insertOnDuplicate($mainTable, $linkRows, ['link_id']); - } - if ($positionRows) { - // process linked product positions - $this->_connection->insertOnDuplicate( - $resource->getAttributeTypeTable('int'), - $positionRows, - ['value'] - ); - } + $this->processLinkBunches($bunch, $resource, $nextLinkId, $positionAttrId); } return $this; } - // phpcs:enable /** * Save product attributes. @@ -1610,7 +1508,6 @@ public function getImagesFromRow(array $rowData) * @SuppressWarnings(PHPMD.ExcessiveMethodLength) * @SuppressWarnings(PHPMD.UnusedLocalVariable) * @throws LocalizedException - * phpcs:disable Generic.Metrics.NestingLevel */ protected function _saveProducts() { @@ -1817,42 +1714,44 @@ protected function _saveProducts() $rowData[$column] = $uploadedFile; } - if ($uploadedFile && !isset($mediaGallery[$storeId][$rowSku][$uploadedFile])) { - if (isset($existingImages[$rowSku][$uploadedFile])) { - $currentFileData = $existingImages[$rowSku][$uploadedFile]; - if (isset($rowLabels[$column][$columnImageKey]) - && $rowLabels[$column][$columnImageKey] != - $currentFileData['label'] - ) { - $labelsForUpdate[] = [ - 'label' => $rowLabels[$column][$columnImageKey], - 'imageData' => $currentFileData - ]; - } - - if (array_key_exists($uploadedFile, $imageHiddenStates) - && $currentFileData['disabled'] != $imageHiddenStates[$uploadedFile] - ) { - $imagesForChangeVisibility[] = [ - 'disabled' => $imageHiddenStates[$uploadedFile], - 'imageData' => $currentFileData - ]; - } - } else { - if ($column == self::COL_MEDIA_IMAGE) { - $rowData[$column][] = $uploadedFile; - } - $mediaGallery[$storeId][$rowSku][$uploadedFile] = [ - 'attribute_id' => $this->getMediaGalleryAttributeId(), - 'label' => isset($rowLabels[$column][$columnImageKey]) - ? $rowLabels[$column][$columnImageKey] - : '', - 'position' => ++$position, - 'disabled' => isset($imageHiddenStates[$columnImage]) - ? $imageHiddenStates[$columnImage] : '0', - 'value' => $uploadedFile, + if (!$uploadedFile || isset($mediaGallery[$storeId][$rowSku][$uploadedFile])) { + continue; + } + + if (isset($existingImages[$rowSku][$uploadedFile])) { + $currentFileData = $existingImages[$rowSku][$uploadedFile]; + if (isset($rowLabels[$column][$columnImageKey]) + && $rowLabels[$column][$columnImageKey] != + $currentFileData['label'] + ) { + $labelsForUpdate[] = [ + 'label' => $rowLabels[$column][$columnImageKey], + 'imageData' => $currentFileData + ]; + } + + if (array_key_exists($uploadedFile, $imageHiddenStates) + && $currentFileData['disabled'] != $imageHiddenStates[$uploadedFile] + ) { + $imagesForChangeVisibility[] = [ + 'disabled' => $imageHiddenStates[$uploadedFile], + 'imageData' => $currentFileData ]; } + } else { + if ($column == self::COL_MEDIA_IMAGE) { + $rowData[$column][] = $uploadedFile; + } + $mediaGallery[$storeId][$rowSku][$uploadedFile] = [ + 'attribute_id' => $this->getMediaGalleryAttributeId(), + 'label' => isset($rowLabels[$column][$columnImageKey]) + ? $rowLabels[$column][$columnImageKey] + : '', + 'position' => ++$position, + 'disabled' => isset($imageHiddenStates[$columnImage]) + ? $imageHiddenStates[$columnImage] : '0', + 'value' => $uploadedFile, + ]; } } } @@ -1983,7 +1882,6 @@ protected function _saveProducts() return $this; } - // phpcs:enable /** * Prepare array with image states (visible or hidden from product page) @@ -2732,9 +2630,12 @@ public function parseMultiselectValues($values, $delimiter = self::PSEUDO_MULTI_ return explode($delimiter, $values); } if (preg_match_all('~"((?:[^"]|"")*)"~', $values, $matches)) { - return $values = array_map(function ($value) { - return str_replace('""', '"', $value); - }, $matches[1]); + return $values = array_map( + function ($value) { + return str_replace('""', '"', $value); + }, + $matches[1] + ); } return [$values]; } @@ -2905,7 +2806,8 @@ protected function getProductUrlSuffix($storeId = null) protected function getUrlKey($rowData) { if (!empty($rowData[self::URL_KEY])) { - return $this->productUrl->formatUrlKey($rowData[self::URL_KEY]); + $urlKey = (string) $rowData[self::URL_KEY]; + return trim(strtolower($urlKey)); } if (!empty($rowData[self::COL_NAME])) { @@ -3132,4 +3034,167 @@ private function getValidationErrorLevel($sku): string ? ProcessingError::ERROR_LEVEL_CRITICAL : ProcessingError::ERROR_LEVEL_NOT_CRITICAL; } + + /** + * Processes link bunches + * + * @param array $bunch + * @param Link $resource + * @param int $nextLinkId + * @param array $positionAttrId + * @return void + */ + private function processLinkBunches( + array $bunch, + Link $resource, + int $nextLinkId, + array $positionAttrId + ): void { + $productIds = []; + $linkRows = []; + $positionRows = []; + + $bunch = array_filter($bunch, [$this, 'isRowAllowedToImport'], ARRAY_FILTER_USE_BOTH); + foreach ($bunch as $rowData) { + $sku = $rowData[self::COL_SKU]; + $productId = $this->skuProcessor->getNewSku($sku)[$this->getProductEntityLinkField()]; + $productIds[] = $productId; + $productLinkKeys = $this->fetchProductLinks($resource, $productId); + $linkNameToId = array_filter( + $this->_linkNameToId, + function ($linkName) use ($rowData) { + return isset($rowData[$linkName . 'sku']); + }, + ARRAY_FILTER_USE_KEY + ); + foreach ($linkNameToId as $linkName => $linkId) { + $linkSkus = explode($this->getMultipleValueSeparator(), $rowData[$linkName . 'sku']); + $linkPositions = !empty($rowData[$linkName . 'position']) + ? explode($this->getMultipleValueSeparator(), $rowData[$linkName . 'position']) + : []; + + $linkSkus = array_filter( + $linkSkus, + function ($linkedSku) use ($sku) { + $linkedSku = trim($linkedSku); + return ($this->skuProcessor->getNewSku($linkedSku) !== null || $this->isSkuExist($linkedSku)) + && strcasecmp($linkedSku, $sku) !== 0; + } + ); + foreach ($linkSkus as $linkedKey => $linkedSku) { + $linkedId = $this->getProductLinkedId($linkedSku); + if ($linkedId == null) { + // Import file links to a SKU which is skipped for some reason, which leads to a "NULL" + // link causing fatal errors. + $formatStr = 'WARNING: Orphaned link skipped: From SKU %s (ID %d) to SKU %s, Link type id: %d'; + $exception = new \Exception(sprintf($formatStr, $sku, $productId, $linkedSku, $linkId)); + $this->_logger->critical($exception); + continue; + } + $linkKey = $this->composeLinkKey($productId, $linkedId, $linkId); + $productLinkKeys[$linkKey] = $productLinkKeys[$linkKey] ?? $nextLinkId; + + $linkRows[$linkKey] = $linkRows[$linkKey] ?? [ + 'link_id' => $productLinkKeys[$linkKey], + 'product_id' => $productId, + 'linked_product_id' => $linkedId, + 'link_type_id' => $linkId, + ]; + + if (!empty($linkPositions[$linkedKey])) { + $positionRows[] = [ + 'link_id' => $productLinkKeys[$linkKey], + 'product_link_attribute_id' => $positionAttrId[$linkId], + 'value' => $linkPositions[$linkedKey], + ]; + } + $nextLinkId++; + } + } + } + $this->saveLinksData($resource, $productIds, $linkRows, $positionRows); + } + + /** + * Fetches Product Links + * + * @param Link $resource + * @param int $productId + * @return array + */ + private function fetchProductLinks(Link $resource, int $productId) : array + { + $productLinkKeys = []; + $select = $this->_connection->select()->from( + $resource->getTable('catalog_product_link'), + ['id' => 'link_id', 'linked_id' => 'linked_product_id', 'link_type_id' => 'link_type_id'] + )->where( + 'product_id = :product_id' + ); + $bind = [':product_id' => $productId]; + foreach ($this->_connection->fetchAll($select, $bind) as $linkData) { + $linkKey = $this->composeLinkKey($productId, $linkData['linked_id'], $linkData['link_type_id']); + $productLinkKeys[$linkKey] = $linkData['id']; + } + + return $productLinkKeys; + } + + /** + * Gets the Id of the Sku + * + * @param string $linkedSku + * @return int|null + */ + private function getProductLinkedId(string $linkedSku) : ?int + { + $linkedSku = trim($linkedSku); + $newSku = $this->skuProcessor->getNewSku($linkedSku); + $linkedId = !empty($newSku) ? $newSku['entity_id'] : $this->getExistingSku($linkedSku)['entity_id']; + return $linkedId; + } + + /** + * Saves information about product links + * + * @param Link $resource + * @param array $productIds + * @param array $linkRows + * @param array $positionRows + * @throws LocalizedException + */ + private function saveLinksData(Link $resource, array $productIds, array $linkRows, array $positionRows) + { + $mainTable = $resource->getMainTable(); + if (Import::BEHAVIOR_APPEND != $this->getBehavior() && $productIds) { + $this->_connection->delete( + $mainTable, + $this->_connection->quoteInto('product_id IN (?)', array_unique($productIds)) + ); + } + if ($linkRows) { + $this->_connection->insertOnDuplicate($mainTable, $linkRows, ['link_id']); + } + if ($positionRows) { + // process linked product positions + $this->_connection->insertOnDuplicate( + $resource->getAttributeTypeTable('int'), + $positionRows, + ['value'] + ); + } + } + + /** + * Composes the link key + * + * @param int $productId + * @param int $linkedId + * @param int $linkTypeId + * @return string + */ + private function composeLinkKey(int $productId, int $linkedId, int $linkTypeId) : string + { + return "{$productId}-{$linkedId}-{$linkTypeId}"; + } } diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/ActionGroup/AdminExportActionGroup.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/ActionGroup/AdminExportActionGroup.xml index b9eea2b114634..65588daa96cc4 100644 --- a/app/code/Magento/CatalogImportExport/Test/Mftf/ActionGroup/AdminExportActionGroup.xml +++ b/app/code/Magento/CatalogImportExport/Test/Mftf/ActionGroup/AdminExportActionGroup.xml @@ -53,13 +53,34 @@ <arguments> <argument name="rowIndex" type="string"/> </arguments> - <reloadPage stepKey="refreshPage"/> - <waitForPageLoad stepKey="waitFormReload"/> + <amOnPage url="{{AdminExportIndexPage.url}}" stepKey="goToExportIndexPage"/> <click stepKey="clickSelectBtn" selector="{{AdminExportAttributeSection.selectByIndex(rowIndex)}}"/> <click stepKey="clickOnDelete" selector="{{AdminExportAttributeSection.delete(rowIndex)}}" after="clickSelectBtn"/> <waitForElementVisible selector="{{AdminProductGridConfirmActionSection.title}}" stepKey="waitForConfirmModal"/> <click selector="{{AdminProductGridConfirmActionSection.ok}}" stepKey="confirmDelete"/> - <waitForPageLoad stepKey="waitForExportDataDeleted" /> + <waitForElementVisible selector="{{AdminDataGridTableSection.dataGridEmpty}}" stepKey="waitDataGridEmptyMessageAppears"/> + <see selector="{{AdminDataGridTableSection.dataGridEmpty}}" userInput="We couldn't find any records." stepKey="assertDataGridEmptyMessage"/> + </actionGroup> + + <actionGroup name="deleteAllExportedFiles"> + <amOnPage url="{{AdminExportIndexPage.url}}" stepKey="goToExportIndexPage"/> + <executeInSelenium + function=" + function ($webdriver) use ($I) { + $buttons = $webdriver->findElements(\Facebook\WebDriver\WebDriverBy::xpath('//tr[@data-repeat-index=\'0\']//button')); + while(!empty($buttons)) { + $buttons[0]->click(); + $I->waitForElementVisible('//tr[@data-repeat-index=\'0\']//a[text()=\'Delete\']', 10); + $deleteButton = $webdriver->findElement(\Facebook\WebDriver\WebDriverBy::xpath('//tr[@data-repeat-index=\'0\']//a[text()=\'Delete\']')); + $deleteButton->click(); + $I->waitForElementVisible('.modal-popup.confirm button.action-accept', 10); + $I->click('.modal-popup.confirm button.action-accept'); + $I->waitForPageLoad(60); + $buttons = $webdriver->findElements(\Facebook\WebDriver\WebDriverBy::xpath('//tr[@data-repeat-index=\'0\']//button')); + } + }" + stepKey="deleteAllExportedFilesOneByOne"/> + <waitForElementVisible selector="{{AdminDataGridTableSection.dataGridEmpty}}" stepKey="waitDataGridEmptyMessageAppears"/> <see selector="{{AdminDataGridTableSection.dataGridEmpty}}" userInput="We couldn't find any records." stepKey="assertDataGridEmptyMessage"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportBundleProductTest.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportBundleProductTest.xml index 1f5ae6b6905bc..281c8c0db307a 100644 --- a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportBundleProductTest.xml +++ b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportBundleProductTest.xml @@ -18,6 +18,9 @@ <testCaseId value="MC-14008"/> <group value="catalog_import_export"/> <group value="mtf_migrated"/> + <skip> + <issueId value="MC-15934"/> + </skip> </annotations> <before> <!--Create bundle product with dynamic price with two simple products --> @@ -80,19 +83,15 @@ <requiredEntity createDataKey="secondSimpleProductForFixedWithAttribute"/> </createData> - <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <!-- Run cron twice --> <magentoCLI command="cron:run" stepKey="runCron1"/> <magentoCLI command="cron:run" stepKey="runCron2"/> + + <!-- Login as admin --> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="deleteAllExportedFiles" stepKey="clearExportedFilesList"/> </before> <after> - <!-- Delete exported file --> - <actionGroup ref="deleteExportedFile" stepKey="deleteExportedFile"> - <argument name="rowIndex" value="0"/> - </actionGroup> - <!-- Delete products creations --> <deleteData createDataKey="createDynamicBundleProduct" stepKey="deleteDynamicBundleProduct"/> <deleteData createDataKey="firstSimpleProductForDynamic" stepKey="deleteFirstSimpleProductForDynamic"/> @@ -105,6 +104,10 @@ <deleteData createDataKey="secondSimpleProductForFixedWithAttribute" stepKey="deleteSecondSimpleProductForFixedWithAttribute"/> <deleteData createDataKey="createProductAttribute" stepKey="deleteProductAttribute"/> + <!-- Delete exported file --> + <actionGroup ref="deleteExportedFile" stepKey="deleteExportedFile"> + <argument name="rowIndex" value="0"/> + </actionGroup> <!-- Log out --> <actionGroup ref="logout" stepKey="logout"/> </after> @@ -117,6 +120,7 @@ <!-- Run cron --> <magentoCLI command="cron:run" stepKey="runCron3"/> + <magentoCLI command="cron:run" stepKey="runCron4"/> <!-- Download product --> <actionGroup ref="downloadFileByRowIndex" stepKey="downloadCreatedProducts"> diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportGroupedProductWithSpecialPriceTest.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportGroupedProductWithSpecialPriceTest.xml index a587d71ba0e68..c47b7dc83af28 100644 --- a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportGroupedProductWithSpecialPriceTest.xml +++ b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportGroupedProductWithSpecialPriceTest.xml @@ -18,6 +18,9 @@ <testCaseId value="MC-14009"/> <group value="catalog_import_export"/> <group value="mtf_migrated"/> + <skip> + <issueId value="MC-15934"/> + </skip> </annotations> <before> <!-- Create first simple product and add special price --> @@ -48,19 +51,15 @@ <requiredEntity createDataKey="createSecondSimpleProduct"/> </updateData> - <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <!-- Run cron twice --> <magentoCLI command="cron:run" stepKey="runCron1"/> <magentoCLI command="cron:run" stepKey="runCron2"/> + + <!-- Login as admin --> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="deleteAllExportedFiles" stepKey="clearExportedFilesList"/> </before> <after> - <!-- Delete exported file --> - <actionGroup ref="deleteExportedFile" stepKey="deleteExportedFile"> - <argument name="rowIndex" value="0"/> - </actionGroup> - <!-- Deleted created products --> <deleteData createDataKey="createFirstSimpleProduct" stepKey="deleteFirstSimpleProduct"/> <deleteData createDataKey="createSecondSimpleProduct" stepKey="deleteSecondSimpleProduct"/> @@ -69,6 +68,10 @@ <!-- Delete category --> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <!-- Delete exported file --> + <actionGroup ref="deleteExportedFile" stepKey="deleteExportedFile"> + <argument name="rowIndex" value="0"/> + </actionGroup> <!-- Log out --> <actionGroup ref="logout" stepKey="logout"/> </after> @@ -82,6 +85,7 @@ <!-- Run cron --> <magentoCLI command="cron:run" stepKey="runCron3"/> + <magentoCLI command="cron:run" stepKey="runCron4"/> <!-- Download product --> <actionGroup ref="downloadFileByRowIndex" stepKey="downloadCreatedProducts"> diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleAndConfigurableProductsWithCustomOptionsTest.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleAndConfigurableProductsWithCustomOptionsTest.xml index 6f64da4693692..160abe617995d 100644 --- a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleAndConfigurableProductsWithCustomOptionsTest.xml +++ b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleAndConfigurableProductsWithCustomOptionsTest.xml @@ -18,6 +18,9 @@ <testCaseId value="MC-14005"/> <group value="catalog_import_export"/> <group value="mtf_migrated"/> + <skip> + <issueId value="MC-15934"/> + </skip> </annotations> <before> <!-- Create category --> @@ -73,19 +76,15 @@ <requiredEntity createDataKey="createConfigSecondChildProduct"/> </createData> - <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <!-- Run cron twice --> <magentoCLI command="cron:run" stepKey="runCron1"/> <magentoCLI command="cron:run" stepKey="runCron2"/> + + <!-- Login as admin --> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="deleteAllExportedFiles" stepKey="clearExportedFilesList"/> </before> <after> - <!-- Delete exported file --> - <actionGroup ref="deleteExportedFile" stepKey="deleteExportedFile"> - <argument name="rowIndex" value="0"/> - </actionGroup> - <!-- Delete configurable product creation --> <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> <deleteData createDataKey="createConfigFirstChildProduct" stepKey="deleteConfigFirstChildProduct"/> @@ -93,6 +92,10 @@ <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <!-- Delete exported file --> + <actionGroup ref="deleteExportedFile" stepKey="deleteExportedFile"> + <argument name="rowIndex" value="0"/> + </actionGroup> <!-- Log out --> <actionGroup ref="logout" stepKey="logout"/> </after> @@ -109,6 +112,7 @@ <!-- Run cron --> <magentoCLI command="cron:run" stepKey="runCron3"/> + <magentoCLI command="cron:run" stepKey="runCron4"/> <!-- Download product --> <actionGroup ref="downloadFileByRowIndex" stepKey="downloadCreatedProducts"> diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductAndConfigurableProductsWithAssignedImagesTest.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductAndConfigurableProductsWithAssignedImagesTest.xml index 993f1c9cd9da2..3b8da4055ab7e 100644 --- a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductAndConfigurableProductsWithAssignedImagesTest.xml +++ b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductAndConfigurableProductsWithAssignedImagesTest.xml @@ -18,6 +18,9 @@ <testCaseId value="MC-14004"/> <group value="catalog_import_export"/> <group value="mtf_migrated"/> + <skip> + <issueId value="MC-17140"/> + </skip> </annotations> <before> <!-- Create category --> @@ -89,19 +92,15 @@ <requiredEntity createDataKey="createConfigProduct"/> </createData> - <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <!-- Run cron twice --> <magentoCLI command="cron:run" stepKey="runCron1"/> <magentoCLI command="cron:run" stepKey="runCron2"/> + + <!-- Login as admin --> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="deleteAllExportedFiles" stepKey="clearExportedFilesList"/> </before> <after> - <!-- Delete exported file --> - <actionGroup ref="deleteExportedFile" stepKey="deleteExportedFile"> - <argument name="rowIndex" value="0"/> - </actionGroup> - <!-- Delete configurable product creation --> <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> <deleteData createDataKey="createConfigFirstChildProduct" stepKey="deleteConfigFirstChildProduct"/> @@ -109,6 +108,10 @@ <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <!-- Delete exported file --> + <actionGroup ref="deleteExportedFile" stepKey="deleteExportedFile"> + <argument name="rowIndex" value="0"/> + </actionGroup> <!-- Log out --> <actionGroup ref="logout" stepKey="logout"/> </after> @@ -125,6 +128,7 @@ <!-- Run cron --> <magentoCLI command="cron:run" stepKey="runCron3"/> + <magentoCLI command="cron:run" stepKey="runCron4"/> <!-- Download product --> <actionGroup ref="downloadFileByRowIndex" stepKey="downloadCreatedProducts"> diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductAssignedToMainWebsiteAndConfigurableProductAssignedToCustomWebsiteTest.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductAssignedToMainWebsiteAndConfigurableProductAssignedToCustomWebsiteTest.xml index 491d20604a08b..271b4621d1a96 100644 --- a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductAssignedToMainWebsiteAndConfigurableProductAssignedToCustomWebsiteTest.xml +++ b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductAssignedToMainWebsiteAndConfigurableProductAssignedToCustomWebsiteTest.xml @@ -71,19 +71,15 @@ <requiredEntity createDataKey="createConfigSecondChildProduct"/> </createData> - <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <!-- Run cron twice --> <magentoCLI command="cron:run" stepKey="runCron1"/> <magentoCLI command="cron:run" stepKey="runCron2"/> + + <!-- Login as admin --> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="deleteAllExportedFiles" stepKey="clearExportedFilesList"/> </before> <after> - <!-- Delete exported file --> - <actionGroup ref="deleteExportedFile" stepKey="deleteExportedFile"> - <argument name="rowIndex" value="0"/> - </actionGroup> - <!-- Delete simple product --> <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> @@ -94,6 +90,10 @@ <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <!-- Delete exported file --> + <actionGroup ref="deleteExportedFile" stepKey="deleteExportedFile"> + <argument name="rowIndex" value="0"/> + </actionGroup> <!-- Log out --> <actionGroup ref="logout" stepKey="logout"/> </after> @@ -107,6 +107,7 @@ <!-- Run cron --> <magentoCLI command="cron:run" stepKey="runCron3"/> + <magentoCLI command="cron:run" stepKey="runCron4"/> <!-- Download product --> <actionGroup ref="downloadFileByRowIndex" stepKey="downloadCreatedProducts"> diff --git a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductWithCustomAttributeTest.xml b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductWithCustomAttributeTest.xml index f671b54803e35..f958978a9efae 100644 --- a/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductWithCustomAttributeTest.xml +++ b/app/code/Magento/CatalogImportExport/Test/Mftf/Test/AdminExportSimpleProductWithCustomAttributeTest.xml @@ -18,6 +18,9 @@ <testCaseId value="MC-14007"/> <group value="catalog_import_export"/> <group value="mtf_migrated"/> + <skip> + <issueId value="MC-15934"/> + </skip> </annotations> <before> <!-- Create simple product with custom attribute set --> @@ -28,24 +31,24 @@ <requiredEntity createDataKey="createAttributeSet"/> </createData> - <!-- Login as admin --> - <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <!-- Run cron twice --> <magentoCLI command="cron:run" stepKey="runCron1"/> <magentoCLI command="cron:run" stepKey="runCron2"/> + + <!-- Login as admin --> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <actionGroup ref="deleteAllExportedFiles" stepKey="clearExportedFilesList"/> </before> <after> - <!-- Delete exported file --> - <actionGroup ref="deleteExportedFile" stepKey="deleteExportedFile"> - <argument name="rowIndex" value="0"/> - </actionGroup> - <!-- Delete product creations --> <deleteData createDataKey="createSimpleProductWithCustomAttributeSet" stepKey="deleteSimpleProductWithCustomAttributeSet"/> <deleteData createDataKey="createAttributeSet" stepKey="deleteAttributeSet"/> <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <!-- Delete exported file --> + <actionGroup ref="deleteExportedFile" stepKey="deleteExportedFile"> + <argument name="rowIndex" value="0"/> + </actionGroup> <!-- Log out --> <actionGroup ref="logout" stepKey="logout"/> </after> @@ -59,6 +62,7 @@ <!-- Run cron --> <magentoCLI command="cron:run" stepKey="runCron3"/> + <magentoCLI command="cron:run" stepKey="runCron4"/> <!-- Download product --> <actionGroup ref="downloadFileByRowIndex" stepKey="downloadCreatedProducts"> diff --git a/app/code/Magento/CatalogInventory/Helper/Minsaleqty.php b/app/code/Magento/CatalogInventory/Helper/Minsaleqty.php index b4f5d8b670fb2..96bf5bd965355 100644 --- a/app/code/Magento/CatalogInventory/Helper/Minsaleqty.php +++ b/app/code/Magento/CatalogInventory/Helper/Minsaleqty.php @@ -95,7 +95,7 @@ protected function serializeValue($value) } return $this->serializer->serialize($data); } else { - return ''; + return $value; } } diff --git a/app/code/Magento/CatalogInventory/Test/Mftf/Data/CatalogInventoryConfigData.xml b/app/code/Magento/CatalogInventory/Test/Mftf/Data/CatalogInventoryConfigData.xml new file mode 100644 index 0000000000000..e14c36446fc2b --- /dev/null +++ b/app/code/Magento/CatalogInventory/Test/Mftf/Data/CatalogInventoryConfigData.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="StockOptionsDisplayOutOfStockProductsEnable"> + <data key="path">cataloginventory/options/show_out_of_stock</data> + <data key="scope_id">0</data> + <data key="label">Yes</data> + <data key="value">1</data> + </entity> + <entity name="StockOptionsDisplayOutOfStockProductsDisable"> + <data key="path">cataloginventory/options/show_out_of_stock</data> + <data key="scope_id">0</data> + <data key="label">No</data> + <data key="value">0</data> + </entity> +</entities> diff --git a/app/code/Magento/CatalogInventory/Test/Mftf/Data/CatalogInventryConfigData.xml b/app/code/Magento/CatalogInventory/Test/Mftf/Data/CatalogInventryConfigData.xml new file mode 100644 index 0000000000000..3a49b821ead5f --- /dev/null +++ b/app/code/Magento/CatalogInventory/Test/Mftf/Data/CatalogInventryConfigData.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="EnableCatalogInventoryConfigData"> + <!--Default Value --> + <data key="path">cataloginventory/options/can_subtract</data> + <data key="scope_id">0</data> + <data key="label">Yes</data> + <data key="value">1</data> + </entity> + <entity name="DisableCatalogInventoryConfigData"> + <data key="path">cataloginventory/options/can_subtract</data> + <data key="scope_id">0</data> + <data key="label">No</data> + <data key="value">0</data> + </entity> +</entities> \ No newline at end of file diff --git a/app/code/Magento/CatalogInventory/Test/Unit/Helper/MinsaleqtyTest.php b/app/code/Magento/CatalogInventory/Test/Unit/Helper/MinsaleqtyTest.php index f008ed7d9d694..051e002b80c3e 100644 --- a/app/code/Magento/CatalogInventory/Test/Unit/Helper/MinsaleqtyTest.php +++ b/app/code/Magento/CatalogInventory/Test/Unit/Helper/MinsaleqtyTest.php @@ -216,7 +216,7 @@ public function testMakeStorableArrayFieldValue($value, $result, $serializeCallC public function makeStorableArrayFieldValueDataProvider() { return [ - 'invalid bool' => [false, ''], + 'invalid bool' => [false, false], 'invalid empty string' => ['', ''], 'valid numeric' => ['22', '22'], 'valid empty array' => [[], '[]', 1], @@ -240,7 +240,8 @@ public function makeStorableArrayFieldValueDataProvider() '[1]', 1, [0 => 1.0] - ] + ], + 'json value' => ['{"32000":2,"0":1}', '{"32000":2,"0":1}'], ]; } } diff --git a/app/code/Magento/CatalogInventory/view/frontend/templates/qtyincrements.phtml b/app/code/Magento/CatalogInventory/view/frontend/templates/qtyincrements.phtml index 8e0dbf1278ed2..8b63d29be8154 100644 --- a/app/code/Magento/CatalogInventory/view/frontend/templates/qtyincrements.phtml +++ b/app/code/Magento/CatalogInventory/view/frontend/templates/qtyincrements.phtml @@ -4,14 +4,12 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - /** * @var $block \Magento\CatalogInventory\Block\Qtyincrements */ ?> <?php if ($block->getProductQtyIncrements()) : ?> <div class="product pricing"> - <?= /* @escapeNotVerified */ __('%1 is available to buy in increments of %2', $block->getProductName(), $block->getProductQtyIncrements()) ?> + <?= $block->escapeHtml(__('%1 is available to buy in increments of %2', $block->getProductName(), $block->getProductQtyIncrements())) ?> </div> <?php endif ?> diff --git a/app/code/Magento/CatalogInventory/view/frontend/templates/stockqty/composite.phtml b/app/code/Magento/CatalogInventory/view/frontend/templates/stockqty/composite.phtml index 481ed1297a801..de667d19fadb0 100644 --- a/app/code/Magento/CatalogInventory/view/frontend/templates/stockqty/composite.phtml +++ b/app/code/Magento/CatalogInventory/view/frontend/templates/stockqty/composite.phtml @@ -4,30 +4,28 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - /** * @var $block \Magento\CatalogInventory\Block\Stockqty\Composite */ ?> -<?php if ($block->isMsgVisible()): ?> +<?php if ($block->isMsgVisible()) : ?> <div class="availability only"> <a href="#" - data-mage-init='{"toggleAdvanced": {"selectorsToggleClass": "active", "baseToggleClass": "expanded", "toggleContainers": "#<?= /* @escapeNotVerified */ $block->getDetailsPlaceholderId() ?>"}}' - id="<?= /* @escapeNotVerified */ $block->getPlaceholderId() ?>" - title="<?= /* @escapeNotVerified */ __('Only %1 left', ($block->getStockQtyLeft())) ?>" + data-mage-init='{"toggleAdvanced": {"selectorsToggleClass": "active", "baseToggleClass": "expanded", "toggleContainers": "#<?= $block->escapeHtmlAttr($block->getDetailsPlaceholderId()) ?>"}}' + id="<?= $block->escapeHtmlAttr($block->getPlaceholderId()) ?>" + title="<?= $block->escapeHtmlAttr(__('Only %1 left', ($block->getStockQtyLeft()))) ?>" class="action show"> - <?= /* @escapeNotVerified */ __('Only %1 left', "<strong>{$block->getStockQtyLeft()}</strong>") ?> + <?= /* @noEscape */ __('Only %1 left', "<strong>{$block->escapeHtml($block->getStockQtyLeft())}</strong>") ?> </a> </div> - <div class="availability only detailed" id="<?= /* @escapeNotVerified */ $block->getDetailsPlaceholderId() ?>"> + <div class="availability only detailed" id="<?= $block->escapeHtmlAttr($block->getDetailsPlaceholderId()) ?>"> <div class="table-wrapper"> <table class="data table"> - <caption class="table-caption"><?= /* @escapeNotVerified */ __('Product availability') ?></caption> + <caption class="table-caption"><?= $block->escapeHtml(__('Product availability')) ?></caption> <thead> <tr> - <th class="col item" scope="col"><?= /* @escapeNotVerified */ __('Product Name') ?></th> - <th class="col qty" scope="col"><?= /* @escapeNotVerified */ __('Qty') ?></th> + <th class="col item" scope="col"><?= $block->escapeHtml(__('Product Name')) ?></th> + <th class="col qty" scope="col"><?= $block->escapeHtml(__('Qty')) ?></th> </tr> </thead> <tbody> @@ -35,8 +33,8 @@ <?php $childProductStockQty = $block->getProductStockQty($childProduct); ?> <?php if ($childProductStockQty > 0) : ?> <tr> - <td data-th="<?= $block->escapeHtml(__('Product Name')) ?>" class="col item"><?= /* @escapeNotVerified */ $childProduct->getName() ?></td> - <td data-th="<?= $block->escapeHtml(__('Qty')) ?>" class="col qty"><?= /* @escapeNotVerified */ $childProductStockQty ?></td> + <td data-th="<?= $block->escapeHtmlAttr(__('Product Name')) ?>" class="col item"><?= $block->escapeHtml($childProduct->getName()) ?></td> + <td data-th="<?= $block->escapeHtmlAttr(__('Qty')) ?>" class="col qty"><?= $block->escapeHtml($childProductStockQty) ?></td> </tr> <?php endif ?> <?php endforeach ?> diff --git a/app/code/Magento/CatalogInventory/view/frontend/templates/stockqty/default.phtml b/app/code/Magento/CatalogInventory/view/frontend/templates/stockqty/default.phtml index 43fb697de2621..c32cb9dd6ecda 100644 --- a/app/code/Magento/CatalogInventory/view/frontend/templates/stockqty/default.phtml +++ b/app/code/Magento/CatalogInventory/view/frontend/templates/stockqty/default.phtml @@ -4,14 +4,12 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - /** * @var $block \Magento\CatalogInventory\Block\Stockqty\DefaultStockqty */ ?> -<?php if ($block->isMsgVisible()): ?> - <div class="availability only" title="<?= /* @escapeNotVerified */ __('Only %1 left', ($block->getStockQtyLeft())) ?>"> - <?= /* @escapeNotVerified */ __('Only %1 left', "<strong>{$block->getStockQtyLeft()}</strong>") ?> +<?php if ($block->isMsgVisible()) : ?> + <div class="availability only" title="<?= $block->escapeHtmlAttr(__('Only %1 left', ($block->getStockQtyLeft()))) ?>"> + <?= /* @noEscape */ __('Only %1 left', "<strong>{$block->escapeHtml($block->getStockQtyLeft())}</strong>") ?> </div> <?php endif ?> diff --git a/app/code/Magento/CatalogRule/Model/Indexer/IndexBuilder.php b/app/code/Magento/CatalogRule/Model/Indexer/IndexBuilder.php index 1f62200fc6b1b..e12eabba76401 100644 --- a/app/code/Magento/CatalogRule/Model/Indexer/IndexBuilder.php +++ b/app/code/Magento/CatalogRule/Model/Indexer/IndexBuilder.php @@ -7,6 +7,7 @@ namespace Magento\CatalogRule\Model\Indexer; use Magento\Catalog\Model\Product; +use Magento\CatalogRule\Model\ResourceModel\Rule\Collection as RuleCollection; use Magento\CatalogRule\Model\ResourceModel\Rule\CollectionFactory as RuleCollectionFactory; use Magento\CatalogRule\Model\Rule; use Magento\Framework\App\ObjectManager; @@ -15,6 +16,8 @@ use Magento\CatalogRule\Model\Indexer\IndexerTableSwapperInterface as TableSwapper; /** + * Catalog rule index builder + * * @api * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) @@ -270,14 +273,14 @@ public function reindexByIds(array $ids) */ protected function doReindexByIds($ids) { - $this->cleanByIds($ids); + $this->cleanProductIndex($ids); $products = $this->productLoader->getProducts($ids); - foreach ($this->getActiveRules() as $rule) { - foreach ($products as $product) { - $this->applyRule($rule, $product); - } + $activeRules = $this->getActiveRules(); + foreach ($products as $product) { + $this->applyRules($activeRules, $product); } + $this->reindexRuleGroupWebsite->execute(); } /** @@ -322,6 +325,30 @@ protected function doReindexFull() ); } + /** + * Clean product index + * + * @param array $productIds + * @return void + */ + private function cleanProductIndex(array $productIds): void + { + $where = ['product_id IN (?)' => $productIds]; + $this->connection->delete($this->getTable('catalogrule_product'), $where); + } + + /** + * Clean product price index + * + * @param array $productIds + * @return void + */ + private function cleanProductPriceIndex(array $productIds): void + { + $where = ['product_id IN (?)' => $productIds]; + $this->connection->delete($this->getTable('catalogrule_product_price'), $where); + } + /** * Clean by product ids * @@ -330,51 +357,35 @@ protected function doReindexFull() */ protected function cleanByIds($productIds) { - $query = $this->connection->deleteFromSelect( - $this->connection - ->select() - ->from($this->resource->getTableName('catalogrule_product'), 'product_id') - ->distinct() - ->where('product_id IN (?)', $productIds), - $this->resource->getTableName('catalogrule_product') - ); - $this->connection->query($query); - - $query = $this->connection->deleteFromSelect( - $this->connection->select() - ->from($this->resource->getTableName('catalogrule_product_price'), 'product_id') - ->distinct() - ->where('product_id IN (?)', $productIds), - $this->resource->getTableName('catalogrule_product_price') - ); - $this->connection->query($query); + $this->cleanProductIndex($productIds); + $this->cleanProductPriceIndex($productIds); } /** + * Assign product to rule + * * @param Rule $rule * @param Product $product - * @return $this - * @throws \Exception - * @SuppressWarnings(PHPMD.NPathComplexity) + * @return void */ - protected function applyRule(Rule $rule, $product) + private function assignProductToRule(Rule $rule, Product $product): void { - $ruleId = $rule->getId(); - $productEntityId = $product->getId(); - $websiteIds = array_intersect($product->getWebsiteIds(), $rule->getWebsiteIds()); - if (!$rule->validate($product)) { - return $this; + return; } + $ruleId = (int) $rule->getId(); + $productEntityId = (int) $product->getId(); + $ruleProductTable = $this->getTable('catalogrule_product'); $this->connection->delete( - $this->resource->getTableName('catalogrule_product'), + $ruleProductTable, [ - $this->connection->quoteInto('rule_id = ?', $ruleId), - $this->connection->quoteInto('product_id = ?', $productEntityId) + 'rule_id = ?' => $ruleId, + 'product_id = ?' => $productEntityId, ] ); + $websiteIds = array_intersect($product->getWebsiteIds(), $rule->getWebsiteIds()); $customerGroupIds = $rule->getCustomerGroupIds(); $fromTime = strtotime($rule->getFromDate()); $toTime = strtotime($rule->getToDate()); @@ -385,36 +396,44 @@ protected function applyRule(Rule $rule, $product) $actionStop = $rule->getStopRulesProcessing(); $rows = []; - try { - foreach ($websiteIds as $websiteId) { - foreach ($customerGroupIds as $customerGroupId) { - $rows[] = [ - 'rule_id' => $ruleId, - 'from_time' => $fromTime, - 'to_time' => $toTime, - 'website_id' => $websiteId, - 'customer_group_id' => $customerGroupId, - 'product_id' => $productEntityId, - 'action_operator' => $actionOperator, - 'action_amount' => $actionAmount, - 'action_stop' => $actionStop, - 'sort_order' => $sortOrder, - ]; - - if (count($rows) == $this->batchCount) { - $this->connection->insertMultiple($this->getTable('catalogrule_product'), $rows); - $rows = []; - } + foreach ($websiteIds as $websiteId) { + foreach ($customerGroupIds as $customerGroupId) { + $rows[] = [ + 'rule_id' => $ruleId, + 'from_time' => $fromTime, + 'to_time' => $toTime, + 'website_id' => $websiteId, + 'customer_group_id' => $customerGroupId, + 'product_id' => $productEntityId, + 'action_operator' => $actionOperator, + 'action_amount' => $actionAmount, + 'action_stop' => $actionStop, + 'sort_order' => $sortOrder, + ]; + + if (count($rows) == $this->batchCount) { + $this->connection->insertMultiple($ruleProductTable, $rows); + $rows = []; } } - - if (!empty($rows)) { - $this->connection->insertMultiple($this->resource->getTableName('catalogrule_product'), $rows); - } - } catch (\Exception $e) { - throw $e; } + if ($rows) { + $this->connection->insertMultiple($ruleProductTable, $rows); + } + } + /** + * Apply rule + * + * @param Rule $rule + * @param Product $product + * @return $this + * @throws \Exception + * @SuppressWarnings(PHPMD.NPathComplexity) + */ + protected function applyRule(Rule $rule, $product) + { + $this->assignProductToRule($rule, $product); $this->reindexRuleProductPrice->execute($this->batchCount, $product); $this->reindexRuleGroupWebsite->execute(); @@ -422,6 +441,25 @@ protected function applyRule(Rule $rule, $product) } /** + * Apply rules + * + * @param RuleCollection $ruleCollection + * @param Product $product + * @return void + */ + private function applyRules(RuleCollection $ruleCollection, Product $product): void + { + foreach ($ruleCollection as $rule) { + $this->assignProductToRule($rule, $product); + } + + $this->cleanProductPriceIndex([$product->getId()]); + $this->reindexRuleProductPrice->execute($this->batchCount, $product); + } + + /** + * Retrieve table name + * * @param string $tableName * @return string */ @@ -431,6 +469,8 @@ protected function getTable($tableName) } /** + * Update rule product data + * * @param Rule $rule * @return $this * @deprecated 100.2.0 @@ -456,6 +496,8 @@ protected function updateRuleProductData(Rule $rule) } /** + * Apply all rules + * * @param Product|null $product * @throws \Exception * @return $this @@ -495,8 +537,10 @@ protected function deleteOldData() } /** + * Calculate rule product price + * * @param array $ruleData - * @param null $productData + * @param array $productData * @return float * @deprecated 100.2.0 * @see ProductPriceCalculator::calculate @@ -507,6 +551,8 @@ protected function calcRuleProductPrice($ruleData, $productData = null) } /** + * Get rule products statement + * * @param int $websiteId * @param Product|null $product * @return \Zend_Db_Statement_Interface @@ -520,6 +566,8 @@ protected function getRuleProductsStmt($websiteId, Product $product = null) } /** + * Save rule product prices + * * @param array $arrData * @return $this * @throws \Exception @@ -535,7 +583,7 @@ protected function saveRuleProductPrices($arrData) /** * Get active rules * - * @return array + * @return RuleCollection */ protected function getActiveRules() { @@ -545,7 +593,7 @@ protected function getActiveRules() /** * Get active rules * - * @return array + * @return RuleCollection */ protected function getAllRules() { @@ -553,6 +601,8 @@ protected function getAllRules() } /** + * Get product + * * @param int $productId * @return Product */ @@ -565,6 +615,8 @@ protected function getProduct($productId) } /** + * Log critical exception + * * @param \Exception $e * @return void */ diff --git a/app/code/Magento/CatalogRule/Model/Indexer/RuleProductPricesPersistor.php b/app/code/Magento/CatalogRule/Model/Indexer/RuleProductPricesPersistor.php index 0b1264a216257..25bcfb8f20e5f 100644 --- a/app/code/Magento/CatalogRule/Model/Indexer/RuleProductPricesPersistor.php +++ b/app/code/Magento/CatalogRule/Model/Indexer/RuleProductPricesPersistor.php @@ -76,25 +76,19 @@ public function execute(array $priceData, $useAdditionalTable = false) ); } - $productIds = []; - - try { - foreach ($priceData as $key => $data) { - $productIds['product_id'] = $data['product_id']; - $priceData[$key]['rule_date'] = $this->dateFormat->formatDate($data['rule_date'], false); - $priceData[$key]['latest_start_date'] = $this->dateFormat->formatDate( - $data['latest_start_date'], - false - ); - $priceData[$key]['earliest_end_date'] = $this->dateFormat->formatDate( - $data['earliest_end_date'], - false - ); - } - $connection->insertOnDuplicate($indexTable, $priceData); - } catch (\Exception $e) { - throw $e; + foreach ($priceData as $key => $data) { + $priceData[$key]['rule_date'] = $this->dateFormat->formatDate($data['rule_date'], false); + $priceData[$key]['latest_start_date'] = $this->dateFormat->formatDate( + $data['latest_start_date'], + false + ); + $priceData[$key]['earliest_end_date'] = $this->dateFormat->formatDate( + $data['earliest_end_date'], + false + ); } + $connection->insertOnDuplicate($indexTable, $priceData); + return true; } } diff --git a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/AdminAssertCustomerGroupOnCatalogPriceRuleFormActionGroup.xml b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/AdminAssertCustomerGroupOnCatalogPriceRuleFormActionGroup.xml new file mode 100644 index 0000000000000..ef498b95a5dca --- /dev/null +++ b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/AdminAssertCustomerGroupOnCatalogPriceRuleFormActionGroup.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminAssertCustomerGroupOnCatalogPriceRuleForm"> + <arguments> + <argument name="customerGroupName" type="string"/> + </arguments> + <amOnPage url="{{AdminNewCatalogRulePage.url}}" stepKey="amOnCatalogPriceRuleCreatePage"/> + <waitForElementVisible selector="{{AdminNewCatalogPriceRule.customerGroups}}" stepKey="waitForElementVisible"/> + <see selector="{{AdminNewCatalogPriceRule.customerGroups}}" userInput="{{customerGroupName}}" stepKey="assertCustomerGroupPresentOnCatalogPriceRuleForm"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/AdminOpenNewCatalogPriceRuleFormPageActionGroup.xml b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/AdminOpenNewCatalogPriceRuleFormPageActionGroup.xml index 072e8b24b0336..8651a17cb969e 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/AdminOpenNewCatalogPriceRuleFormPageActionGroup.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/AdminOpenNewCatalogPriceRuleFormPageActionGroup.xml @@ -8,7 +8,7 @@ <actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> <actionGroup name="AdminOpenNewCatalogPriceRuleFormPageActionGroup"> - <amOnPage url="{{CatalogRuleNewPage.url}}" stepKey="openNewCatalogPriceRulePage" /> + <amOnPage url="{{AdminNewCatalogRulePage.url}}" stepKey="openNewCatalogPriceRulePage" /> <waitForPageLoad stepKey="waitForPageLoad" /> </actionGroup> </actionGroups> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml index d744065fabc10..de0d2baee2dd1 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/ActionGroup/CatalogPriceRuleActionGroup.xml @@ -136,4 +136,25 @@ <actionGroup name="selectNotLoggedInCustomerGroupActionGroup"> <selectOption selector="{{AdminNewCatalogPriceRule.customerGroups}}" userInput="NOT LOGGED IN" stepKey="selectCustomerGroup"/> </actionGroup> + + <!-- Open rule for Edit --> + <actionGroup name="OpenCatalogPriceRule"> + <arguments> + <argument name="ruleName" type="string" defaultValue="CustomCatalogRule.name"/> + </arguments> + <amOnPage url="{{AdminCatalogPriceRuleGridPage.url}}" stepKey="goToAdminCatalogPriceRuleGridPage"/> + <conditionalClick selector="{{AdminDataGridHeaderSection.clearFilters}}" dependentSelector="{{AdminDataGridHeaderSection.clearFilters}}" visible="true" stepKey="clearExistingFilters"/> + <fillField selector="{{AdminCatalogPriceRuleGridSection.filterByRuleName}}" userInput="{{ruleName}}" stepKey="filterByRuleName"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickSearch"/> + <click selector="{{AdminGridTableSection.row('1')}}" stepKey="clickEdit"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + </actionGroup> + + <actionGroup name="RemoveCatalogPriceRule" extends="OpenCatalogPriceRule"> + <click selector="{{AdminMainActionsSection.delete}}" after="waitForPageLoad" stepKey="clickToDelete"/> + <waitForElementVisible selector="{{AdminConfirmationModalSection.ok}}" after="clickToDelete" stepKey="waitForElementVisible"/> + <click selector="{{AdminConfirmationModalSection.ok}}" after="waitForElementVisible" stepKey="clickToConfirm"/> + <waitForElementVisible selector="{{AdminMessagesSection.success}}" after="clickToConfirm" stepKey="waitForSuccessMessage"/> + <see selector="{{AdminMessagesSection.success}}" userInput="You deleted the rule." after="waitForSuccessMessage" stepKey="verifyRuleIsDeleted"/> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Page/AdminCatalogPriceRuleGridPage.xml b/app/code/Magento/CatalogRule/Test/Mftf/Page/AdminCatalogPriceRuleGridPage.xml new file mode 100644 index 0000000000000..24e4b27604f0d --- /dev/null +++ b/app/code/Magento/CatalogRule/Test/Mftf/Page/AdminCatalogPriceRuleGridPage.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="AdminCatalogPriceRuleGridPage" url="catalog_rule/promo_catalog/" module="Magento_CatalogRule" area="admin"> + <section name="AdminCatalogPriceRuleGridSection"/> + </page> +</pages> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Page/CatalogRuleNewPage.xml b/app/code/Magento/CatalogRule/Test/Mftf/Page/AdminNewCatalogRulePage.xml similarity index 65% rename from app/code/Magento/CatalogRule/Test/Mftf/Page/CatalogRuleNewPage.xml rename to app/code/Magento/CatalogRule/Test/Mftf/Page/AdminNewCatalogRulePage.xml index ad3e40b37c5b0..c5307bf4e22f9 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Page/CatalogRuleNewPage.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Page/AdminNewCatalogRulePage.xml @@ -8,7 +8,6 @@ <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> - <page name="CatalogRuleNewPage" url="catalog_rule/promo_catalog/new/" module="Magento_CatalogRule" area="admin"> - <section name="AdminNewCatalogPriceRule"/> + <page name="AdminNewCatalogRulePage" url="catalog_rule/promo_catalog/new/" module="Magento_CatalogRule" area="admin"> </page> </pages> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminCatalogPriceRuleGridSection.xml b/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminCatalogPriceRuleGridSection.xml new file mode 100644 index 0000000000000..21f1401b624c9 --- /dev/null +++ b/app/code/Magento/CatalogRule/Test/Mftf/Section/AdminCatalogPriceRuleGridSection.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminCatalogPriceRuleGridSection"> + <element name="filterByRuleName" type="input" selector="#promo_catalog_grid_filter_name"/> + <element name="attribute" type="text" selector="//*[@id='promo_catalog_grid_table']//td[contains(text(), '{{attributeValue}}')]" parameterized="true"/> + <element name="applyRulesButton" type="button" selector="#apply_rules" timeout="30"/> + </section> +</sections> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleEntityTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleEntityTest.xml index 02539110dc1a9..06392764290ac 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleEntityTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/AdminDeleteCatalogPriceRuleEntityTest.xml @@ -209,6 +209,7 @@ <selectOption selector="{{StorefrontProductInfoMainSection.productAttributeOptionsSelectButton}}" userInput="option1" stepKey="selectOption1"/> <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addToCart1"/> <waitForPageLoad time="30" stepKey="waitForPageLoad4"/> + <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessage"/> <see selector="{{StorefrontMessagesSection.success}}" userInput="You added $$createConfigProduct1.name$ to your shopping cart." stepKey="seeAddToCartSuccessMessage"/> <click selector="{{StorefrontMinicartSection.showCart}}" stepKey="openMiniShoppingCart1"/> <waitForPageLoad time="30" stepKey="waitForPageLoad5"/> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleAndConfigurableProductTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleAndConfigurableProductTest.xml index be2e31d0766bd..b9e7bfb4d41e4 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleAndConfigurableProductTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleAndConfigurableProductTest.xml @@ -17,6 +17,9 @@ <testCaseId value="MC-14770"/> <group value="CatalogRule"/> <group value="mtf_migrated"/> + <skip> + <issueId value="MC-17140"/> + </skip> </annotations> <before> <!-- Login as Admin --> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleProductAndFixedMethodTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleProductAndFixedMethodTest.xml index 7b7953c1d9b06..3405d0c4e776d 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleProductAndFixedMethodTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleProductAndFixedMethodTest.xml @@ -17,6 +17,9 @@ <testCaseId value="MC-14771"/> <group value="CatalogRule"/> <group value="mtf_migrated"/> + <skip> + <issueId value="MC-17140"/> + </skip> </annotations> <before> <!-- Login as Admin --> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleProductForNewCustomerGroupTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleProductForNewCustomerGroupTest.xml index e4af21cac6723..c3bcde92bd1f2 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleProductForNewCustomerGroupTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleProductForNewCustomerGroupTest.xml @@ -17,6 +17,9 @@ <testCaseId value="MC-14772"/> <group value="CatalogRule"/> <group value="mtf_migrated"/> + <skip> + <issueId value="MC-17140"/> + </skip> </annotations> <before> <!-- Login as Admin --> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleProductWithCustomOptionsTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleProductWithCustomOptionsTest.xml index 5b7e722c92a02..055eacaeb2b78 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleProductWithCustomOptionsTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/ApplyCatalogRuleForSimpleProductWithCustomOptionsTest.xml @@ -17,6 +17,9 @@ <testCaseId value="MC-14769"/> <group value="CatalogRule"/> <group value="mtf_migrated"/> + <skip> + <issueId value="MC-16684"/> + </skip> </annotations> <before> <!-- Login as Admin --> diff --git a/app/code/Magento/CatalogRule/Test/Mftf/Test/StorefrontInactiveCatalogRuleTest.xml b/app/code/Magento/CatalogRule/Test/Mftf/Test/StorefrontInactiveCatalogRuleTest.xml index c6ecc1c6d9658..22fcf6870c19d 100644 --- a/app/code/Magento/CatalogRule/Test/Mftf/Test/StorefrontInactiveCatalogRuleTest.xml +++ b/app/code/Magento/CatalogRule/Test/Mftf/Test/StorefrontInactiveCatalogRuleTest.xml @@ -57,6 +57,7 @@ <!-- Verify price is not discounted in the cart --> <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addToCart"/> <waitForPageLoad stepKey="waitForCart"/> + <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessage"/> <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCheckout"/> <waitForPageLoad stepKey="waitForCheckout"/> <see selector="{{CheckoutCartSummarySection.subtotal}}" userInput="$$createProduct.price$$" stepKey="seePrice3"/> diff --git a/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/IndexBuilderTest.php b/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/IndexBuilderTest.php index 521e4e1d59897..920dcb8e1ede5 100644 --- a/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/IndexBuilderTest.php +++ b/app/code/Magento/CatalogRule/Test/Unit/Model/Indexer/IndexBuilderTest.php @@ -144,14 +144,12 @@ protected function setUp() ); $this->ruleCollectionFactory = $this->createPartialMock( \Magento\CatalogRule\Model\ResourceModel\Rule\CollectionFactory::class, - ['create', 'addFieldToFilter'] + ['create'] ); $this->backend = $this->createMock(\Magento\Eav\Model\Entity\Attribute\Backend\AbstractBackend::class); $this->select = $this->createMock(\Magento\Framework\DB\Select::class); $this->metadataPool = $this->createMock(\Magento\Framework\EntityManager\MetadataPool::class); - $metadata = $this->getMockBuilder(\Magento\Framework\EntityManager\EntityMetadata::class) - ->disableOriginalConstructor() - ->getMock(); + $metadata = $this->createMock(\Magento\Framework\EntityManager\EntityMetadata::class); $this->metadataPool->expects($this->any())->method('getMetadata')->willReturn($metadata); $this->connection = $this->createMock(\Magento\Framework\DB\Adapter\AdapterInterface::class); $this->db = $this->createMock(\Zend_Db_Statement_Interface::class); @@ -181,10 +179,16 @@ protected function setUp() $this->rules->expects($this->any())->method('getWebsiteIds')->will($this->returnValue([1])); $this->rules->expects($this->any())->method('getCustomerGroupIds')->will($this->returnValue([1])); - $this->ruleCollectionFactory->expects($this->any())->method('create')->will($this->returnSelf()); - $this->ruleCollectionFactory->expects($this->any())->method('addFieldToFilter')->will( - $this->returnValue([$this->rules]) - ); + $ruleCollection = $this->createMock(\Magento\CatalogRule\Model\ResourceModel\Rule\Collection::class); + $this->ruleCollectionFactory->expects($this->once()) + ->method('create') + ->willReturn($ruleCollection); + $ruleCollection->expects($this->once()) + ->method('addFieldToFilter') + ->willReturnSelf(); + $ruleIterator = new \ArrayIterator([$this->rules]); + $ruleCollection->method('getIterator') + ->willReturn($ruleIterator); $this->product->expects($this->any())->method('load')->will($this->returnSelf()); $this->product->expects($this->any())->method('getId')->will($this->returnValue(1)); @@ -213,19 +217,20 @@ protected function setUp() ] ); - $this->reindexRuleProductPrice = - $this->getMockBuilder(\Magento\CatalogRule\Model\Indexer\ReindexRuleProductPrice::class) - ->disableOriginalConstructor() - ->getMock(); - $this->reindexRuleGroupWebsite = - $this->getMockBuilder(\Magento\CatalogRule\Model\Indexer\ReindexRuleGroupWebsite::class) - ->disableOriginalConstructor() - ->getMock(); - $this->setProperties($this->indexBuilder, [ - 'metadataPool' => $this->metadataPool, - 'reindexRuleProductPrice' => $this->reindexRuleProductPrice, - 'reindexRuleGroupWebsite' => $this->reindexRuleGroupWebsite - ]); + $this->reindexRuleProductPrice = $this->createMock( + \Magento\CatalogRule\Model\Indexer\ReindexRuleProductPrice::class + ); + $this->reindexRuleGroupWebsite = $this->createMock( + \Magento\CatalogRule\Model\Indexer\ReindexRuleGroupWebsite::class + ); + $this->setProperties( + $this->indexBuilder, + [ + 'metadataPool' => $this->metadataPool, + 'reindexRuleProductPrice' => $this->reindexRuleProductPrice, + 'reindexRuleGroupWebsite' => $this->reindexRuleGroupWebsite, + ] + ); } /** diff --git a/app/code/Magento/CatalogRule/etc/db_schema.xml b/app/code/Magento/CatalogRule/etc/db_schema.xml index 894f057ba73d1..59082e93b04c2 100644 --- a/app/code/Magento/CatalogRule/etc/db_schema.xml +++ b/app/code/Magento/CatalogRule/etc/db_schema.xml @@ -23,7 +23,7 @@ <column xsi:type="int" name="sort_order" padding="10" unsigned="true" nullable="false" identity="false" default="0" comment="Sort Order"/> <column xsi:type="varchar" name="simple_action" nullable="true" length="32" comment="Simple Action"/> - <column xsi:type="decimal" name="discount_amount" scale="4" precision="20" unsigned="false" nullable="false" + <column xsi:type="decimal" name="discount_amount" scale="6" precision="20" unsigned="false" nullable="false" default="0" comment="Discount Amount"/> <constraint xsi:type="primary" referenceId="PRIMARY"> <column name="rule_id"/> @@ -49,7 +49,7 @@ default="0" comment="Product Id"/> <column xsi:type="varchar" name="action_operator" nullable="true" length="10" default="to_fixed" comment="Action Operator"/> - <column xsi:type="decimal" name="action_amount" scale="4" precision="20" unsigned="false" nullable="false" + <column xsi:type="decimal" name="action_amount" scale="6" precision="20" unsigned="false" nullable="false" default="0" comment="Action Amount"/> <column xsi:type="smallint" name="action_stop" padding="6" unsigned="false" nullable="false" identity="false" default="0" comment="Action Stop"/> @@ -92,7 +92,7 @@ <column xsi:type="int" name="customer_group_id" padding="11" unsigned="false" nullable="true" identity="false"/> <column xsi:type="int" name="product_id" padding="10" unsigned="true" nullable="false" identity="false" default="0" comment="Product Id"/> - <column xsi:type="decimal" name="rule_price" scale="4" precision="12" unsigned="false" nullable="false" + <column xsi:type="decimal" name="rule_price" scale="6" precision="20" unsigned="false" nullable="false" default="0" comment="Rule Price"/> <column xsi:type="smallint" name="website_id" padding="5" unsigned="true" nullable="false" identity="false" comment="Website Id"/> @@ -189,7 +189,7 @@ default="0" comment="Product Id"/> <column xsi:type="varchar" name="action_operator" nullable="true" default="to_fixed" length="10" comment="Action Operator"/> - <column xsi:type="decimal" name="action_amount" scale="4" precision="12" unsigned="false" nullable="false" + <column xsi:type="decimal" name="action_amount" scale="6" precision="20" unsigned="false" nullable="false" default="0" comment="Action Amount"/> <column xsi:type="smallint" name="action_stop" padding="6" unsigned="false" nullable="false" identity="false" default="0" comment="Action Stop"/> @@ -233,7 +233,7 @@ <column xsi:type="int" name="customer_group_id" padding="11" unsigned="false" nullable="true" identity="false"/> <column xsi:type="int" name="product_id" padding="10" unsigned="true" nullable="false" identity="false" default="0" comment="Product Id"/> - <column xsi:type="decimal" name="rule_price" scale="4" precision="12" unsigned="false" nullable="false" + <column xsi:type="decimal" name="rule_price" scale="6" precision="20" unsigned="false" nullable="false" default="0" comment="Rule Price"/> <column xsi:type="smallint" name="website_id" padding="5" unsigned="true" nullable="false" identity="false" comment="Website Id"/> diff --git a/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithAssignedSimpleProductsTest.xml b/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithAssignedSimpleProductsTest.xml index 5bb45f58bcd27..3e700b5bcfb1b 100644 --- a/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithAssignedSimpleProductsTest.xml +++ b/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithAssignedSimpleProductsTest.xml @@ -17,6 +17,9 @@ <testCaseId value="MC-14063"/> <group value="catalogRuleConfigurable"/> <group value="mtf_migrated"/> + <skip> + <issueId value="MC-17140"/> + </skip> </annotations> <before> <!-- Create category for first configurable product --> diff --git a/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithOptionsTest.xml b/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithOptionsTest.xml index 408e181aa1c87..e53e51c626aa9 100644 --- a/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithOptionsTest.xml +++ b/app/code/Magento/CatalogRuleConfigurable/Test/Mftf/Test/AdminApplyCatalogRuleForConfigurableProductWithOptionsTest.xml @@ -17,6 +17,9 @@ <testCaseId value="MC-14062"/> <group value="catalogRuleConfigurable"/> <group value="mtf_migrated"/> + <skip> + <issueId value="MC-17140"/> + </skip> </annotations> <before> <!-- Create category --> diff --git a/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/DataProvider.php b/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/DataProvider.php index 39cb95747c2cf..cd2529a8fd725 100644 --- a/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/DataProvider.php +++ b/app/code/Magento/CatalogSearch/Model/Indexer/Fulltext/Action/DataProvider.php @@ -573,11 +573,10 @@ public function prepareProductIndex($indexData, $productData, $storeId) foreach ($attributeData as $attributeId => $attributeValues) { $value = $this->getAttributeValue($attributeId, $attributeValues, $storeId); if (!empty($value)) { - if (isset($index[$attributeId])) { - $index[$attributeId][$entityId] = $value; - } else { - $index[$attributeId] = [$entityId => $value]; + if (!isset($index[$attributeId])) { + $index[$attributeId] = []; } + $index[$attributeId][$entityId] = $value; } } } @@ -645,9 +644,12 @@ private function getAttributeOptionValue($attributeId, $valueIds, $storeId) $attribute->setStoreId($storeId); $options = $attribute->getSource()->toOptionArray(); $this->attributeOptions[$optionKey] = array_column($options, 'label', 'value'); - $this->attributeOptions[$optionKey] = array_map(function ($value) { - return $this->filterAttributeValue($value); - }, $this->attributeOptions[$optionKey]); + $this->attributeOptions[$optionKey] = array_map( + function ($value) { + return $this->filterAttributeValue($value); + }, + $this->attributeOptions[$optionKey] + ); } else { $this->attributeOptions[$optionKey] = null; } diff --git a/app/code/Magento/CatalogSearch/Model/Search/RequestGenerator.php b/app/code/Magento/CatalogSearch/Model/Search/RequestGenerator.php index 0adc2fcecbfa7..8f8ba39ebd329 100644 --- a/app/code/Magento/CatalogSearch/Model/Search/RequestGenerator.php +++ b/app/code/Magento/CatalogSearch/Model/Search/RequestGenerator.php @@ -103,7 +103,7 @@ private function generateRequest($attributeType, $container, $useFulltext) } } /** @var $attribute Attribute */ - if (!$attribute->getIsSearchable() || in_array($attribute->getAttributeCode(), ['price', 'sku'], true)) { + if (!$attribute->getIsSearchable() || in_array($attribute->getAttributeCode(), ['price'], true)) { // Some fields have their own specific handlers continue; } diff --git a/app/code/Magento/CatalogSearch/Test/Unit/Model/Search/RequestGeneratorTest.php b/app/code/Magento/CatalogSearch/Test/Unit/Model/Search/RequestGeneratorTest.php index b52c9cfd67494..a8c654652a32f 100644 --- a/app/code/Magento/CatalogSearch/Test/Unit/Model/Search/RequestGeneratorTest.php +++ b/app/code/Magento/CatalogSearch/Test/Unit/Model/Search/RequestGeneratorTest.php @@ -9,6 +9,9 @@ use Magento\CatalogSearch\Model\Search\RequestGenerator\GeneratorResolver; use Magento\CatalogSearch\Model\Search\RequestGenerator\GeneratorInterface; +/** + * Test for \Magento\CatalogSearch\Model\Search\RequestGenerator + */ class RequestGeneratorTest extends \PHPUnit\Framework\TestCase { /** @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager */ @@ -61,7 +64,7 @@ public function attributesProvider() return [ [ [ - 'quick_search_container' => ['queries' => 0, 'filters' => 0, 'aggregations' => 0], + 'quick_search_container' => ['queries' => 1, 'filters' => 0, 'aggregations' => 0], 'advanced_search_container' => ['queries' => 0, 'filters' => 0, 'aggregations' => 0], 'catalog_view_container' => ['queries' => 0, 'filters' => 0, 'aggregations' => 0] ], diff --git a/app/code/Magento/CatalogSearch/etc/search_request.xml b/app/code/Magento/CatalogSearch/etc/search_request.xml index d7bfb2e6b4a5c..6f9eb6e20666e 100644 --- a/app/code/Magento/CatalogSearch/etc/search_request.xml +++ b/app/code/Magento/CatalogSearch/etc/search_request.xml @@ -19,7 +19,6 @@ <queryReference clause="must" ref="visibility"/> </query> <query xsi:type="matchQuery" value="$search_term$" name="search"> - <match field="sku"/> <match field="*"/> </query> <query xsi:type="filteredQuery" name="category"> diff --git a/app/code/Magento/CatalogSearch/view/frontend/templates/advanced/form.phtml b/app/code/Magento/CatalogSearch/view/frontend/templates/advanced/form.phtml index 95ea7fcef3a1a..3712f221233ee 100644 --- a/app/code/Magento/CatalogSearch/view/frontend/templates/advanced/form.phtml +++ b/app/code/Magento/CatalogSearch/view/frontend/templates/advanced/form.phtml @@ -4,8 +4,7 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - +// phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis ?> <?php /** @@ -14,108 +13,120 @@ * @var $block \Magento\CatalogSearch\Block\Advanced\Form */ ?> -<?php $maxQueryLength = $this->helper('Magento\CatalogSearch\Helper\Data')->getMaxQueryLength();?> -<form class="form search advanced" action="<?= /* @escapeNotVerified */ $block->getSearchPostUrl() ?>" method="get" id="form-validate"> +<?php $maxQueryLength = $this->helper(\Magento\CatalogSearch\Helper\Data::class)->getMaxQueryLength();?> +<form class="form search advanced" action="<?= $block->escapeUrl($block->getSearchPostUrl()) ?>" method="get" id="form-validate"> <fieldset class="fieldset"> - <legend class="legend"><span><?= /* @escapeNotVerified */ __('Search Settings') ?></span></legend><br /> - <?php foreach ($block->getSearchableAttributes() as $_attribute): ?> - <?php $_code = $_attribute->getAttributeCode() ?> - <div class="field <?= /* @escapeNotVerified */ $_code ?>"> - <label class="label" for="<?= /* @escapeNotVerified */ $_code ?>"> + <legend class="legend"><span><?= $block->escapeHtml(__('Search Settings')) ?></span></legend><br /> + <?php foreach ($block->getSearchableAttributes() as $_attribute) : ?> + <?php $_code = $_attribute->getAttributeCode() ?> + <div class="field <?= $block->escapeHtmlAttr($_code) ?>"> + <label class="label" for="<?= $block->escapeHtmlAttr($_code) ?>"> <span><?= $block->escapeHtml(__($block->getAttributeLabel($_attribute))) ?></span> </label> <div class="control"> - <?php switch ($block->getAttributeInputType($_attribute)): - case 'number': ?> + <?php + switch ($block->getAttributeInputType($_attribute)) : + case 'number': + ?> <div class="range fields group group-2"> <div class="field no-label"> <div class="control"> <input type="text" - name="<?= /* @escapeNotVerified */ $_code ?>[from]" + name="<?= $block->escapeHtmlAttr($_code) ?>[from]" value="<?= $block->escapeHtml($block->getAttributeValue($_attribute, 'from')) ?>" - id="<?= /* @escapeNotVerified */ $_code ?>" + id="<?= $block->escapeHtmlAttr($_code) ?>" title="<?= $block->escapeHtml($block->getAttributeLabel($_attribute)) ?>" class="input-text" - maxlength="<?= /* @escapeNotVerified */ $maxQueryLength ?>" - data-validate="{number:true, 'less-than-equals-to':'#<?= /* @escapeNotVerified */ $_code ?>_to'}" /> + maxlength="<?= $block->escapeHtmlAttr($maxQueryLength) ?>" + data-validate="{number:true, 'less-than-equals-to':'#<?= $block->escapeHtmlAttr($_code) ?>_to'}" /> </div> </div> <div class="field no-label"> <div class="control"> <input type="text" - name="<?= /* @escapeNotVerified */ $_code ?>[to]" + name="<?= $block->escapeHtmlAttr($_code) ?>[to]" value="<?= $block->escapeHtml($block->getAttributeValue($_attribute, 'to')) ?>" - id="<?= /* @escapeNotVerified */ $_code ?>_to" + id="<?= $block->escapeHtmlAttr($_code) ?>_to" title="<?= $block->escapeHtml($block->getAttributeLabel($_attribute)) ?>" class="input-text" - maxlength="<?= /* @escapeNotVerified */ $maxQueryLength ?>" - data-validate="{number:true, 'greater-than-equals-to':'#<?= /* @escapeNotVerified */ $_code ?>'}" /> + maxlength="<?= $block->escapeHtmlAttr($maxQueryLength) ?>" + data-validate="{number:true, 'greater-than-equals-to':'#<?= $block->escapeHtmlAttr($_code) ?>'}" /> </div> </div> </div> - <?php break; - case 'price': ?> + <?php + break; + case 'price': + ?> <div class="range price fields group group-2"> <div class="field no-label"> <div class="control"> - <input name="<?= /* @escapeNotVerified */ $_code ?>[from]" + <input name="<?= $block->escapeHtmlAttr($_code) ?>[from]" value="<?= $block->escapeHtml($block->getAttributeValue($_attribute, 'from')) ?>" - id="<?= /* @escapeNotVerified */ $_code ?>" + id="<?= $block->escapeHtmlAttr($_code) ?>" title="<?= $block->escapeHtml($block->getAttributeLabel($_attribute)) ?>" class="input-text" type="text" - maxlength="<?= /* @escapeNotVerified */ $maxQueryLength ?>" - data-validate="{number:true, 'less-than-equals-to':'#<?= /* @escapeNotVerified */ $_code ?>_to'}" /> + maxlength="<?= $block->escapeHtmlAttr($maxQueryLength) ?>" + data-validate="{number:true, 'less-than-equals-to':'#<?= $block->escapeHtmlAttr($_code) ?>_to'}" /> </div> </div> <div class="field with-addon no-label"> <div class="control"> <div class="addon"> - <input name="<?= /* @escapeNotVerified */ $_code ?>[to]" + <input name="<?= $block->escapeHtmlAttr($_code) ?>[to]" value="<?= $block->escapeHtml($block->getAttributeValue($_attribute, 'to')) ?>" - id="<?= /* @escapeNotVerified */ $_code ?>_to" + id="<?= $block->escapeHtmlAttr($_code) ?>_to" title="<?= $block->escapeHtml($block->getAttributeLabel($_attribute)) ?>" class="input-text" type="text" - maxlength="<?= /* @escapeNotVerified */ $maxQueryLength ?>" - data-validate="{number:true, 'greater-than-equals-to':'#<?= /* @escapeNotVerified */ $_code ?>'}" /> + maxlength="<?= $block->escapeHtmlAttr($maxQueryLength) ?>" + data-validate="{number:true, 'greater-than-equals-to':'#<?= $block->escapeHtmlAttr($_code) ?>'}" /> <label class="addafter" - for="<?= /* @escapeNotVerified */ $_code ?>_to"> - <?= /* @escapeNotVerified */ $block->getCurrency($_attribute) ?> + for="<?= $block->escapeHtmlAttr($_code) ?>_to"> + <?= $block->escapeHtml($block->getCurrency($_attribute)) ?> </label> </div> </div> </div> </div> - <?php break; - case 'select': ?> - <?= /* @escapeNotVerified */ $block->getAttributeSelectElement($_attribute) ?> - <?php break; - case 'yesno': ?> - <?= /* @escapeNotVerified */ $block->getAttributeYesNoElement($_attribute) ?> - <?php break; - case 'date': ?> + <?php + break; + case 'select': + ?> + <?= /* @noEscape */ $block->getAttributeSelectElement($_attribute) ?> + <?php + break; + case 'yesno': + ?> + <?= /* @noEscape */ $block->getAttributeYesNoElement($_attribute) ?> + <?php + break; + case 'date': + ?> <div class="range dates fields group group-2"> <div class="field date no-label"> <div class="control"> - <?= /* @escapeNotVerified */ $block->getDateInput($_attribute, 'from') ?> + <?= /* @noEscape */ $block->getDateInput($_attribute, 'from') ?> </div> </div> <div class="field date no-label"> <div class="control"> - <?= /* @escapeNotVerified */ $block->getDateInput($_attribute, 'to') ?> + <?= /* @noEscape */ $block->getDateInput($_attribute, 'to') ?> </div> </div> </div> - <?php break; - default: ?> + <?php + break; + default: + ?> <input type="text" - name="<?= /* @escapeNotVerified */ $_code ?>" - id="<?= /* @escapeNotVerified */ $_code ?>" + name="<?= $block->escapeHtmlAttr($_code) ?>" + id="<?= $block->escapeHtmlAttr($_code) ?>" value="<?= $block->escapeHtml($block->getAttributeValue($_attribute)) ?>" title="<?= $block->escapeHtml($block->getAttributeLabel($_attribute)) ?>" - class="input-text <?= /* @escapeNotVerified */ $block->getAttributeValidationClass($_attribute) ?>" - maxlength="<?= /* @escapeNotVerified */ $maxQueryLength ?>" /> + class="input-text <?= $block->escapeHtmlAttr($block->getAttributeValidationClass($_attribute)) ?>" + maxlength="<?= $block->escapeHtmlAttr($maxQueryLength) ?>" /> <?php endswitch; ?> </div> </div> @@ -126,7 +137,7 @@ <button type="submit" class="action search primary" title="<?= $block->escapeHtml(__('Search')) ?>"> - <span><?= /* @escapeNotVerified */ __('Search') ?></span> + <span><?= $block->escapeHtml(__('Search')) ?></span> </button> </div> </div> @@ -147,8 +158,8 @@ require([ } }, messages: { - 'price[to]': {'greater-than-equals-to': '<?= /* @escapeNotVerified */ __('Please enter a valid price range.') ?>'}, - 'price[from]': {'less-than-equals-to': '<?= /* @escapeNotVerified */ __('Please enter a valid price range.') ?>'} + 'price[to]': {'greater-than-equals-to': '<?= $block->escapeJs(__('Please enter a valid price range.')) ?>'}, + 'price[from]': {'less-than-equals-to': '<?= $block->escapeJs(__('Please enter a valid price range.')) ?>'} } }); }); diff --git a/app/code/Magento/CatalogSearch/view/frontend/templates/advanced/link.phtml b/app/code/Magento/CatalogSearch/view/frontend/templates/advanced/link.phtml index 09098b1ccd003..2e183291da778 100644 --- a/app/code/Magento/CatalogSearch/view/frontend/templates/advanced/link.phtml +++ b/app/code/Magento/CatalogSearch/view/frontend/templates/advanced/link.phtml @@ -4,13 +4,13 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile +// phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis /** @var \Magento\CatalogSearch\Helper\Data $helper */ -$helper = $this->helper('Magento\CatalogSearch\Helper\Data'); +$helper = $this->helper(\Magento\CatalogSearch\Helper\Data::class); ?> <div class="nested"> - <a class="action advanced" href="<?= /* @escapeNotVerified */ $helper->getAdvancedSearchUrl() ?>" data-action="advanced-search"> - <?= /* @escapeNotVerified */ __('Advanced Search') ?> + <a class="action advanced" href="<?= $block->escapeUrl($helper->getAdvancedSearchUrl()) ?>" data-action="advanced-search"> + <?= $block->escapeHtml(__('Advanced Search')) ?> </a> </div> diff --git a/app/code/Magento/CatalogSearch/view/frontend/templates/advanced/result.phtml b/app/code/Magento/CatalogSearch/view/frontend/templates/advanced/result.phtml index 3f616ab791dfe..e21b031d69521 100644 --- a/app/code/Magento/CatalogSearch/view/frontend/templates/advanced/result.phtml +++ b/app/code/Magento/CatalogSearch/view/frontend/templates/advanced/result.phtml @@ -3,9 +3,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <?php /** @@ -14,43 +11,43 @@ /** this changes need for valid apply filters and configuration before search process is started */ $productList = $block->getProductListHtml(); ?> -<?php if ($results = $block->getResultCount()): ?> +<?php if ($results = $block->getResultCount()) : ?> <div class="search found"> <?php if ($results == 1) : ?> - <?= /* @escapeNotVerified */ __('<strong>%1 item</strong> were found using the following search criteria', $results) ?> - <?php else: ?> - <?= /* @escapeNotVerified */ __('<strong>%1 items</strong> were found using the following search criteria', $results) ?> + <?= /* @noEscape */ __('<strong>%1 item</strong> were found using the following search criteria', $results) ?> + <?php else : ?> + <?= /* @noEscape */ __('<strong>%1 items</strong> were found using the following search criteria', $results) ?> <?php endif; ?> </div> -<?php else: ?> +<?php else : ?> <div role="alert" class="message error"> <div> - <?= /* @escapeNotVerified */ __('We can\'t find any items matching these search criteria.') ?> <a href="<?= /* @escapeNotVerified */ $block->getFormUrl() ?>"><?= /* @escapeNotVerified */ __('Modify your search.') ?></a> + <?= $block->escapeHtml(__('We can\'t find any items matching these search criteria.')) ?> <a href="<?= $block->escapeUrl($block->getFormUrl()) ?>"><?= $block->escapeHtml(__('Modify your search.')) ?></a> </div> </div> <?php endif; ?> <?php $searchCriterias = $block->getSearchCriterias(); ?> <div class="search summary"> - <?php foreach (['left', 'right'] as $side): ?> - <?php if (@$searchCriterias[$side]): ?> + <?php foreach (['left', 'right'] as $side) : ?> + <?php if (!empty($searchCriterias[$side])) : ?> <ul class="items"> - <?php foreach ($searchCriterias[$side] as $criteria): ?> + <?php foreach ($searchCriterias[$side] as $criteria) : ?> <li class="item"><strong><?= $block->escapeHtml(__($criteria['name'])) ?>:</strong> <?= $block->escapeHtml($criteria['value']) ?></li> <?php endforeach; ?> </ul> <?php endif; ?> <?php endforeach; ?> </div> -<?php if ($block->getResultCount()): ?> +<?php if ($block->getResultCount()) : ?> <div class="message notice"> <div> - <?= /* @escapeNotVerified */ __("Don't see what you're looking for?") ?> - <a href="<?= /* @escapeNotVerified */ $block->getFormUrl() ?>"><?= /* @escapeNotVerified */ __('Modify your search.') ?></a> + <?= $block->escapeHtml(__("Don't see what you're looking for?")) ?> + <a href="<?= $block->escapeUrl($block->getFormUrl()) ?>"><?= $block->escapeHtml(__('Modify your search.')) ?></a> </div> </div> <?php endif; ?> -<?php if ($block->getResultCount()): ?> - <div class="search results"><?= /* @escapeNotVerified */ $productList ?></div> +<?php if ($block->getResultCount()) : ?> + <div class="search results"><?= /* @noEscape */ $productList ?></div> <?php endif; ?> <?php $block->getSearchCriterias(); ?> diff --git a/app/code/Magento/CatalogSearch/view/frontend/templates/result.phtml b/app/code/Magento/CatalogSearch/view/frontend/templates/result.phtml index 2757ae3b5f7ed..c63e6ff4abe0f 100644 --- a/app/code/Magento/CatalogSearch/view/frontend/templates/result.phtml +++ b/app/code/Magento/CatalogSearch/view/frontend/templates/result.phtml @@ -3,34 +3,31 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> -<?php if ($block->getResultCount()): ?> -<?= $block->getChildHtml('tagged_product_list_rss_link') ?> +<?php if ($block->getResultCount()) : ?> + <?= /* @noEscape */ $block->getChildHtml('tagged_product_list_rss_link') ?> <div class="search results"> - <?php if ($messages = $block->getNoteMessages()):?> + <?php if ($messages = $block->getNoteMessages()) : ?> <div class="message notice"> <div> - <?php foreach ($messages as $message):?> - <?= /* @escapeNotVerified */ $message ?><br /> - <?php endforeach;?> + <?php foreach ($messages as $message) : ?> + <?= /* @noEscape */ $message ?><br /> + <?php endforeach; ?> </div> </div> <?php endif; ?> <?= $block->getProductListHtml() ?> </div> -<?php else: ?> +<?php else : ?> <div class="message notice"> <div> - <?= /* @escapeNotVerified */ ($block->getNoResultText()) ? $block->getNoResultText() : __('Your search returned no results.') ?> - <?= $block->getAdditionalHtml() ?> - <?php if ($messages = $block->getNoteMessages()):?> - <?php foreach ($messages as $message):?> - <br /><?= /* @escapeNotVerified */ $message ?> - <?php endforeach;?> + <?= $block->escapeHtml($block->getNoResultText() ? $block->getNoResultText() : __('Your search returned no results.')) ?> + <?= /* @noEscape */ $block->getAdditionalHtml() ?> + <?php if ($messages = $block->getNoteMessages()) : ?> + <?php foreach ($messages as $message) : ?> + <br /><?= /* @noEscape */ $message ?> + <?php endforeach; ?> <?php endif; ?> </div> </div> diff --git a/app/code/Magento/CatalogSearch/view/frontend/templates/search_terms_log.phtml b/app/code/Magento/CatalogSearch/view/frontend/templates/search_terms_log.phtml index 61609bdf66bda..38ef11933a46f 100644 --- a/app/code/Magento/CatalogSearch/view/frontend/templates/search_terms_log.phtml +++ b/app/code/Magento/CatalogSearch/view/frontend/templates/search_terms_log.phtml @@ -3,14 +3,13 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile ?> -<?php if ($block->getSearchTermsLog()->isPageCacheable()): ?> +<?php if ($block->getSearchTermsLog()->isPageCacheable()) : ?> <script type="text/x-magento-init"> { "*": { "Magento_CatalogSearch/js/search-terms-log": { - "url": "<?= /* @escapeNotVerified */ $block->getUrl('catalogsearch/searchTermsLog/save') ?>" + "url": "<?= $block->escapeUrl($block->getUrl('catalogsearch/searchTermsLog/save')) ?>" } } } diff --git a/app/code/Magento/CatalogUrlRewrite/Model/Category/Plugin/Storage.php b/app/code/Magento/CatalogUrlRewrite/Model/Category/Plugin/Storage.php index 572152e84b2d7..00bf88675e752 100644 --- a/app/code/Magento/CatalogUrlRewrite/Model/Category/Plugin/Storage.php +++ b/app/code/Magento/CatalogUrlRewrite/Model/Category/Plugin/Storage.php @@ -11,6 +11,9 @@ use Magento\UrlRewrite\Service\V1\Data\UrlRewrite; use Magento\CatalogUrlRewrite\Model\ResourceModel\Category\Product; +/** + * Storage Plugin + */ class Storage { /** @@ -36,6 +39,8 @@ public function __construct( } /** + * Save product/category urlRewrite association + * * @param \Magento\UrlRewrite\Model\StorageInterface $object * @param \Magento\UrlRewrite\Service\V1\Data\UrlRewrite[] $result * @param \Magento\UrlRewrite\Service\V1\Data\UrlRewrite[] $urls @@ -53,13 +58,15 @@ public function afterReplace(StorageInterface $object, array $result, array $url 'product_id' => $record->getEntityId(), ]; } - if ($toSave) { + if (count($toSave) > 0) { $this->productResource->saveMultiple($toSave); } return $result; } /** + * Remove product/category urlRewrite association + * * @param \Magento\UrlRewrite\Model\StorageInterface $object * @param array $data * @return void @@ -71,6 +78,8 @@ public function beforeDeleteByData(StorageInterface $object, array $data) } /** + * Filter urls + * * @param \Magento\UrlRewrite\Service\V1\Data\UrlRewrite[] $urls * @return \Magento\UrlRewrite\Service\V1\Data\UrlRewrite[] */ @@ -96,6 +105,8 @@ protected function filterUrls(array $urls) } /** + * Check if url is correct + * * @param UrlRewrite $url * @return bool */ diff --git a/app/code/Magento/CatalogUrlRewrite/Model/Product/AnchorUrlRewriteGenerator.php b/app/code/Magento/CatalogUrlRewrite/Model/Product/AnchorUrlRewriteGenerator.php index a7cc894c9a022..4a191b54dea68 100644 --- a/app/code/Magento/CatalogUrlRewrite/Model/Product/AnchorUrlRewriteGenerator.php +++ b/app/code/Magento/CatalogUrlRewrite/Model/Product/AnchorUrlRewriteGenerator.php @@ -13,7 +13,12 @@ use Magento\Framework\Exception\NoSuchEntityException; use Magento\UrlRewrite\Service\V1\Data\UrlRewrite; use Magento\UrlRewrite\Service\V1\Data\UrlRewriteFactory; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\App\ObjectManager; +/** + * Generate url rewrites for anchor categories + */ class AnchorUrlRewriteGenerator { /** @@ -52,7 +57,7 @@ public function __construct( * @param int $storeId * @param Product $product * @param ObjectRegistry $productCategories - * @return UrlRewrite[] + * @return UrlRewrite[]|array */ public function generate($storeId, Product $product, ObjectRegistry $productCategories) { diff --git a/app/code/Magento/CatalogUrlRewrite/Model/Product/CategoriesUrlRewriteGenerator.php b/app/code/Magento/CatalogUrlRewrite/Model/Product/CategoriesUrlRewriteGenerator.php index 9e787e74ae073..a7e4b511ecdd2 100644 --- a/app/code/Magento/CatalogUrlRewrite/Model/Product/CategoriesUrlRewriteGenerator.php +++ b/app/code/Magento/CatalogUrlRewrite/Model/Product/CategoriesUrlRewriteGenerator.php @@ -11,7 +11,12 @@ use Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator; use Magento\UrlRewrite\Service\V1\Data\UrlRewrite; use Magento\UrlRewrite\Service\V1\Data\UrlRewriteFactory; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\App\ObjectManager; +/** + * Generate url rewrites for categories + */ class CategoriesUrlRewriteGenerator { /** @@ -28,8 +33,10 @@ class CategoriesUrlRewriteGenerator * @param ProductUrlPathGenerator $productUrlPathGenerator * @param UrlRewriteFactory $urlRewriteFactory */ - public function __construct(ProductUrlPathGenerator $productUrlPathGenerator, UrlRewriteFactory $urlRewriteFactory) - { + public function __construct( + ProductUrlPathGenerator $productUrlPathGenerator, + UrlRewriteFactory $urlRewriteFactory + ) { $this->productUrlPathGenerator = $productUrlPathGenerator; $this->urlRewriteFactory = $urlRewriteFactory; } diff --git a/app/code/Magento/CatalogUrlRewrite/Model/ProductScopeRewriteGenerator.php b/app/code/Magento/CatalogUrlRewrite/Model/ProductScopeRewriteGenerator.php index 6b838f83d31e4..9d26184e2c2d4 100644 --- a/app/code/Magento/CatalogUrlRewrite/Model/ProductScopeRewriteGenerator.php +++ b/app/code/Magento/CatalogUrlRewrite/Model/ProductScopeRewriteGenerator.php @@ -13,6 +13,7 @@ use Magento\CatalogUrlRewrite\Model\Product\CategoriesUrlRewriteGenerator; use Magento\CatalogUrlRewrite\Model\Product\CurrentUrlRewritesRegenerator; use Magento\CatalogUrlRewrite\Service\V1\StoreViewService; +use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\App\ObjectManager; use Magento\Store\Model\Store; use Magento\Store\Model\StoreManagerInterface; @@ -34,6 +35,11 @@ class ProductScopeRewriteGenerator */ private $storeManager; + /** + * @var ScopeConfigInterface + */ + private $config; + /** * @var ObjectRegistryFactory */ @@ -79,6 +85,8 @@ class ProductScopeRewriteGenerator * @param AnchorUrlRewriteGenerator $anchorUrlRewriteGenerator * @param \Magento\UrlRewrite\Model\MergeDataProviderFactory|null $mergeDataProviderFactory * @param CategoryRepositoryInterface|null $categoryRepository + * @param ScopeConfigInterface|null $config + * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( StoreViewService $storeViewService, @@ -89,7 +97,8 @@ public function __construct( CurrentUrlRewritesRegenerator $currentUrlRewritesRegenerator, AnchorUrlRewriteGenerator $anchorUrlRewriteGenerator, MergeDataProviderFactory $mergeDataProviderFactory = null, - CategoryRepositoryInterface $categoryRepository = null + CategoryRepositoryInterface $categoryRepository = null, + ScopeConfigInterface $config = null ) { $this->storeViewService = $storeViewService; $this->storeManager = $storeManager; @@ -104,6 +113,7 @@ public function __construct( $this->mergeDataProviderPrototype = $mergeDataProviderFactory->create(); $this->categoryRepository = $categoryRepository ?: ObjectManager::getInstance()->get(CategoryRepositoryInterface::class); + $this->config = $config ?: ObjectManager::getInstance()->get(ScopeConfigInterface::class); } /** @@ -173,9 +183,13 @@ public function generateForSpecificStoreView($storeId, $productCategories, Produ $mergeDataProvider->merge( $this->canonicalUrlRewriteGenerator->generate($storeId, $product) ); - $mergeDataProvider->merge( - $this->categoriesUrlRewriteGenerator->generate($storeId, $product, $productCategories) - ); + + if ($this->isCategoryRewritesEnabled()) { + $mergeDataProvider->merge( + $this->categoriesUrlRewriteGenerator->generate($storeId, $product, $productCategories) + ); + } + $mergeDataProvider->merge( $this->currentUrlRewritesRegenerator->generate( $storeId, @@ -184,9 +198,13 @@ public function generateForSpecificStoreView($storeId, $productCategories, Produ $rootCategoryId ) ); - $mergeDataProvider->merge( - $this->anchorUrlRewriteGenerator->generate($storeId, $product, $productCategories) - ); + + if ($this->isCategoryRewritesEnabled()) { + $mergeDataProvider->merge( + $this->anchorUrlRewriteGenerator->generate($storeId, $product, $productCategories) + ); + } + $mergeDataProvider->merge( $this->currentUrlRewritesRegenerator->generateAnchor( $storeId, @@ -195,6 +213,7 @@ public function generateForSpecificStoreView($storeId, $productCategories, Produ $rootCategoryId ) ); + return $mergeDataProvider->getData(); } @@ -216,10 +235,12 @@ public function isCategoryProperForGenerating(Category $category, $storeId) } /** + * Check if URL key has been changed + * * Checks if URL key has been changed for provided category and returns reloaded category, * in other case - returns provided category. * - * @param $storeId + * @param int $storeId * @param Category $category * @return Category */ @@ -236,4 +257,14 @@ private function getCategoryWithOverriddenUrlKey($storeId, Category $category) } return $this->categoryRepository->get($category->getEntityId(), $storeId); } + + /** + * Check config value of generate_category_product_rewrites + * + * @return bool + */ + private function isCategoryRewritesEnabled() + { + return (bool)$this->config->getValue('catalog/seo/generate_category_product_rewrites'); + } } diff --git a/app/code/Magento/CatalogUrlRewrite/Model/ProductUrlPathGenerator.php b/app/code/Magento/CatalogUrlRewrite/Model/ProductUrlPathGenerator.php index 4fdb9a3e2138d..ac3a5092bb3bf 100644 --- a/app/code/Magento/CatalogUrlRewrite/Model/ProductUrlPathGenerator.php +++ b/app/code/Magento/CatalogUrlRewrite/Model/ProductUrlPathGenerator.php @@ -3,8 +3,21 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\CatalogUrlRewrite\Model; +use Magento\Store\Model\Store; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Category; +use Magento\Catalog\Model\Product; +use Magento\CatalogUrlRewrite\Model\CategoryUrlPathGenerator; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Store\Model\ScopeInterface; +use Magento\Store\Model\StoreManagerInterface; + +/** + * Class ProductUrlPathGenerator + */ class ProductUrlPathGenerator { const XML_PATH_PRODUCT_URL_SUFFIX = 'catalog/seo/product_url_suffix'; @@ -17,36 +30,36 @@ class ProductUrlPathGenerator protected $productUrlSuffix = []; /** - * @var \Magento\Store\Model\StoreManagerInterface + * @var StoreManagerInterface */ protected $storeManager; /** - * @var \Magento\Framework\App\Config\ScopeConfigInterface + * @var ScopeConfigInterface */ protected $scopeConfig; /** - * @var \Magento\CatalogUrlRewrite\Model\CategoryUrlPathGenerator + * @var CategoryUrlPathGenerator */ protected $categoryUrlPathGenerator; /** - * @var \Magento\Catalog\Api\ProductRepositoryInterface + * @var ProductRepositoryInterface */ protected $productRepository; /** - * @param \Magento\Store\Model\StoreManagerInterface $storeManager - * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig + * @param StoreManagerInterface $storeManager + * @param ScopeConfigInterface $scopeConfig * @param CategoryUrlPathGenerator $categoryUrlPathGenerator - * @param \Magento\Catalog\Api\ProductRepositoryInterface $productRepository + * @param ProductRepositoryInterface $productRepository */ public function __construct( - \Magento\Store\Model\StoreManagerInterface $storeManager, - \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, - \Magento\CatalogUrlRewrite\Model\CategoryUrlPathGenerator $categoryUrlPathGenerator, - \Magento\Catalog\Api\ProductRepositoryInterface $productRepository + StoreManagerInterface $storeManager, + ScopeConfigInterface $scopeConfig, + CategoryUrlPathGenerator $categoryUrlPathGenerator, + ProductRepositoryInterface $productRepository ) { $this->storeManager = $storeManager; $this->scopeConfig = $scopeConfig; @@ -57,8 +70,8 @@ public function __construct( /** * Retrieve Product Url path (with category if exists) * - * @param \Magento\Catalog\Model\Product $product - * @param \Magento\Catalog\Model\Category $category + * @param Product $product + * @param Category $category * * @return string */ @@ -78,10 +91,10 @@ public function getUrlPath($product, $category = null) /** * Prepare URL Key with stored product data (fallback for "Use Default Value" logic) * - * @param \Magento\Catalog\Model\Product $product + * @param Product $product * @return string */ - protected function prepareProductDefaultUrlKey(\Magento\Catalog\Model\Product $product) + protected function prepareProductDefaultUrlKey(Product $product) { $storedProduct = $this->productRepository->getById($product->getId()); $storedUrlKey = $storedProduct->getUrlKey(); @@ -91,9 +104,9 @@ protected function prepareProductDefaultUrlKey(\Magento\Catalog\Model\Product $p /** * Retrieve Product Url path with suffix * - * @param \Magento\Catalog\Model\Product $product + * @param Product $product * @param int $storeId - * @param \Magento\Catalog\Model\Category $category + * @param Category $category * @return string */ public function getUrlPathWithSuffix($product, $storeId, $category = null) @@ -104,8 +117,8 @@ public function getUrlPathWithSuffix($product, $storeId, $category = null) /** * Get canonical product url path * - * @param \Magento\Catalog\Model\Product $product - * @param \Magento\Catalog\Model\Category|null $category + * @param Product $product + * @param Category|null $category * @return string */ public function getCanonicalUrlPath($product, $category = null) @@ -117,7 +130,7 @@ public function getCanonicalUrlPath($product, $category = null) /** * Generate product url key based on url_key entered by merchant or product name * - * @param \Magento\Catalog\Model\Product $product + * @param Product $product * @return string|null */ public function getUrlKey($product) @@ -129,13 +142,15 @@ public function getUrlKey($product) /** * Prepare url key for product * - * @param \Magento\Catalog\Model\Product $product + * @param Product $product * @return string */ - protected function prepareProductUrlKey(\Magento\Catalog\Model\Product $product) + protected function prepareProductUrlKey(Product $product) { - $urlKey = $product->getUrlKey(); - return $product->formatUrlKey($urlKey === '' || $urlKey === null ? $product->getName() : $urlKey); + $urlKey = (string)$product->getUrlKey(); + $urlKey = trim(strtolower($urlKey)); + + return $urlKey ?: $product->formatUrlKey($product->getName()); } /** @@ -153,7 +168,7 @@ protected function getProductUrlSuffix($storeId = null) if (!isset($this->productUrlSuffix[$storeId])) { $this->productUrlSuffix[$storeId] = $this->scopeConfig->getValue( self::XML_PATH_PRODUCT_URL_SUFFIX, - \Magento\Store\Model\ScopeInterface::SCOPE_STORE, + ScopeInterface::SCOPE_STORE, $storeId ); } diff --git a/app/code/Magento/CatalogUrlRewrite/Model/Storage/DbStorage.php b/app/code/Magento/CatalogUrlRewrite/Model/Storage/DbStorage.php index f0351467e5f0e..4bbecbfae6bcf 100644 --- a/app/code/Magento/CatalogUrlRewrite/Model/Storage/DbStorage.php +++ b/app/code/Magento/CatalogUrlRewrite/Model/Storage/DbStorage.php @@ -3,32 +3,40 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\CatalogUrlRewrite\Model\Storage; use Magento\CatalogUrlRewrite\Model\ResourceModel\Category\Product; use Magento\UrlRewrite\Model\Storage\DbStorage as BaseDbStorage; use Magento\UrlRewrite\Service\V1\Data\UrlRewrite; +/** + * Class DbStorage + */ class DbStorage extends BaseDbStorage { /** - * {@inheritDoc} + * @inheritDoc */ protected function prepareSelect(array $data) { $metadata = []; - if (array_key_exists(UrlRewrite::METADATA, $data)) { + if (isset($data[UrlRewrite::METADATA])) { $metadata = $data[UrlRewrite::METADATA]; unset($data[UrlRewrite::METADATA]); } $select = $this->connection->select(); - $select->from([ - 'url_rewrite' => $this->resource->getTableName(self::TABLE_NAME) - ]); + $select->from( + [ + 'url_rewrite' => $this->resource->getTableName(self::TABLE_NAME) + ] + ); $select->joinLeft( ['relation' => $this->resource->getTableName(Product::TABLE_NAME)], - 'url_rewrite.url_rewrite_id = relation.url_rewrite_id' + 'url_rewrite.url_rewrite_id = relation.url_rewrite_id', + ['relation.category_id', 'relation.product_id'] ); foreach ($data as $column => $value) { diff --git a/app/code/Magento/CatalogUrlRewrite/Model/Storage/DynamicStorage.php b/app/code/Magento/CatalogUrlRewrite/Model/Storage/DynamicStorage.php new file mode 100644 index 0000000000000..edca633fb14cc --- /dev/null +++ b/app/code/Magento/CatalogUrlRewrite/Model/Storage/DynamicStorage.php @@ -0,0 +1,259 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CatalogUrlRewrite\Model\Storage; + +use Magento\Catalog\Model\ResourceModel\ProductFactory; +use Magento\CatalogUrlRewrite\Model\CategoryUrlPathGenerator; +use Magento\Framework\Api\DataObjectHelper; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\App\ResourceConnection; +use Magento\CatalogUrlRewrite\Model\ResourceModel\Category\Product; +use Magento\Store\Model\ScopeInterface; +use Magento\UrlRewrite\Model\OptionProvider; +use Magento\UrlRewrite\Model\Storage\DbStorage as BaseDbStorage; +use Magento\UrlRewrite\Service\V1\Data\UrlRewrite; +use Magento\UrlRewrite\Service\V1\Data\UrlRewriteFactory; +use Psr\Log\LoggerInterface; + +/** + * Class DbStorage + */ +class DynamicStorage extends BaseDbStorage +{ + /** + * @var ScopeConfigInterface + */ + private $config; + + /** + * @var ProductFactory + */ + private $productFactory; + + /** + * @param UrlRewriteFactory $urlRewriteFactory + * @param DataObjectHelper $dataObjectHelper + * @param ResourceConnection $resource + * @param ScopeConfigInterface $config + * @param ProductFactory $productFactory + * @param LoggerInterface|null $logger + */ + public function __construct( + UrlRewriteFactory $urlRewriteFactory, + DataObjectHelper $dataObjectHelper, + ResourceConnection $resource, + ScopeConfigInterface $config, + ProductFactory $productFactory, + LoggerInterface $logger = null + ) { + parent::__construct($urlRewriteFactory, $dataObjectHelper, $resource, $logger); + $this->config = $config; + $this->productFactory = $productFactory; + } + + /** + * @inheritDoc + */ + protected function prepareSelect(array $data) + { + $metadata = []; + if (isset($data[UrlRewrite::METADATA])) { + $metadata = $data[UrlRewrite::METADATA]; + unset($data[UrlRewrite::METADATA]); + } + $select = $this->connection->select(); + $select->from( + [ + 'url_rewrite' => $this->resource->getTableName(self::TABLE_NAME) + ] + ); + $select->joinLeft( + ['relation' => $this->resource->getTableName(Product::TABLE_NAME)], + 'url_rewrite.url_rewrite_id = relation.url_rewrite_id' + ); + foreach ($data as $column => $value) { + $select->where('url_rewrite.' . $column . ' IN (?)', $value); + } + if (empty($metadata['category_id'])) { + $select->where('relation.category_id IS NULL'); + } else { + $select->where( + 'relation.category_id = ?', + $metadata['category_id'] + ); + } + return $select; + } + + /** + * @inheritdoc + */ + protected function doFindOneByData(array $data) + { + if (isset($data[UrlRewrite::REQUEST_PATH]) + && isset($data[UrlRewrite::STORE_ID]) + && is_string($data[UrlRewrite::REQUEST_PATH]) + ) { + return $this->findProductRewriteByRequestPath($data); + } + + $filterResults = $this->findProductRewritesByFilter($data); + if (!empty($filterResults)) { + return reset($filterResults); + } else { + return null; + } + } + + /** + * @inheritdoc + */ + protected function doFindAllByData(array $data) + { + $rewrites = parent::doFindAllByData($data); + + $remainingProducts = []; + if (isset($data[UrlRewrite::ENTITY_ID]) && is_array($data[UrlRewrite::ENTITY_ID])) { + $remainingProducts = array_fill_keys($data[UrlRewrite::ENTITY_ID], 1); + foreach ($rewrites as $rewrite) { + $id = $rewrite[UrlRewrite::ENTITY_ID]; + if (isset($remainingProducts[$id])) { + unset($remainingProducts[$id]); + } + } + } + + if (!empty($remainingProducts)) { + $data[UrlRewrite::ENTITY_ID] = array_keys($remainingProducts); + $rewrites = array_merge($rewrites, $this->findProductRewritesByFilter($data)); + } + + return $rewrites; + } + + /** + * Get category urlSuffix from config + * + * @param int $storeId + * @return string + */ + private function getCategoryUrlSuffix($storeId = null): string + { + return $this->config->getValue( + CategoryUrlPathGenerator::XML_PATH_CATEGORY_URL_SUFFIX, + ScopeInterface::SCOPE_STORE, + $storeId + ); + } + + /** + * Find product rewrite by request data + * + * @param array $data + * @return array|null + */ + private function findProductRewriteByRequestPath(array $data): ?array + { + $requestPath = $data[UrlRewrite::REQUEST_PATH] ?? null; + + $productUrl = $this->getBaseName($requestPath); + $data[UrlRewrite::REQUEST_PATH] = [$productUrl]; + + $productFromDb = $this->connection->fetchRow($this->prepareSelect($data)); + if ($productFromDb === false) { + return null; + } + $categorySuffix = $this->getCategoryUrlSuffix($data[UrlRewrite::STORE_ID]); + $productResource = $this->productFactory->create(); + $categoryPath = str_replace('/' . $productUrl, '', $requestPath); + if ($productFromDb[UrlRewrite::REDIRECT_TYPE]) { + $productUrl = $productFromDb[UrlRewrite::TARGET_PATH]; + } + if ($categoryPath) { + $data[UrlRewrite::REQUEST_PATH] = [$categoryPath . $categorySuffix]; + unset($data[UrlRewrite::IS_AUTOGENERATED]); + $categoryFromDb = $this->connection->fetchRow($this->prepareSelect($data)); + + if ($categoryFromDb[UrlRewrite::REDIRECT_TYPE]) { + $productFromDb[UrlRewrite::REDIRECT_TYPE] = OptionProvider::PERMANENT; + $categoryPath = str_replace($categorySuffix, '', $categoryFromDb[UrlRewrite::TARGET_PATH]); + } + + if ($categoryFromDb === false + || !$productResource->canBeShowInCategory( + $productFromDb[UrlRewrite::ENTITY_ID], + $categoryFromDb[UrlRewrite::ENTITY_ID] + ) + ) { + return null; + } + + $productFromDb[UrlRewrite::TARGET_PATH] = $productFromDb[UrlRewrite::TARGET_PATH] + . '/category/' . $categoryFromDb[UrlRewrite::ENTITY_ID]; + } + + if ($productFromDb[UrlRewrite::REDIRECT_TYPE]) { + $productFromDb[UrlRewrite::TARGET_PATH] = $categoryPath . '/' . $productUrl; + } + + $productFromDb[UrlRewrite::REQUEST_PATH] = $requestPath; + + return $productFromDb; + } + + /** + * Find product rewrites by filter array + * + * @param array $data + * @return array + */ + private function findProductRewritesByFilter(array $data): array + { + if (empty($data[UrlRewrite::ENTITY_TYPE])) { + return []; + } + $rewrites = []; + $metadata = $data[UrlRewrite::METADATA] ?? []; + if (isset($data[UrlRewrite::METADATA])) { + unset($data[UrlRewrite::METADATA]); + } + $productsFromDb = $this->connection->fetchAll($this->prepareSelect($data)); + + if (!empty($metadata['category_id'])) { + $categoryId = $metadata['category_id']; + $data[UrlRewrite::ENTITY_ID] = $categoryId; + $data[UrlRewrite::ENTITY_TYPE] = 'category'; + $categoryFromDb = $this->connection->fetchRow($this->prepareSelect($data)); + foreach ($productsFromDb as $productFromDb) { + $productUrl = $this->getBaseName($productFromDb[UrlRewrite::REQUEST_PATH]); + $productFromDb[UrlRewrite::REQUEST_PATH] = str_replace( + $this->getCategoryUrlSuffix($data[UrlRewrite::STORE_ID]), + '', + $categoryFromDb[UrlRewrite::REQUEST_PATH] + ) + . '/' . $productUrl; + $rewrites[] = $productFromDb; + } + } else { + $rewrites = $productsFromDb; + } + + return $rewrites; + } + + /** + * Return base name for path + * + * @param string|null $string + * @return string + */ + private function getBaseName($string): string + { + return preg_replace('|.*?([^/]+)$|', '\1', $string, 1); + } +} diff --git a/app/code/Magento/CatalogUrlRewrite/Model/TableCleaner.php b/app/code/Magento/CatalogUrlRewrite/Model/TableCleaner.php new file mode 100644 index 0000000000000..070836380ac63 --- /dev/null +++ b/app/code/Magento/CatalogUrlRewrite/Model/TableCleaner.php @@ -0,0 +1,82 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CatalogUrlRewrite\Model; + +use Magento\Framework\App\Cache\TypeListInterface; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\App\Config\Value as ConfigValue; +use Magento\Framework\Data\Collection\AbstractDb; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Model\Context; +use Magento\Framework\Model\ResourceModel\AbstractResource; +use Magento\Framework\Registry; +use Magento\UrlRewrite\Model\ResourceModel\UrlRewrite; + +/** + * Table Cleaner in case of switching generate_category_product_rewrites off + */ +class TableCleaner extends ConfigValue +{ + const AUTO_GENERATED_ROW_FLAG = 1; + const URL_REWRITE_GENERATION_OFF_FLAG = 0; + + /** + * @var UrlRewrite + */ + private $urlRewrite; + + /** + * @param UrlRewrite $urlRewrite + * @param Context $context + * @param Registry $registry + * @param ScopeConfigInterface $config + * @param TypeListInterface $cacheTypeList + * @param AbstractResource|null $resource + * @param AbstractDb|null $resourceCollection + * @param array $data + */ + public function __construct( + UrlRewrite $urlRewrite, + Context $context, + Registry $registry, + ScopeConfigInterface $config, + TypeListInterface $cacheTypeList, + AbstractResource $resource = null, + AbstractDb $resourceCollection = null, + array $data = [] + ) { + parent::__construct($context, $registry, $config, $cacheTypeList, $resource, $resourceCollection, $data); + $this->urlRewrite = $urlRewrite; + } + + /** + * @inheritDoc + * @return ConfigValue + * @throws LocalizedException + */ + public function afterSave() + { + if ($this->getValue() == self::URL_REWRITE_GENERATION_OFF_FLAG) { + $this->clearOldData(); + } + return parent::afterSave(); + } + + /** + * Clear urlrewrites for products in categories + */ + private function clearOldData(): void + { + $tableName = $this->urlRewrite->getMainTable(); + $conditions = [ + 'metadata LIKE ?' => '{"category_id"%', + 'is_autogenerated = ?' => self::AUTO_GENERATED_ROW_FLAG + ]; + $this->urlRewrite->getConnection()->delete($tableName, $conditions); + } +} diff --git a/app/code/Magento/CatalogUrlRewrite/Observer/AfterImportDataObserver.php b/app/code/Magento/CatalogUrlRewrite/Observer/AfterImportDataObserver.php index 7b60c85049767..f51c2825279ba 100644 --- a/app/code/Magento/CatalogUrlRewrite/Observer/AfterImportDataObserver.php +++ b/app/code/Magento/CatalogUrlRewrite/Observer/AfterImportDataObserver.php @@ -6,22 +6,36 @@ namespace Magento\CatalogUrlRewrite\Observer; use Magento\Catalog\Model\Category; +use Magento\Catalog\Model\Product; use Magento\Catalog\Model\Product\Visibility; +use Magento\Catalog\Model\ProductFactory; use Magento\Catalog\Model\ResourceModel\Category\Collection as CategoryCollection; use Magento\Catalog\Model\ResourceModel\Category\CollectionFactory as CategoryCollectionFactory; use Magento\CatalogImportExport\Model\Import\Product as ImportProduct; +use Magento\CatalogUrlRewrite\Model\ObjectRegistry; +use Magento\CatalogUrlRewrite\Model\ObjectRegistryFactory; +use Magento\CatalogUrlRewrite\Model\ProductUrlPathGenerator; use Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator; +use Magento\CatalogUrlRewrite\Service\V1\StoreViewService; +use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\App\ObjectManager; +use Magento\Framework\DataObject; use Magento\Framework\Event\Observer; use Magento\Framework\Event\ObserverInterface; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Exception\NoSuchEntityException; use Magento\ImportExport\Model\Import as ImportExport; use Magento\Store\Model\Store; +use Magento\Store\Model\StoreManagerInterface; +use Magento\UrlRewrite\Model\Exception\UrlAlreadyExistsException; +use Magento\UrlRewrite\Model\MergeDataProvider; use Magento\UrlRewrite\Model\MergeDataProviderFactory; use Magento\UrlRewrite\Model\OptionProvider; use Magento\UrlRewrite\Model\UrlFinderInterface; use Magento\UrlRewrite\Model\UrlPersistInterface; use Magento\UrlRewrite\Service\V1\Data\UrlRewrite; use Magento\UrlRewrite\Service\V1\Data\UrlRewriteFactory; +use RuntimeException; /** * Class AfterImportDataObserver @@ -37,12 +51,12 @@ class AfterImportDataObserver implements ObserverInterface const URL_KEY_ATTRIBUTE_CODE = 'url_key'; /** - * @var \Magento\CatalogUrlRewrite\Service\V1\StoreViewService + * @var StoreViewService */ protected $storeViewService; /** - * @var \Magento\Catalog\Model\Product + * @var Product */ protected $product; @@ -57,42 +71,42 @@ class AfterImportDataObserver implements ObserverInterface protected $products = []; /** - * @var \Magento\CatalogUrlRewrite\Model\ObjectRegistryFactory + * @var ObjectRegistryFactory */ protected $objectRegistryFactory; /** - * @var \Magento\CatalogUrlRewrite\Model\ObjectRegistry + * @var ObjectRegistry */ protected $productCategories; /** - * @var \Magento\UrlRewrite\Model\UrlFinderInterface + * @var UrlFinderInterface */ protected $urlFinder; /** - * @var \Magento\Store\Model\StoreManagerInterface + * @var StoreManagerInterface */ protected $storeManager; /** - * @var \Magento\UrlRewrite\Model\UrlPersistInterface + * @var UrlPersistInterface */ protected $urlPersist; /** - * @var \Magento\UrlRewrite\Service\V1\Data\UrlRewriteFactory + * @var UrlRewriteFactory */ protected $urlRewriteFactory; /** - * @var \Magento\CatalogImportExport\Model\Import\Product + * @var ImportProduct */ protected $import; /** - * @var \Magento\Catalog\Model\ProductFactory + * @var ProductFactory */ protected $catalogProductFactory; @@ -102,7 +116,7 @@ class AfterImportDataObserver implements ObserverInterface protected $acceptableCategories; /** - * @var \Magento\CatalogUrlRewrite\Model\ProductUrlPathGenerator + * @var ProductUrlPathGenerator */ protected $productUrlPathGenerator; @@ -139,7 +153,7 @@ class AfterImportDataObserver implements ObserverInterface ]; /** - * @var \Magento\UrlRewrite\Model\MergeDataProvider + * @var MergeDataProvider */ private $mergeDataProviderPrototype; @@ -158,29 +172,37 @@ class AfterImportDataObserver implements ObserverInterface private $categoriesCache = []; /** - * @param \Magento\Catalog\Model\ProductFactory $catalogProductFactory - * @param \Magento\CatalogUrlRewrite\Model\ObjectRegistryFactory $objectRegistryFactory - * @param \Magento\CatalogUrlRewrite\Model\ProductUrlPathGenerator $productUrlPathGenerator - * @param \Magento\CatalogUrlRewrite\Service\V1\StoreViewService $storeViewService - * @param \Magento\Store\Model\StoreManagerInterface $storeManager + * @var ScopeConfigInterface|null + */ + private $scopeConfig; + + /** + * @param ProductFactory $catalogProductFactory + * @param ObjectRegistryFactory $objectRegistryFactory + * @param ProductUrlPathGenerator $productUrlPathGenerator + * @param StoreViewService $storeViewService + * @param StoreManagerInterface $storeManager * @param UrlPersistInterface $urlPersist * @param UrlRewriteFactory $urlRewriteFactory * @param UrlFinderInterface $urlFinder - * @param \Magento\UrlRewrite\Model\MergeDataProviderFactory|null $mergeDataProviderFactory + * @param MergeDataProviderFactory|null $mergeDataProviderFactory * @param CategoryCollectionFactory|null $categoryCollectionFactory + * @param ScopeConfigInterface|null $scopeConfig + * @throws RuntimeException * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( - \Magento\Catalog\Model\ProductFactory $catalogProductFactory, - \Magento\CatalogUrlRewrite\Model\ObjectRegistryFactory $objectRegistryFactory, - \Magento\CatalogUrlRewrite\Model\ProductUrlPathGenerator $productUrlPathGenerator, - \Magento\CatalogUrlRewrite\Service\V1\StoreViewService $storeViewService, - \Magento\Store\Model\StoreManagerInterface $storeManager, + ProductFactory $catalogProductFactory, + ObjectRegistryFactory $objectRegistryFactory, + ProductUrlPathGenerator $productUrlPathGenerator, + StoreViewService $storeViewService, + StoreManagerInterface $storeManager, UrlPersistInterface $urlPersist, UrlRewriteFactory $urlRewriteFactory, UrlFinderInterface $urlFinder, MergeDataProviderFactory $mergeDataProviderFactory = null, - CategoryCollectionFactory $categoryCollectionFactory = null + CategoryCollectionFactory $categoryCollectionFactory = null, + ScopeConfigInterface $scopeConfig = null ) { $this->urlPersist = $urlPersist; $this->catalogProductFactory = $catalogProductFactory; @@ -196,16 +218,17 @@ public function __construct( $this->mergeDataProviderPrototype = $mergeDataProviderFactory->create(); $this->categoryCollectionFactory = $categoryCollectionFactory ?: ObjectManager::getInstance()->get(CategoryCollectionFactory::class); + $this->scopeConfig = $scopeConfig ?: + ObjectManager::getInstance()->get(ScopeConfigInterface::class); } /** - * Action after data import. - * - * Save new url rewrites and remove old if exist. + * Action after data import. Save new url rewrites and remove old if exist. * * @param Observer $observer - * * @return void + * @throws LocalizedException + * @throws UrlAlreadyExistsException */ public function execute(Observer $observer) { @@ -225,8 +248,8 @@ public function execute(Observer $observer) * Create product model from imported data for URL rewrite purposes. * * @param array $rowData - * - * @return ImportExport + * @return AfterImportDataObserver|null + * @throws LocalizedException * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ protected function _populateForUrlGeneration($rowData) @@ -272,17 +295,17 @@ protected function _populateForUrlGeneration($rowData) /** * Add store id to product data. * - * @param \Magento\Catalog\Model\Product $product + * @param Product $product * @param array $rowData * @return void */ - protected function setStoreToProduct(\Magento\Catalog\Model\Product $product, array $rowData) + protected function setStoreToProduct(Product $product, array $rowData) { if (!empty($rowData[ImportProduct::COL_STORE]) && ($storeId = $this->import->getStoreIdByCode($rowData[ImportProduct::COL_STORE])) ) { $product->setStoreId($storeId); - } elseif (!$product->hasData(\Magento\Catalog\Model\Product::STORE_ID)) { + } elseif (!$product->hasData(Product::STORE_ID)) { $product->setStoreId(Store::DEFAULT_STORE_ID); } } @@ -290,7 +313,7 @@ protected function setStoreToProduct(\Magento\Catalog\Model\Product $product, ar /** * Add product to import * - * @param \Magento\Catalog\Model\Product $product + * @param Product $product * @param string $storeId * @return $this */ @@ -309,7 +332,7 @@ protected function addProductToImport($product, $storeId) /** * Populate global product * - * @param \Magento\Catalog\Model\Product $product + * @param Product $product * @return $this */ protected function populateGlobalProduct($product) @@ -328,13 +351,16 @@ protected function populateGlobalProduct($product) /** * Generate product url rewrites * - * @return \Magento\UrlRewrite\Service\V1\Data\UrlRewrite[] + * @return UrlRewrite[] + * @throws LocalizedException */ protected function generateUrls() { $mergeDataProvider = clone $this->mergeDataProviderPrototype; $mergeDataProvider->merge($this->canonicalUrlRewriteGenerate()); - $mergeDataProvider->merge($this->categoriesUrlRewriteGenerate()); + if ($this->isCategoryRewritesEnabled()) { + $mergeDataProvider->merge($this->categoriesUrlRewriteGenerate()); + } $mergeDataProvider->merge($this->currentUrlRewritesRegenerate()); $this->productCategories = null; @@ -383,6 +409,7 @@ protected function canonicalUrlRewriteGenerate() * Generate list based on categories. * * @return UrlRewrite[] + * @throws LocalizedException */ protected function categoriesUrlRewriteGenerate() { @@ -530,9 +557,10 @@ protected function retrieveCategoryFromMetadata($url) /** * Check, category suited for url-rewrite generation. * - * @param \Magento\Catalog\Model\Category $category + * @param Category $category * @param int $storeId * @return bool + * @throws NoSuchEntityException */ protected function isCategoryProperForGenerating($category, $storeId) { @@ -541,7 +569,7 @@ protected function isCategoryProperForGenerating($category, $storeId) return $this->acceptableCategories[$storeId][$category->getId()]; } $acceptable = false; - if ($category->getParentId() != \Magento\Catalog\Model\Category::TREE_ROOT_ID) { + if ($category->getParentId() != Category::TREE_ROOT_ID) { list(, $rootCategoryId) = $category->getParentIds(); $acceptable = ($rootCategoryId == $this->storeManager->getStore($storeId)->getRootCategoryId()); } @@ -557,7 +585,8 @@ protected function isCategoryProperForGenerating($category, $storeId) * * @param int $categoryId * @param int $storeId - * @return Category|\Magento\Framework\DataObject + * @return Category|DataObject + * @throws LocalizedException */ private function getCategoryById($categoryId, $storeId) { @@ -574,4 +603,14 @@ private function getCategoryById($categoryId, $storeId) return $this->categoriesCache[$categoryId][$storeId]; } + + /** + * Check config value of generate_category_product_rewrites + * + * @return bool + */ + private function isCategoryRewritesEnabled() + { + return (bool)$this->scopeConfig->getValue('catalog/seo/generate_category_product_rewrites'); + } } diff --git a/app/code/Magento/CatalogUrlRewrite/Observer/CategoryProcessUrlRewriteMovingObserver.php b/app/code/Magento/CatalogUrlRewrite/Observer/CategoryProcessUrlRewriteMovingObserver.php index 0afdf774c7eb1..244aaf4d5cdc9 100644 --- a/app/code/Magento/CatalogUrlRewrite/Observer/CategoryProcessUrlRewriteMovingObserver.php +++ b/app/code/Magento/CatalogUrlRewrite/Observer/CategoryProcessUrlRewriteMovingObserver.php @@ -74,8 +74,8 @@ public function __construct( UrlRewriteBunchReplacer $urlRewriteBunchReplacer, DatabaseMapPool $databaseMapPool, $dataUrlRewriteClassNames = [ - DataCategoryUrlRewriteDatabaseMap::class, - DataProductUrlRewriteDatabaseMap::class + DataCategoryUrlRewriteDatabaseMap::class, + DataProductUrlRewriteDatabaseMap::class ] ) { $this->categoryUrlRewriteGenerator = $categoryUrlRewriteGenerator; @@ -88,6 +88,8 @@ public function __construct( } /** + * Execute observer functional + * * @param \Magento\Framework\Event\Observer $observer * @return void */ @@ -105,8 +107,12 @@ public function execute(\Magento\Framework\Event\Observer $observer) $categoryUrlRewriteResult = $this->categoryUrlRewriteGenerator->generate($category, true); $this->urlRewriteHandler->deleteCategoryRewritesForChildren($category); $this->urlRewriteBunchReplacer->doBunchReplace($categoryUrlRewriteResult); - $productUrlRewriteResult = $this->urlRewriteHandler->generateProductUrlRewrites($category); - $this->urlRewriteBunchReplacer->doBunchReplace($productUrlRewriteResult); + + if ($this->isCategoryRewritesEnabled()) { + $productUrlRewriteResult = $this->urlRewriteHandler->generateProductUrlRewrites($category); + $this->urlRewriteBunchReplacer->doBunchReplace($productUrlRewriteResult); + } + //frees memory for maps that are self-initialized in multiple classes that were called by the generators $this->resetUrlRewritesDataMaps($category); } @@ -124,4 +130,14 @@ private function resetUrlRewritesDataMaps($category) $this->databaseMapPool->resetMap($className, $category->getEntityId()); } } + + /** + * Check config value of generate_category_product_rewrites + * + * @return bool + */ + private function isCategoryRewritesEnabled() + { + return (bool)$this->scopeConfig->getValue('catalog/seo/generate_category_product_rewrites'); + } } diff --git a/app/code/Magento/CatalogUrlRewrite/Observer/CategoryProcessUrlRewriteSavingObserver.php b/app/code/Magento/CatalogUrlRewrite/Observer/CategoryProcessUrlRewriteSavingObserver.php index 3cfd49b1d210a..62ce54cd67185 100644 --- a/app/code/Magento/CatalogUrlRewrite/Observer/CategoryProcessUrlRewriteSavingObserver.php +++ b/app/code/Magento/CatalogUrlRewrite/Observer/CategoryProcessUrlRewriteSavingObserver.php @@ -12,10 +12,12 @@ use Magento\CatalogUrlRewrite\Model\Map\DataCategoryUrlRewriteDatabaseMap; use Magento\CatalogUrlRewrite\Model\Map\DataProductUrlRewriteDatabaseMap; use Magento\CatalogUrlRewrite\Model\UrlRewriteBunchReplacer; +use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\Event\ObserverInterface; use Magento\Store\Model\ResourceModel\Group\CollectionFactory; use Magento\Store\Model\ResourceModel\Group\Collection as StoreGroupCollection; use Magento\Framework\App\ObjectManager; +use Magento\Store\Model\ScopeInterface; /** * Generates Category Url Rewrites after save and Products Url Rewrites assigned to the category that's being saved @@ -52,11 +54,17 @@ class CategoryProcessUrlRewriteSavingObserver implements ObserverInterface */ private $storeGroupFactory; + /** + * @var ScopeConfigInterface + */ + private $scopeConfig; + /** * @param CategoryUrlRewriteGenerator $categoryUrlRewriteGenerator * @param UrlRewriteHandler $urlRewriteHandler * @param UrlRewriteBunchReplacer $urlRewriteBunchReplacer * @param DatabaseMapPool $databaseMapPool + * @param ScopeConfigInterface $scopeConfig * @param string[] $dataUrlRewriteClassNames * @param CollectionFactory|null $storeGroupFactory */ @@ -65,6 +73,7 @@ public function __construct( UrlRewriteHandler $urlRewriteHandler, UrlRewriteBunchReplacer $urlRewriteBunchReplacer, DatabaseMapPool $databaseMapPool, + ScopeConfigInterface $scopeConfig, $dataUrlRewriteClassNames = [ DataCategoryUrlRewriteDatabaseMap::class, DataProductUrlRewriteDatabaseMap::class @@ -78,6 +87,7 @@ public function __construct( $this->dataUrlRewriteClassNames = $dataUrlRewriteClassNames; $this->storeGroupFactory = $storeGroupFactory ?: ObjectManager::getInstance()->get(CollectionFactory::class); + $this->scopeConfig = $scopeConfig; } /** @@ -105,13 +115,15 @@ public function execute(\Magento\Framework\Event\Observer $observer) $categoryUrlRewriteResult = $this->categoryUrlRewriteGenerator->generate($category); $this->urlRewriteBunchReplacer->doBunchReplace($categoryUrlRewriteResult); } - if ($this->isChangedOnlyProduct($category)) { - $productUrlRewriteResult = - $this->urlRewriteHandler->updateProductUrlRewritesForChangedProduct($category); - $this->urlRewriteBunchReplacer->doBunchReplace($productUrlRewriteResult); - } else { - $productUrlRewriteResult = $this->urlRewriteHandler->generateProductUrlRewrites($category); - $this->urlRewriteBunchReplacer->doBunchReplace($productUrlRewriteResult); + if ($this->isCategoryRewritesEnabled()) { + if ($this->isChangedOnlyProduct($category)) { + $productUrlRewriteResult = + $this->urlRewriteHandler->updateProductUrlRewritesForChangedProduct($category); + $this->urlRewriteBunchReplacer->doBunchReplace($productUrlRewriteResult); + } else { + $productUrlRewriteResult = $this->urlRewriteHandler->generateProductUrlRewrites($category); + $this->urlRewriteBunchReplacer->doBunchReplace($productUrlRewriteResult); + } } $mapsGenerated = true; } @@ -189,4 +201,14 @@ private function resetUrlRewritesDataMaps($category) $this->databaseMapPool->resetMap($className, $category->getEntityId()); } } + + /** + * Check config value of generate_category_product_rewrites + * + * @return bool + */ + private function isCategoryRewritesEnabled() + { + return (bool)$this->scopeConfig->getValue('catalog/seo/generate_category_product_rewrites'); + } } diff --git a/app/code/Magento/CatalogUrlRewrite/Observer/UrlRewriteHandler.php b/app/code/Magento/CatalogUrlRewrite/Observer/UrlRewriteHandler.php index b4a35f323e1bc..8f89c3a2fcd1f 100644 --- a/app/code/Magento/CatalogUrlRewrite/Observer/UrlRewriteHandler.php +++ b/app/code/Magento/CatalogUrlRewrite/Observer/UrlRewriteHandler.php @@ -16,6 +16,7 @@ use Magento\CatalogUrlRewrite\Model\CategoryUrlRewriteGenerator; use Magento\CatalogUrlRewrite\Model\ProductScopeRewriteGenerator; use Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator; +use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\App\ObjectManager; use Magento\Framework\Serialize\Serializer\Json; use Magento\UrlRewrite\Model\MergeDataProvider; @@ -80,6 +81,11 @@ class UrlRewriteHandler */ private $productScopeRewriteGenerator; + /** + * @var ScopeConfigInterface + */ + private $scopeConfig; + /** * @param ChildrenCategoriesProvider $childrenCategoriesProvider * @param CategoryUrlRewriteGenerator $categoryUrlRewriteGenerator @@ -90,6 +96,8 @@ class UrlRewriteHandler * @param MergeDataProviderFactory|null $mergeDataProviderFactory * @param Json|null $serializer * @param ProductScopeRewriteGenerator|null $productScopeRewriteGenerator + * @param ScopeConfigInterface|null $scopeConfig + * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( ChildrenCategoriesProvider $childrenCategoriesProvider, @@ -100,7 +108,8 @@ public function __construct( CategoryProductUrlPathGenerator $categoryBasedProductRewriteGenerator, MergeDataProviderFactory $mergeDataProviderFactory = null, Json $serializer = null, - ProductScopeRewriteGenerator $productScopeRewriteGenerator = null + ProductScopeRewriteGenerator $productScopeRewriteGenerator = null, + ScopeConfigInterface $scopeConfig = null ) { $this->childrenCategoriesProvider = $childrenCategoriesProvider; $this->categoryUrlRewriteGenerator = $categoryUrlRewriteGenerator; @@ -115,6 +124,7 @@ public function __construct( $this->serializer = $serializer ?: $objectManager->get(Json::class); $this->productScopeRewriteGenerator = $productScopeRewriteGenerator ?: $objectManager->get(ProductScopeRewriteGenerator::class); + $this->scopeConfig = $scopeConfig ?? $objectManager->get(ScopeConfigInterface::class); } /** @@ -225,6 +235,7 @@ private function getCategoryProductsUrlRewrites( $rootCategoryId = null ) { $mergeDataProvider = clone $this->mergeDataProviderPrototype; + $generateProductRewrite = (bool)$this->scopeConfig->getValue('catalog/seo/generate_category_product_rewrites'); /** @var Collection $productCollection */ $productCollection = $this->productCollectionFactory->create(); @@ -245,6 +256,7 @@ private function getCategoryProductsUrlRewrites( $this->isSkippedProduct[$category->getEntityId()][] = $product->getId(); $product->setStoreId($storeId); $product->setData('save_rewrites_history', $saveRewriteHistory); + $product->setData('generate_rewrites', $generateProductRewrite); $mergeDataProvider->merge( $this->categoryBasedProductRewriteGenerator->generate($product, $rootCategoryId) ); diff --git a/app/code/Magento/CatalogUrlRewrite/Plugin/DynamicCategoryRewrites.php b/app/code/Magento/CatalogUrlRewrite/Plugin/DynamicCategoryRewrites.php new file mode 100644 index 0000000000000..139452116f5d2 --- /dev/null +++ b/app/code/Magento/CatalogUrlRewrite/Plugin/DynamicCategoryRewrites.php @@ -0,0 +1,96 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CatalogUrlRewrite\Plugin; + +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\UrlRewrite\Service\V1\Data\UrlRewrite; +use Magento\CatalogUrlRewrite\Model\Storage\DynamicStorage; +use Magento\CatalogUrlRewrite\Model\Storage\DbStorage; + +/** + * Class DbStorage + */ +class DynamicCategoryRewrites +{ + /** + * @var ScopeConfigInterface + */ + private $config; + + /** + * @var DynamicStorage + */ + private $dynamicStorage; + + /** + * @param ScopeConfigInterface|null $config + * @param DynamicStorage $dynamicStorage + */ + public function __construct( + ScopeConfigInterface $config, + DynamicStorage $dynamicStorage + ) { + $this->config = $config; + $this->dynamicStorage = $dynamicStorage; + } + + /** + * Check config value of generate_category_product_rewrites + * + * @return bool + */ + private function isCategoryRewritesEnabled(): bool + { + return (bool)$this->config->getValue('catalog/seo/generate_category_product_rewrites'); + } + + /** + * Execute proxy + * + * @param callable $proceed + * @param array $data + * @param string $functionName + * @return mixed + */ + private function proxy(callable $proceed, array $data, string $functionName) + { + if ($this->isCategoryRewritesEnabled()) { + return $proceed($data); + } + + return $this->dynamicStorage->$functionName($data); + } + + /** + * Find rewrite by specific data + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + * @param DbStorage $subject + * @param callable $proceed + * @param array $data + * @return UrlRewrite|null + */ + public function aroundFindOneByData(DbStorage $subject, callable $proceed, array $data) + { + return $this->proxy($proceed, $data, 'findOneByData'); + } + + /** + * Find rewrites by specific data + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + * @param DbStorage $subject + * @param callable $proceed + * @param array $data + * @return UrlRewrite[] + */ + public function aroundFindAllByData(DbStorage $subject, callable $proceed, array $data) + { + return $this->proxy($proceed, $data, 'findAllByData'); + } +} diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/ProductScopeRewriteGeneratorTest.php b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/ProductScopeRewriteGeneratorTest.php index 06be01445df4c..d4e0978dda66e 100644 --- a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/ProductScopeRewriteGeneratorTest.php +++ b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/ProductScopeRewriteGeneratorTest.php @@ -50,6 +50,9 @@ class ProductScopeRewriteGeneratorTest extends \PHPUnit\Framework\TestCase /** @var \Magento\Catalog\Model\Category|\PHPUnit_Framework_MockObject_MockObject */ private $categoryMock; + /** @var \Magento\Framework\App\Config\ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject */ + private $configMock; + public function setUp() { $this->serializer = $this->createMock(\Magento\Framework\Serialize\Serializer\Json::class); @@ -96,6 +99,7 @@ function ($value) { ); $this->mergeDataProvider = new \Magento\UrlRewrite\Model\MergeDataProvider(); $mergeDataProviderFactory->expects($this->once())->method('create')->willReturn($this->mergeDataProvider); + $this->configMock = $this->getMockBuilder(\Magento\Framework\App\Config\ScopeConfigInterface::class)->getMock(); $this->productScopeGenerator = (new ObjectManager($this))->getObject( \Magento\CatalogUrlRewrite\Model\ProductScopeRewriteGenerator::class, @@ -107,7 +111,8 @@ function ($value) { 'objectRegistryFactory' => $this->objectRegistryFactory, 'storeViewService' => $this->storeViewService, 'storeManager' => $this->storeManager, - 'mergeDataProviderFactory' => $mergeDataProviderFactory + 'mergeDataProviderFactory' => $mergeDataProviderFactory, + 'config' => $this->configMock ] ); $this->categoryMock = $this->getMockBuilder(Category::class)->disableOriginalConstructor()->getMock(); @@ -115,6 +120,9 @@ function ($value) { public function testGenerationForGlobalScope() { + $this->configMock->expects($this->any())->method('getValue') + ->with('catalog/seo/generate_category_product_rewrites') + ->willReturn('1'); $product = $this->createMock(\Magento\Catalog\Model\Product::class); $product->expects($this->any())->method('getStoreId')->will($this->returnValue(null)); $product->expects($this->any())->method('getStoreIds')->will($this->returnValue([1])); diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/ProductUrlPathGeneratorTest.php b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/ProductUrlPathGeneratorTest.php index 7435096642de2..5076577447af3 100644 --- a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/ProductUrlPathGeneratorTest.php +++ b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/ProductUrlPathGeneratorTest.php @@ -11,6 +11,9 @@ use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; use Magento\Store\Model\ScopeInterface; +/** + * Class ProductUrlPathGeneratorTest + */ class ProductUrlPathGeneratorTest extends \PHPUnit\Framework\TestCase { /** @var \Magento\CatalogUrlRewrite\Model\ProductUrlPathGenerator */ @@ -77,10 +80,11 @@ protected function setUp(): void public function getUrlPathDataProvider(): array { return [ - 'path based on url key' => ['url-key', null, 'url-key'], - 'path based on product name 1' => ['', 'product-name', 'product-name'], - 'path based on product name 2' => [null, 'product-name', 'product-name'], - 'path based on product name 3' => [false, 'product-name', 'product-name'] + 'path based on url key uppercase' => ['Url-Key', null, 0, 'url-key'], + 'path based on url key' => ['url-key', null, 0, 'url-key'], + 'path based on product name 1' => ['', 'product-name', 1, 'product-name'], + 'path based on product name 2' => [null, 'product-name', 1, 'product-name'], + 'path based on product name 3' => [false, 'product-name', 1, 'product-name'] ]; } @@ -88,16 +92,18 @@ public function getUrlPathDataProvider(): array * @dataProvider getUrlPathDataProvider * @param string|null|bool $urlKey * @param string|null|bool $productName + * @param int $formatterCalled * @param string $result * @return void */ - public function testGetUrlPath($urlKey, $productName, $result): void + public function testGetUrlPath($urlKey, $productName, $formatterCalled, $result): void { $this->product->expects($this->once())->method('getData')->with('url_path') ->will($this->returnValue(null)); $this->product->expects($this->any())->method('getUrlKey')->will($this->returnValue($urlKey)); $this->product->expects($this->any())->method('getName')->will($this->returnValue($productName)); - $this->product->expects($this->once())->method('formatUrlKey')->will($this->returnArgument(0)); + $this->product->expects($this->exactly($formatterCalled)) + ->method('formatUrlKey')->will($this->returnArgument(0)); $this->assertEquals($result, $this->productUrlPathGenerator->getUrlPath($this->product, null)); } diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/CategoryProcessUrlRewriteMovingObserverTest.php b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/CategoryProcessUrlRewriteMovingObserverTest.php index b12da6243a903..57162b44f9826 100644 --- a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/CategoryProcessUrlRewriteMovingObserverTest.php +++ b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/CategoryProcessUrlRewriteMovingObserverTest.php @@ -98,12 +98,15 @@ public function testCategoryProcessUrlRewriteAfterMovingWithChangedParentId() ->disableOriginalConstructor() ->setMethods(['getCategory']) ->getMock(); - $categoryMock = $this->createPartialMock(Category::class, [ - 'dataHasChangedFor', - 'getEntityId', - 'getStoreId', - 'setData' - ]); + $categoryMock = $this->createPartialMock( + Category::class, + [ + 'dataHasChangedFor', + 'getEntityId', + 'getStoreId', + 'setData' + ] + ); $categoryMock->expects($this->once())->method('dataHasChangedFor')->with('parent_id') ->willReturn(true); @@ -111,6 +114,7 @@ public function testCategoryProcessUrlRewriteAfterMovingWithChangedParentId() $observerMock->expects($this->once())->method('getEvent')->willReturn($eventMock); $this->scopeConfigMock->expects($this->once())->method('isSetFlag') ->with(UrlKeyRenderer::XML_PATH_SEO_SAVE_HISTORY)->willReturn(true); + $this->scopeConfigMock->method('getValue')->willReturn(true); $this->categoryUrlRewriteGeneratorMock->expects($this->once())->method('generate') ->with($categoryMock, true)->willReturn(['category-url-rewrite']); $this->urlRewriteHandlerMock->expects($this->once())->method('generateProductUrlRewrites') diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/CategoryProcessUrlRewriteSavingObserverTest.php b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/CategoryProcessUrlRewriteSavingObserverTest.php index afdb548887577..cdea134758101 100644 --- a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/CategoryProcessUrlRewriteSavingObserverTest.php +++ b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/CategoryProcessUrlRewriteSavingObserverTest.php @@ -12,6 +12,7 @@ use Magento\CatalogUrlRewrite\Model\UrlRewriteBunchReplacer; use Magento\CatalogUrlRewrite\Observer\UrlRewriteHandler; use Magento\CatalogUrlRewrite\Model\Map\DatabaseMapPool; +use Magento\Framework\App\Config\ScopeConfigInterface as ScopeConfigInterfaceAlias; use Magento\Store\Model\ResourceModel\Group\CollectionFactory; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; use Magento\Catalog\Model\Category; @@ -61,6 +62,11 @@ class CategoryProcessUrlRewriteSavingObserverTest extends \PHPUnit\Framework\Tes */ private $storeGroupFactory; + /** + * @var \PHPUnit\Framework\MockObject\MockObject + */ + private $scopeConfigMock; + /** * {@inheritDoc} */ @@ -70,12 +76,16 @@ protected function setUp() \Magento\Framework\Event\Observer::class, ['getEvent', 'getData'] ); - $this->category = $this->createPartialMock(Category::class, [ - 'hasData', - 'getParentId', - 'dataHasChangedFor', - 'getChangedProductIds', - ]); + $this->category = $this->createPartialMock( + Category::class, + [ + 'hasData', + 'getParentId', + 'getStoreId', + 'dataHasChangedFor', + 'getChangedProductIds', + ] + ); $this->observer->expects($this->any()) ->method('getEvent') ->willReturnSelf(); @@ -100,6 +110,11 @@ protected function setUp() ->setMethods(['create']) ->disableOriginalConstructor() ->getMock(); + $this->scopeConfigMock = $this->getMockBuilder(ScopeConfigInterfaceAlias::class) + ->setMethods(['getValue']) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $this->scopeConfigMock->method('getValue')->willReturn(true); $this->categoryProcessUrlRewriteSavingObserver = (new ObjectManagerHelper($this))->getObject( CategoryProcessUrlRewriteSavingObserver::class, @@ -109,6 +124,7 @@ protected function setUp() 'urlRewriteBunchReplacer' => $this->urlRewriteBunchReplacerMock, 'databaseMapPool' => $this->databaseMapPoolMock, 'storeGroupFactory' => $this->storeGroupFactory, + 'scopeConfig' => $this->scopeConfigMock ] ); } @@ -200,6 +216,7 @@ public function testExecuteHasChanges() $this->category->expects($this->any()) ->method('getChangedProductIds') ->willReturn([]); + $this->category->method('getStoreId')->willReturn(1); $result1 = ['test']; $this->categoryUrlRewriteGeneratorMock->expects($this->once()) diff --git a/app/code/Magento/CatalogUrlRewrite/etc/adminhtml/system.xml b/app/code/Magento/CatalogUrlRewrite/etc/adminhtml/system.xml index 4aa2e7f40c7c0..1e1f4e86fa3dc 100644 --- a/app/code/Magento/CatalogUrlRewrite/etc/adminhtml/system.xml +++ b/app/code/Magento/CatalogUrlRewrite/etc/adminhtml/system.xml @@ -28,6 +28,15 @@ <label>Create Permanent Redirect for URLs if URL Key Changed</label> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> </field> + <field id="generate_category_product_rewrites" translate="label" type="select" sortOrder="6" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="1"> + <label>Generate "category/product" URL Rewrites</label> + <backend_model>Magento\CatalogUrlRewrite\Model\TableCleaner</backend_model> + <source_model>Magento\Config\Model\Config\Source\Yesno</source_model> + <comment> + <![CDATA[<strong style="color:red">Warning!</strong> Turning this option off will result in permanent removal of category/product URL rewrites without an ability to restore them.]]> + </comment> + <frontend_class>generate_category_product_rewrites</frontend_class> + </field> </group> </section> </system> diff --git a/app/code/Magento/CatalogUrlRewrite/etc/config.xml b/app/code/Magento/CatalogUrlRewrite/etc/config.xml new file mode 100644 index 0000000000000..d05c0b4b7aa59 --- /dev/null +++ b/app/code/Magento/CatalogUrlRewrite/etc/config.xml @@ -0,0 +1,16 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd"> + <default> + <catalog> + <seo> + <generate_category_product_rewrites>1</generate_category_product_rewrites> + </seo> + </catalog> + </default> +</config> diff --git a/app/code/Magento/CatalogUrlRewrite/etc/di.xml b/app/code/Magento/CatalogUrlRewrite/etc/di.xml index f6426677e8ce8..e6fbcaefd0768 100644 --- a/app/code/Magento/CatalogUrlRewrite/etc/di.xml +++ b/app/code/Magento/CatalogUrlRewrite/etc/di.xml @@ -24,6 +24,9 @@ <type name="Magento\UrlRewrite\Model\StorageInterface"> <plugin name="storage_plugin" type="Magento\CatalogUrlRewrite\Model\Category\Plugin\Storage"/> </type> + <type name="Magento\CatalogUrlRewrite\Model\Storage\DbStorage"> + <plugin name="dynamic_storage_plugin" type="Magento\CatalogUrlRewrite\Plugin\DynamicCategoryRewrites"/> + </type> <type name="Magento\CatalogUrlRewrite\Model\Map\UrlRewriteFinder"> <arguments> <argument name="urlRewriteClassNames" xsi:type="array"> @@ -32,4 +35,14 @@ </argument> </arguments> </type> + <type name="Magento\UrlRewrite\Model\CompositeUrlFinder"> + <arguments> + <argument name="children" xsi:type="array"> + <item name="catalog" xsi:type="array"> + <item name="class" xsi:type="string">Magento\CatalogUrlRewrite\Model\Storage\DbStorage</item> + <item name="sortOrder" xsi:type="number">20</item> + </item> + </argument> + </arguments> + </type> </config> diff --git a/app/code/Magento/CatalogUrlRewrite/etc/frontend/di.xml b/app/code/Magento/CatalogUrlRewrite/etc/frontend/di.xml new file mode 100644 index 0000000000000..0c7ab27e51d8c --- /dev/null +++ b/app/code/Magento/CatalogUrlRewrite/etc/frontend/di.xml @@ -0,0 +1,10 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> + +</config> diff --git a/app/code/Magento/CatalogUrlRewrite/i18n/en_US.csv b/app/code/Magento/CatalogUrlRewrite/i18n/en_US.csv index 2dce6b233cb95..b3335dc3523ca 100644 --- a/app/code/Magento/CatalogUrlRewrite/i18n/en_US.csv +++ b/app/code/Magento/CatalogUrlRewrite/i18n/en_US.csv @@ -5,3 +5,4 @@ "Product URL Suffix","Product URL Suffix" "Use Categories Path for Product URLs","Use Categories Path for Product URLs" "Create Permanent Redirect for URLs if URL Key Changed","Create Permanent Redirect for URLs if URL Key Changed" +"Generate "category/product" URL Rewrites","Generate "category/product" URL Rewrites" \ No newline at end of file diff --git a/app/code/Magento/CatalogUrlRewrite/view/adminhtml/layout/adminhtml_system_config_edit.xml b/app/code/Magento/CatalogUrlRewrite/view/adminhtml/layout/adminhtml_system_config_edit.xml new file mode 100644 index 0000000000000..c4766138843a1 --- /dev/null +++ b/app/code/Magento/CatalogUrlRewrite/view/adminhtml/layout/adminhtml_system_config_edit.xml @@ -0,0 +1,14 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> + <body> + <referenceContainer name="js"> + <block name="js.confirm_remove_old_urls" template="Magento_CatalogUrlRewrite::confirm.phtml"/> + </referenceContainer> + </body> +</page> diff --git a/app/code/Magento/CatalogUrlRewrite/view/adminhtml/templates/confirm.phtml b/app/code/Magento/CatalogUrlRewrite/view/adminhtml/templates/confirm.phtml new file mode 100644 index 0000000000000..698872054faec --- /dev/null +++ b/app/code/Magento/CatalogUrlRewrite/view/adminhtml/templates/confirm.phtml @@ -0,0 +1,33 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +// @codingStandardsIgnoreFile + +?> +<script> + require([ + "jquery", + "Magento_Ui/js/modal/confirm", + "mage/translate", + ], function(jQuery, confirmation, $t) { + //confirmation for removing category/product URL rewrites + jQuery('select.generate_category_product_rewrites').on('change', function () { + if (this.value == 0) { + confirmation({ + title: $t('Turn off "category/products" URL rewrites?'), + content: $t('Turning off automatic generation of "category/products" URL rewrites will result in permanent removal of all the currently existing “category/product” type URL rewrites without an ability to restore them back. ' + + 'This may potentially cause unresolved “category/product” type URL conflicts which you have to resolve by updating URL key manually.'), + actions: { + cancel: function () { + jQuery('select.generate_category_product_rewrites').val(1); + return false; + }, + } + }) + } + }); + }); +</script> diff --git a/app/code/Magento/Checkout/Model/Layout/AbstractTotalsProcessor.php b/app/code/Magento/Checkout/Model/Layout/AbstractTotalsProcessor.php index 12a8838a7e9ed..ca577ed714a6e 100644 --- a/app/code/Magento/Checkout/Model/Layout/AbstractTotalsProcessor.php +++ b/app/code/Magento/Checkout/Model/Layout/AbstractTotalsProcessor.php @@ -3,9 +3,11 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Checkout\Model\Layout; use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Store\Model\ScopeInterface; /** * Abstract totals processor. @@ -13,6 +15,7 @@ * Can be used to process totals information that will be rendered during checkout. * Abstract class provides sorting routing to sort total information based on configuration settings. * + * phpcs:disable Magento2.Classes.AbstractApi * @api */ abstract class AbstractTotalsProcessor @@ -35,12 +38,14 @@ public function __construct( } /** + * Sort total information based on configuration settings. + * * @param array $totals * @return array */ public function sortTotals($totals) { - $configData = $this->scopeConfig->getValue('sales/totals_sort'); + $configData = $this->scopeConfig->getValue('sales/totals_sort', ScopeInterface::SCOPE_STORES); foreach ($totals as $code => &$total) { //convert JS naming style to config naming style $code = str_replace('-', '_', $code); diff --git a/app/code/Magento/Checkout/Model/PaymentInformationManagement.php b/app/code/Magento/Checkout/Model/PaymentInformationManagement.php index e0de45a3f0dea..2eced5c642261 100644 --- a/app/code/Magento/Checkout/Model/PaymentInformationManagement.php +++ b/app/code/Magento/Checkout/Model/PaymentInformationManagement.php @@ -112,6 +112,12 @@ public function savePaymentInformation( $quoteRepository = $this->getCartRepository(); /** @var \Magento\Quote\Model\Quote $quote */ $quote = $quoteRepository->getActive($cartId); + $customerId = $quote->getBillingAddress() + ->getCustomerId(); + if (!$billingAddress->getCustomerId() && $customerId) { + //It's necessary to verify the price rules with the customer data + $billingAddress->setCustomerId($customerId); + } $quote->removeAddress($quote->getBillingAddress()->getId()); $quote->setBillingAddress($billingAddress); $quote->setDataChanges(true); diff --git a/app/code/Magento/Checkout/Model/ShippingInformationManagement.php b/app/code/Magento/Checkout/Model/ShippingInformationManagement.php index cd3bd2c5a7deb..499e02d681f9c 100644 --- a/app/code/Magento/Checkout/Model/ShippingInformationManagement.php +++ b/app/code/Magento/Checkout/Model/ShippingInformationManagement.php @@ -191,7 +191,9 @@ public function saveAddressInformation( $shippingAddress = $quote->getShippingAddress(); - if (!$shippingAddress->getShippingRateByCode($shippingAddress->getShippingMethod())) { + if (!$quote->getIsVirtual() + && !$shippingAddress->getShippingRateByCode($shippingAddress->getShippingMethod()) + ) { throw new NoSuchEntityException( __('Carrier with such method not found: %1, %2', $carrierCode, $methodCode) ); diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertStorefrontShoppingCartSummaryItemsActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertStorefrontShoppingCartSummaryItemsActionGroup.xml index 54fde1be70bce..9b963273b0409 100644 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertStorefrontShoppingCartSummaryItemsActionGroup.xml +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertStorefrontShoppingCartSummaryItemsActionGroup.xml @@ -13,10 +13,11 @@ <argument name="total" type="string"/> </arguments> <seeInCurrentUrl url="{{CheckoutCartPage.url}}" stepKey="assertUrl"/> - <waitForPageLoad stepKey="waitForCartPage"/> - <see userInput="{{subtotal}}" selector="{{CheckoutCartSummarySection.subtotal}}" stepKey="assertSubtotal"/> - <waitForPageLoad stepKey="waitForPageToLoad"/> - <see userInput="{{total}}" selector="{{CheckoutCartSummarySection.total}}" stepKey="assertTotal"/> + <waitForElementVisible selector="{{CheckoutCartSummarySection.subtotal}}" stepKey="waitForSubtotalVisible"/> + <see selector="{{CheckoutCartSummarySection.subtotal}}" userInput="{{subtotal}}" stepKey="assertSubtotal"/> + <waitForElementVisible selector="{{CheckoutCartSummarySection.total}}" stepKey="waitForTotalVisible"/> + <waitForElementVisible selector="{{CheckoutCartSummarySection.totalAmount(total)}}" stepKey="waitForTotalAmountVisible"/> + <see selector="{{CheckoutCartSummarySection.total}}" userInput="{{total}}" stepKey="assertTotal"/> <seeElement selector="{{CheckoutCartSummarySection.proceedToCheckout}}" stepKey="seeProceedToCheckoutButton"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertStorefrontShoppingCartSummaryWithShippingActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertStorefrontShoppingCartSummaryWithShippingActionGroup.xml index 5eb3de3a1af82..7d8406adc9039 100644 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertStorefrontShoppingCartSummaryWithShippingActionGroup.xml +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/AssertStorefrontShoppingCartSummaryWithShippingActionGroup.xml @@ -11,10 +11,7 @@ <arguments> <argument name="shipping" type="string"/> </arguments> - <waitForLoadingMaskToDisappear stepKey="waitForMaskToDisappear" after="assertSubtotal"/> - <waitForElementVisible selector="{{CheckoutCartSummarySection.shipping}}" time="60" stepKey="waitForElementToBeVisible" after="waitForMaskToDisappear"/> - <!-- Shipping can take a long time to change in builds, can't rely on an explicit wait--> - <wait time="30" stepKey="waitForShippingDetailsToLoad" after="waitForElementToBeVisible"/> - <see userInput="{{shipping}}" selector="{{CheckoutCartSummarySection.shipping}}" stepKey="assertShipping" after="waitForShippingDetailsToLoad" /> + <waitForElementVisible selector="{{CheckoutCartSummarySection.shipping}}" stepKey="waitForElementToBeVisible" after="assertSubtotal"/> + <waitForText userInput="{{shipping}}" selector="{{CheckoutCartSummarySection.shipping}}" time="30" stepKey="assertShipping" after="waitForElementToBeVisible"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/GuestCheckoutFillNewBillingAddressActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/GuestCheckoutFillNewBillingAddressActionGroup.xml index 34f2cfe7f7fff..59e997eccecc0 100644 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/GuestCheckoutFillNewBillingAddressActionGroup.xml +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/GuestCheckoutFillNewBillingAddressActionGroup.xml @@ -70,6 +70,6 @@ <argument name="paymentMethod" type="string"/> </arguments> <waitForElement selector="{{CheckoutPaymentSection.paymentSectionTitle}}" time="30" after="waitForLoading3" stepKey="waitForPaymentSectionLoaded"/> - <conditionalClick selector="{{CheckoutPaymentSection.paymentMethodByName(paymentMethod)}}" dependentSelector="{{CheckoutPaymentSection.billingAddress}}" visible="false" parametrized="true" before="enterFirstName" stepKey="clickCheckMoneyOrderPayment"/> + <conditionalClick selector="{{CheckoutPaymentSection.paymentMethodByName(paymentMethod)}}" dependentSelector="{{CheckoutPaymentSection.billingAddress}}" visible="false" before="enterFirstName" stepKey="clickCheckMoneyOrderPayment"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/LoginAsCustomerOnCheckoutPageActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/LoginAsCustomerOnCheckoutPageActionGroup.xml index 26d926d18a380..06a49b856b5e2 100644 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/LoginAsCustomerOnCheckoutPageActionGroup.xml +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/LoginAsCustomerOnCheckoutPageActionGroup.xml @@ -18,8 +18,10 @@ <waitForElementVisible selector="{{CheckoutShippingSection.password}}" stepKey="waitForElementVisible"/> <fillField selector="{{CheckoutShippingSection.password}}" userInput="{{customer.password}}" stepKey="fillPasswordField"/> <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappear2"/> + <waitForElementVisible selector="{{CheckoutShippingSection.loginButton}}" stepKey="waitForLoginButtonVisible"/> <doubleClick selector="{{CheckoutShippingSection.loginButton}}" stepKey="clickLoginBtn"/> <waitForLoadingMaskToDisappear stepKey="waitForLoadingMaskToDisappear3"/> - <waitForPageLoad stepKey="waitForLogin"/> + <waitForPageLoad stepKey="waitToBeLoggedIn"/> + <waitForElementNotVisible selector="{{CheckoutShippingSection.email}}" stepKey="waitForEmailInvisible" time ="60"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontAddProductToCartFromCategoryActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontAddProductToCartFromCategoryActionGroup.xml new file mode 100644 index 0000000000000..de08fe7427c28 --- /dev/null +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontAddProductToCartFromCategoryActionGroup.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontAddProductToCartFromCategoryActionGroup"> + <arguments> + <argument name="productName" type="string"/> + </arguments> + <scrollTo selector="{{StorefrontCategoryProductSection.ProductInfoByName(productName)}}" stepKey="scroll"/> + <moveMouseOver selector="{{StorefrontCategoryProductSection.ProductInfoByName(productName)}}" stepKey="moveMouseOverProduct" /> + <click selector="{{StorefrontCategoryProductSection.ProductAddToCartByName(productName)}}" stepKey="clickAddToCart" /> + <waitForAjaxLoad stepKey="waitForAjax"/> + </actionGroup> +</actionGroups> + + diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontAddToTheCartActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontAddToTheCartActionGroup.xml index 88e81199e3705..1603758016e0d 100644 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontAddToTheCartActionGroup.xml +++ b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontAddToTheCartActionGroup.xml @@ -13,5 +13,6 @@ <scrollTo selector="{{StorefrontProductActionSection.addToCart}}" stepKey="scrollToAddToCartButton"/> <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addToCart"/> <waitForPageLoad stepKey="waitForPageToLoad"/> + <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessage"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontCheckoutCustomerSignInActionGroup.xml b/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontCheckoutCustomerSignInActionGroup.xml deleted file mode 100644 index b6c45dd6145ab..0000000000000 --- a/app/code/Magento/Checkout/Test/Mftf/ActionGroup/StorefrontCheckoutCustomerSignInActionGroup.xml +++ /dev/null @@ -1,22 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> - -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <actionGroup name="StorefrontCheckoutCustomerSignInActionGroup"> - <arguments> - <argument name="customerEmail" type="string" defaultValue="$$createCustomer.email$$"/> - <argument name="password" type="string" defaultValue="$$createCustomer.password$$"/> - </arguments> - <fillField selector="{{CheckoutShippingSection.email}}" userInput="{{customerEmail}}" stepKey="fillEmailAddress"/> - <waitForElementVisible selector="{{CheckoutShippingSection.password}}" stepKey="waitForPasswordFieldToBeVisible"/> - <fillField selector="{{CheckoutShippingSection.password}}" userInput="{{password}}" stepKey="fillPassword"/> - <click selector="{{CheckoutShippingSection.loginButton}}" stepKey="clickLoginButton"/> - <waitForPageLoad stepKey="waitForLoginPageToLoad"/> - </actionGroup> -</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartProductSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartProductSection.xml index 977f559f0071e..2024d249418ac 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartProductSection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartProductSection.xml @@ -30,7 +30,7 @@ <element name="ProductPriceByOption" type="text" selector="//*[contains(@class, 'item-options')]/dd[normalize-space(.)='{{var1}}']/ancestor::tr//td[contains(@class, 'price')]//span[@class='price']" parameterized="true"/> <element name="RemoveItem" type="button" selector="//table[@id='shopping-cart-table']//tbody//tr[contains(@class,'item-actions')]//a[contains(@class,'action-delete')]"/> - <element name="removeProductByName" selector="//*[contains(text(), '{{productName}}')]/ancestor::tbody//a[@class='action action-delete']" parameterized="true" timeout="30"/> + <element name="removeProductByName" type="text" selector="//*[contains(text(), '{{productName}}')]/ancestor::tbody//a[@class='action action-delete']" parameterized="true" timeout="30"/> <element name="productName" type="text" selector="//tbody[@class='cart item']//strong[@class='product-item-name']"/> <element name="nthItemOption" type="block" selector=".item:nth-of-type({{numElement}}) .item-options" parameterized="true"/> <element name="nthEditButton" type="block" selector=".item:nth-of-type({{numElement}}) .action-edit" parameterized="true"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartSummarySection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartSummarySection.xml index 9df109a6c45c4..c9acfb62aa7a1 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartSummarySection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutCartSummarySection.xml @@ -15,7 +15,9 @@ <element name="shippingMethodForm" type="text" selector="#co-shipping-method-form"/> <element name="shippingMethod" type="text" selector="//*[@id='cart-totals']//tr[@class='totals shipping excl']//th//span[@class='value']"/> <element name="shipping" type="text" selector="//*[@id='cart-totals']//tr[@class='totals shipping excl']//td//span[@class='price']"/> - <element name="total" type="text" selector="//*[@id='cart-totals']//tr[@class='grand totals']//td//span[@class='price']" timeout="10"/> + <element name="shippingAmount" type="text" selector="//*[@id='cart-totals']//tr[@class='totals shipping excl']//td//span[@class='price' and contains(text(), '{{amount}}')]" parameterized="true"/> + <element name="total" type="text" selector="//*[@id='cart-totals']//tr[@class='grand totals']//td//span[@class='price']"/> + <element name="totalAmount" type="text" selector="//*[@id='cart-totals']//tr[@class='grand totals']//td//span[@class='price' and contains(text(), '{{amount}}')]" parameterized="true"/> <element name="proceedToCheckout" type="button" selector=".action.primary.checkout span" timeout="30"/> <element name="discountAmount" type="text" selector="td[data-th='Discount']"/> <element name="shippingHeading" type="button" selector="#block-shipping-heading"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutOrderSummarySection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutOrderSummarySection.xml index 3a5b16c5a52ca..d3ad2aed96946 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutOrderSummarySection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutOrderSummarySection.xml @@ -18,5 +18,6 @@ <element name="billingAddress" type="textarea" selector="//*[@class='box box-address-billing']//address"/> <element name="additionalAddress" type="text" selector=".block.block-addresses-list"/> <element name="miniCartTabClosed" type="button" selector=".title[aria-expanded='false']" timeout="30"/> + <element name="itemsQtyInCart" type="text" selector=".items-in-cart > .title > strong > span"/> </section> </sections> diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutShippingSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutShippingSection.xml index 97ae206a67005..f90ec0aad4624 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutShippingSection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutShippingSection.xml @@ -39,5 +39,7 @@ <element name="editActiveAddress" type="button" selector="//div[@class='shipping-address-item selected-item']//span[text()='Edit']" timeout="30"/> <element name="loginButton" type="button" selector=".action.login" timeout="30"/> <element name="shipHereButton" type="button" selector="//div[text()='{{street}}']/button[@class='action action-select-shipping-item']" parameterized="true" timeout="30"/> + <element name="textFieldAttrRequireMessage" type="text" selector="//input[@name='custom_attributes[{{attribute}}]']/ancestor::div[contains(@class, 'control')]/div/span" parameterized="true" timeout="30"/> + <element name="textFieldAttribute" type="input" selector="[name*='custom_attributes[{{attribute}}]']" parameterized="true" timeout="30"/> </section> </sections> diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutSuccessMainSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutSuccessMainSection.xml index bc65f8a2c0816..81e63baddf5a5 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutSuccessMainSection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutSuccessMainSection.xml @@ -16,7 +16,7 @@ <element name="orderLink" type="text" selector="a[href*=order_id].order-number" timeout="30"/> <element name="orderNumberText" type="text" selector=".checkout-success > p:nth-child(1)"/> <element name="continueShoppingButton" type="button" selector=".action.primary.continue" timeout="30"/> - <element name="createAnAccount" type="button" selector="input[value='Create an Account']" timeout="30"/> + <element name="createAnAccount" type="button" selector="[data-bind*="i18n: 'Create an Account'"]" timeout="30"/> <element name="printLink" type="button" selector=".print" timeout="30"/> </section> </sections> diff --git a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutSuccessRegisterSection.xml b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutSuccessRegisterSection.xml index 0d692e4ab143e..baee6cc7177c8 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutSuccessRegisterSection.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Section/CheckoutSuccessRegisterSection.xml @@ -11,7 +11,7 @@ <section name="CheckoutSuccessRegisterSection"> <element name="registerMessage" type="text" selector="#registration p:nth-child(1)"/> <element name="customerEmail" type="text" selector="#registration p:nth-child(2)"/> - <element name="createAccountButton" type="button" selector="#registration form input[type='submit']" timeout="30"/> + <element name="createAccountButton" type="button" selector="[data-bind*="i18n: 'Create an Account'"]" timeout="30"/> <element name="orderNumber" type="text" selector="//p[text()='Your order # is: ']//span"/> </section> </sections> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/AddressStateFieldShouldNotAcceptJustIntegerValuesTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/AddressStateFieldShouldNotAcceptJustIntegerValuesTest.xml index 71a0c7f7fbdb3..f3e31d23f715b 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/AddressStateFieldShouldNotAcceptJustIntegerValuesTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/AddressStateFieldShouldNotAcceptJustIntegerValuesTest.xml @@ -12,10 +12,10 @@ <annotations> <features value="Checkout"/> <stories value="MAGETWO-91465"/> - <title value="Guest Checkout"/> + <title value="Guest Checkout - address State field should not allow just integer values"/> <description value="Address State field should not allow just integer values"/> <severity value="MAJOR"/> - <testCaseId value="MAGETWO-93203"/> + <testCaseId value="MC-6223"/> <group value="checkout"/> </annotations> <before> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/CheckCheckoutSuccessPageTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/CheckCheckoutSuccessPageTest.xml index e19627e7435d6..9714b76a05613 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/CheckCheckoutSuccessPageTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/CheckCheckoutSuccessPageTest.xml @@ -15,7 +15,7 @@ <title value="Customer Checkout"/> <description value="To be sure that other elements of Success page are shown for placed order as registered Customer."/> <severity value="CRITICAL"/> - <testCaseId value="MAGETWO-60345"/> + <testCaseId value="MC-16488"/> <group value="checkout"/> </annotations> @@ -135,10 +135,10 @@ <annotations> <features value="Checkout"/> <stories value="Success page elements are presented for placed order as Guest"/> - <title value="Customer Checkout"/> - <description value="To be sure that other elements of Success page are presented for placed order as Guest."/> + <title value="Guest Checkout - elements of success page are presented for placed order as guest"/> + <description value="To be sure that other elements of Success page are presented for placed order as Guest"/> <severity value="CRITICAL"/> - <testCaseId value="MAGETWO-60346"/> + <testCaseId value="MC-16490"/> <group value="checkout"/> </annotations> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml index 5335ec2ad775d..a4864d612a45f 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml @@ -9,6 +9,11 @@ <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="EndToEndB2CGuestUserTest"> + <annotations> + <skip> + <issueId value="MC-16684"/> + </skip> + </annotations> <!-- Step 3: User adds products to cart --> <comment userInput="Start of adding products to cart" stepKey="startOfAddingProductsToCart" after="endOfBrowsingCatalog"/> <!-- Add Simple Product 1 to cart --> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml index 65627787e2a05..a4784a5cdc227 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml @@ -9,6 +9,11 @@ <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="EndToEndB2CLoggedInUserTest"> + <annotations> + <skip> + <issueId value="MC-16684"/> + </skip> + </annotations> <!-- Step 3: User adds products to cart --> <comment userInput="Start of adding products to cart" stepKey="startOfAddingProductsToCart" after="endOfBrowsingCatalog"/> <!-- Add Simple Product 1 to cart --> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutAsCustomerUsingNewAddressTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutAsCustomerUsingNewAddressTest.xml index a4c357141b9e2..bafad6f28a680 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutAsCustomerUsingNewAddressTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutAsCustomerUsingNewAddressTest.xml @@ -14,7 +14,7 @@ <stories value="OnePageCheckout within Offline Payment Methods"/> <title value="OnePageCheckout as customer using new address test"/> <description value="Checkout as customer using new address"/> - <severity value="CRITICAl"/> + <severity value="CRITICAL"/> <testCaseId value="MC-14740"/> <group value="checkout"/> <group value="mtf_migrated"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutAsCustomerUsingNonDefaultAddressTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutAsCustomerUsingNonDefaultAddressTest.xml index 7651e1dad8388..2c341a5c4c1ab 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutAsCustomerUsingNonDefaultAddressTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutAsCustomerUsingNonDefaultAddressTest.xml @@ -14,7 +14,7 @@ <stories value="OnePageCheckout within Offline Payment Methods"/> <title value="OnePageCheckout as customer using non default address test"/> <description value="Checkout as customer using non default address"/> - <severity value="CRITICAl"/> + <severity value="CRITICAL"/> <testCaseId value="MC-14739"/> <group value="checkout"/> <group value="mtf_migrated"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutUsingSignInLinkTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutUsingSignInLinkTest.xml index 24f6646ba2ff4..990459d7c81b7 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutUsingSignInLinkTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutUsingSignInLinkTest.xml @@ -14,7 +14,7 @@ <stories value="OnePageCheckout within Offline Payment Methods"/> <title value="OnePageCheckout using sign in link test"/> <description value="Checkout using 'Sign In' link"/> - <severity value="CRITICAl"/> + <severity value="CRITICAL"/> <testCaseId value="MC-14738"/> <group value="checkout"/> <group value="mtf_migrated"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutWithAllProductTypesTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutWithAllProductTypesTest.xml index b48475604868b..3ec73aec580d5 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutWithAllProductTypesTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/OnePageCheckoutWithAllProductTypesTest.xml @@ -14,7 +14,7 @@ <stories value="OnePageCheckout within Offline Payment Methods"/> <title value="OnePageCheckout with all product types test"/> <description value="Checkout with all product types"/> - <severity value="CRITICAl"/> + <severity value="CRITICAL"/> <testCaseId value="MC-14742"/> <group value="checkout"/> <group value="mtf_migrated"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontAddProductWithAllTypesOfCustomOptionToTheShoppingCartWithoutAnySelectedOptionTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontAddProductWithAllTypesOfCustomOptionToTheShoppingCartWithoutAnySelectedOptionTest.xml index 2ac84000fd1e8..d108dc3657a40 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontAddProductWithAllTypesOfCustomOptionToTheShoppingCartWithoutAnySelectedOptionTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StoreFrontAddProductWithAllTypesOfCustomOptionToTheShoppingCartWithoutAnySelectedOptionTest.xml @@ -36,7 +36,8 @@ </actionGroup> <!--Click on Add To Cart button--> - <actionGroup ref="StorefrontAddToTheCartActionGroup" stepKey="clickOnAddToCartButton"/> + <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addToCart"/> + <waitForPageLoad stepKey="waitForPageToLoad"/> <!--Assert all types of product options field displayed Required message --> <actionGroup ref="AssertStorefrontSeeElementActionGroup" stepKey="assertRequiredProductOptionField"> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddBundleDynamicProductToShoppingCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddBundleDynamicProductToShoppingCartTest.xml index 755c0cb9c93a0..cb34e1b3810b8 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddBundleDynamicProductToShoppingCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddBundleDynamicProductToShoppingCartTest.xml @@ -15,6 +15,9 @@ <testCaseId value="MC-14715"/> <severity value="CRITICAL"/> <group value="mtf_migrated"/> + <skip> + <issueId value="MC-16684"/> + </skip> </annotations> <before> @@ -86,8 +89,8 @@ <!--Assert Shopping Cart Summary--> <actionGroup ref="AssertStorefrontShoppingCartSummaryWithShippingActionGroup" stepKey="AssertCartSummary" > <argument name="subtotal" value="$100.00"/> - <argument name="shipping" value="$10.00"/> - <argument name="total" value="$110.00"/> + <argument name="shipping" value="10.00"/> + <argument name="total" value="110.00"/> </actionGroup> <!--Assert Product items in cart --> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddBundleDynamicProductToShoppingCartWithDisableMiniCartSidebarTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddBundleDynamicProductToShoppingCartWithDisableMiniCartSidebarTest.xml index afbc6216ff9ca..b9b88f0c6dfbd 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddBundleDynamicProductToShoppingCartWithDisableMiniCartSidebarTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddBundleDynamicProductToShoppingCartWithDisableMiniCartSidebarTest.xml @@ -15,6 +15,9 @@ <testCaseId value="MC-14719"/> <severity value="CRITICAL"/> <group value="mtf_migrated"/> + <skip> + <issueId value="MC-16684"/> + </skip> </annotations> <before> @@ -104,8 +107,8 @@ <!--Assert Shopping Cart Summary--> <actionGroup ref="AssertStorefrontShoppingCartSummaryWithShippingActionGroup" stepKey="AssertCartSummary" > <argument name="subtotal" value="$100.00"/> - <argument name="shipping" value="$10.00"/> - <argument name="total" value="$110.00"/> + <argument name="shipping" value="10.00"/> + <argument name="total" value="110.00"/> </actionGroup> <!--Enabled Shopping Cart Sidebar --> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddConfigurableProductToShoppingCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddConfigurableProductToShoppingCartTest.xml index 452f37e29b850..90dc47ca9c215 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddConfigurableProductToShoppingCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddConfigurableProductToShoppingCartTest.xml @@ -15,6 +15,9 @@ <testCaseId value="MC-14716"/> <severity value="CRITICAL"/> <group value="mtf_migrated"/> + <skip> + <issueId value="MC-16684"/> + </skip> </annotations> <before> @@ -131,8 +134,8 @@ <!--Assert Shopping Cart Summary --> <actionGroup ref="AssertStorefrontShoppingCartSummaryWithShippingActionGroup" stepKey="AssertCartSummary" > <argument name="subtotal" value="$40.00"/> - <argument name="shipping" value="$10.00"/> - <argument name="total" value="$50.00"/> + <argument name="shipping" value="10.00"/> + <argument name="total" value="50.00"/> </actionGroup> <!--Assert Product Details In Checkout cart --> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddDownloadableProductToShoppingCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddDownloadableProductToShoppingCartTest.xml index 8a501ca7bc28f..7a4655bb19ce3 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddDownloadableProductToShoppingCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddDownloadableProductToShoppingCartTest.xml @@ -49,7 +49,7 @@ <!--Assert Shopping Cart Summary--> <actionGroup ref="AssertStorefrontShoppingCartSummaryItemsActionGroup" stepKey="AssertCartSummary" > <argument name="subtotal" value="$123.00"/> - <argument name="total" value="$123.00"/> + <argument name="total" value="123.00"/> </actionGroup> <!--Assert Product Details In Checkout cart --> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddGroupedProductToShoppingCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddGroupedProductToShoppingCartTest.xml index a5fa13e15e64d..42f7be0f6af06 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddGroupedProductToShoppingCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddGroupedProductToShoppingCartTest.xml @@ -15,9 +15,13 @@ <testCaseId value="MC-14718"/> <severity value="CRITICAL"/> <group value="mtf_migrated"/> + <skip> + <issueId value="MC-16684"/> + </skip> </annotations> <before> + <createData entity="FlatRateShippingMethodDefault" stepKey="setDefaultFlatRateShippingMethod"/> <!--Create Grouped product with three simple product --> <createData entity="ApiProductWithDescription" stepKey="simple1" before="simple2"> <field key="price">100.00</field> @@ -94,8 +98,8 @@ <!--Assert Shopping Cart Summary--> <actionGroup ref="AssertStorefrontShoppingCartSummaryWithShippingActionGroup" stepKey="AssertCartSummary" > <argument name="subtotal" value="$1,400.00"/> - <argument name="shipping" value="$30.00"/> - <argument name="total" value="$1,430.00"/> + <argument name="shipping" value="30.00"/> + <argument name="total" value="1,430.00"/> </actionGroup> <!-- Assert product1 details in Mini Cart --> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddOneBundleMultiSelectOptionToTheShoppingCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddOneBundleMultiSelectOptionToTheShoppingCartTest.xml index 84c13eb13d48d..c852a1050fc38 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddOneBundleMultiSelectOptionToTheShoppingCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddOneBundleMultiSelectOptionToTheShoppingCartTest.xml @@ -15,6 +15,9 @@ <testCaseId value="MC-14727"/> <severity value="CRITICAL"/> <group value="mtf_migrated"/> + <skip> + <issueId value="MC-16684"/> + </skip> </annotations> <before> @@ -85,8 +88,8 @@ <!--Assert Shopping Cart Summary--> <actionGroup ref="AssertStorefrontShoppingCartSummaryWithShippingActionGroup" stepKey="AssertCartSummary" > <argument name="subtotal" value="$50.00"/> - <argument name="shipping" value="$5.00"/> - <argument name="total" value="$55.00"/> + <argument name="shipping" value="5.00"/> + <argument name="total" value="55.00"/> </actionGroup> <!--Assert Product items in cart --> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddTwoBundleMultiSelectOptionsToTheShoppingCartTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddTwoBundleMultiSelectOptionsToTheShoppingCartTest.xml index 83c25fb109d03..7137804f12898 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddTwoBundleMultiSelectOptionsToTheShoppingCartTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontAddTwoBundleMultiSelectOptionsToTheShoppingCartTest.xml @@ -15,6 +15,9 @@ <testCaseId value="MC-14728"/> <severity value="CRITICAL"/> <group value="mtf_migrated"/> + <skip> + <issueId value="MC-16684"/> + </skip> </annotations> <before> @@ -83,8 +86,8 @@ <!--Assert Shopping Cart Summary--> <actionGroup ref="AssertStorefrontShoppingCartSummaryWithShippingActionGroup" stepKey="AssertCartSummary" > <argument name="subtotal" value="$60.00"/> - <argument name="shipping" value="$5.00"/> - <argument name="total" value="$65.00"/> + <argument name="shipping" value="5.00"/> + <argument name="total" value="65.00"/> </actionGroup> <!--Assert Product items in cart --> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckCartItemDisplayWhenMoreItemsAddedToTheCartThanDefaultDisplayLimitTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckCartItemDisplayWhenMoreItemsAddedToTheCartThanDefaultDisplayLimitTest.xml index fec5bb9fadb6e..9ec0bac48d08f 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckCartItemDisplayWhenMoreItemsAddedToTheCartThanDefaultDisplayLimitTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCheckCartItemDisplayWhenMoreItemsAddedToTheCartThanDefaultDisplayLimitTest.xml @@ -15,6 +15,9 @@ <testCaseId value="MC-14720"/> <severity value="CRITICAL"/> <group value="mtf_migrated"/> + <skip> + <issueId value="MC-17140"/> + </skip> </annotations> <before> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest.xml index fadc9ec50ad8d..f77e3df11713d 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontCustomerCheckoutTest.xml @@ -12,7 +12,7 @@ <annotations> <features value="Checkout"/> <stories value="Checkout via the Admin"/> - <title value="Customer Checkout"/> + <title value="Customer Checkout via the Admin"/> <description value="Should be able to place an order as a customer."/> <severity value="CRITICAL"/> <testCaseId value="MC-5922"/> @@ -252,7 +252,7 @@ <click stepKey="clickNextButton" selector="{{CheckoutShippingMethodsSection.next}}" /> <waitForPageLoad stepKey="waitBillingForm"/> <seeInCurrentUrl url="{{CheckoutPage.url}}/#payment" stepKey="assertCheckoutPaymentUrl"/> - <dontsee selector="{{CheckoutPaymentSection.paymentMethodByName('Check / Money order')}}" stepKey="paymentMethodDoesNotAvailable"/> + <dontSee selector="{{CheckoutPaymentSection.paymentMethodByName('Check / Money order')}}" stepKey="paymentMethodDoesNotAvailable"/> <!-- Fill UK Address and verify that payment available and checkout successful --> <uncheckOption selector="{{StorefrontCheckoutPaymentMethodSection.billingAddressSameAsShippingShared}}" stepKey="uncheckBillingAddressSameAsShippingCheckCheckBox"/> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest.xml index ff61b3be08af1..15410921f1bb1 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontGuestCheckoutTest.xml @@ -12,10 +12,10 @@ <annotations> <features value="Checkout"/> <stories value="Checkout via Guest Checkout"/> - <title value="Guest Checkout"/> - <description value="Should be able to place an order as a Guest."/> + <title value="Guest Checkout - guest should be able to place an order"/> + <description value="Should be able to place an order as a Guest"/> <severity value="CRITICAL"/> - <testCaseId value="MAGETWO-72094"/> + <testCaseId value="MC-12825"/> <group value="checkout"/> </annotations> <before> @@ -78,6 +78,9 @@ <severity value="CRITICAL"/> <testCaseId value="MAGETWO-97001"/> <group value="checkout"/> + <skip> + <issueId value="MC-17140"/> + </skip> </annotations> <before> <magentoCLI stepKey="disableSidebar" command="config:set checkout/sidebar/display 0" /> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdatePriceInShoppingCartAfterProductSaveTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdatePriceInShoppingCartAfterProductSaveTest.xml index b4747a6bf7273..d27cb83a8ca5b 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdatePriceInShoppingCartAfterProductSaveTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontUpdatePriceInShoppingCartAfterProductSaveTest.xml @@ -17,6 +17,9 @@ <severity value="CRITICAL"/> <testCaseId value="MAGETWO-58179"/> <group value="checkout"/> + <skip> + <issueId value="MC-16684"/> + </skip> </annotations> <before> <createData entity="SimpleProduct2" stepKey="createSimpleProduct"> diff --git a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontValidateEmailOnCheckoutTest.xml b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontValidateEmailOnCheckoutTest.xml index 1b27e1d53adad..65f5dd365b215 100644 --- a/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontValidateEmailOnCheckoutTest.xml +++ b/app/code/Magento/Checkout/Test/Mftf/Test/StorefrontValidateEmailOnCheckoutTest.xml @@ -11,10 +11,12 @@ <test name="StorefrontValidateEmailOnCheckoutTest"> <annotations> <features value="Checkout"/> - <title value="Email validation for Guest on checkout flow"/> - <description value="Email validation for Guest on checkout flow"/> + <stories value="Guest Checkout e-mail validation"/> + <title value="Guest e-mail address validation on Checkout process"/> + <description value="Guest should not be able to place an order when invalid e-mail address provided"/> <stories value="Guest Checkout"/> - <testCaseId value="MC-14695" /> + <testCaseId value="MC-14695"/> + <severity value="CRITICAL"/> <group value="checkout"/> <group value="shoppingCart"/> <group value="mtf_migrated"/> @@ -28,27 +30,27 @@ </after> <actionGroup ref="StorefrontOpenProductPageActionGroup" stepKey="openProductStorefront"> - <argument name="productUrl" value="$$simpleProduct.custom_attributes[url_key]$$" /> + <argument name="productUrl" value="$$simpleProduct.custom_attributes[url_key]$$"/> </actionGroup> - <actionGroup ref="StorefrontClickAddToCartOnProductPageActionGroup" stepKey="addToCartFromStorefrontProductPage" /> + <actionGroup ref="StorefrontClickAddToCartOnProductPageActionGroup" stepKey="addToCartFromStorefrontProductPage"/> - <actionGroup ref="StorefrontOpenCheckoutPageActionGroup" stepKey="openCheckoutPage" /> - <actionGroup ref="AssertStorefrontEmailTooltipContentOnCheckoutActionGroup" stepKey="assertEmailTooltipContent" /> - <actionGroup ref="AssertStorefrontEmailNoteMessageOnCheckoutActionGroup" stepKey="assertEmailNoteMessage" /> + <actionGroup ref="StorefrontOpenCheckoutPageActionGroup" stepKey="openCheckoutPage"/> + <actionGroup ref="AssertStorefrontEmailTooltipContentOnCheckoutActionGroup" stepKey="assertEmailTooltipContent"/> + <actionGroup ref="AssertStorefrontEmailNoteMessageOnCheckoutActionGroup" stepKey="assertEmailNoteMessage"/> <actionGroup ref="StorefrontFillEmailFieldOnCheckoutActionGroup" stepKey="fillIncorrectEmailFirstAttempt"> - <argument name="email" value="John" /> + <argument name="email" value="John"/> </actionGroup> - <actionGroup ref="AssertStorefrontEmailValidationMessageOnCheckoutActionGroup" stepKey="verifyValidationErrorMessageFirstAttempt" /> + <actionGroup ref="AssertStorefrontEmailValidationMessageOnCheckoutActionGroup" stepKey="verifyValidationErrorMessageFirstAttempt"/> <actionGroup ref="StorefrontFillEmailFieldOnCheckoutActionGroup" stepKey="fillIncorrectEmailSecondAttempt"> - <argument name="email" value="johndoe#example.com" /> + <argument name="email" value="johndoe#example.com"/> </actionGroup> - <actionGroup ref="AssertStorefrontEmailValidationMessageOnCheckoutActionGroup" stepKey="verifyValidationErrorMessageSecondAttempt" /> + <actionGroup ref="AssertStorefrontEmailValidationMessageOnCheckoutActionGroup" stepKey="verifyValidationErrorMessageSecondAttempt"/> <actionGroup ref="StorefrontFillEmailFieldOnCheckoutActionGroup" stepKey="fillIncorrectEmailThirdAttempt"> - <argument name="email" value="johndoe@example.c" /> + <argument name="email" value="johndoe@example.c"/> </actionGroup> - <actionGroup ref="AssertStorefrontEmailValidationMessageOnCheckoutActionGroup" stepKey="verifyValidationErrorMessageThirdAttempt" /> + <actionGroup ref="AssertStorefrontEmailValidationMessageOnCheckoutActionGroup" stepKey="verifyValidationErrorMessageThirdAttempt"/> </test> </tests> diff --git a/app/code/Magento/Checkout/Test/Unit/Model/PaymentInformationManagementTest.php b/app/code/Magento/Checkout/Test/Unit/Model/PaymentInformationManagementTest.php index ea841e86586ba..df5c255398ebd 100644 --- a/app/code/Magento/Checkout/Test/Unit/Model/PaymentInformationManagementTest.php +++ b/app/code/Magento/Checkout/Test/Unit/Model/PaymentInformationManagementTest.php @@ -163,6 +163,31 @@ public function testSavePaymentInformationAndPlaceOrderWithLocolizedException() $this->model->savePaymentInformationAndPlaceOrder($cartId, $paymentMock, $billingAddressMock); } + /** + * Test for save payment and place order with new billing address + * + * @return void + */ + public function testSavePaymentInformationAndPlaceOrderWithNewBillingAddress(): void + { + $cartId = 100; + $quoteBillingAddressId = 1; + $customerId = 1; + $quoteMock = $this->createMock(\Magento\Quote\Model\Quote::class); + $quoteBillingAddress = $this->createMock(\Magento\Quote\Model\Quote\Address::class); + $billingAddressMock = $this->createMock(\Magento\Quote\Api\Data\AddressInterface::class); + $paymentMock = $this->createMock(\Magento\Quote\Api\Data\PaymentInterface::class); + + $quoteBillingAddress->method('getCustomerId')->willReturn($customerId); + $quoteMock->method('getBillingAddress')->willReturn($quoteBillingAddress); + $quoteBillingAddress->method('getId')->willReturn($quoteBillingAddressId); + $this->cartRepositoryMock->method('getActive')->with($cartId)->willReturn($quoteMock); + + $this->paymentMethodManagementMock->expects($this->once())->method('set')->with($cartId, $paymentMock); + $billingAddressMock->expects($this->once())->method('setCustomerId')->with($customerId); + $this->assertTrue($this->model->savePaymentInformation($cartId, $paymentMock, $billingAddressMock)); + } + /** * @param int $cartId * @param \PHPUnit_Framework_MockObject_MockObject $billingAddressMock @@ -179,9 +204,10 @@ private function getMockForAssignBillingAddress($cartId, $billingAddressMock) ['setLimitCarrier', 'getShippingMethod', 'getShippingRateByCode'] ); $this->cartRepositoryMock->expects($this->any())->method('getActive')->with($cartId)->willReturn($quoteMock); - $quoteMock->expects($this->once())->method('getBillingAddress')->willReturn($quoteBillingAddress); + $quoteMock->method('getBillingAddress')->willReturn($quoteBillingAddress); $quoteMock->expects($this->once())->method('getShippingAddress')->willReturn($quoteShippingAddress); $quoteBillingAddress->expects($this->once())->method('getId')->willReturn($billingAddressId); + $quoteBillingAddress->expects($this->once())->method('getId')->willReturn($billingAddressId); $quoteMock->expects($this->once())->method('removeAddress')->with($billingAddressId); $quoteMock->expects($this->once())->method('setBillingAddress')->with($billingAddressMock); $quoteMock->expects($this->once())->method('setDataChanges')->willReturnSelf(); diff --git a/app/code/Magento/Checkout/view/frontend/templates/button.phtml b/app/code/Magento/Checkout/view/frontend/templates/button.phtml index c3edfe30f8bdd..b0087794ea850 100644 --- a/app/code/Magento/Checkout/view/frontend/templates/button.phtml +++ b/app/code/Magento/Checkout/view/frontend/templates/button.phtml @@ -3,15 +3,12 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <?php /** @var $block \Magento\Checkout\Block\Onepage\Success */ ?> <?php if ($block->getCanViewOrder() && $block->getCanPrintOrder()) :?> - <a href="<?= /* @escapeNotVerified */ $block->getPrintUrl() ?>" target="_blank" class="print"> - <?= /* @escapeNotVerified */ __('Print receipt') ?> + <a href="<?= $block->escapeUrl($block->getPrintUrl()) ?>" target="_blank" class="print"> + <?= $block->escapeHtml(__('Print receipt')) ?> </a> <?= $block->getChildHtml() ?> <?php endif;?> diff --git a/app/code/Magento/Checkout/view/frontend/templates/cart.phtml b/app/code/Magento/Checkout/view/frontend/templates/cart.phtml index 929f053febfc7..e71ea8c66288c 100644 --- a/app/code/Magento/Checkout/view/frontend/templates/cart.phtml +++ b/app/code/Magento/Checkout/view/frontend/templates/cart.phtml @@ -12,7 +12,9 @@ */ if ($block->getItemsCount()) { + // phpcs:ignore Magento2.Security.LanguageConstruct.DirectOutput echo $block->getChildHtml('with-items'); } else { + // phpcs:ignore Magento2.Security.LanguageConstruct.DirectOutput echo $block->getChildHtml('no-items'); } diff --git a/app/code/Magento/Checkout/view/frontend/templates/cart/additional/info.phtml b/app/code/Magento/Checkout/view/frontend/templates/cart/additional/info.phtml index cb92e62f1f0c8..b807a6db019b5 100644 --- a/app/code/Magento/Checkout/view/frontend/templates/cart/additional/info.phtml +++ b/app/code/Magento/Checkout/view/frontend/templates/cart/additional/info.phtml @@ -4,8 +4,6 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - ?> <?php /** @@ -16,6 +14,7 @@ <?php $name = $block->getNameInLayout(); foreach ($block->getChildNames($name) as $childName) { + // phpcs:ignore Magento2.Security.LanguageConstruct.DirectOutput echo $block->getChildBlock($childName)->setItem($block->getItem())->toHtml(); } ?> diff --git a/app/code/Magento/Checkout/view/frontend/templates/cart/coupon.phtml b/app/code/Magento/Checkout/view/frontend/templates/cart/coupon.phtml index 1d67b325e01c5..4522500d395b6 100644 --- a/app/code/Magento/Checkout/view/frontend/templates/cart/coupon.phtml +++ b/app/code/Magento/Checkout/view/frontend/templates/cart/coupon.phtml @@ -4,16 +4,17 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - ?> -<div class="block discount" id="block-discount" data-mage-init='{"collapsible":{"openedState": "active", "saveState": false}}'> +<div class="block discount" + id="block-discount" + data-mage-init='{"collapsible":{"openedState": "active", "saveState": false}}' +> <div class="title" data-role="title"> - <strong id="block-discount-heading" role="heading" aria-level="2"><?= /* @escapeNotVerified */ __('Apply Discount Code') ?></strong> + <strong id="block-discount-heading" role="heading" aria-level="2"><?= $block->escapeHtml(__('Apply Discount Code')) ?></strong> </div> <div class="content" data-role="content" aria-labelledby="block-discount-heading"> <form id="discount-coupon-form" - action="<?= /* @escapeNotVerified */ $block->getUrl('checkout/cart/couponPost') ?>" + action="<?= $block->escapeUrl($block->getUrl('checkout/cart/couponPost')) ?>" method="post" data-mage-init='{"discountCode":{"couponCodeSelector": "#coupon_code", "removeCouponSelector": "#remove-coupon", @@ -22,21 +23,30 @@ <div class="fieldset coupon<?= strlen($block->getCouponCode()) ? ' applied' : '' ?>"> <input type="hidden" name="remove" id="remove-coupon" value="0" /> <div class="field"> - <label for="coupon_code" class="label"><span><?= /* @escapeNotVerified */ __('Enter discount code') ?></span></label> + <label for="coupon_code" class="label"><span><?= $block->escapeHtml(__('Enter discount code')) ?></span></label> <div class="control"> - <input type="text" class="input-text" id="coupon_code" name="coupon_code" value="<?= $block->escapeHtml($block->getCouponCode()) ?>" placeholder="<?= $block->escapeHtml(__('Enter discount code')) ?>" <?php if (strlen($block->getCouponCode())): ?> disabled="disabled" <?php endif; ?> /> + <input type="text" + class="input-text" + id="coupon_code" + name="coupon_code" + value="<?= $block->escapeHtmlAttr($block->getCouponCode()) ?>" + placeholder="<?= $block->escapeHtmlAttr(__('Enter discount code')) ?>" + <?php if (strlen($block->getCouponCode())) :?> + disabled="disabled" + <?php endif; ?> + /> </div> </div> <div class="actions-toolbar"> - <?php if (!strlen($block->getCouponCode())): ?> + <?php if (!strlen($block->getCouponCode())) :?> <div class="primary"> - <button class="action apply primary" type="button" value="<?= /* @escapeNotVerified */ __('Apply Discount') ?>"> - <span><?= /* @escapeNotVerified */ __('Apply Discount') ?></span> + <button class="action apply primary" type="button" value="<?= $block->escapeHtmlAttr(__('Apply Discount')) ?>"> + <span><?= $block->escapeHtml(__('Apply Discount')) ?></span> </button> </div> - <?php else: ?> + <?php else :?> <div class="primary"> - <button type="button" class="action cancel primary" value="<?= /* @escapeNotVerified */ __('Cancel Coupon') ?>"><span><?= /* @escapeNotVerified */ __('Cancel Coupon') ?></span></button> + <button type="button" class="action cancel primary" value="<?= $block->escapeHtmlAttr(__('Cancel Coupon')) ?>"><span><?= $block->escapeHtml(__('Cancel Coupon')) ?></span></button> </div> <?php endif; ?> </div> diff --git a/app/code/Magento/Checkout/view/frontend/templates/cart/form.phtml b/app/code/Magento/Checkout/view/frontend/templates/cart/form.phtml index 84ab9b13d8f3a..e1ab036c7d889 100644 --- a/app/code/Magento/Checkout/view/frontend/templates/cart/form.phtml +++ b/app/code/Magento/Checkout/view/frontend/templates/cart/form.phtml @@ -4,52 +4,56 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile +// phpcs:disable Magento2.Templates.ThisInTemplate /** @var $block \Magento\Checkout\Block\Cart\Grid */ ?> -<?php $mergedCells = ($this->helper('Magento\Tax\Helper\Data')->displayCartBothPrices() ? 2 : 1); ?> +<?php $mergedCells = ($this->helper(Magento\Tax\Helper\Data::class)->displayCartBothPrices() ? 2 : 1); ?> <?= $block->getChildHtml('form_before') ?> -<form action="<?= /* @escapeNotVerified */ $block->getUrl('checkout/cart/updatePost') ?>" +<form action="<?= $block->escapeUrl($block->getUrl('checkout/cart/updatePost')) ?>" method="post" id="form-validate" data-mage-init='{"Magento_Checkout/js/action/update-shopping-cart": - {"validationURL" : "/checkout/cart/updateItemQty", + {"validationURL" : "<?= $block->escapeUrl($block->getUrl('checkout/cart/updateItemQty')) ?>", "updateCartActionContainer": "#update_cart_action_container"} }' class="form form-cart"> <?= $block->getBlockHtml('formkey') ?> <div class="cart table-wrapper<?= $mergedCells == 2 ? ' detailed' : '' ?>"> - <?php if ($block->getPagerHtml()): ?> - <div class="cart-products-toolbar cart-products-toolbar-top toolbar" data-attribute="cart-products-toolbar-top"><?= $block->getPagerHtml() ?></div> + <?php if ($block->getPagerHtml()) :?> + <div class="cart-products-toolbar cart-products-toolbar-top toolbar" + data-attribute="cart-products-toolbar-top"><?= $block->getPagerHtml() ?> + </div> <?php endif ?> <table id="shopping-cart-table" class="cart items data table" data-mage-init='{"shoppingCart":{"emptyCartButton": ".action.clear", "updateCartActionContainer": "#update_cart_action_container"}}'> - <caption class="table-caption"><?= /* @escapeNotVerified */ __('Shopping Cart Items') ?></caption> + <caption class="table-caption"><?= $block->escapeHtml(__('Shopping Cart Items')) ?></caption> <thead> <tr> - <th class="col item" scope="col"><span><?= /* @escapeNotVerified */ __('Item') ?></span></th> - <th class="col price" scope="col"><span><?= /* @escapeNotVerified */ __('Price') ?></span></th> - <th class="col qty" scope="col"><span><?= /* @escapeNotVerified */ __('Qty') ?></span></th> - <th class="col subtotal" scope="col"><span><?= /* @escapeNotVerified */ __('Subtotal') ?></span></th> + <th class="col item" scope="col"><span><?= $block->escapeHtml(__('Item')) ?></span></th> + <th class="col price" scope="col"><span><?= $block->escapeHtml(__('Price')) ?></span></th> + <th class="col qty" scope="col"><span><?= $block->escapeHtml(__('Qty')) ?></span></th> + <th class="col subtotal" scope="col"><span><?= $block->escapeHtml(__('Subtotal')) ?></span></th> </tr> </thead> - <?php foreach ($block->getItems() as $_item): ?> + <?php foreach ($block->getItems() as $_item) :?> <?= $block->getItemHtml($_item) ?> <?php endforeach ?> </table> - <?php if ($block->getPagerHtml()): ?> - <div class="cart-products-toolbar cart-products-toolbar-bottom toolbar" data-attribute="cart-products-toolbar-bottom"><?= $block->getPagerHtml() ?></div> + <?php if ($block->getPagerHtml()) :?> + <div class="cart-products-toolbar cart-products-toolbar-bottom toolbar" + data-attribute="cart-products-toolbar-bottom"><?= $block->getPagerHtml() ?> + </div> <?php endif ?> </div> <div class="cart main actions"> - <?php if ($block->getContinueShoppingUrl()): ?> + <?php if ($block->getContinueShoppingUrl()) :?> <a class="action continue" href="<?= $block->escapeUrl($block->getContinueShoppingUrl()) ?>" title="<?= $block->escapeHtml(__('Continue Shopping')) ?>"> - <span><?= /* @escapeNotVerified */ __('Continue Shopping') ?></span> + <span><?= $block->escapeHtml(__('Continue Shopping')) ?></span> </a> <?php endif; ?> <button type="submit" @@ -58,7 +62,7 @@ value="empty_cart" title="<?= $block->escapeHtml(__('Clear Shopping Cart')) ?>" class="action clear" id="empty_cart_button"> - <span><?= /* @escapeNotVerified */ __('Clear Shopping Cart') ?></span> + <span><?= $block->escapeHtml(__('Clear Shopping Cart')) ?></span> </button> <button type="submit" name="update_cart_action" @@ -66,7 +70,7 @@ value="update_qty" title="<?= $block->escapeHtml(__('Update Shopping Cart')) ?>" class="action update"> - <span><?= /* @escapeNotVerified */ __('Update Shopping Cart') ?></span> + <span><?= $block->escapeHtml(__('Update Shopping Cart')) ?></span> </button> <input type="hidden" value="" id="update_cart_action_container" data-cart-item-update=""/> </div> diff --git a/app/code/Magento/Checkout/view/frontend/templates/cart/item/configure/updatecart.phtml b/app/code/Magento/Checkout/view/frontend/templates/cart/item/configure/updatecart.phtml index bfb7ddc55cda6..3b09512eb505b 100644 --- a/app/code/Magento/Checkout/view/frontend/templates/cart/item/configure/updatecart.phtml +++ b/app/code/Magento/Checkout/view/frontend/templates/cart/item/configure/updatecart.phtml @@ -4,25 +4,23 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - /** @var $block \Magento\Catalog\Block\Product\View */ ?> <?php $_product = $block->getProduct(); ?> <?php $buttonTitle = __('Update Cart'); ?> -<?php if ($_product->isSaleable()): ?> +<?php if ($_product->isSaleable()) :?> <div class="box-tocart update"> <fieldset class="fieldset"> - <?php if ($block->shouldRenderQuantity()): ?> + <?php if ($block->shouldRenderQuantity()) :?> <div class="field qty"> - <label class="label" for="qty"><span><?= /* @escapeNotVerified */ __('Qty') ?></span></label> + <label class="label" for="qty"><span><?= $block->escapeHtml(__('Qty')) ?></span></label> <div class="control"> <input type="number" name="qty" id="qty" min="0" value="" - title="<?= /* @escapeNotVerified */ __('Qty') ?>" + title="<?= $block->escapeHtmlAttr(__('Qty')) ?>" class="input-text qty" data-validate="<?= $block->escapeHtml(json_encode($block->getQuantityValidators())) ?>"/> </div> @@ -30,10 +28,10 @@ <?php endif; ?> <div class="actions"> <button type="submit" - title="<?= /* @escapeNotVerified */ $buttonTitle ?>" + title="<?= $block->escapeHtmlAttr($buttonTitle) ?>" class="action primary tocart" id="product-updatecart-button"> - <span><?= /* @escapeNotVerified */ $buttonTitle ?></span> + <span><?= $block->escapeHtml($buttonTitle) ?></span> </button> <?= $block->getChildHtml('', true) ?> </div> diff --git a/app/code/Magento/Checkout/view/frontend/templates/cart/item/default.phtml b/app/code/Magento/Checkout/view/frontend/templates/cart/item/default.phtml index d15794fb761bb..77dde1eab482a 100644 --- a/app/code/Magento/Checkout/view/frontend/templates/cart/item/default.phtml +++ b/app/code/Magento/Checkout/view/frontend/templates/cart/item/default.phtml @@ -4,7 +4,8 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile +// phpcs:disable Magento2.Templates.ThisInTemplate +// phpcs:disable Magento2.Files.LineLength.MaxExceeded /** @var $block \Magento\Checkout\Block\Cart\Item\Renderer */ @@ -12,72 +13,82 @@ $_item = $block->getItem(); $product = $_item->getProduct(); $isVisibleProduct = $product->isVisibleInSiteVisibility(); /** @var \Magento\Msrp\Helper\Data $helper */ -$helper = $this->helper('Magento\Msrp\Helper\Data'); +$helper = $this->helper(Magento\Msrp\Helper\Data::class); $canApplyMsrp = $helper->isShowBeforeOrderConfirm($product) && $helper->isMinimalPriceLessMsrp($product); ?> <tbody class="cart item"> <tr class="item-info"> <td data-th="<?= $block->escapeHtml(__('Item')) ?>" class="col item"> - <?php if ($block->hasProductUrl()):?> - <a href="<?= /* @escapeNotVerified */ $block->getProductUrl() ?>" + <?php if ($block->hasProductUrl()) :?> + <a href="<?= $block->escapeUrl($block->getProductUrl()) ?>" title="<?= $block->escapeHtml($block->getProductName()) ?>" tabindex="-1" class="product-item-photo"> - <?php else:?> + <?php else :?> <span class="product-item-photo"> <?php endif;?> <?= $block->getImage($block->getProductForThumbnail(), 'cart_page_product_thumbnail')->toHtml() ?> - <?php if ($block->hasProductUrl()):?> + <?php if ($block->hasProductUrl()) :?> </a> - <?php else: ?> + <?php else :?> </span> <?php endif; ?> <div class="product-item-details"> <strong class="product-item-name"> - <?php if ($block->hasProductUrl()):?> - <a href="<?= /* @escapeNotVerified */ $block->getProductUrl() ?>"><?= $block->escapeHtml($block->getProductName()) ?></a> - <?php else: ?> + <?php if ($block->hasProductUrl()) :?> + <a href="<?= $block->escapeUrl($block->getProductUrl()) ?>"><?= $block->escapeHtml($block->getProductName()) ?></a> + <?php else :?> <?= $block->escapeHtml($block->getProductName()) ?> <?php endif; ?> </strong> - <?php if ($_options = $block->getOptionList()):?> + <?php if ($_options = $block->getOptionList()) :?> <dl class="item-options"> - <?php foreach ($_options as $_option) : ?> + <?php foreach ($_options as $_option) :?> <?php $_formatedOptionValue = $block->getFormatedOptionValue($_option) ?> <dt><?= $block->escapeHtml($_option['label']) ?></dt> <dd> - <?php if (isset($_formatedOptionValue['full_view'])): ?> - <?= /* @escapeNotVerified */ $_formatedOptionValue['full_view'] ?> - <?php else: ?> + <?php if (isset($_formatedOptionValue['full_view'])) :?> + <?= $block->escapeHtml($_formatedOptionValue['full_view']) ?> + <?php else :?> <?= $block->escapeHtml($_formatedOptionValue['value'], ['span']) ?> <?php endif; ?> </dd> <?php endforeach; ?> </dl> <?php endif;?> - <?php if ($messages = $block->getMessages()): ?> - <?php foreach ($messages as $message): ?> - <div class="cart item message <?= /* @escapeNotVerified */ $message['type'] ?>"><div><?= $block->escapeHtml($message['text']) ?></div></div> + <?php if ($messages = $block->getMessages()) :?> + <?php foreach ($messages as $message) :?> + <div class= "cart item message <?= $block->escapeHtmlAttr($message['type']) ?>"> + <div><?= $block->escapeHtml($message['text']) ?></div> + </div> <?php endforeach; ?> <?php endif; ?> <?php $addInfoBlock = $block->getProductAdditionalInformationBlock(); ?> - <?php if ($addInfoBlock): ?> + <?php if ($addInfoBlock) :?> <?= $addInfoBlock->setItem($_item)->toHtml() ?> <?php endif;?> </div> </td> - <?php if ($canApplyMsrp): ?> + <?php if ($canApplyMsrp) :?> <td class="col msrp" data-th="<?= $block->escapeHtml(__('Price')) ?>"> <span class="pricing msrp"> - <span class="msrp notice"><?= /* @escapeNotVerified */ __('See price before order confirmation.') ?></span> + <span class="msrp notice"><?= $block->escapeHtml(__('See price before order confirmation.')) ?></span> <?php $helpLinkId = 'cart-msrp-help-' . $_item->getId(); ?> - <a href="#" class="action help map" id="<?= /* @escapeNotVerified */ ($helpLinkId) ?>" data-mage-init='{"addToCart":{"helpLinkId": "#<?= /* @escapeNotVerified */ $helpLinkId ?>","productName": "<?= /* @escapeNotVerified */ $product->getName() ?>","showAddToCart": false}}'> - <span><?= /* @escapeNotVerified */ __("What's this?") ?></span> + <a href="#" class="action help map" + id="<?= ($block->escapeHtmlAttr($helpLinkId)) ?>" + data-mage-init='{"addToCart":{ + "helpLinkId": "#<?= $block->escapeJs($block->escapeHtml($helpLinkId)) ?>", + "productName": "<?= $block->escapeJs($block->escapeHtml($product->getName())) ?>", + "showAddToCart": false + } + }' + > + <span><?= $block->escapeHtml(__("What's this?")) ?></span> </a> </span> </td> - <?php else: ?> + <?php else :?> <td class="col price" data-th="<?= $block->escapeHtml(__('Price')) ?>"> <?= $block->getUnitPriceHtml($_item) ?> </td> @@ -85,15 +96,15 @@ $canApplyMsrp = $helper->isShowBeforeOrderConfirm($product) && $helper->isMinima <td class="col qty" data-th="<?= $block->escapeHtml(__('Qty')) ?>"> <div class="field qty"> <div class="control qty"> - <label for="cart-<?= /* @escapeNotVerified */ $_item->getId() ?>-qty"> - <span class="label"><?= /* @escapeNotVerified */ __('Qty') ?></span> - <input id="cart-<?= /* @escapeNotVerified */ $_item->getId() ?>-qty" - name="cart[<?= /* @escapeNotVerified */ $_item->getId() ?>][qty]" - data-cart-item-id="<?= $block->escapeHtml($_item->getSku()) ?>" - value="<?= /* @escapeNotVerified */ $block->getQty() ?>" + <label for="cart-<?= $block->escapeHtmlAttr($_item->getId()) ?>-qty"> + <span class="label"><?= $block->escapeHtml(__('Qty')) ?></span> + <input id="cart-<?= $block->escapeHtmlAttr($_item->getId()) ?>-qty" + name="cart[<?= $block->escapeHtmlAttr($_item->getId()) ?>][qty]" + data-cart-item-id="<?= $block->escapeHtmlAttr($_item->getSku()) ?>" + value="<?= $block->escapeHtmlAttr($block->getQty()) ?>" type="number" size="4" - title="<?= $block->escapeHtml(__('Qty')) ?>" + title="<?= $block->escapeHtmlAttr(__('Qty')) ?>" class="input-text qty" data-validate="{required:true,'validate-greater-than-zero':true}" data-role="cart-item-qty"/> @@ -103,9 +114,9 @@ $canApplyMsrp = $helper->isShowBeforeOrderConfirm($product) && $helper->isMinima </td> <td class="col subtotal" data-th="<?= $block->escapeHtml(__('Subtotal')) ?>"> - <?php if ($canApplyMsrp): ?> + <?php if ($canApplyMsrp) :?> <span class="cart msrp subtotal">--</span> - <?php else: ?> + <?php else :?> <?= $block->getRowTotalHtml($_item) ?> <?php endif; ?> </td> @@ -113,7 +124,7 @@ $canApplyMsrp = $helper->isShowBeforeOrderConfirm($product) && $helper->isMinima <tr class="item-actions"> <td colspan="4"> <div class="actions-toolbar"> - <?= /* @escapeNotVerified */ $block->getActions($_item) ?> + <?= /* @noEscape */ $block->getActions($_item) ?> </div> </td> </tr> diff --git a/app/code/Magento/Checkout/view/frontend/templates/cart/item/price/sidebar.phtml b/app/code/Magento/Checkout/view/frontend/templates/cart/item/price/sidebar.phtml index d7a625695b476..f8e692fc6f71c 100644 --- a/app/code/Magento/Checkout/view/frontend/templates/cart/item/price/sidebar.phtml +++ b/app/code/Magento/Checkout/view/frontend/templates/cart/item/price/sidebar.phtml @@ -4,12 +4,16 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile +// phpcs:disable Magento2.Templates.ThisInTemplate /** @var $block \Magento\Checkout\Block\Item\Price\Renderer */ ?> <?php $_item = $block->getItem() ?> <div class="price-container"> - <span class="price-label"><?= /* @escapeNotVerified */ __('Price') ?></span> - <span class="price-wrapper"><?= /* @escapeNotVerified */ $this->helper('Magento\Checkout\Helper\Data')->formatPrice($_item->getCalculationPrice()) ?></span> + <span class="price-label"><?= $block->escapeHtml(__('Price')) ?></span> + <span class="price-wrapper"> + <?= $block->escapeHtml( + $this->helper(Magento\Checkout\Helper\Data::class)->formatPrice($_item->getCalculationPrice()) + ) ?> + </span> </div> diff --git a/app/code/Magento/Checkout/view/frontend/templates/cart/item/renderer/actions/edit.phtml b/app/code/Magento/Checkout/view/frontend/templates/cart/item/renderer/actions/edit.phtml index da0a83f05ef60..357bbf27772b5 100644 --- a/app/code/Magento/Checkout/view/frontend/templates/cart/item/renderer/actions/edit.phtml +++ b/app/code/Magento/Checkout/view/frontend/templates/cart/item/renderer/actions/edit.phtml @@ -4,14 +4,12 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - /** @var $block \Magento\Checkout\Block\Cart\Item\Renderer\Actions\Edit */ ?> -<?php if ($block->isProductVisibleInSiteVisibility()): ?> +<?php if ($block->isProductVisibleInSiteVisibility()) :?> <a class="action action-edit" - href="<?= /* @escapeNotVerified */ $block->getConfigureUrl() ?>" - title="<?= $block->escapeHtml(__('Edit item parameters')) ?>"> - <span><?= /* @escapeNotVerified */ __('Edit') ?></span> + href="<?= $block->escapeUrl($block->getConfigureUrl()) ?>" + title="<?= $block->escapeHtmlAttr(__('Edit item parameters')) ?>"> + <span><?= $block->escapeHtml(__('Edit')) ?></span> </a> <?php endif ?> diff --git a/app/code/Magento/Checkout/view/frontend/templates/cart/item/renderer/actions/remove.phtml b/app/code/Magento/Checkout/view/frontend/templates/cart/item/renderer/actions/remove.phtml index 445721ca5d0c2..8b72b6c55e821 100644 --- a/app/code/Magento/Checkout/view/frontend/templates/cart/item/renderer/actions/remove.phtml +++ b/app/code/Magento/Checkout/view/frontend/templates/cart/item/renderer/actions/remove.phtml @@ -4,15 +4,13 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - /** @var $block \Magento\Checkout\Block\Cart\Item\Renderer\Actions\Remove */ ?> <a href="#" title="<?= $block->escapeHtml(__('Remove item')) ?>" class="action action-delete" - data-post='<?= /* @escapeNotVerified */ $block->getDeletePostJson() ?>'> + data-post='<?= /* @noEscape */ $block->getDeletePostJson() ?>'> <span> - <?= /* @escapeNotVerified */ __('Remove item') ?> + <?= $block->escapeHtml(__('Remove item')) ?> </span> </a> diff --git a/app/code/Magento/Checkout/view/frontend/templates/cart/methods.phtml b/app/code/Magento/Checkout/view/frontend/templates/cart/methods.phtml index d329e2e8c1770..b045f4ec98ff7 100644 --- a/app/code/Magento/Checkout/view/frontend/templates/cart/methods.phtml +++ b/app/code/Magento/Checkout/view/frontend/templates/cart/methods.phtml @@ -4,20 +4,18 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - ?> <?php /** @var $block \Magento\Checkout\Block\Cart */ ?> -<?php if (!$block->hasError()): ?> -<?php $methods = $block->getMethods('methods') ?: $block->getMethods('top_methods') ?> -<ul class="checkout methods items checkout-methods-items"> -<?php foreach ($methods as $method): ?> - <?php $methodHtml = $block->getMethodHtml($method); ?> - <?php if (trim($methodHtml) !== ''): ?> - <li class="item"><?= /* @escapeNotVerified */ $methodHtml ?></li> - <?php endif; ?> -<?php endforeach; ?> -</ul> +<?php if (!$block->hasError()) :?> + <?php $methods = $block->getMethods('methods') ?: $block->getMethods('top_methods') ?> + <ul class="checkout methods items checkout-methods-items"> + <?php foreach ($methods as $method) :?> + <?php $methodHtml = $block->getMethodHtml($method); ?> + <?php if (trim($methodHtml) !== '') :?> + <li class="item"><?= /* @noEscape */ $methodHtml ?></li> + <?php endif; ?> + <?php endforeach; ?> + </ul> <?php endif; ?> diff --git a/app/code/Magento/Checkout/view/frontend/templates/cart/minicart.phtml b/app/code/Magento/Checkout/view/frontend/templates/cart/minicart.phtml index 20be9cd010c64..8928bbabcb718 100644 --- a/app/code/Magento/Checkout/view/frontend/templates/cart/minicart.phtml +++ b/app/code/Magento/Checkout/view/frontend/templates/cart/minicart.phtml @@ -4,15 +4,13 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - /** @var $block \Magento\Checkout\Block\Cart\Sidebar */ ?> <div data-block="minicart" class="minicart-wrapper"> - <a class="action showcart" href="<?= /* @escapeNotVerified */ $block->getShoppingCartUrl() ?>" + <a class="action showcart" href="<?= $block->escapeUrl($block->getShoppingCartUrl()) ?>" data-bind="scope: 'minicart_content'"> - <span class="text"><?= /* @escapeNotVerified */ __('My Cart') ?></span> + <span class="text"><?= $block->escapeHtml(__('My Cart')) ?></span> <span class="counter qty empty" data-bind="css: { empty: !!getCartParam('summary_count') == false }, blockLoader: isLoading"> <span class="counter-number"><!-- ko text: getCartParam('summary_count') --><!-- /ko --></span> @@ -24,7 +22,7 @@ </span> </span> </a> - <?php if ($block->getIsNeedToDisplaySideBar()): ?> + <?php if ($block->getIsNeedToDisplaySideBar()) :?> <div class="block block-minicart" data-role="dropdownDialog" data-mage-init='{"dropdownDialog":{ @@ -41,7 +39,7 @@ </div> <?= $block->getChildHtml('minicart.addons') ?> </div> - <?php else: ?> + <?php else :?> <script> require(['jquery'], function ($) { $('a.action.showcart').click(function() { @@ -51,15 +49,17 @@ </script> <?php endif ?> <script> - window.checkout = <?= /* @escapeNotVerified */ $block->getSerializedConfig() ?>; + window.checkout = <?= /* @noEscape */ $block->getSerializedConfig() ?>; </script> <script type="text/x-magento-init"> { "[data-block='minicart']": { - "Magento_Ui/js/core/app": <?= /* @escapeNotVerified */ $block->getJsLayout() ?> + "Magento_Ui/js/core/app": <?= /* @noEscape */ $block->getJsLayout() ?> }, "*": { - "Magento_Ui/js/block-loader": "<?= /* @escapeNotVerified */ $block->getViewFileUrl('images/loader-1.gif') ?>" + "Magento_Ui/js/block-loader": "<?= $block->escapeJs( + $block->escapeUrl($block->getViewFileUrl('images/loader-1.gif')) + ) ?>" } } </script> diff --git a/app/code/Magento/Checkout/view/frontend/templates/cart/noItems.phtml b/app/code/Magento/Checkout/view/frontend/templates/cart/noItems.phtml index 67ac4a9335565..ac150b2aa98b9 100644 --- a/app/code/Magento/Checkout/view/frontend/templates/cart/noItems.phtml +++ b/app/code/Magento/Checkout/view/frontend/templates/cart/noItems.phtml @@ -3,14 +3,20 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile + /** @var $block \Magento\Checkout\Block\Cart */ ?> <div class="cart-empty"> <?= $block->getChildHtml('checkout_cart_empty_widget') ?> - <p><?= /* @escapeNotVerified */ __('You have no items in your shopping cart.') ?></p> - <p><?php /* @escapeNotVerified */ echo __('Click <a href="%1">here</a> to continue shopping.', - $block->escapeUrl($block->getContinueShoppingUrl())) ?></p> + <p><?= $block->escapeHtml(__('You have no items in your shopping cart.')) ?></p> + <p><?= $block->escapeHtml( + __( + 'Click <a href="%1">here</a> to continue shopping.', + $block->escapeUrl($block->getContinueShoppingUrl()) + ), + ['a'] + ) ?> + </p> <?= $block->getChildHtml('shopping.cart.table.after') ?> </div> <script type="text/x-magento-init"> @@ -19,4 +25,4 @@ "Magento_Checkout/js/empty-cart": {} } } -</script> \ No newline at end of file +</script> diff --git a/app/code/Magento/Checkout/view/frontend/templates/cart/shipping.phtml b/app/code/Magento/Checkout/view/frontend/templates/cart/shipping.phtml index b5ddb8446ba05..a44d37dccfdc5 100644 --- a/app/code/Magento/Checkout/view/frontend/templates/cart/shipping.phtml +++ b/app/code/Magento/Checkout/view/frontend/templates/cart/shipping.phtml @@ -4,36 +4,47 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - ?> <?php /** @var $block \Magento\Checkout\Block\Cart\Shipping */ ?> -<div id="block-shipping" class="block shipping" data-mage-init='{"collapsible":{"openedState": "active", "saveState": true}}'> +<div id="block-shipping" + class="block shipping" + data-mage-init='{"collapsible":{"openedState": "active", "saveState": true}}' +> <div class="title" data-role="title"> <strong id="block-shipping-heading" role="heading" aria-level="2"> - <?= /* @escapeNotVerified */ $block->getQuote()->isVirtual() ? __('Estimate Tax') : __('Estimate Shipping and Tax') ?> + <?= $block->getQuote()->isVirtual() + ? $block->escapeHtml(__('Estimate Tax')) + : $block->escapeHtml(__('Estimate Shipping and Tax')) + ?> </strong> </div> - <div id="block-summary" data-bind="scope:'block-summary'" class="content" data-role="content" aria-labelledby="block-shipping-heading"> + <div id="block-summary" + data-bind="scope:'block-summary'" + class="content" + data-role="content" + aria-labelledby="block-shipping-heading" + > <!-- ko template: getTemplate() --><!-- /ko --> <script type="text/x-magento-init"> { "#block-summary": { - "Magento_Ui/js/core/app": <?= /* @escapeNotVerified */ $block->getJsLayout() ?> + "Magento_Ui/js/core/app": <?= /* @noEscape */ $block->getJsLayout() ?> } } </script> <script> - window.checkoutConfig = <?= /* @escapeNotVerified */ $block->getSerializedCheckoutConfig() ?>; + window.checkoutConfig = <?= /* @noEscape */ $block->getSerializedCheckoutConfig() ?>; window.customerData = window.checkoutConfig.customerData; window.isCustomerLoggedIn = window.checkoutConfig.isCustomerLoggedIn; require([ 'mage/url', 'Magento_Ui/js/block-loader' ], function(url, blockLoader) { - blockLoader("<?= /* @escapeNotVerified */ $block->getViewFileUrl('images/loader-1.gif') ?>"); - return url.setBaseUrl('<?= /* @escapeNotVerified */ $block->getBaseUrl() ?>'); + blockLoader( + "<?= $block->escapeJs($block->escapeUrl($block->getViewFileUrl('images/loader-1.gif'))) ?>" + ); + return url.setBaseUrl('<?= $block->escapeJs($block->escapeUrl($block->getBaseUrl())) ?>'); }) </script> </div> diff --git a/app/code/Magento/Checkout/view/frontend/templates/cart/totals.phtml b/app/code/Magento/Checkout/view/frontend/templates/cart/totals.phtml index f39b70df98424..784c4c39076e6 100644 --- a/app/code/Magento/Checkout/view/frontend/templates/cart/totals.phtml +++ b/app/code/Magento/Checkout/view/frontend/templates/cart/totals.phtml @@ -15,7 +15,7 @@ <script type="text/x-magento-init"> { "#cart-totals": { - "Magento_Ui/js/core/app": <?= /* @escapeNotVerified */ $block->getJsLayout() ?> + "Magento_Ui/js/core/app": <?= /* @noEscape */ $block->getJsLayout() ?> } } </script> diff --git a/app/code/Magento/Checkout/view/frontend/templates/item/price/row.phtml b/app/code/Magento/Checkout/view/frontend/templates/item/price/row.phtml index 37a945b238d30..25124d91b6ea7 100644 --- a/app/code/Magento/Checkout/view/frontend/templates/item/price/row.phtml +++ b/app/code/Magento/Checkout/view/frontend/templates/item/price/row.phtml @@ -4,7 +4,7 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile +// phpcs:disable Magento2.Templates.ThisInTemplate /** @var $block \Magento\Checkout\Block\Item\Price\Renderer */ @@ -12,6 +12,8 @@ $_item = $block->getItem(); ?> <span class="price-excluding-tax" data-label="<?= $block->escapeHtml(__('Excl. Tax')) ?>"> <span class="cart-price"> - <?= /* @escapeNotVerified */ $this->helper('Magento\Checkout\Helper\Data')->formatPrice($_item->getRowTotal()) ?> + <?= $block->escapeHtml( + $this->helper(Magento\Checkout\Helper\Data::class)->formatPrice($_item->getRowTotal()) + ) ?> </span> </span> diff --git a/app/code/Magento/Checkout/view/frontend/templates/item/price/unit.phtml b/app/code/Magento/Checkout/view/frontend/templates/item/price/unit.phtml index 45a6ef48e36d6..c6ee4beb00c5a 100644 --- a/app/code/Magento/Checkout/view/frontend/templates/item/price/unit.phtml +++ b/app/code/Magento/Checkout/view/frontend/templates/item/price/unit.phtml @@ -4,7 +4,7 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile +// phpcs:disable Magento2.Templates.ThisInTemplate /** @var $block \Magento\Checkout\Block\Item\Price\Renderer */ @@ -12,6 +12,8 @@ $_item = $block->getItem(); ?> <span class="price-including-tax" data-label="<?= $block->escapeHtml(__('Excl. Tax')) ?>"> <span class="cart-price"> - <?= /* @escapeNotVerified */ $this->helper('Magento\Checkout\Helper\Data')->formatPrice($_item->getCalculationPrice()) ?> + <?= $block->escapeHtml( + $this->helper(Magento\Checkout\Helper\Data::class)->formatPrice($_item->getCalculationPrice()) + ) ?> </span> </span> diff --git a/app/code/Magento/Checkout/view/frontend/templates/js/components.phtml b/app/code/Magento/Checkout/view/frontend/templates/js/components.phtml index bad5acc209b5f..6cf15f4770150 100644 --- a/app/code/Magento/Checkout/view/frontend/templates/js/components.phtml +++ b/app/code/Magento/Checkout/view/frontend/templates/js/components.phtml @@ -4,7 +4,5 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - ?> <?= $block->getChildHtml() ?> diff --git a/app/code/Magento/Checkout/view/frontend/templates/messages/addCartSuccessMessage.phtml b/app/code/Magento/Checkout/view/frontend/templates/messages/addCartSuccessMessage.phtml index e835037b5fcb4..a6686444d2ed5 100644 --- a/app/code/Magento/Checkout/view/frontend/templates/messages/addCartSuccessMessage.phtml +++ b/app/code/Magento/Checkout/view/frontend/templates/messages/addCartSuccessMessage.phtml @@ -3,7 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile + /** @var \Magento\Framework\View\Element\Template $block */ ?> diff --git a/app/code/Magento/Checkout/view/frontend/templates/onepage.phtml b/app/code/Magento/Checkout/view/frontend/templates/onepage.phtml index 47a56e8f333bc..55f7039f33344 100644 --- a/app/code/Magento/Checkout/view/frontend/templates/onepage.phtml +++ b/app/code/Magento/Checkout/view/frontend/templates/onepage.phtml @@ -4,13 +4,13 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile +/** @var $block \Magento\Checkout\Block\Onepage */ ?> <div id="checkout" data-bind="scope:'checkout'" class="checkout-container"> <div id="checkout-loader" data-role="checkout-loader" class="loading-mask" data-mage-init='{"checkoutLoader": {}}'> <div class="loader"> - <img src="<?= /* @escapeNotVerified */ $block->getViewFileUrl('images/loader-1.gif') ?>" - alt="<?= /* @escapeNotVerified */ __('Loading...') ?>" + <img src="<?= $block->escapeUrl($block->getViewFileUrl('images/loader-1.gif')) ?>" + alt="<?= $block->escapeHtmlAttr(__('Loading...')) ?>" style="position: absolute;"> </div> </div> @@ -18,12 +18,12 @@ <script type="text/x-magento-init"> { "#checkout": { - "Magento_Ui/js/core/app": <?= /* @escapeNotVerified */ $block->getJsLayout() ?> + "Magento_Ui/js/core/app": <?= /* @noEscape */ $block->getJsLayout() ?> } } </script> <script> - window.checkoutConfig = <?= /* @escapeNotVerified */ $block->getSerializedCheckoutConfig() ?>; + window.checkoutConfig = <?= /* @noEscape */ $block->getSerializedCheckoutConfig() ?>; // Create aliases for customer.js model from customer module window.isCustomerLoggedIn = window.checkoutConfig.isCustomerLoggedIn; window.customerData = window.checkoutConfig.customerData; @@ -33,8 +33,8 @@ 'mage/url', 'Magento_Ui/js/block-loader' ], function(url, blockLoader) { - blockLoader("<?= /* @escapeNotVerified */ $block->getViewFileUrl('images/loader-1.gif') ?>"); - return url.setBaseUrl('<?= /* @escapeNotVerified */ $block->getBaseUrl() ?>'); + blockLoader("<?= $block->escapeJs($block->escapeUrl($block->getViewFileUrl('images/loader-1.gif'))) ?>"); + return url.setBaseUrl('<?= $block->escapeJs($block->escapeUrl($block->getBaseUrl())) ?>'); }) </script> </div> diff --git a/app/code/Magento/Checkout/view/frontend/templates/onepage/failure.phtml b/app/code/Magento/Checkout/view/frontend/templates/onepage/failure.phtml index 43791ef496745..3888ec6504982 100644 --- a/app/code/Magento/Checkout/view/frontend/templates/onepage/failure.phtml +++ b/app/code/Magento/Checkout/view/frontend/templates/onepage/failure.phtml @@ -4,9 +4,16 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - +/** @var $block \Magento\Checkout\Block\Onepage\Failure */ ?> -<?php if ($block->getRealOrderId()) : ?><p><?= /* @escapeNotVerified */ __('Order #') . $block->getRealOrderId() ?></p><?php endif ?> -<?php if ($error = $block->getErrorMessage()) : ?><p><?= /* @escapeNotVerified */ $error ?></p><?php endif ?> -<p><?= /* @escapeNotVerified */ __('Click <a href="%1">here</a> to continue shopping.', $block->escapeUrl($block->getContinueShoppingUrl())) ?></p> +<?php if ($block->getRealOrderId()) :?> + <p><?= $block->escapeHtml(__('Order #') . $block->getRealOrderId()) ?></p> +<?php endif ?> +<?php if ($error = $block->getErrorMessage()) :?> + <p><?= $block->escapeHtml($error) ?></p> +<?php endif ?> +<p><?= $block->escapeHtml( + _('Click <a href="%1">here</a> to continue shopping.', $block->escapeUrl($block->getContinueShoppingUrl())), + ['a'] +) ?> +</p> diff --git a/app/code/Magento/Checkout/view/frontend/templates/onepage/link.phtml b/app/code/Magento/Checkout/view/frontend/templates/onepage/link.phtml index 53a1fe8783509..b667764ac7bba 100644 --- a/app/code/Magento/Checkout/view/frontend/templates/onepage/link.phtml +++ b/app/code/Magento/Checkout/view/frontend/templates/onepage/link.phtml @@ -4,15 +4,21 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile +/** @var $block \Magento\Checkout\Block\Onepage\Link */ ?> -<?php if ($block->isPossibleOnepageCheckout()):?> +<?php if ($block->isPossibleOnepageCheckout()) :?> <button type="button" data-role="proceed-to-checkout" - title="<?= /* @escapeNotVerified */ __('Proceed to Checkout') ?>" - data-mage-init='{"Magento_Checkout/js/proceed-to-checkout":{"checkoutUrl":"<?= /* @escapeNotVerified */ $block->getCheckoutUrl() ?>"}}' + title="<?= $block->escapeHtmlAttr(__('Proceed to Checkout')) ?>" + data-mage-init='{ + "Magento_Checkout/js/proceed-to-checkout":{ + "checkoutUrl":"<?= $block->escapeJs($block->escapeUrl($block->getCheckoutUrl())) ?>" + } + }' class="action primary checkout<?= ($block->isDisabled()) ? ' disabled' : '' ?>" - <?php if ($block->isDisabled()):?>disabled="disabled"<?php endif; ?>> - <span><?= /* @escapeNotVerified */ __('Proceed to Checkout') ?></span> + <?php if ($block->isDisabled()) :?> + disabled="disabled" + <?php endif; ?>> + <span><?= $block->escapeHtml(__('Proceed to Checkout')) ?></span> </button> <?php endif?> diff --git a/app/code/Magento/Checkout/view/frontend/templates/onepage/review/item.phtml b/app/code/Magento/Checkout/view/frontend/templates/onepage/review/item.phtml index 2428cc010779d..2a7ccc38e9d83 100644 --- a/app/code/Magento/Checkout/view/frontend/templates/onepage/review/item.phtml +++ b/app/code/Magento/Checkout/view/frontend/templates/onepage/review/item.phtml @@ -4,11 +4,12 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile +// phpcs:disable Magento2.Templates.ThisInTemplate /** @var $block Magento\Checkout\Block\Cart\Item\Renderer */ $_item = $block->getItem(); +$taxDataHelper = $this->helper(Magento\Tax\Helper\Data::class); ?> <tbody class="cart item"> <tr> @@ -17,47 +18,53 @@ $_item = $block->getItem(); <?= $block->getImage($block->getProductForThumbnail(), 'cart_page_product_thumbnail')->toHtml() ?> </span> <div class="product-item-details"> - <strong class="product name product-item-name"><?= $block->escapeHtml($block->getProductName()) ?></strong> - <?php if ($_options = $block->getOptionList()):?> - <dl class="item-options"> - <?php foreach ($_options as $_option) : ?> - <?php $_formatedOptionValue = $block->getFormatedOptionValue($_option) ?> - <dt><?= $block->escapeHtml($_option['label']) ?></dt> - <dd> - <?php if (isset($_formatedOptionValue['full_view'])): ?> - <?= /* @escapeNotVerified */ $_formatedOptionValue['full_view'] ?> - <?php else: ?> - <?= /* @escapeNotVerified */ $_formatedOptionValue['value'] ?> - <?php endif; ?> - </dd> - <?php endforeach; ?> - </dl> + <strong class="product name product-item-name"> + <?= $block->escapeHtml($block->getProductName()) ?> + </strong> + <?php if ($_options = $block->getOptionList()) :?> + <dl class="item-options"> + <?php foreach ($_options as $_option) :?> + <?php $_formatedOptionValue = $block->getFormatedOptionValue($_option) ?> + <dt><?= $block->escapeHtml($_option['label']) ?></dt> + <dd> + <?php if (isset($_formatedOptionValue['full_view'])) :?> + <?= $block->escapeHtml($_formatedOptionValue['full_view']) ?> + <?php else :?> + <?= $block->escapeHtml($_formatedOptionValue['value']) ?> + <?php endif; ?> + </dd> + <?php endforeach; ?> + </dl> <?php endif;?> - <?php if ($addtInfoBlock = $block->getProductAdditionalInformationBlock()):?> + <?php if ($addtInfoBlock = $block->getProductAdditionalInformationBlock()) :?> <?= $addtInfoBlock->setItem($_item)->toHtml() ?> <?php endif;?> </div> </td> <td class="col price" data-th="<?= $block->escapeHtml(__('Price')) ?>"> - <?php if ($this->helper('Magento\Tax\Helper\Data')->displayCartPriceInclTax() || $this->helper('Magento\Tax\Helper\Data')->displayCartBothPrices()): ?> + <?php if ($taxDataHelper->displayCartPriceInclTax() || $taxDataHelper->displayCartBothPrices()) :?> <span class="price-including-tax" data-label="<?= $block->escapeHtml(__('Incl. Tax')) ?>"> <?= $block->getUnitPriceInclTaxHtml($_item) ?> </span> <?php endif; ?> - <?php if ($this->helper('Magento\Tax\Helper\Data')->displayCartPriceExclTax() || $this->helper('Magento\Tax\Helper\Data')->displayCartBothPrices()): ?> + <?php if ($taxDataHelper->displayCartPriceExclTax() || $taxDataHelper->displayCartBothPrices()) :?> <span class="price-excluding-tax" data-label="<?= $block->escapeHtml(__('Excl. Tax')) ?>"> <?= $block->getUnitPriceExclTaxHtml($_item) ?> </span> <?php endif; ?> </td> - <td class="col qty" data-th="<?= $block->escapeHtml(__('Qty')) ?>"><span class="qty"><?= /* @escapeNotVerified */ $_item->getQty() ?></span></td> + <td class="col qty" + data-th="<?= $block->escapeHtml(__('Qty')) ?>" + > + <span class="qty"><?= $block->escapeHtml($_item->getQty()) ?></span> + </td> <td class="col subtotal" data-th="<?= $block->escapeHtml(__('Subtotal')) ?>"> - <?php if ($this->helper('Magento\Tax\Helper\Data')->displayCartPriceInclTax() || $this->helper('Magento\Tax\Helper\Data')->displayCartBothPrices()): ?> + <?php if ($taxDataHelper->displayCartPriceInclTax() || $taxDataHelper->displayCartBothPrices()) :?> <span class="price-including-tax" data-label="<?= $block->escapeHtml(__('Incl. Tax')) ?>"> <?= $block->getRowTotalInclTaxHtml($_item) ?> </span> <?php endif; ?> - <?php if ($this->helper('Magento\Tax\Helper\Data')->displayCartPriceExclTax() || $this->helper('Magento\Tax\Helper\Data')->displayCartBothPrices()): ?> + <?php if ($taxDataHelper->displayCartPriceExclTax() || $taxDataHelper->displayCartBothPrices()) :?> <span class="price-excluding-tax" data-label="<?= $block->escapeHtml(__('Excl. Tax')) ?>"> <?= $block->getRowTotalExclTaxHtml($_item) ?> </span> diff --git a/app/code/Magento/Checkout/view/frontend/templates/onepage/review/item/price/row_excl_tax.phtml b/app/code/Magento/Checkout/view/frontend/templates/onepage/review/item/price/row_excl_tax.phtml index 7ee3e416b9ade..909631d06e68a 100644 --- a/app/code/Magento/Checkout/view/frontend/templates/onepage/review/item/price/row_excl_tax.phtml +++ b/app/code/Magento/Checkout/view/frontend/templates/onepage/review/item/price/row_excl_tax.phtml @@ -4,12 +4,12 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile +// phpcs:disable Magento2.Templates.ThisInTemplate /** @var $block \Magento\Checkout\Block\Item\Price\Renderer */ $_item = $block->getItem(); ?> <span class="cart-price"> - <?= /* @escapeNotVerified */ $this->helper('Magento\Checkout\Helper\Data')->formatPrice($_item->getRowTotal()) ?> + <?= $block->escapeHtml($this->helper(Magento\Checkout\Helper\Data::class)->formatPrice($_item->getRowTotal())) ?> </span> diff --git a/app/code/Magento/Checkout/view/frontend/templates/onepage/review/item/price/row_incl_tax.phtml b/app/code/Magento/Checkout/view/frontend/templates/onepage/review/item/price/row_incl_tax.phtml index 2f364aafbbcc0..9f87ab7ef2ded 100644 --- a/app/code/Magento/Checkout/view/frontend/templates/onepage/review/item/price/row_incl_tax.phtml +++ b/app/code/Magento/Checkout/view/frontend/templates/onepage/review/item/price/row_incl_tax.phtml @@ -4,13 +4,13 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile +// phpcs:disable Magento2.Templates.ThisInTemplate /** @var $block \Magento\Checkout\Block\Item\Price\Renderer */ $_item = $block->getItem(); ?> -<?php $_incl = $this->helper('Magento\Checkout\Helper\Data')->getSubtotalInclTax($_item); ?> +<?php $_incl = $this->helper(Magento\Checkout\Helper\Data::class)->getSubtotalInclTax($_item); ?> <span class="cart-price"> - <?= /* @escapeNotVerified */ $this->helper('Magento\Checkout\Helper\Data')->formatPrice($_incl) ?> + <?= $block->escapeHtml($this->helper(Magento\Checkout\Helper\Data::class)->formatPrice($_incl)) ?> </span> diff --git a/app/code/Magento/Checkout/view/frontend/templates/onepage/review/item/price/unit_excl_tax.phtml b/app/code/Magento/Checkout/view/frontend/templates/onepage/review/item/price/unit_excl_tax.phtml index a1ec004c2a886..9da1cfe85e56b 100644 --- a/app/code/Magento/Checkout/view/frontend/templates/onepage/review/item/price/unit_excl_tax.phtml +++ b/app/code/Magento/Checkout/view/frontend/templates/onepage/review/item/price/unit_excl_tax.phtml @@ -4,12 +4,14 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile +// phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis /** @var $block \Magento\Checkout\Block\Item\Price\Renderer */ $_item = $block->getItem(); ?> <span class="cart-price"> - <?= /* @escapeNotVerified */ $this->helper('Magento\Checkout\Helper\Data')->formatPrice($_item->getCalculationPrice()) ?> + <?= $block->escapeHtml( + $this->helper(Magento\Checkout\Helper\Data::class)->formatPrice($_item->getCalculationPrice()) + ) ?> </span> diff --git a/app/code/Magento/Checkout/view/frontend/templates/onepage/review/item/price/unit_incl_tax.phtml b/app/code/Magento/Checkout/view/frontend/templates/onepage/review/item/price/unit_incl_tax.phtml index 0ed3c05ee6d1f..8de3176e0b963 100644 --- a/app/code/Magento/Checkout/view/frontend/templates/onepage/review/item/price/unit_incl_tax.phtml +++ b/app/code/Magento/Checkout/view/frontend/templates/onepage/review/item/price/unit_incl_tax.phtml @@ -4,13 +4,13 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile +// phpcs:disable Magento2.Templates.ThisInTemplate /** @var $block \Magento\Checkout\Block\Item\Price\Renderer */ $_item = $block->getItem(); ?> -<?php $_incl = $this->helper('Magento\Checkout\Helper\Data')->getPriceInclTax($_item); ?> +<?php $_incl = $this->helper(Magento\Checkout\Helper\Data::class)->getPriceInclTax($_item); ?> <span class="cart-price"> - <?= /* @escapeNotVerified */ $this->helper('Magento\Checkout\Helper\Data')->formatPrice($_incl) ?> + <?= $block->escapeHtml($this->helper(Magento\Checkout\Helper\Data::class)->formatPrice($_incl)) ?> </span> diff --git a/app/code/Magento/Checkout/view/frontend/templates/registration.phtml b/app/code/Magento/Checkout/view/frontend/templates/registration.phtml index f239fbd47dec2..da36b4b61d656 100644 --- a/app/code/Magento/Checkout/view/frontend/templates/registration.phtml +++ b/app/code/Magento/Checkout/view/frontend/templates/registration.phtml @@ -4,7 +4,7 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile +/** @var $block \Magento\Checkout\Block\Registration */ ?> <div id="registration" data-bind="scope:'registration'"> <br /> @@ -17,8 +17,9 @@ "registration": { "component": "Magento_Checkout/js/view/registration", "config": { - "registrationUrl": "<?= /* @escapeNotVerified */ $block->getCreateAccountUrl() ?>", - "email": "<?= /* @escapeNotVerified */ $block->getEmailAddress() ?>" + "registrationUrl": + "<?= $block->escapeJs($block->escapeUrl($block->getCreateAccountUrl())) ?>", + "email": "<?= $block->escapeJs($block->getEmailAddress()) ?>" }, "children": { "errors": { diff --git a/app/code/Magento/Checkout/view/frontend/templates/shipping/price.phtml b/app/code/Magento/Checkout/view/frontend/templates/shipping/price.phtml index 892b7926525f3..ba1a7a20376b3 100644 --- a/app/code/Magento/Checkout/view/frontend/templates/shipping/price.phtml +++ b/app/code/Magento/Checkout/view/frontend/templates/shipping/price.phtml @@ -4,10 +4,8 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - ?> <?php /** @var $block \Magento\Checkout\Block\Shipping\Price */ ?> <?php $shippingPrice = $block->getShippingPrice(); ?> -<?= /* @escapeNotVerified */ $shippingPrice ?> +<?= $block->escapeHtml($shippingPrice) ?> diff --git a/app/code/Magento/Checkout/view/frontend/templates/success.phtml b/app/code/Magento/Checkout/view/frontend/templates/success.phtml index b3517eab8a5d3..828b4eb86c330 100644 --- a/app/code/Magento/Checkout/view/frontend/templates/success.phtml +++ b/app/code/Magento/Checkout/view/frontend/templates/success.phtml @@ -4,25 +4,23 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - ?> <?php /** @var $block \Magento\Checkout\Block\Onepage\Success */ ?> <div class="checkout-success"> - <?php if ($block->getOrderId()):?> + <?php if ($block->getOrderId()) :?> <?php if ($block->getCanViewOrder()) :?> - <p><?= __('Your order number is: %1.', sprintf('<a href="%s" class="order-number"><strong>%s</strong></a>', $block->escapeHtml($block->getViewOrderUrl()), $block->escapeHtml($block->getOrderId()))) ?></p> + <p><?= $block->escapeHtml(__('Your order number is: %1.', sprintf('<a href="%s" class="order-number"><strong>%s</strong></a>', $block->escapeUrl($block->getViewOrderUrl()), $block->getOrderId())), ['a', 'strong']) ?></p> <?php else :?> - <p><?= __('Your order # is: <span>%1</span>.', $block->escapeHtml($block->getOrderId())) ?></p> + <p><?= $block->escapeHtml(__('Your order # is: <span>%1</span>.', $block->getOrderId()), ['span']) ?></p> <?php endif;?> - <p><?= /* @escapeNotVerified */ __('We\'ll email you an order confirmation with details and tracking info.') ?></p> + <p><?= $block->escapeHtml(__('We\'ll email you an order confirmation with details and tracking info.')) ?></p> <?php endif;?> <?= $block->getAdditionalInfoHtml() ?> <div class="actions-toolbar"> <div class="primary"> - <a class="action primary continue" href="<?= /* @escapeNotVerified */ $block->getContinueUrl() ?>"><span><?= /* @escapeNotVerified */ __('Continue Shopping') ?></span></a> + <a class="action primary continue" href="<?= $block->escapeUrl($block->getContinueUrl()) ?>"><span><?= $block->escapeHtml(__('Continue Shopping')) ?></span></a> </div> </div> </div> diff --git a/app/code/Magento/Checkout/view/frontend/templates/total/default.phtml b/app/code/Magento/Checkout/view/frontend/templates/total/default.phtml index 2ea1cdd7f53f5..eeee3e4bf0be9 100644 --- a/app/code/Magento/Checkout/view/frontend/templates/total/default.phtml +++ b/app/code/Magento/Checkout/view/frontend/templates/total/default.phtml @@ -4,18 +4,39 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile +// phpcs:disable Magento2.Templates.ThisInTemplate +/** @var $block \Magento\Checkout\Block\Total\DefaultTotal */ ?> <tr class="totals"> - <th colspan="<?= /* @escapeNotVerified */ $block->getColspan() ?>" style="<?= /* @escapeNotVerified */ $block->getTotal()->getStyle() ?>" class="mark" scope="row"> - <?php if ($block->getRenderingArea() == $block->getTotal()->getArea()): ?><strong><?php endif; ?> - <?= $block->escapeHtml($block->getTotal()->getTitle()) ?> - <?php if ($block->getRenderingArea() == $block->getTotal()->getArea()): ?></strong><?php endif; ?> + <th + colspan="<?= $block->escapeHtmlAttr($block->getColspan()) ?>" + style="<?= $block->escapeHtmlAttr($block->getTotal()->getStyle()) ?>" + class="mark" scope="row" + > + <?php if ($block->getRenderingArea() == $block->getTotal()->getArea()) :?> + <strong> + <?php endif; ?> + <?= $block->escapeHtml($block->getTotal()->getTitle()) ?> + <?php if ($block->getRenderingArea() == $block->getTotal()->getArea()) :?> + </strong> + <?php endif; ?> </th> - <td style="<?= /* @escapeNotVerified */ $block->getTotal()->getStyle() ?>" class="amount" data-th="<?= $block->escapeHtml($block->getTotal()->getTitle()) ?>"> - <?php if ($block->getRenderingArea() == $block->getTotal()->getArea()): ?><strong><?php endif; ?> - <span><?= /* @escapeNotVerified */ $this->helper('Magento\Checkout\Helper\Data')->formatPrice($block->getTotal()->getValue()) ?></span> - <?php if ($block->getRenderingArea() == $block->getTotal()->getArea()): ?></strong><?php endif; ?> + <td + style="<?= $block->escapeHtmlAttr($block->getTotal()->getStyle()) ?>" + class="amount" + data-th="<?= $block->escapeHtmlAttr($block->getTotal()->getTitle()) ?>" + > + <?php if ($block->getRenderingArea() == $block->getTotal()->getArea()) :?> + <strong> + <?php endif; ?> + <span> + <?= $block->escapeHtml( + $this->helper(Magento\Checkout\Helper\Data::class)->formatPrice($block->getTotal()->getValue()) + ) ?> + </span> + <?php if ($block->getRenderingArea() == $block->getTotal()->getArea()) :?> + </strong> + <?php endif; ?> </td> </tr> diff --git a/app/code/Magento/Checkout/view/frontend/web/js/view/payment/default.js b/app/code/Magento/Checkout/view/frontend/web/js/view/payment/default.js index 7b200860c4d55..1b5463c0770a3 100644 --- a/app/code/Magento/Checkout/view/frontend/web/js/view/payment/default.js +++ b/app/code/Magento/Checkout/view/frontend/web/js/view/payment/default.js @@ -133,15 +133,14 @@ define([ event.preventDefault(); } - if (this.validate() && additionalValidators.validate()) { + if (this.validate() && + additionalValidators.validate() && + this.isPlaceOrderActionAllowed() === true + ) { this.isPlaceOrderActionAllowed(false); this.getPlaceOrderDeferredObject() - .fail( - function () { - self.isPlaceOrderActionAllowed(true); - } - ).done( + .done( function () { self.afterPlaceOrder(); @@ -149,6 +148,10 @@ define([ redirectOnSuccessAction.execute(); } } + ).always( + function () { + self.isPlaceOrderActionAllowed(true); + } ); return true; diff --git a/app/code/Magento/Checkout/view/frontend/web/template/registration.html b/app/code/Magento/Checkout/view/frontend/web/template/registration.html index ea94726e5443e..5cc0d189e7c5b 100644 --- a/app/code/Magento/Checkout/view/frontend/web/template/registration.html +++ b/app/code/Magento/Checkout/view/frontend/web/template/registration.html @@ -11,8 +11,8 @@ <!-- ko if: isFormVisible --> <p data-bind="i18n: 'You can track your order status by creating an account.'"></p> <p><span data-bind="i18n: 'Email Address'"></span>: <span data-bind="text: getEmailAddress()"></span></p> - <form method="get" data-bind="attr: { action: getUrl() }"> - <input type="submit" class="action primary" data-bind="value: $t('Create an Account')" /> - </form> + <a class="action primary" data-bind="attr: { href: getUrl() }"> + <span data-bind="i18n: 'Create an Account'" /> + </a> <!--/ko--> </div> diff --git a/app/code/Magento/CheckoutAgreements/view/frontend/templates/additional_agreements.phtml b/app/code/Magento/CheckoutAgreements/view/frontend/templates/additional_agreements.phtml index 28a6e998d8d4e..9013a39f8e6f6 100644 --- a/app/code/Magento/CheckoutAgreements/view/frontend/templates/additional_agreements.phtml +++ b/app/code/Magento/CheckoutAgreements/view/frontend/templates/additional_agreements.phtml @@ -4,8 +4,6 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - /** * @var $block \Magento\CheckoutAgreements\Block\Agreements */ diff --git a/app/code/Magento/CheckoutAgreements/view/frontend/templates/agreements.phtml b/app/code/Magento/CheckoutAgreements/view/frontend/templates/agreements.phtml index b0c6384bcc9fe..5cb256090c196 100644 --- a/app/code/Magento/CheckoutAgreements/view/frontend/templates/agreements.phtml +++ b/app/code/Magento/CheckoutAgreements/view/frontend/templates/agreements.phtml @@ -4,7 +4,7 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile +// phpcs:disable Magento2.Files.LineLength ?> <?php @@ -17,30 +17,42 @@ } ?> <ol id="checkout-agreements" class="agreements checkout items"> <?php /** @var \Magento\CheckoutAgreements\Api\Data\AgreementInterface $agreement */ ?> - <?php foreach ($block->getAgreements() as $agreement): ?> + <?php foreach ($block->getAgreements() as $agreement) :?> <li class="item"> - <div class="checkout-agreement-item-content"<?= ($agreement->getContentHeight() ? ' style="height:' . $agreement->getContentHeight() . '"' : '') ?>> - <?php if ($agreement->getIsHtml()):?> - <?= /* @escapeNotVerified */ $agreement->getContent() ?> - <?php else:?> - <?= nl2br($block->escapeHtml($agreement->getContent())) ?> + <div class="checkout-agreement-item-content"<?= $block->escapeHtmlAttr($agreement->getContentHeight() ? ' style="height:' . $agreement->getContentHeight() . '"' : '') ?>> + <?php if ($agreement->getIsHtml()) :?> + <?= /* @noEscape */ $agreement->getContent() ?> + <?php else :?> + <?= $block->escapeHtml(nl2br($agreement->getContent())) ?> <?php endif; ?> </div> - <form id="checkout-agreements-form-<?= /* @escapeNotVerified */ $agreement->getAgreementId() ?>" class="field choice agree required"> - <?php if($agreement->getMode() == \Magento\CheckoutAgreements\Model\AgreementModeOptions::MODE_MANUAL): ?> + <form id="checkout-agreements-form-<?= (int) $agreement->getAgreementId() ?>" class="field choice agree required"> + <?php if ($agreement->getMode() == \Magento\CheckoutAgreements\Model\AgreementModeOptions::MODE_MANUAL) :?> <input type="checkbox" - id="agreement-<?= /* @escapeNotVerified */ $agreement->getAgreementId() ?>" - name="agreement[<?= /* @escapeNotVerified */ $agreement->getAgreementId() ?>]" + id="agreement-<?= (int) $agreement->getAgreementId() ?>" + name="agreement[<?= (int) $agreement->getAgreementId() ?>]" value="1" title="<?= $block->escapeHtml($agreement->getCheckboxText()) ?>" class="checkbox" data-validate="{required:true}"/> - <label class="label" for="agreement-<?= /* @escapeNotVerified */ $agreement->getAgreementId() ?>"> - <span><?= $agreement->getIsHtml() ? $agreement->getCheckboxText() : $block->escapeHtml($agreement->getCheckboxText()) ?></span> + <label class="label" for="agreement-<?= (int) $agreement->getAgreementId() ?>"> + <span> + <?php if ($agreement->getIsHtml()) :?> + <?= /* @noEscape */ $agreement->getCheckboxText() ?> + <?php else :?> + <?= $block->escapeHtml($agreement->getCheckboxText()) ?> + <?php endif; ?> + </span> </label> - <?php elseif($agreement->getMode() == \Magento\CheckoutAgreements\Model\AgreementModeOptions::MODE_AUTO): ?> - <div id="checkout-agreements-form-<?= /* @escapeNotVerified */ $agreement->getAgreementId() ?>" class="field choice agree"> - <span><?= $agreement->getIsHtml() ? $agreement->getCheckboxText() : $block->escapeHtml($agreement->getCheckboxText()) ?></span> + <?php elseif ($agreement->getMode() == \Magento\CheckoutAgreements\Model\AgreementModeOptions::MODE_AUTO) :?> + <div id="checkout-agreements-form-<?= (int) $agreement->getAgreementId() ?>" class="field choice agree"> + <span> + <?php if ($agreement->getIsHtml()) :?> + <?= /* @noEscape */ $agreement->getCheckboxText() ?> + <?php else :?> + <?= $block->escapeHtml($agreement->getCheckboxText()) ?> + <?php endif; ?> + </span> </div> <?php endif; ?> </form> diff --git a/app/code/Magento/CheckoutAgreements/view/frontend/templates/multishipping_agreements.phtml b/app/code/Magento/CheckoutAgreements/view/frontend/templates/multishipping_agreements.phtml index 33227f0cdce3c..fb2d5168d21de 100644 --- a/app/code/Magento/CheckoutAgreements/view/frontend/templates/multishipping_agreements.phtml +++ b/app/code/Magento/CheckoutAgreements/view/frontend/templates/multishipping_agreements.phtml @@ -5,7 +5,7 @@ */ // @deprecated -// @codingStandardsIgnoreFile +// phpcs:disable Magento2.Files.LineLength ?> <?php @@ -18,31 +18,43 @@ } ?> <ol id="checkout-agreements" class="agreements checkout items"> <?php /** @var \Magento\CheckoutAgreements\Api\Data\AgreementInterface $agreement */ ?> - <?php foreach ($block->getAgreements() as $agreement): ?> + <?php foreach ($block->getAgreements() as $agreement) :?> <li class="item"> - <div class="checkout-agreement-item-content"<?= ($agreement->getContentHeight() ? ' style="height:' . $agreement->getContentHeight() . '"' : '') ?>> - <?php if ($agreement->getIsHtml()):?> - <?= /* @escapeNotVerified */ $agreement->getContent() ?> - <?php else:?> - <?= nl2br($block->escapeHtml($agreement->getContent())) ?> + <div class="checkout-agreement-item-content"<?= $block->escapeHtmlAttr($agreement->getContentHeight() ? ' style="height:' . $agreement->getContentHeight() . '"' : '') ?>> + <?php if ($agreement->getIsHtml()) :?> + <?= /* @noEscape */ $agreement->getContent() ?> + <?php else :?> + <?= $block->escapeHtml(nl2br($agreement->getContent())) ?> <?php endif; ?> </div> - <?php if($agreement->getMode() == \Magento\CheckoutAgreements\Model\AgreementModeOptions::MODE_MANUAL): ?> - <div id="checkout-agreements-form-<?= /* @escapeNotVerified */ $agreement->getAgreementId() ?>" class="field choice agree required"> + <?php if ($agreement->getMode() == \Magento\CheckoutAgreements\Model\AgreementModeOptions::MODE_MANUAL) :?> + <div id="checkout-agreements-form-<?= (int) $agreement->getAgreementId() ?>" class="field choice agree required"> <input type="checkbox" - id="agreement-<?= /* @escapeNotVerified */ $agreement->getAgreementId() ?>" - name="agreement[<?= /* @escapeNotVerified */ $agreement->getAgreementId() ?>]" + id="agreement-<?= (int) $agreement->getAgreementId() ?>" + name="agreement[<?= (int) $agreement->getAgreementId() ?>]" value="1" title="<?= $block->escapeHtml($agreement->getCheckboxText()) ?>" class="checkbox" data-validate="{required:true}"/> - <label class="label" for="agreement-<?= /* @escapeNotVerified */ $agreement->getAgreementId() ?>"> - <span><?= $agreement->getIsHtml() ? $agreement->getCheckboxText() : $block->escapeHtml($agreement->getCheckboxText()) ?></span> + <label class="label" for="agreement-<?= (int) $agreement->getAgreementId() ?>"> + <span> + <?php if ($agreement->getIsHtml()) :?> + <?= /* @noEscape */ $agreement->getCheckboxText() ?> + <?php else :?> + <?= $block->escapeHtml($agreement->getCheckboxText()) ?> + <?php endif; ?> + </span> </label> </div> - <?php elseif($agreement->getMode() == \Magento\CheckoutAgreements\Model\AgreementModeOptions::MODE_AUTO): ?> - <div id="checkout-agreements-form-<?= /* @escapeNotVerified */ $agreement->getAgreementId() ?>" class="field choice agree"> - <span><?= $agreement->getIsHtml() ? $agreement->getCheckboxText() : $block->escapeHtml($agreement->getCheckboxText()) ?></span> + <?php elseif ($agreement->getMode() == \Magento\CheckoutAgreements\Model\AgreementModeOptions::MODE_AUTO) :?> + <div id="checkout-agreements-form-<?= (int) $agreement->getAgreementId() ?>" class="field choice agree"> + <span> + <?php if ($agreement->getIsHtml()) :?> + <?= /* @noEscape */ $agreement->getCheckboxText() ?> + <?php else :?> + <?= $block->escapeHtml($agreement->getCheckboxText()) ?> + <?php endif; ?> + </span> </div> <?php endif; ?> </li> diff --git a/app/code/Magento/CheckoutAgreementsGraphQl/Model/Resolver/CheckoutAgreements.php b/app/code/Magento/CheckoutAgreementsGraphQl/Model/Resolver/CheckoutAgreements.php new file mode 100644 index 0000000000000..3daf88226d8e5 --- /dev/null +++ b/app/code/Magento/CheckoutAgreementsGraphQl/Model/Resolver/CheckoutAgreements.php @@ -0,0 +1,91 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\CheckoutAgreementsGraphQl\Model\Resolver; + +use Magento\CheckoutAgreements\Model\AgreementModeOptions; +use Magento\CheckoutAgreements\Model\ResourceModel\Agreement\Collection; +use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Query\ResolverInterface; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +use Magento\CheckoutAgreements\Api\Data\AgreementInterface; +use Magento\CheckoutAgreements\Model\ResourceModel\Agreement\CollectionFactory; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Store\Model\ScopeInterface; +use Magento\Store\Model\StoreManagerInterface; + +/** + * Checkout Agreements resolver, used for GraphQL request processing + */ +class CheckoutAgreements implements ResolverInterface +{ + /** + * @var CollectionFactory + */ + private $agreementCollectionFactory; + + /** + * @var StoreManagerInterface + */ + private $storeManager; + + /** + * @var ScopeConfigInterface + */ + private $scopeConfig; + + /** + * @param CollectionFactory $agreementCollectionFactory + * @param StoreManagerInterface $storeManager + * @param ScopeConfigInterface $scopeConfig + */ + public function __construct( + CollectionFactory $agreementCollectionFactory, + StoreManagerInterface $storeManager, + ScopeConfigInterface $scopeConfig + ) { + $this->agreementCollectionFactory = $agreementCollectionFactory; + $this->storeManager = $storeManager; + $this->scopeConfig = $scopeConfig; + } + + /** + * @inheritdoc + */ + public function resolve( + Field $field, + $context, + ResolveInfo $info, + array $value = null, + array $args = null + ) { + if (!$this->scopeConfig->isSetFlag('checkout/options/enable_agreements', ScopeInterface::SCOPE_STORE)) { + return []; + } + + /** @var Collection $agreementsCollection */ + $agreementsCollection = $this->agreementCollectionFactory->create(); + $agreementsCollection->addStoreFilter($this->storeManager->getStore()->getId()); + $agreementsCollection->addFieldToFilter(AgreementInterface::IS_ACTIVE, 1); + + $checkoutAgreementData = []; + /** @var AgreementInterface $checkoutAgreement */ + foreach ($agreementsCollection->getItems() as $checkoutAgreement) { + $checkoutAgreementData[] = [ + AgreementInterface::AGREEMENT_ID => $checkoutAgreement->getAgreementId(), + AgreementInterface::CONTENT => $checkoutAgreement->getContent(), + AgreementInterface::NAME => $checkoutAgreement->getName(), + AgreementInterface::CONTENT_HEIGHT => $checkoutAgreement->getContentHeight(), + AgreementInterface::CHECKBOX_TEXT => $checkoutAgreement->getCheckboxText(), + AgreementInterface::IS_HTML => $checkoutAgreement->getIsHtml(), + AgreementInterface::MODE => + AgreementModeOptions::MODE_AUTO === (int)$checkoutAgreement->getMode() ? 'AUTO' : 'MANUAL', + ]; + } + return $checkoutAgreementData; + } +} diff --git a/app/code/Magento/CheckoutAgreementsGraphQl/README.md b/app/code/Magento/CheckoutAgreementsGraphQl/README.md new file mode 100644 index 0000000000000..3ef735e3937f5 --- /dev/null +++ b/app/code/Magento/CheckoutAgreementsGraphQl/README.md @@ -0,0 +1,4 @@ +# CheckoutAgreementsGraphQl + +**CheckoutAgreementsGraphQl** provides type information for the GraphQl module +to generate Checkout Agreements fields for Checkout Agreements information endpoints. diff --git a/app/code/Magento/CheckoutAgreementsGraphQl/composer.json b/app/code/Magento/CheckoutAgreementsGraphQl/composer.json new file mode 100644 index 0000000000000..5972d48b35ea9 --- /dev/null +++ b/app/code/Magento/CheckoutAgreementsGraphQl/composer.json @@ -0,0 +1,26 @@ +{ + "name": "magento/module-checkout-agreements-graph-ql", + "description": "N/A", + "type": "magento2-module", + "require": { + "php": "~7.1.3||~7.2.0", + "magento/framework": "*", + "magento/module-store": "*", + "magento/module-checkout-agreements": "*" + }, + "suggest": { + "magento/module-graph-ql": "*" + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "autoload": { + "files": [ + "registration.php" + ], + "psr-4": { + "Magento\\CheckoutAgreementsGraphQl\\": "" + } + } +} diff --git a/app/code/Magento/CheckoutAgreementsGraphQl/etc/module.xml b/app/code/Magento/CheckoutAgreementsGraphQl/etc/module.xml new file mode 100644 index 0000000000000..d18e8ee17d097 --- /dev/null +++ b/app/code/Magento/CheckoutAgreementsGraphQl/etc/module.xml @@ -0,0 +1,10 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> + <module name="Magento_CheckoutAgreementsGraphQl" /> +</config> diff --git a/app/code/Magento/CheckoutAgreementsGraphQl/etc/schema.graphqls b/app/code/Magento/CheckoutAgreementsGraphQl/etc/schema.graphqls new file mode 100644 index 0000000000000..64ef9411dfca6 --- /dev/null +++ b/app/code/Magento/CheckoutAgreementsGraphQl/etc/schema.graphqls @@ -0,0 +1,21 @@ +# Copyright © Magento, Inc. All rights reserved. +# See COPYING.txt for license details. + +type Query { + checkoutAgreements: [CheckoutAgreement] @resolver(class: "Magento\\CheckoutAgreementsGraphQl\\Model\\Resolver\\CheckoutAgreements") @doc(description: "The Checkout Agreements information") +} + +type CheckoutAgreement @doc(description: "Defines all Checkout Agreement information") { + agreement_id: Int! @doc(description: "Checkout Agreement identifier") + name: String! @doc(description: "Checkout Agreement name") + content: String! @doc(description: "Checkout Agreement content") + content_height: String @doc(description: "Checkout Agreement content height") + checkbox_text: String! @doc(description: "Checkout Agreement checkbox text") + is_html: Boolean! @doc(description: "Is Checkout Agreement content in HTML format") + mode: CheckoutAgreementMode! +} + +enum CheckoutAgreementMode { + AUTO + MANUAL +} diff --git a/app/code/Magento/CheckoutAgreementsGraphQl/registration.php b/app/code/Magento/CheckoutAgreementsGraphQl/registration.php new file mode 100644 index 0000000000000..b0b4839f33d1f --- /dev/null +++ b/app/code/Magento/CheckoutAgreementsGraphQl/registration.php @@ -0,0 +1,10 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Framework\Component\ComponentRegistrar; + +ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_CheckoutAgreementsGraphQl', __DIR__); diff --git a/app/code/Magento/Cms/Controller/Adminhtml/Page/Index.php b/app/code/Magento/Cms/Controller/Adminhtml/Page/Index.php index 04557ddaeec78..d0ee1453eda10 100644 --- a/app/code/Magento/Cms/Controller/Adminhtml/Page/Index.php +++ b/app/code/Magento/Cms/Controller/Adminhtml/Page/Index.php @@ -7,6 +7,8 @@ use Magento\Framework\App\Action\HttpGetActionInterface; use Magento\Backend\App\Action\Context; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\App\Request\DataPersistorInterface; use Magento\Framework\View\Result\PageFactory; /** @@ -26,16 +28,24 @@ class Index extends \Magento\Backend\App\Action implements HttpGetActionInterfac */ protected $resultPageFactory; + /** + * @var DataPersistorInterface + */ + private $dataPersistor; + /** * @param Context $context * @param PageFactory $resultPageFactory + * @param DataPersistorInterface $dataPersistor */ public function __construct( Context $context, - PageFactory $resultPageFactory + PageFactory $resultPageFactory, + DataPersistorInterface $dataPersistor = null ) { parent::__construct($context); $this->resultPageFactory = $resultPageFactory; + $this->dataPersistor = $dataPersistor ?: ObjectManager::getInstance()->get(DataPersistorInterface::class); } /** @@ -52,8 +62,7 @@ public function execute() $resultPage->addBreadcrumb(__('Manage Pages'), __('Manage Pages')); $resultPage->getConfig()->getTitle()->prepend(__('Pages')); - $dataPersistor = $this->_objectManager->get(\Magento\Framework\App\Request\DataPersistorInterface::class); - $dataPersistor->clear('cms_page'); + $this->dataPersistor->clear('cms_page'); return $resultPage; } diff --git a/app/code/Magento/Cms/Model/Block.php b/app/code/Magento/Cms/Model/Block.php index e65675ceee9ec..0261ef46a4942 100644 --- a/app/code/Magento/Cms/Model/Block.php +++ b/app/code/Magento/Cms/Model/Block.php @@ -12,8 +12,8 @@ /** * CMS block model * - * @method Block setStoreId(array $storeId) - * @method array getStoreId() + * @method Block setStoreId(int $storeId) + * @method int getStoreId() */ class Block extends AbstractModel implements BlockInterface, IdentityInterface { @@ -41,6 +41,8 @@ class Block extends AbstractModel implements BlockInterface, IdentityInterface protected $_eventPrefix = 'cms_block'; /** + * Construct. + * * @return void */ protected function _construct() diff --git a/app/code/Magento/Cms/Model/Page.php b/app/code/Magento/Cms/Model/Page.php index d950f484cd1d9..8eefe26236ba5 100644 --- a/app/code/Magento/Cms/Model/Page.php +++ b/app/code/Magento/Cms/Model/Page.php @@ -16,8 +16,8 @@ * Cms Page Model * * @api - * @method Page setStoreId(array $storeId) - * @method array getStoreId() + * @method Page setStoreId(int $storeId) + * @method int getStoreId() * @SuppressWarnings(PHPMD.ExcessivePublicCount) * @since 100.0.2 */ @@ -103,8 +103,7 @@ public function getStores() } /** - * Check if page identifier exist for specific store - * return page id if page exists + * Check if page identifier exist for specific store return page id if page exists * * @param string $identifier * @param int $storeId @@ -116,8 +115,7 @@ public function checkIdentifier($identifier, $storeId) } /** - * Prepare page's statuses. - * Available event cms_page_get_available_statuses to customize statuses. + * Prepare page's statuses, available event cms_page_get_available_statuses to customize statuses. * * @return array */ @@ -538,7 +536,7 @@ public function setIsActive($isActive) } /** - * {@inheritdoc} + * @inheritdoc * @since 101.0.0 */ public function beforeSave() @@ -571,6 +569,8 @@ public function beforeSave() } /** + * Returns scope config. + * * @return ScopeConfigInterface */ private function getScopeConfig() diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminCMSPageMassActionSelectActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminCMSPageMassActionSelectActionGroup.xml new file mode 100644 index 0000000000000..2945538ed5f9f --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminCMSPageMassActionSelectActionGroup.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminCMSPageMassActionSelectActionGroup"> + <arguments> + <argument name="action" type="string" /> + </arguments> + <click selector="{{CmsPagesPageActionsSection.massActionsButton}}" stepKey="clickMassActionDropdown"/> + <click selector="{{CmsPagesPageActionsSection.massActionsOption(action)}}" stepKey="clickAction"/> + <waitForPageLoad stepKey="waitForPageToReload"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpenCMSPagesGridActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpenCMSPagesGridActionGroup.xml new file mode 100644 index 0000000000000..2439953cde0ec --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminOpenCMSPagesGridActionGroup.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminOpenCMSPagesGridActionGroup"> + <amOnPage url="{{CmsPagesPage.url}}" stepKey="navigateToCMSPagesGrid"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminSelectCMSPageInGridActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminSelectCMSPageInGridActionGroup.xml new file mode 100644 index 0000000000000..4de1157e3180b --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AdminSelectCMSPageInGridActionGroup.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminSelectCMSPageInGridActionGroup"> + <arguments> + <argument name="identifier" type="string"/> + </arguments> + <checkOption selector="{{CmsPagesPageActionsSection.pageRowCheckboxByIdentifier(identifier)}}" stepKey="selectCmsPageInGrid"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertCMSPageInGridActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertCMSPageInGridActionGroup.xml new file mode 100644 index 0000000000000..84feb0a16c4ef --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertCMSPageInGridActionGroup.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertCMSPageInGridActionGroup"> + <arguments> + <argument name="cmsPage" type="entity" /> + </arguments> + + <seeElement stepKey="seeElementByCmsPageIdentifier" selector="{{AdminDataGridTableSection.rowTemplateStrict(cmsPage.identifier)}}" /> + <see userInput="{{cmsPage.title}}" stepKey="seeCmsPageTitle" selector="{{AdminDataGridTableSection.rowTemplateStrict(cmsPage.identifier)}}" /> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertCMSPageNotFoundOnStorefrontActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertCMSPageNotFoundOnStorefrontActionGroup.xml new file mode 100644 index 0000000000000..b92413a379454 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/AssertCMSPageNotFoundOnStorefrontActionGroup.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AssertCMSPageNotFoundOnStorefrontActionGroup"> + <see userInput="Whoops, our bad..." stepKey="seePageErrorNotFound"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Cms/Test/Mftf/ActionGroup/StorefrontGoToCMSPageActionGroup.xml b/app/code/Magento/Cms/Test/Mftf/ActionGroup/StorefrontGoToCMSPageActionGroup.xml new file mode 100644 index 0000000000000..0ec8db3f27c9c --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/ActionGroup/StorefrontGoToCMSPageActionGroup.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontGoToCMSPageActionGroup"> + <arguments> + <argument name="identifier" type="string"/> + </arguments> + <amOnPage url="{{StorefrontHomePage.url}}{{identifier}}" stepKey="amOnCmsPageOnStorefront"/> + <waitForPageLoad stepKey="waitForPageLoadOnStorefront"/> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Cms/Test/Mftf/Data/WysiwygConfigData.xml b/app/code/Magento/Cms/Test/Mftf/Data/WysiwygConfigData.xml new file mode 100644 index 0000000000000..46a968959407f --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/Data/WysiwygConfigData.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="WysiwygEnabledByDefault"> + <data key="path">cms/wysiwyg/enabled</data> + <data key="scope_id">0</data> + <data key="value">enabled</data> + </entity> + <entity name="WysiwygDisabledByDefault"> + <data key="path">cms/wysiwyg/enabled</data> + <data key="scope_id">0</data> + <data key="value">hidden</data> + </entity> + <entity name="WysiwygTinyMCE3Enable"> + <data key="path">cms/wysiwyg/editor</data> + <data key="scope_id">0</data> + <data key="value">Magento_Tinymce3/tinymce3Adapter</data> + </entity> + <entity name="WysiwygTinyMCE4Enable"> + <data key="path">cms/wysiwyg/editor</data> + <data key="scope_id">0</data> + <data key="value">mage/adminhtml/wysiwyg/tiny_mce/tinymce4Adapter</data> + </entity> +</entities> diff --git a/app/code/Magento/Cms/Test/Mftf/Section/CmsPagesPageActionsSection.xml b/app/code/Magento/Cms/Test/Mftf/Section/CmsPagesPageActionsSection.xml index 11d8bb23313fb..494c98ca44e7f 100644 --- a/app/code/Magento/Cms/Test/Mftf/Section/CmsPagesPageActionsSection.xml +++ b/app/code/Magento/Cms/Test/Mftf/Section/CmsPagesPageActionsSection.xml @@ -27,5 +27,8 @@ <element name="savePageSuccessMessage" type="text" selector=".message-success"/> <element name="delete" type="button" selector="//div[text()='{{var1}}']/parent::td//following-sibling::td[@class='data-grid-actions-cell']//a[text()='Delete']" parameterized="true"/> <element name="deleteConfirm" type="button" selector=".action-primary.action-accept" timeout="60"/> + <element name="pageRowCheckboxByIdentifier" type="checkbox" selector="//table[@data-role='grid']//td[count(../../..//th[./*[.='URL Key']]/preceding-sibling::th) + 1][./*[.='{{identifier}}']]/../td//input[@data-action='select-row']" parameterized="true" /> + <element name="massActionsButton" type="button" selector="//div[@class='admin__data-grid-header'][(not(ancestor::*[@class='sticky-header']) and not(contains(@style,'visibility: hidden'))) or (ancestor::*[@class='sticky-header' and not(contains(@style,'display: none'))])]//button[contains(@class, 'action-select')]" /> + <element name="massActionsOption" type="button" selector="//div[@class='admin__data-grid-header'][(not(ancestor::*[@class='sticky-header']) and not(contains(@style,'visibility: hidden'))) or (ancestor::*[@class='sticky-header' and not(contains(@style,'display: none'))])]//span[contains(@class, 'action-menu-item') and .= '{{action}}']" parameterized="true"/> </section> </sections> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddImageToWYSIWYGBlockTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddImageToWYSIWYGBlockTest.xml index 03edc69e6d625..5baf75d43c53f 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddImageToWYSIWYGBlockTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddImageToWYSIWYGBlockTest.xml @@ -16,6 +16,9 @@ <description value="Admin should be able to add image to WYSIWYG content of Block"/> <severity value="CRITICAL"/> <testCaseId value="MAGETWO-84376"/> + <skip> + <issueId value="MC-17232"/> + </skip> </annotations> <before> <createData entity="_defaultCmsPage" stepKey="createCMSPage" /> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddImageToWYSIWYGCMSTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddImageToWYSIWYGCMSTest.xml index 205850f888797..e63a6be51bcc0 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddImageToWYSIWYGCMSTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddImageToWYSIWYGCMSTest.xml @@ -16,6 +16,9 @@ <description value="Admin should be able to add image to WYSIWYG content of CMS Page"/> <severity value="CRITICAL"/> <testCaseId value="MAGETWO-85825"/> + <skip> + <issueId value="MC-17232"/> + </skip> </annotations> <before> <createData entity="_defaultCmsPage" stepKey="createCMSPage" /> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddVariableToWYSIWYGBlockTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddVariableToWYSIWYGBlockTest.xml index 8fea72764f280..ce34a8d09c302 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddVariableToWYSIWYGBlockTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddVariableToWYSIWYGBlockTest.xml @@ -49,9 +49,9 @@ <waitForLoadingMaskToDisappear stepKey="waitForPageLoad3"/> <!--see Insert Variable button disabled--> <see selector="{{VariableSection.InsertVariableBtnDisabled}}" userInput="Insert Variable" stepKey="seeInsertWidgetDisabled" /> - <!--see Cancel button enabed--> + <!--see Cancel button enabled--> <see selector="{{VariableSection.CancelBtnEnabled}}" userInput="Cancel" stepKey="seeCancelBtnEnabled" /> - <!--see 4 colums--> + <!--see 4 columns--> <see selector="{{VariableSection.ColName('Select')}}" userInput="Select" stepKey="selectCol" /> <see selector="{{VariableSection.ColName('Variable Name')}}" userInput="Variable Name" stepKey="variableCol" /> <see selector="{{VariableSection.ColName('Type')}}" userInput="Type" stepKey="typeCol" /> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddVariableToWYSIWYGCMSTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddVariableToWYSIWYGCMSTest.xml index 9e5eb2558d6f2..3b501859e606e 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddVariableToWYSIWYGCMSTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddVariableToWYSIWYGCMSTest.xml @@ -43,9 +43,9 @@ <waitForText userInput="Insert Variable" stepKey="waitForSlideOutOpen"/> <!--see Insert Variable button disabled--> <see selector="{{VariableSection.InsertVariableBtnDisabled}}" userInput="Insert Variable" stepKey="seeInsertWidgetDisabled" /> - <!--see Cancel button enabed--> + <!--see Cancel button enabled--> <see selector="{{VariableSection.CancelBtnEnabled}}" userInput="Cancel" stepKey="seeCancelBtnEnabled" /> - <!--see 4 colums--> + <!--see 4 columns--> <see selector="{{VariableSection.ColName('Select')}}" userInput="Select" stepKey="selectCol" /> <see selector="{{VariableSection.ColName('Variable Name')}}" userInput="Variable Name" stepKey="variableCol" /> <see selector="{{VariableSection.ColName('Type')}}" userInput="Type" stepKey="typeCol" /> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogCategoryLinkTypeTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogCategoryLinkTypeTest.xml index 393e25e474f12..552eae407391d 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogCategoryLinkTypeTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogCategoryLinkTypeTest.xml @@ -37,7 +37,7 @@ <see userInput="Inserting a widget does not create a widget instance." stepKey="seeMessage" /> <!--see Insert Widget button disabled--> <see selector="{{WidgetSection.InsertWidgetBtnDisabled}}" userInput="Insert Widget" stepKey="seeInsertWidgetDisabled" /> - <!--see Cancel button enabed--> + <!--see Cancel button enabled--> <see selector="{{WidgetSection.CancelBtnEnabled}}" userInput="Cancel" stepKey="seeCancelBtnEnabled" /> <!--Select "Widget Type"--> <selectOption selector="{{WidgetSection.WidgetType}}" userInput="Catalog Category Link" stepKey="selectCatalogCategoryLink" /> @@ -64,9 +64,19 @@ <see userInput="Hello CMS Page!" stepKey="seeContent"/> <!--see widget on Storefront--> <see userInput="$$createPreReqCategory.name$$" stepKey="seeCategoryLink"/> + <magentoCLI command="config:set catalog/seo/generate_category_product_rewrites 0" stepKey="disableGenerateUrlRewrite"/> + <amOnPage url="{{_defaultCmsPage.identifier}}" stepKey="amOnPageTestPage2"/> + <waitForPageLoad stepKey="wait6" /> + <see userInput="Hello CMS Page!" stepKey="seeContent2"/> + <!--see widget on Storefront--> + <grabAttributeFrom selector=".widget a" userInput="href" stepKey="dataHref" /> + <assertRegExp expected="|$$createPreReqCategory.name$$.html|i" + expectedType="string" actual="$dataHref" actualType="variable" + stepKey="seeProductLinkInCategory"/> <after> <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCatalog" /> <actionGroup ref="DisabledWYSIWYG" stepKey="disableWYSIWYG"/> + <magentoCLI command="config:set catalog/seo/generate_category_product_rewrites 1" stepKey="enableGenerateUrlRewrite"/> <actionGroup ref="logout" stepKey="logout"/> </after> </test> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogProductLinkTypeTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogProductLinkTypeTest.xml index 9ee9d27de477a..d75d422afa2a4 100644 --- a/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogProductLinkTypeTest.xml +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminAddWidgetToWYSIWYGWithCatalogProductLinkTypeTest.xml @@ -41,7 +41,7 @@ <waitForPageLoad stepKey="wait3"/> <!--see Insert Widget button disabled--> <see selector="{{WidgetSection.InsertWidgetBtnDisabled}}" userInput="Insert Widget" stepKey="seeInsertWidgetDisabled" /> - <!--see Cancel button enabed--> + <!--see Cancel button enabled--> <see selector="{{WidgetSection.CancelBtnEnabled}}" userInput="Cancel" stepKey="seeCancelBtnEnabled" /> <!--Select "Widget Type"--> <selectOption selector="{{WidgetSection.WidgetType}}" userInput="Catalog Product Link" stepKey="selectCatalogProductLink" /> @@ -71,10 +71,19 @@ <!--see widget on Storefront--> <see userInput="Hello CMS Page!" stepKey="seeContent"/> <see userInput="$$createPreReqProduct.name$$" stepKey="seeProductLink"/> + <magentoCLI command="config:set catalog/seo/generate_category_product_rewrites 0" stepKey="disableGenerateUrlRewrite"/> + <amOnPage url="{{_defaultCmsPage.identifier}}" stepKey="amOnPageTestPage2"/> + <waitForPageLoad stepKey="wait8" /> + <!--see widget on Storefront--> + <grabAttributeFrom selector=".widget a" userInput="href" stepKey="dataHref" /> + <assertRegExp expected="|$$createPreReqCategory.name$$/$$createPreReqProduct.name$$.html|i" + expectedType="string" actual="$dataHref" actualType="variable" + stepKey="seeProductLinkInCategory"/> <after> <deleteData createDataKey="createPreReqCategory" stepKey="deletePreReqCatalog" /> <deleteData createDataKey="createPreReqProduct" stepKey="deletePreReqProduct" /> <actionGroup ref="DisabledWYSIWYG" stepKey="disableWYSIWYG"/> + <magentoCLI command="config:set catalog/seo/generate_category_product_rewrites 1" stepKey="enableGenerateUrlRewrite"/> <actionGroup ref="logout" stepKey="logout"/> </after> </test> diff --git a/app/code/Magento/Cms/Test/Mftf/Test/AdminCmsPageMassActionTest.xml b/app/code/Magento/Cms/Test/Mftf/Test/AdminCmsPageMassActionTest.xml new file mode 100644 index 0000000000000..7cc0719dcbeb2 --- /dev/null +++ b/app/code/Magento/Cms/Test/Mftf/Test/AdminCmsPageMassActionTest.xml @@ -0,0 +1,85 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCmsPageMassActionTest"> + <annotations> + <features value="CmsPage"/> + <title value="Create two CMS Pages and perform mass disable action"/> + <description value="Admin should be able to perform mass actions to CMS pages"/> + <stories value="Admin Grid Mass Action" /> + <testCaseId value="MC-14659" /> + <severity value="CRITICAL"/> + <group value="backend"/> + <group value="CMSContent"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <createData entity="_defaultCmsPage" stepKey="firstCMSPage" /> + <createData entity="_duplicatedCMSPage" stepKey="secondCMSPage" /> + </before> + <after> + <deleteData createDataKey="firstCMSPage" stepKey="deleteFirstCMSPage" /> + <deleteData createDataKey="secondCMSPage" stepKey="deleteSecondCMSPage" /> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!--Go to Grid page--> + <actionGroup ref="AdminOpenCMSPagesGridActionGroup" stepKey="navigateToCMSPageGrid"/> + <actionGroup ref="AdminGridFilterResetActionGroup" stepKey="clearPossibleGridFilters"/> + + <!--Select pages in Grid--> + <actionGroup ref="AdminSelectCMSPageInGridActionGroup" stepKey="selectFirstCMSPage"> + <argument name="identifier" value="$$firstCMSPage.identifier$$"/> + </actionGroup> + <actionGroup ref="AdminSelectCMSPageInGridActionGroup" stepKey="selectSecondCMSPage"> + <argument name="identifier" value="$$secondCMSPage.identifier$$"/> + </actionGroup> + + <!-- Disable Pages--> + <actionGroup ref="AdminCMSPageMassActionSelectActionGroup" stepKey="disablePages"> + <argument name="action" value="Disable" /> + </actionGroup> + + <actionGroup ref="AssertMessageInAdminPanelActionGroup" stepKey="assertSuccessMessage"> + <argument name="message" value="A total of 2 record(s) have been disabled." /> + </actionGroup> + + <!--Verify pages in Grid--> + <actionGroup ref="AdminGridFilterResetActionGroup" stepKey="clearGridFilters"/> + <actionGroup ref="AdminGridFilterFillInputFieldActionGroup" stepKey="filterGridByFirstCmsPageIdentifier"> + <argument name="filterInputName" value="identifier" /> + <argument name="filterValue" value="$$firstCMSPage.identifier$$" /> + </actionGroup> + <actionGroup ref="AdminGridFilterApplyActionGroup" stepKey="applyFirstGridFilters"/> + <actionGroup ref="AssertCMSPageInGridActionGroup" stepKey="assertFirstCmsPageInGrid"> + <argument name="cmsPage" value="$$firstCMSPage$$" /> + </actionGroup> + + <actionGroup ref="AdminGridFilterFillInputFieldActionGroup" stepKey="filterGridBySecondCmsPageIdentifier"> + <argument name="filterInputName" value="identifier" /> + <argument name="filterValue" value="$$secondCMSPage.identifier$$" /> + </actionGroup> + <actionGroup ref="AdminGridFilterApplyActionGroup" stepKey="applySecondGridFilters"/> + <actionGroup ref="AssertCMSPageInGridActionGroup" stepKey="assertSecondCmsPageInGrid"> + <argument name="cmsPage" value="$$secondCMSPage$$" /> + </actionGroup> + <actionGroup ref="AdminGridFilterResetActionGroup" stepKey="clearGridFiltersToIsolateTest"/> + + <!--Verify pages are disabled on Storefront--> + <actionGroup ref="StorefrontGoToCMSPageActionGroup" stepKey="goToFirstCMSPageOnStorefront"> + <argument name="identifier" value="$$firstCMSPage.identifier$$"/> + </actionGroup> + <actionGroup ref="AssertCMSPageNotFoundOnStorefrontActionGroup" stepKey="seeNotFoundErrorForFirstPage"/> + <actionGroup ref="StorefrontGoToCMSPageActionGroup" stepKey="goToSecondCMSPageOnStorefront"> + <argument name="identifier" value="$$secondCMSPage.identifier$$"/> + </actionGroup> + <actionGroup ref="AssertCMSPageNotFoundOnStorefrontActionGroup" stepKey="seeNotFoundErrorForSecondPage"/> + </test> +</tests> diff --git a/app/code/Magento/Cms/view/adminhtml/ui_component/cms_page_form.xml b/app/code/Magento/Cms/view/adminhtml/ui_component/cms_page_form.xml index 9781675a6d917..9fd8b3efb3ea8 100644 --- a/app/code/Magento/Cms/view/adminhtml/ui_component/cms_page_form.xml +++ b/app/code/Magento/Cms/view/adminhtml/ui_component/cms_page_form.xml @@ -285,6 +285,7 @@ <settings> <validation> <rule name="validate-date" xsi:type="boolean">true</rule> + <rule name="validate-date-range" xsi:type="string">custom_theme_from</rule> </validation> <dataType>text</dataType> <label translate="true">To</label> diff --git a/app/code/Magento/CmsGraphQl/Model/Resolver/DataProvider/Page.php b/app/code/Magento/CmsGraphQl/Model/Resolver/DataProvider/Page.php index 8e1e770b01e9d..40825e70a994e 100644 --- a/app/code/Magento/CmsGraphQl/Model/Resolver/DataProvider/Page.php +++ b/app/code/Magento/CmsGraphQl/Model/Resolver/DataProvider/Page.php @@ -8,8 +8,10 @@ namespace Magento\CmsGraphQl\Model\Resolver\DataProvider; use Magento\Cms\Api\Data\PageInterface; +use Magento\Cms\Api\GetPageByIdentifierInterface; use Magento\Cms\Api\PageRepositoryInterface; use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Store\Model\StoreManagerInterface; use Magento\Widget\Model\Template\FilterEmulate; /** @@ -18,38 +20,82 @@ class Page { /** - * @var FilterEmulate + * @var GetPageByIdentifierInterface */ - private $widgetFilter; + private $pageByIdentifier; /** * @var PageRepositoryInterface */ private $pageRepository; + /** + * @var StoreManagerInterface + */ + private $storeManager; + + /** + * @var FilterEmulate + */ + private $widgetFilter; + /** * @param PageRepositoryInterface $pageRepository * @param FilterEmulate $widgetFilter + * @param GetPageByIdentifierInterface $getPageByIdentifier + * @param StoreManagerInterface $storeManager */ public function __construct( PageRepositoryInterface $pageRepository, - FilterEmulate $widgetFilter + FilterEmulate $widgetFilter, + GetPageByIdentifierInterface $getPageByIdentifier, + StoreManagerInterface $storeManager ) { + $this->pageRepository = $pageRepository; $this->widgetFilter = $widgetFilter; + $this->pageByIdentifier = $getPageByIdentifier; + $this->storeManager = $storeManager; } /** - * Get the page data + * Returns page data by page_id * * @param int $pageId * @return array * @throws NoSuchEntityException */ - public function getData(int $pageId): array + public function getDataByPageId(int $pageId): array { $page = $this->pageRepository->getById($pageId); + return $this->convertPageData($page); + } + + /** + * Returns page data by page identifier + * + * @param string $pageIdentifier + * @return array + * @throws NoSuchEntityException + */ + public function getDataByPageIdentifier(string $pageIdentifier): array + { + $storeId = (int)$this->storeManager->getStore()->getId(); + $page = $this->pageByIdentifier->execute($pageIdentifier, $storeId); + + return $this->convertPageData($page); + } + + /** + * Convert page data + * + * @param PageInterface $page + * @return array + * @throws NoSuchEntityException + */ + private function convertPageData(PageInterface $page) + { if (false === $page->isActive()) { throw new NoSuchEntityException(); } @@ -57,7 +103,6 @@ public function getData(int $pageId): array $renderedContent = $this->widgetFilter->filter($page->getContent()); $pageData = [ - PageInterface::PAGE_ID => $page->getId(), 'url_key' => $page->getIdentifier(), PageInterface::TITLE => $page->getTitle(), PageInterface::CONTENT => $renderedContent, @@ -66,6 +111,8 @@ public function getData(int $pageId): array PageInterface::META_TITLE => $page->getMetaTitle(), PageInterface::META_DESCRIPTION => $page->getMetaDescription(), PageInterface::META_KEYWORDS => $page->getMetaKeywords(), + PageInterface::PAGE_ID => $page->getId(), + PageInterface::IDENTIFIER => $page->getIdentifier(), ]; return $pageData; } diff --git a/app/code/Magento/CmsGraphQl/Model/Resolver/Page.php b/app/code/Magento/CmsGraphQl/Model/Resolver/Page.php index 1077ab81551c2..64891cfeaa87a 100644 --- a/app/code/Magento/CmsGraphQl/Model/Resolver/Page.php +++ b/app/code/Magento/CmsGraphQl/Model/Resolver/Page.php @@ -26,6 +26,7 @@ class Page implements ResolverInterface private $pageDataProvider; /** + * * @param PageDataProvider $pageDataProvider */ public function __construct( @@ -44,35 +45,18 @@ public function resolve( array $value = null, array $args = null ) { - $pageId = $this->getPageId($args); - $pageData = $this->getPageData($pageId); - - return $pageData; - } - - /** - * @param array $args - * @return int - * @throws GraphQlInputException - */ - private function getPageId(array $args): int - { - if (!isset($args['id'])) { - throw new GraphQlInputException(__('"Page id should be specified')); + if (!isset($args['id']) && !isset($args['identifier'])) { + throw new GraphQlInputException(__('"Page id/identifier should be specified')); } - return (int)$args['id']; - } + $pageData = []; - /** - * @param int $pageId - * @return array - * @throws GraphQlNoSuchEntityException - */ - private function getPageData(int $pageId): array - { try { - $pageData = $this->pageDataProvider->getData($pageId); + if (isset($args['id'])) { + $pageData = $this->pageDataProvider->getDataByPageId((int)$args['id']); + } elseif (isset($args['identifier'])) { + $pageData = $this->pageDataProvider->getDataByPageIdentifier((string)$args['identifier']); + } } catch (NoSuchEntityException $e) { throw new GraphQlNoSuchEntityException(__($e->getMessage()), $e); } diff --git a/app/code/Magento/CmsGraphQl/composer.json b/app/code/Magento/CmsGraphQl/composer.json index 18a6f1aa95e37..e0e8481d59b7b 100644 --- a/app/code/Magento/CmsGraphQl/composer.json +++ b/app/code/Magento/CmsGraphQl/composer.json @@ -6,6 +6,7 @@ "php": "~7.1.3||~7.2.0", "magento/framework": "*", "magento/module-cms": "*", + "magento/module-store": "*", "magento/module-widget": "*" }, "suggest": { diff --git a/app/code/Magento/CmsGraphQl/etc/schema.graphqls b/app/code/Magento/CmsGraphQl/etc/schema.graphqls index 04d2efa4c7cde..3558d853aa4df 100644 --- a/app/code/Magento/CmsGraphQl/etc/schema.graphqls +++ b/app/code/Magento/CmsGraphQl/etc/schema.graphqls @@ -12,7 +12,8 @@ type StoreConfig @doc(description: "The type contains information about a store type Query { cmsPage ( - id: Int @doc(description: "Id of the CMS page") + id: Int @doc(description: "Id of the CMS page") @deprecated(reason: "The `id` is deprecated. Use `identifier` instead.") @doc(description: "The CMS page query returns information about a CMS page") + identifier: String @doc(description: "Identifier of the CMS page") ): CmsPage @resolver(class: "Magento\\CmsGraphQl\\Model\\Resolver\\Page") @doc(description: "The CMS page query returns information about a CMS page") @cache(cacheTag: "cms_p", cacheIdentity: "Magento\\CmsGraphQl\\Model\\Resolver\\Page\\Identity") cmsBlocks ( identifiers: [String] @doc(description: "Identifiers of the CMS blocks") @@ -20,6 +21,7 @@ type Query { } type CmsPage @doc(description: "CMS page defines all CMS page information") { + identifier: String @doc(description: "Identifier of the CMS page") url_key: String @doc(description: "URL key of CMS page") title: String @doc(description: "CMS page title") content: String @doc(description: "CMS page content") diff --git a/app/code/Magento/Config/Console/Command/ConfigSetCommand.php b/app/code/Magento/Config/Console/Command/ConfigSetCommand.php index cb79daddbf5f9..999d8e41af5bc 100644 --- a/app/code/Magento/Config/Console/Command/ConfigSetCommand.php +++ b/app/code/Magento/Config/Console/Command/ConfigSetCommand.php @@ -114,13 +114,13 @@ protected function configure() ), new InputOption( static::OPTION_LOCK_ENV, - 'le', + 'e', InputOption::VALUE_NONE, 'Lock value which prevents modification in the Admin (will be saved in app/etc/env.php)' ), new InputOption( static::OPTION_LOCK_CONFIG, - 'lc', + 'c', InputOption::VALUE_NONE, 'Lock and share value with other installations, prevents modification in the Admin ' . '(will be saved in app/etc/config.php)' @@ -139,8 +139,10 @@ protected function configure() /** * Creates and run appropriate processor, depending on input options. * - * {@inheritdoc} + * @param InputInterface $input + * @param OutputInterface $output * @since 100.2.0 + * @return int|null */ protected function execute(InputInterface $input, OutputInterface $output) { diff --git a/app/code/Magento/Config/Test/Mftf/ActionGroup/GeneralConfigurationActionGroup.xml b/app/code/Magento/Config/Test/Mftf/ActionGroup/GeneralConfigurationActionGroup.xml index f05cf5be3448e..14eca30d0f730 100644 --- a/app/code/Magento/Config/Test/Mftf/ActionGroup/GeneralConfigurationActionGroup.xml +++ b/app/code/Magento/Config/Test/Mftf/ActionGroup/GeneralConfigurationActionGroup.xml @@ -22,7 +22,7 @@ <actionGroup name="SelectTopDestinationsCountry"> <arguments> - <argument name="countries" type="countryArray"/> + <argument name="countries" type="entity"/> </arguments> <selectOption selector="{{CountryOptionsSection.topDestinations}}" parameterArray="[{{countries.country}}]" stepKey="selectTopDestinationsCountry"/> <click selector="#save" stepKey="saveConfig"/> @@ -31,7 +31,7 @@ <actionGroup name="UnSelectTopDestinationsCountry"> <arguments> - <argument name="countries" type="countryArray"/> + <argument name="countries" type="entity"/> </arguments> <unselectOption selector="{{CountryOptionsSection.topDestinations}}" parameterArray="[{{countries.country}}]" stepKey="unSelectTopDestinationsCountry"/> <click selector="#save" stepKey="saveConfig"/> @@ -40,7 +40,7 @@ <actionGroup name="SelectCountriesWithRequiredRegion"> <arguments> - <argument name="countries" type="countryArray"/> + <argument name="countries" type="entity"/> </arguments> <amOnPage url="{{AdminConfigGeneralPage.url}}" stepKey="navigateToAdminConfigGeneralPage"/> <conditionalClick selector="{{StateOptionsSection.stateOptions}}" dependentSelector="{{StateOptionsSection.countriesWithRequiredRegions}}" visible="false" stepKey="expandStateOptionsTab" /> diff --git a/app/code/Magento/Config/Test/Mftf/Section/CatalogSection.xml b/app/code/Magento/Config/Test/Mftf/Section/CatalogSection.xml index e999dbc42a6af..e024d9e5b2e47 100644 --- a/app/code/Magento/Config/Test/Mftf/Section/CatalogSection.xml +++ b/app/code/Magento/Config/Test/Mftf/Section/CatalogSection.xml @@ -20,6 +20,15 @@ <element name="flatCatalogCategoryCheckBox" type="checkbox" selector="#catalog_frontend_flat_catalog_category_inherit"/> <element name="flatCatalogCategory" type="select" selector="#catalog_frontend_flat_catalog_category"/> <element name="flatCatalogProduct" type="select" selector="#catalog_frontend_flat_catalog_product"/> + <element name="seo" type="button" selector="#catalog_seo-head"/> + <element name="CheckIfSeoTabExpand" type="button" selector="#catalog_seo-head:not(.open)"/> + <element name="GenerateUrlRewrites" type="select" selector="#catalog_seo_generate_category_product_rewrites"/> <element name="successMessage" type="text" selector="#messages"/> </section> + <section name="GenerateUrlRewritesConfirm"> + <element name="title" type="text" selector=".modal-popup.confirm h1.modal-title"/> + <element name="message" type="text" selector=".modal-popup.confirm div.modal-content"/> + <element name="cancel" type="button" selector=".modal-popup.confirm button.action-dismiss"/> + <element name="ok" type="button" selector=".modal-popup.confirm button.action-accept" timeout="60"/> + </section> </sections> diff --git a/app/code/Magento/Config/view/adminhtml/templates/system/config/edit.phtml b/app/code/Magento/Config/view/adminhtml/templates/system/config/edit.phtml index a1e26b7805d4b..4ba26c598cfbd 100644 --- a/app/code/Magento/Config/view/adminhtml/templates/system/config/edit.phtml +++ b/app/code/Magento/Config/view/adminhtml/templates/system/config/edit.phtml @@ -32,6 +32,8 @@ require([ "jquery", "uiRegistry", + "Magento_Ui/js/modal/confirm", + "mage/translate", "mage/mage", "prototype", "mage/adminhtml/form", @@ -384,5 +386,6 @@ require([ registry.set('adminSystemConfig', adminSystemConfig); adminSystemConfig.navigateToElement(<?php echo /* @noEscape */ $block->getConfigSearchParamsJson(); ?>); + }); </script> diff --git a/app/code/Magento/ConfigurableImportExport/Model/Import/Product/Type/Configurable.php b/app/code/Magento/ConfigurableImportExport/Model/Import/Product/Type/Configurable.php index 3f4565771e70b..2767e725cc9b0 100644 --- a/app/code/Magento/ConfigurableImportExport/Model/Import/Product/Type/Configurable.php +++ b/app/code/Magento/ConfigurableImportExport/Model/Import/Product/Type/Configurable.php @@ -596,7 +596,7 @@ protected function _parseVariations($rowData) $additionalRow['_super_attribute_position'] = $position; $additionalRows[] = $additionalRow; $additionalRow = []; - $position += 1; + $position ++; } } else { throw new LocalizedException( @@ -937,7 +937,7 @@ public function isRowValid(array $rowData, $rowNum, $isNewProduct = true) } foreach ($dataWithExtraVirtualRows as $option) { if (isset($option['_super_products_sku'])) { - if (in_array($option['_super_products_sku'], $skus)) { + if (in_array($option['_super_products_sku'], $skus, true)) { $error = true; $this->_entityModel->addRowError( sprintf( diff --git a/app/code/Magento/ConfigurableImportExport/Test/Unit/Model/Import/Product/Type/ConfigurableTest.php b/app/code/Magento/ConfigurableImportExport/Test/Unit/Model/Import/Product/Type/ConfigurableTest.php index 4446f98cff515..8d75fd902e911 100644 --- a/app/code/Magento/ConfigurableImportExport/Test/Unit/Model/Import/Product/Type/ConfigurableTest.php +++ b/app/code/Magento/ConfigurableImportExport/Test/Unit/Model/Import/Product/Type/ConfigurableTest.php @@ -616,6 +616,83 @@ public function testIsRowValid() } } + public function testRowValidationForNumericalSkus() + { + // Set _attributes to avoid error in Magento\CatalogImportExport\Model\Import\Product\Type\AbstractType. + $this->setPropertyValue($this->configurable, '_attributes', [ + 'Default' => [], + ]); + // Avoiding errors about attributes not being super + $this->setPropertyValue( + $this->configurable, + '_superAttributes', + [ + 'testattr2' => [ + 'options' => [ + 'attr2val1' => 1, + 'attr2val2' => 2, + ] + ], + ] + ); + + $rowValidationDataProvider = $this->rowValidationDataProvider(); + + // Checking that variations with duplicate sku are invalid + $result = $this->configurable->isRowValid($rowValidationDataProvider['duplicateProduct'], 0); + $this->assertFalse($result); + + // Checking that variations with SKUs that are the same when interpreted as number, + // but different when interpreted as string are valid + $result = $this->configurable->isRowValid($rowValidationDataProvider['nonDuplicateProduct'], 0); + $this->assertTrue($result); + } + + /** + * @return array + */ + public function rowValidationDataProvider() + { + return [ + 'duplicateProduct' => [ + 'sku' => 'configurableNumericalSkuDuplicateVariation', + 'store_view_code' => null, + 'attribute_set_code' => 'Default', + 'product_type' => 'configurable', + 'name' => 'Configurable Product with duplicate numerical SKUs in variations', + 'product_websites' => 'website_1', + 'configurable_variation_labels' => 'testattr2=Select Configuration', + 'configurable_variations' => 'sku=1234.1,' + . 'testattr2=attr2val1,' + . 'display=1|sku=1234.1,' + . 'testattr2=attr2val1,' + . 'display=0', + '_store' => null, + '_attribute_set' => 'Default', + '_type' => 'configurable', + '_product_websites' => 'website_1', + ], + 'nonDuplicateProduct' => [ + 'sku' => 'configurableNumericalSkuNonDuplicateVariation', + 'store_view_code' => null, + 'attribute_set_code' => 'Default', + 'product_type' => 'configurable', + 'name' => 'Configurable Product with different numerical SKUs in variations', + 'product_websites' => 'website_1', + 'configurable_variation_labels' => 'testattr2=Select Configuration', + 'configurable_variations' => 'sku=1234.10,' + . 'testattr2=attr2val1,' + . 'display=1|sku=1234.1,' + . 'testattr2=attr2val2,' + . 'display=0', + '_store' => null, + '_attribute_set' => 'Default', + '_type' => 'configurable', + '_product_websites' => 'website_1', + ] + ]; + } + /** * Set object property value. * diff --git a/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Type/Configurable/Attribute.php b/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Type/Configurable/Attribute.php index e93c44893bf58..1b5ea4d020f8e 100644 --- a/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Type/Configurable/Attribute.php +++ b/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Type/Configurable/Attribute.php @@ -1,7 +1,5 @@ <?php /** - * Catalog super product attribute resource model - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ @@ -11,6 +9,9 @@ use Magento\Framework\DB\Adapter\AdapterInterface; use Magento\Store\Model\Store; +/** + * Catalog super product attribute resource model. + */ class Attribute extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb { /** @@ -52,7 +53,7 @@ public function __construct( } /** - * Inititalize connection and define tables + * Initialize connection and define tables * * @return void */ @@ -189,8 +190,7 @@ public function deleteAttributesByProductId($productId) } /** - * @param \Magento\Framework\Model\AbstractModel $object - * @return $this + * @inheritDoc */ protected function _afterLoad(\Magento\Framework\Model\AbstractModel $object) { diff --git a/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Type/Configurable/Product/Collection.php b/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Type/Configurable/Product/Collection.php index 3124a3b8cf0ed..b76954075bcde 100644 --- a/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Type/Configurable/Product/Collection.php +++ b/app/code/Magento/ConfigurableProduct/Model/ResourceModel/Product/Type/Configurable/Product/Collection.php @@ -12,6 +12,7 @@ * * @api * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) * @since 100.0.2 */ class Collection extends \Magento\Catalog\Model\ResourceModel\Product\Collection @@ -26,7 +27,7 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Product\Collection /** * @var \Magento\Catalog\Model\Product[] */ - private $products; + private $products = []; /** * Assign link table name diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminOrderConfigurableProductActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminOrderConfigurableProductActionGroup.xml new file mode 100644 index 0000000000000..1c8fdf26735e8 --- /dev/null +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/AdminOrderConfigurableProductActionGroup.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminOrderConfigureConfigurableProduct"> + <arguments> + <argument name="optionName" type="string" defaultValue="option1"/> + <argument name="productQty" type="string" defaultValue="1"/> + </arguments> + <click selector="{{AdminOrderFormItemsOrderedSection.configureButtonBySku}}" stepKey="clickConfigure"/> + <waitForPageLoad stepKey="waitForConfigurePageLoad"/> + <selectOption selector="{{AdminOrderFormConfigureProductSection.selectOption}}" userInput="{{optionName}}" stepKey="selectOption"/> + <fillField selector="{{AdminOrderFormConfigureProductSection.quantity}}" userInput="{{productQty}}" stepKey="fillProductQty"/> + <click selector="{{AdminOrderFormConfigureProductSection.ok}}" stepKey="clickOk"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/StorefrontAddConfigurableProductToTheCartActionGroup.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/StorefrontAddConfigurableProductToTheCartActionGroup.xml index 9e3935501adee..380ffb1d0c781 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/StorefrontAddConfigurableProductToTheCartActionGroup.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/ActionGroup/StorefrontAddConfigurableProductToTheCartActionGroup.xml @@ -21,6 +21,7 @@ <fillField selector="{{StorefrontProductPageSection.qtyInput}}" userInput="{{qty}}" stepKey="fillProductQuantity"/> <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="clickOnAddToCartButton"/> <waitForPageLoad stepKey="waitForProductToAddInCart"/> + <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessage"/> <seeElement selector="{{StorefrontProductPageSection.successMsg}}" stepKey="seeSuccessSaveMessage"/> </actionGroup> </actionGroups> \ No newline at end of file diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminNewAttributePanelSection.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminNewAttributePanelSection.xml index 658e7a5fec9b3..573f1265931aa 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminNewAttributePanelSection.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Section/AdminNewAttributePanelSection.xml @@ -16,8 +16,8 @@ <element name="visibleOnCatalogPagesOnStorefront" type="select" selector="#is_visible_on_front"/> <element name="useInProductListing" type="select" selector="#used_in_product_listing"/> <element name="usedForStoringInProductListing" type="select" selector="#used_for_sort_by"/> - <element name="storefrontPropertiesTab" selector="#front_fieldset-wrapper"/> - <element name="storefrontPropertiesTitle" selector="//span[text()='Storefront Properties']"/> + <element name="storefrontPropertiesTab" type="button" selector="#front_fieldset-wrapper"/> + <element name="storefrontPropertiesTitle" type="text" selector="//span[text()='Storefront Properties']"/> <element name="container" type="text" selector="#create_new_attribute"/> <element name="saveAttribute" type="button" selector="#save"/> <element name="newAttributeIFrame" type="iframe" selector="create_new_attribute_container"/> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest.xml index 39aa516077c56..1791fc002ab95 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/AdminConfigurableProductUpdateTest.xml @@ -17,6 +17,9 @@ <testCaseId value="MC-88"/> <group value="ConfigurableProduct"/> <severity value="AVERAGE"/> + <skip> + <issueId value="MC-17140"/> + </skip> </annotations> <before> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ConfigurableProductPriceAdditionalStoreViewTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ConfigurableProductPriceAdditionalStoreViewTest.xml index 232dfe8391468..a71f51526c8ab 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ConfigurableProductPriceAdditionalStoreViewTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/ConfigurableProductPriceAdditionalStoreViewTest.xml @@ -207,6 +207,6 @@ <click userInput="$$createCategory.name$$" stepKey="clickOnCategoryName"/> <waitForPageLoad stepKey="waitForPageLoad4"/> <see userInput="$$createConfigProduct.name$$" stepKey="assertProductPresent"/> - <See userInput="$$createConfigChildProduct1.price$$" stepKey="assertProductPricePresent"/> + <see userInput="$$createConfigChildProduct1.price$$" stepKey="assertProductPricePresent"/> </test> </tests> diff --git a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductCategoryViewChildOnlyTest.xml b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductCategoryViewChildOnlyTest.xml index 1959551f8de2d..ac468fc92e4db 100644 --- a/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductCategoryViewChildOnlyTest.xml +++ b/app/code/Magento/ConfigurableProduct/Test/Mftf/Test/StorefrontConfigurableProductCategoryViewChildOnlyTest.xml @@ -17,6 +17,9 @@ <severity value="CRITICAL"/> <testCaseId value="MC-5832"/> <group value="ConfigurableProduct"/> + <skip> + <issueId value="MC-17140"/> + </skip> </annotations> <before> <!-- Create the category --> diff --git a/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/attribute/new/created.phtml b/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/attribute/new/created.phtml index 110defd5248b9..9307da21e6659 100644 --- a/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/attribute/new/created.phtml +++ b/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/attribute/new/created.phtml @@ -8,7 +8,7 @@ <script> (function ($) { - var data = <?= /* @escapeNotVerified */ $block->getAttributesBlockJson() ?>; + var data = <?= /* @noEscape */ $block->getAttributesBlockJson() ?>; var set = data.set || {id: $('#attribute_set_id').val()}; if (data.tab == 'variations') { $('[data-role=product-variations-matrix]').trigger('add', data.attribute); diff --git a/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/attribute/set/js.phtml b/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/attribute/set/js.phtml index 9c4612c972d96..5f49d5eb47442 100644 --- a/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/attribute/set/js.phtml +++ b/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/attribute/set/js.phtml @@ -5,8 +5,6 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - ?> <script> require([ diff --git a/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/composite/fieldset/configurable.phtml b/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/composite/fieldset/configurable.phtml index ecc95cbe3d48f..1166adca97255 100644 --- a/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/composite/fieldset/configurable.phtml +++ b/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/composite/fieldset/configurable.phtml @@ -3,34 +3,32 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - - ?> +// phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis +?> <?php /* @var $block \Magento\ConfigurableProduct\Block\Adminhtml\Product\Composite\Fieldset\Configurable */ ?> <?php $_product = $block->getProduct(); ?> <?php $_attributes = $block->decorateArray($block->getAllowAttributes()); ?> -<?php $_skipSaleableCheck = $this->helper('Magento\Catalog\Helper\Product')->getSkipSaleableCheck(); ?> -<?php if (($_product->isSaleable() || $_skipSaleableCheck) && count($_attributes)):?> +<?php $_skipSaleableCheck = $this->helper(Magento\Catalog\Helper\Product::class)->getSkipSaleableCheck(); ?> +<?php if (($_product->isSaleable() || $_skipSaleableCheck) && count($_attributes)) :?> <fieldset id="catalog_product_composite_configure_fields_configurable" class="fieldset admin__fieldset"> <legend class="legend admin__legend"> - <span><?= /* @escapeNotVerified */ __('Associated Products') ?></span> + <span><?= $block->escapeHtml(__('Associated Products')) ?></span> </legend> <div class="product-options fieldset admin__fieldset"> - <?php foreach ($_attributes as $_attribute): ?> + <?php foreach ($_attributes as $_attribute) : ?> <div class="field admin__field _required required"> <label class="label admin__field-label"><?= $block->escapeHtml($_attribute->getProductAttribute()->getStoreLabel($_product->getStoreId())); - ?></label> + ?></label> <div class="control admin__field-control <?php - if ($_attribute->getDecoratedIsLast()): - ?> last<?php + if ($_attribute->getDecoratedIsLast()) : + ?> last<?php endif; ?>"> - <select name="super_attribute[<?= /* @escapeNotVerified */ $_attribute->getAttributeId() ?>]" - id="attribute<?= /* @escapeNotVerified */ $_attribute->getAttributeId() ?>" + <select name="super_attribute[<?= $block->escapeHtmlAttr($_attribute->getAttributeId()) ?>]" + id="attribute<?= $block->escapeHtmlAttr($_attribute->getAttributeId()) ?>" class="admin__control-select required-entry super-attribute-select"> - <option><?= /* @escapeNotVerified */ __('Choose an Option...') ?></option> + <option><?= $block->escapeHtml(__('Choose an Option...')) ?></option> </select> </div> </div> @@ -43,7 +41,7 @@ require([ "Magento_Catalog/catalog/product/composite/configure" ], function(){ - var config = <?= /* @escapeNotVerified */ $block->getJsonConfig() ?>; + var config = <?= /* @noEscape */ $block->getJsonConfig() ?>; if (window.productConfigure) { config.containerId = window.productConfigure.blockFormFields.id; if (window.productConfigure.restorePhase) { diff --git a/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/edit/attribute/steps/attributes_values.phtml b/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/edit/attribute/steps/attributes_values.phtml index cc25474049190..44413c67ed5ba 100644 --- a/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/edit/attribute/steps/attributes_values.phtml +++ b/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/edit/attribute/steps/attributes_values.phtml @@ -4,18 +4,16 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - /* @var $block \Magento\ConfigurableProduct\Block\Adminhtml\Product\Steps\AttributeValues */ ?> <div data-bind="scope: '<?= /* @noEscape */ $block->getComponentName() ?>'"> <h2 class="steps-wizard-title"><?= $block->escapeHtml( - __('Step 2: Attribute Values') - ); ?></h2> + __('Step 2: Attribute Values') + ); ?></h2> <div class="steps-wizard-info"> <span><?= $block->escapeHtml( - __('Select values from each attribute to include in this product. Each unique combination of values creates a unique product SKU.') - );?></span> + __('Select values from each attribute to include in this product. Each unique combination of values creates a unique product SKU.') + );?></span> </div> <div data-bind="foreach: attributes, sortableList: attributes"> @@ -41,24 +39,24 @@ data-bind="click: $parent.selectAllAttributes" title="<?= $block->escapeHtml(__('Select All')) ?>"> <span><?= $block->escapeHtml( - __('Select All') - ); ?></span> + __('Select All') + ); ?></span> </button> <button type="button" class="action-deselect-all action-tertiary" data-bind="click: $parent.deSelectAllAttributes" title="<?= $block->escapeHtml(__('Deselect All')) ?>"> <span><?= $block->escapeHtml( - __('Deselect All') - ); ?></span> + __('Deselect All') + ); ?></span> </button> <button type="button" class="action-remove-all action-tertiary" data-bind="click: $parent.removeAttribute.bind($parent)" title="<?= $block->escapeHtml(__('Remove Attribute')) ?>"> <span><?= $block->escapeHtml( - __('Remove Attribute') - ); ?></span> + __('Remove Attribute') + ); ?></span> </button> </div> </div> @@ -87,8 +85,8 @@ data-action="save" data-bind="click: $parents[1].saveOption.bind($parent)"> <span><?= $block->escapeHtml( - __('Save Option') - ); ?></span> + __('Save Option') + ); ?></span> </button> <button type="button" class="action-remove" @@ -96,8 +94,8 @@ data-action="remove" data-bind="click: $parents[1].removeOption.bind($parent)"> <span><?= $block->escapeHtml( - __('Remove Option') - ); ?></span> + __('Remove Option') + ); ?></span> </button> </div> </li> @@ -108,8 +106,8 @@ data-action="addOption" data-bind="click: $parent.createOption, visible: canCreateOption"> <span><?= $block->escapeHtml( - __('Create New Value') - ); ?></span> + __('Create New Value') + ); ?></span> </button> </div> </div> diff --git a/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/edit/attribute/steps/bulk.phtml b/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/edit/attribute/steps/bulk.phtml index 9d144b0f569e0..a792a35da8051 100644 --- a/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/edit/attribute/steps/bulk.phtml +++ b/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/edit/attribute/steps/bulk.phtml @@ -3,24 +3,20 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - +// phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis /* @var $block \Magento\ConfigurableProduct\Block\Adminhtml\Product\Steps\Bulk */ ?> <div data-bind="scope: '<?= /* @noEscape */ $block->getComponentName() ?>'" data-role="bulk-step"> <h2 class="steps-wizard-title"><?= $block->escapeHtml(__('Step 3: Bulk Images, Price and Quantity')) ?></h2> <div class="steps-wizard-info"> - <?= /* @escapeNotVerified */ __('Based on your selections %1 new products will be created. Use this step to customize images and price for your new products.', '<span class="new-products-count" data-bind="text:countVariations"></span>') ?> + <?= /* @noEscape */ __('Based on your selections %1 new products will be created. Use this step to customize images and price for your new products.', '<span class="new-products-count" data-bind="text:countVariations"></span>') ?> </div> <div data-bind="with: sections().images" class="steps-wizard-section"> <div data-role="section"> <div class="steps-wizard-section-title"> - <span><?= $block->escapeHtml( - __('Images') - ); ?></span> + <span><?= $block->escapeHtml(__('Images')); ?></span> </div> <ul class="steps-wizard-section-list"> @@ -32,9 +28,7 @@ value="single" data-bind="checked:type"> <label for="apply-single-set-radio" class="admin__field-label"> - <span><?= $block->escapeHtml( - __('Apply single set of images to all SKUs') - ); ?></span> + <span><?= $block->escapeHtml(__('Apply single set of images to all SKUs')); ?></span> </label> </div> </li> @@ -71,10 +65,7 @@ <div data-role="gallery" class="gallery" data-images="[]" - data-types="<?= $block->escapeHtml( - $this->helper('Magento\Framework\Json\Helper\Data')->jsonEncode($block->getImageTypes()) - ) ?>" - > + data-types="<?= $block->escapeHtml($this->helper(Magento\Framework\Json\Helper\Data::class)->jsonEncode($block->getImageTypes())) ?>"> <div class="image image-placeholder"> <div data-role="uploader" class="uploader"> <div class="image-browse"> @@ -92,15 +83,12 @@ </div> </div> - <?php foreach ($block->getImageTypes() as $typeData): - ?> + <?php foreach ($block->getImageTypes() as $typeData) : ?> <input name="<?= $block->escapeHtml($typeData['name']) ?>" class="image-<?= $block->escapeHtml($typeData['code']) ?>" type="hidden" value="<?= $block->escapeHtml($typeData['value']) ?>"/> - <?php - endforeach; - ?> + <?php endforeach; ?> <script data-template="uploader" type="text/x-magento-template"> <div id="<%- data.id %>" class="file-row"> @@ -155,19 +143,12 @@ </div> </div> <ul class="item-roles" data-role="roles-labels"> - <?php - foreach ($block->getMediaAttributes() as $attribute): - ?> - <li data-role-code="<?= $block->escapeHtml( - $attribute->getAttributeCode() - ) ?>" class="item-role item-role-<?= $block->escapeHtml( - $attribute->getAttributeCode() - ) ?>"> + <?php foreach ($block->getMediaAttributes() as $attribute) :?> + <li data-role-code="<?= $block->escapeHtml($attribute->getAttributeCode()) ?>" + class="item-role item-role-<?= $block->escapeHtml($attribute->getAttributeCode()) ?>"> <?= /* @noEscape */ $attribute->getFrontendLabel() ?> </li> - <?php - endforeach; - ?> + <?php endforeach; ?> </ul> </div> </script> @@ -229,9 +210,7 @@ <div class="admin__field field-image-role"> <label class="admin__field-label"> - <span><?= $block->escapeHtml( - __('Role') - ); ?></span> + <span><?= $block->escapeHtml(__('Role')); ?></span> </label> <div class="admin__field-control"> <ul class="multiselect-alt"> @@ -243,42 +222,28 @@ <input class="image-type" data-role="type-selector" type="checkbox" - value="<?= $block->escapeHtml( - $attribute->getAttributeCode() - ) ?>" + value="<?= $block->escapeHtml($attribute->getAttributeCode()) ?>" /> - <?= $block->escapeHtml( - $attribute->getFrontendLabel() - ); ?> + <?= $block->escapeHtml($attribute->getFrontendLabel()); ?> </label> </li> - <?php - endforeach; - ?> + <?php endforeach; ?> </ul> </div> </div> <div class="admin__field admin__field-inline field-image-size" data-role="size"> <label class="admin__field-label"> - <span><?= $block->escapeHtml( - __('Image Size') - ); ?></span> + <span><?= $block->escapeHtml(__('Image Size')); ?></span> </label> - <div class="admin__field-value" data-message="<?= $block->escapeHtml( - __('{size}') - );?>"></div> + <div class="admin__field-value" data-message="<?= $block->escapeHtml(__('{size}'));?>"></div> </div> <div class="admin__field admin__field-inline field-image-resolution" data-role="resolution"> <label class="admin__field-label"> - <span><?= $block->escapeHtml( - __('Image Resolution') - ); ?></span> + <span><?= $block->escapeHtml(__('Image Resolution')); ?></span> </label> - <div class="admin__field-value" data-message="<?= $block->escapeHtml( - __('{width}^{height} px') - );?>"></div> + <div class="admin__field-value" data-message="<?= $block->escapeHtml(__('{width}^{height} px'));?>"></div> </div> <div class="admin__field field-image-hide"> @@ -293,9 +258,7 @@ <% if (data.disabled == 1) { %>checked="checked"<% } %> /> <label for="hide-from-product-page" class="admin__field-label"> - <?= $block->escapeHtml( - __('Hide from Product Page') - ); ?> + <?= $block->escapeHtml(__('Hide from Product Page')); ?> </label> </div> </div> @@ -310,9 +273,7 @@ <fieldset class="admin__fieldset bulk-attribute-values"> <div class="admin__field _required"> <label class="admin__field-label" for="apply-images-attributes"> - <span><?= $block->escapeHtml( - __('Select attribute') - ); ?></span> + <span><?= $block->escapeHtml(__('Select attribute')); ?></span> </label> <div class="admin__field-control"> <select @@ -322,9 +283,7 @@ options: $parent.attributes, optionsText: 'label', value: attribute, - optionsCaption: '<?= $block->escapeHtml( - __("Select") - ); ?>' + optionsCaption: '<?= $block->escapeHtml(__("Select")); ?>' "> </select> </div> @@ -341,24 +300,17 @@ <div data-role="gallery" class="gallery" data-images="[]" - data-types="<?= $block->escapeHtml( - $this->helper('Magento\Framework\Json\Helper\Data')->jsonEncode($block->getImageTypes()) - ) ?>" - > + data-types="<?= $block->escapeHtml($this->helper(Magento\Framework\Json\Helper\Data::class)->jsonEncode($block->getImageTypes())) ?>"> <div class="image image-placeholder"> <div data-role="uploader" class="uploader"> <div class="image-browse"> - <span><?= $block->escapeHtml( - __('Browse Files...') - ); ?></span> + <span><?= $block->escapeHtml(__('Browse Files...')); ?></span> <input type="file" name="image" multiple="multiple" data-url="<?= /* @noEscape */ $block->getUrl('catalog/product_gallery/upload') ?>" /> </div> </div> <div class="product-image-wrapper"> - <p class="image-placeholder-text"><?= $block->escapeHtml( - __('Browse to find or drag image here') - ); ?></p> + <p class="image-placeholder-text"><?= $block->escapeHtml(__('Browse to find or drag image here')); ?></p> </div> <div class="spinner"> <span></span><span></span><span></span><span></span> @@ -366,15 +318,12 @@ </div> </div> - <?php foreach ($block->getImageTypes() as $typeData): - ?> + <?php foreach ($block->getImageTypes() as $typeData) :?> <input name="<?= $block->escapeHtml($typeData['name']) ?>" class="image-<?= $block->escapeHtml($typeData['code']) ?>" type="hidden" value="<?= $block->escapeHtml($typeData['value']) ?>"/> - <?php - endforeach; - ?> + <?php endforeach; ?> <script data-template="uploader" type="text/x-magento-template"> <div id="<%- data.id %>" class="file-row"> @@ -418,15 +367,11 @@ class="action-remove" data-role="delete-button" title="<?= $block->escapeHtml(__('Remove image')) ?>"> - <span><?= $block->escapeHtml( - __('Remove image') - ); ?></span> + <span><?= $block->escapeHtml(__('Remove image')); ?></span> </button> <div class="draggable-handle"></div> </div> - <div class="image-fade"><span><?= $block->escapeHtml( - __('Hidden') - ); ?></span></div> + <div class="image-fade"><span><?= $block->escapeHtml(__('Hidden')); ?></span></div> </div> <div class="item-description"> <div class="item-title" data-role="img-title"><%- data.label %></div> @@ -435,19 +380,12 @@ </div> </div> <ul class="item-roles" data-role="roles-labels"> - <?php - foreach ($block->getMediaAttributes() as $attribute): - ?> - <li data-role-code="<?= $block->escapeHtml( - $attribute->getAttributeCode() - ) ?>" class="item-role item-role-<?= $block->escapeHtml( - $attribute->getAttributeCode() - ) ?>"> + <?php foreach ($block->getMediaAttributes() as $attribute) :?> + <li data-role-code="<?= $block->escapeHtml($attribute->getAttributeCode()) ?>" + class="item-role item-role-<?= $block->escapeHtml($attribute->getAttributeCode()) ?>"> <?= $block->escapeHtml($attribute->getFrontendLabel()) ?> </li> - <?php - endforeach; - ?> + <?php endforeach; ?> </ul> </div> </script> @@ -492,9 +430,7 @@ <fieldset class="admin__fieldset fieldset-image-panel"> <div class="admin__field field-image-description"> <label class="admin__field-label" for="image-description"> - <span><?= $block->escapeHtml( - __('Alt Text') - );?></span> + <span><?= $block->escapeHtml(__('Alt Text'));?></span> </label> <div class="admin__field-control"> @@ -508,56 +444,38 @@ <div class="admin__field field-image-role"> <label class="admin__field-label"> - <span><?= $block->escapeHtml( - __('Role') - );?></span> + <span><?= $block->escapeHtml(__('Role'));?></span> </label> <div class="admin__field-control"> <ul class="multiselect-alt"> - <?php - foreach ($block->getMediaAttributes() as $attribute) : - ?> + <?php foreach ($block->getMediaAttributes() as $attribute) :?> <li class="item"> <label> <input class="image-type" data-role="type-selector" type="checkbox" - value="<?= $block->escapeHtml( - $attribute->getAttributeCode() - ) ?>" + value="<?= $block->escapeHtml($attribute->getAttributeCode()) ?>" /> - <?= $block->escapeHtml( - $attribute->getFrontendLabel() - ) ?> + <?= $block->escapeHtml($attribute->getFrontendLabel()) ?> </label> </li> - <?php - endforeach; - ?> + <?php endforeach; ?> </ul> </div> </div> <div class="admin__field admin__field-inline field-image-size" data-role="size"> <label class="admin__field-label"> - <span><?= $block->escapeHtml( - __('Image Size') - ); ?></span> + <span><?= $block->escapeHtml(__('Image Size')); ?></span> </label> - <div class="admin__field-value" data-message="<?= $block->escapeHtml( - __('{size}') - ); ?>"></div> + <div class="admin__field-value" data-message="<?= $block->escapeHtml(__('{size}')); ?>"></div> </div> <div class="admin__field admin__field-inline field-image-resolution" data-role="resolution"> <label class="admin__field-label"> - <span><?= $block->escapeHtml( - __('Image Resolution') - ); ?></span> + <span><?= $block->escapeHtml(__('Image Resolution')); ?></span> </label> - <div class="admin__field-value" data-message="<?= $block->escapeHtml( - __('{width}^{height} px') - ); ?>"></div> + <div class="admin__field-value" data-message="<?= $block->escapeHtml(__('{width}^{height} px')); ?>"></div> </div> <div class="admin__field field-image-hide"> @@ -572,9 +490,7 @@ <% if (data.disabled == 1) { %>checked="checked"<% } %> /> <label for="hide-from-product-page" class="admin__field-label"> - <?= $block->escapeHtml( - __('Hide from Product Page') - ); ?> + <?= $block->escapeHtml(__('Hide from Product Page')); ?> </label> </div> </div> @@ -593,9 +509,7 @@ <div data-bind="with: sections().price" class="steps-wizard-section"> <div data-role="section"> <div class="steps-wizard-section-title"> - <span><?= $block->escapeHtml( - __('Price') - ); ?></span> + <span><?= $block->escapeHtml(__('Price')); ?></span> </div> <ul class="steps-wizard-section-list"> <li> @@ -607,9 +521,7 @@ data-bind="checked:type" /> <label for="apply-single-price-radio" class="admin__field-label"> - <span><?= $block->escapeHtml( - __('Apply single price to all SKUs') - ); ?></span> + <span><?= $block->escapeHtml(__('Apply single price to all SKUs')); ?></span> </label> </div> </li> @@ -622,9 +534,7 @@ data-bind="checked:type" /> <label for="apply-unique-prices-radio" class="admin__field-label"> - <span><?= $block->escapeHtml( - __('Apply unique prices by attribute to each SKU') - ); ?></span> + <span><?= $block->escapeHtml(__('Apply unique prices by attribute to each SKU')); ?></span> </label> </div> </li> @@ -637,9 +547,7 @@ checked data-bind="checked:type" /> <label for="skip-pricing-radio" class="admin__field-label"> - <span><?= $block->escapeHtml( - __('Skip price at this time') - ); ?></span> + <span><?= $block->escapeHtml(__('Skip price at this time')); ?></span> </label> </div> </li> @@ -648,9 +556,7 @@ <fieldset class="admin__fieldset bulk-attribute-values" data-bind="visible: type() == 'single'"> <div class="admin__field _required"> <label for="apply-single-price-input" class="admin__field-label"> - <span><?= $block->escapeHtml( - __('Price') - ); ?></span> + <span><?= $block->escapeHtml(__('Price')); ?></span> </label> <div class="admin__field-control"> <div class="currency-addon"> @@ -667,9 +573,7 @@ <fieldset class="admin__fieldset bulk-attribute-values"> <div class="admin__field _required"> <label for="select-each-price" class="admin__field-label"> - <span><?= $block->escapeHtml( - __('Select attribute') - ); ?></span> + <span><?= $block->escapeHtml(__('Select attribute')); ?></span> </label> <div class="admin__field-control"> <select id="select-each-price" class="admin__control-select" data-bind=" diff --git a/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/edit/attribute/steps/select_attributes.phtml b/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/edit/attribute/steps/select_attributes.phtml index cfb742e80f719..c3dc614232201 100644 --- a/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/edit/attribute/steps/select_attributes.phtml +++ b/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/edit/attribute/steps/select_attributes.phtml @@ -4,8 +4,6 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - /* @var $block \Magento\ConfigurableProduct\Block\Adminhtml\Product\Steps\SelectAttributes */ ?> <div class="select-attributes-block <?= /* @noEscape */ $block->getData('config/dataScope') ?>" data-role="select-attributes-step"> @@ -13,8 +11,8 @@ <?= /* @noEscape */ $block->getAddNewAttributeButton() ?> </div> <h2 class="steps-wizard-title"><?= $block->escapeHtml( - __('Step 1: Select Attributes') - ); ?></h2> + __('Step 1: Select Attributes') + ); ?></h2> <div class="selected-attributes" data-bind="scope: '<?= /* @noEscape */ $block->getComponentName() ?>'"> <?= $block->escapeHtml( __('Selected Attributes:') diff --git a/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/edit/attribute/steps/summary.phtml b/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/edit/attribute/steps/summary.phtml index 2ded3aa1079a9..379e129b68c7e 100644 --- a/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/edit/attribute/steps/summary.phtml +++ b/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/edit/attribute/steps/summary.phtml @@ -4,14 +4,12 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - /* @var $block \Magento\ConfigurableProduct\Block\Adminhtml\Product\Steps\Summary */ ?> <div data-bind="scope: '<?= /* @noEscape */ $block->getComponentName() ?>'"> <h2 class="steps-wizard-title"><?= $block->escapeHtml( - __('Step 4: Summary') - ); ?></h2> + __('Step 4: Summary') + ); ?></h2> <div class="admin__data-grid-wrap admin__data-grid-wrap-static"> <!-- ko if: gridNew().length --> diff --git a/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/edit/super/config.phtml b/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/edit/super/config.phtml index 07f4e39e43de6..c11a1adc19896 100644 --- a/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/edit/super/config.phtml +++ b/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/edit/super/config.phtml @@ -4,34 +4,32 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - - /** @var $block \Magento\ConfigurableProduct\Block\Adminhtml\Product\Edit\Tab\Variations\Config */ +/** @var $block \Magento\ConfigurableProduct\Block\Adminhtml\Product\Edit\Tab\Variations\Config */ ?> -<div class="entry-edit form-inline" id="<?= /* @escapeNotVerified */ $block->getId() ?>" data-panel="product-variations"> +<div class="entry-edit form-inline" id="<?= $block->escapeHtmlAttr($block->getId()) ?>" data-panel="product-variations"> <div data-bind="scope: 'variation-steps-wizard'" class="product-create-configuration"> <div class="product-create-configuration-info"> <div class="note" data-role="product-create-configuration-info"> - <?= /* @escapeNotVerified */ __('Configurable products allow customers to choose options (Ex: shirt color). - You need to create a simple product for each configuration (Ex: a product for each color).');?> + <?= $block->escapeHtml(__('Configurable products allow customers to choose options (Ex: shirt color). + You need to create a simple product for each configuration (Ex: a product for each color).'));?> </div> </div> <div class="product-create-configuration-actions" data-action="product-create-configuration-buttons"> <div class="product-create-configuration-action"> <button type="button" data-action="open-steps-wizard" title="Create Product Configurations" class="action-secondary" data-bind="click: open"> - <span data-role="button-label" data-edit-label="<?= /* @escapeNotVerified */ __('Edit Configurations') ?>"> - <?= /* @escapeNotVerified */ $block->isHasVariations() + <span data-role="button-label" data-edit-label="<?= $block->escapeHtmlAttr(__('Edit Configurations')) ?>"> + <?= $block->escapeHtml($block->isHasVariations() ? __('Edit Configurations') - : __('Create Configurations') - ?> + : __('Create Configurations')) +?> </span> </button> </div> <div class="product-create-configuration-action" data-bind="scope: 'configurableProductGrid'"> <button class="action-tertiary action-menu-item" type="button" data-action="choose" data-bind="click: showManuallyGrid, visible: button"> - <?= /* @noEscape */ __('Add Products Manually') ?> + <?= $block->escapeHtml(__('Add Products Manually')) ?> </button> </div> </div> diff --git a/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/edit/super/matrix.phtml b/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/edit/super/matrix.phtml index 230e0fd14ccb6..22ff1992c94a7 100644 --- a/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/edit/super/matrix.phtml +++ b/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/edit/super/matrix.phtml @@ -3,9 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - +// phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis /** @var $block \Magento\ConfigurableProduct\Block\Adminhtml\Product\Edit\Tab\Variations\Config\Matrix */ ?> <?php @@ -17,7 +15,7 @@ $currencySymbol = $block->getCurrencySymbol(); <div id="product-variations-matrix" data-role="product-variations-matrix"> <div data-bind="scope: 'configurableVariations'"> <h3 class="hidden" data-bind="css: {hidden: !showVariations() }" class="title"> - <?= /* @escapeNotVerified */ __('Current Variations') ?> + <?= $block->escapeHtml(__('Current Variations')) ?> </h3> <script data-template-for="variation-image" type="text/x-magento-template"> @@ -47,22 +45,22 @@ $currencySymbol = $block->getCurrencySymbol(); <thead> <tr> <th class="data-grid-th data-grid-thumbnail-cell col-image" data-column="image"> - <?= /* @escapeNotVerified */ __('Image') ?> + <?= $block->escapeHtml(__('Image')) ?> </th> <th class="data-grid-th col-name" data-column="name"> - <?= /* @escapeNotVerified */ __('Name') ?> + <?= $block->escapeHtml(__('Name')) ?> </th> <th class="data-grid-th col-sku" data-column="sku"> - <?= /* @escapeNotVerified */ __('SKU') ?> + <?= $block->escapeHtml(__('SKU')) ?> </th> <th class="data-grid-th col-price" data-column="price"> - <?= /* @escapeNotVerified */ __('Price') ?> + <?= $block->escapeHtml(__('Price')) ?> </th> <th class="data-grid-th col-qty" data-column="qty"> - <?= /* @escapeNotVerified */ __('Quantity') ?> + <?= $block->escapeHtml(__('Quantity')) ?> </th> <th class="data-grid-th col-weight" data-column="weight"> - <?= /* @escapeNotVerified */ __('Weight') ?> + <?= $block->escapeHtml(__('Weight')) ?> </th> <!-- ko foreach: getAttributesOptions() --> <th data-bind="attr: {class:'data-grid-th col-' + $data.attribute_code}, @@ -70,7 +68,7 @@ $currencySymbol = $block->getCurrencySymbol(); </th> <!-- /ko --> <th class="data-grid-th"> - <?= /* @escapeNotVerified */ __('Actions') ?> + <?= $block->escapeHtml(__('Actions')) ?> </th> </tr> </thead> @@ -88,7 +86,7 @@ $currencySymbol = $block->getCurrencySymbol(); <input type="hidden" data-bind=" attr: {id: $parent.getRowId(variation, 'image'), name: $parent.getVariationRowName(variation, 'image')}"/> - <span><?= /* @escapeNotVerified */ __('Upload Image') ?></span> + <span><?= $block->escapeHtml(__('Upload Image')) ?></span> <input name="image" type="file" data-url="<?= $block->escapeHtml($block->getImageUploadUrl()) ?>" title="<?= $block->escapeHtml(__('Upload image')) ?>"/> @@ -102,11 +100,11 @@ $currencySymbol = $block->getCurrencySymbol(); <!-- /ko --> <button type="button" class="action toggle no-display" data-toggle="dropdown" data-mage-init='{"dropdown":{}}'> - <span><?= /* @escapeNotVerified */ __('Select') ?></span> + <span><?= $block->escapeHtml(__('Select')) ?></span> </button> <ul class="dropdown"> <li> - <a class="item" data-action="no-image"><?= /* @escapeNotVerified */ __('No Image') ?></a> + <a class="item" data-action="no-image"><?= $block->escapeHtml(__('No Image')) ?></a> </li> </ul> </div> @@ -208,7 +206,7 @@ $currencySymbol = $block->getCurrencySymbol(); " data-action="choose" href="#"> - <?= /* @escapeNotVerified */ __('Choose a different Product') ?> + <?= $block->escapeHtml(__('Choose a different Product')) ?> </a> </li> <li> @@ -219,7 +217,7 @@ $currencySymbol = $block->getCurrencySymbol(); </li> <li> <a class="action-menu-item" data-bind="click: $parent.removeProduct.bind($parent, $index())"> - <?= /* @escapeNotVerified */ __('Remove Product') ?> + <?= $block->escapeHtml(__('Remove Product')) ?> </a> </li> </ul> @@ -233,15 +231,14 @@ $currencySymbol = $block->getCurrencySymbol(); <!-- /ko --> </div> <div data-role="step-wizard-dialog" - data-mage-init='{"Magento_Ui/js/modal/modal":{"type":"slide","title":"<?= /* @escapeNotVerified */ __('Create Product Configurations') ?>", + data-mage-init='{"Magento_Ui/js/modal/modal":{"type":"slide","title":"<?= $block->escapeJs(__('Create Product Configurations')) ?>", "buttons":[]}}' class="no-display"> - <?php - /* @escapeNotVerified */ echo $block->getVariationWizard([ + <?= /* @noEscape */ $block->getVariationWizard([ 'attributes' => $attributes, 'configurations' => $productMatrix ]); - ?> +?> </div> </div> @@ -252,8 +249,8 @@ $currencySymbol = $block->getCurrencySymbol(); "components": { "configurableVariations": { "component": "Magento_ConfigurableProduct/js/variations/variations", - "variations": <?= /* @noEscape */ $this->helper('Magento\Framework\Json\Helper\Data')->jsonEncode($productMatrix) ?>, - "productAttributes": <?= /* @noEscape */ $this->helper('Magento\Framework\Json\Helper\Data')->jsonEncode($attributes) ?>, + "variations": <?= /* @noEscape */ $this->helper(Magento\Framework\Json\Helper\Data::class)->jsonEncode($productMatrix) ?>, + "productAttributes": <?= /* @noEscape */ $this->helper(Magento\Framework\Json\Helper\Data::class)->jsonEncode($attributes) ?>, "productUrl": "<?= /* @noEscape */ $block->getUrl('catalog/product/edit', ['id' => '%id%']) ?>", "currencySymbol": "<?= /* @noEscape */ $currencySymbol ?>", "configurableProductGrid": "configurableProductGrid" diff --git a/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/edit/super/wizard-ajax.phtml b/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/edit/super/wizard-ajax.phtml index 2e38633218652..7b85efdbb73aa 100644 --- a/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/edit/super/wizard-ajax.phtml +++ b/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/edit/super/wizard-ajax.phtml @@ -4,8 +4,6 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - /** @var $block \Magento\ConfigurableProduct\Block\Adminhtml\Product\Edit\Tab\Variations\Config\Matrix */ $productMatrix = $block->getProductMatrix(); diff --git a/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/edit/super/wizard.phtml b/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/edit/super/wizard.phtml index 3a23257cbbf9b..f009962bb97ff 100644 --- a/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/edit/super/wizard.phtml +++ b/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/catalog/product/edit/super/wizard.phtml @@ -3,9 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - +// phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis /** @var $block \Magento\ConfigurableProduct\Block\Adminhtml\Product\Edit\Tab\Variations\Config\Matrix */ ?> <?php @@ -48,9 +46,9 @@ $currencySymbol = $block->getCurrencySymbol(); "attributeSetHandler": "<?= /* @noEscape */ $block->getForm() ?>.configurable_attribute_set_handler_modal", "wizardModalButtonName": "<?= /* @noEscape */ $block->getForm() ?>.configurable.configurable_products_button_set.create_configurable_products_button", "wizardModalButtonTitle": "<?= $block->escapeHtml(__('Edit Configurations')) ?>", - "productAttributes": <?= /* @noEscape */ $this->helper('Magento\Framework\Json\Helper\Data')->jsonEncode($attributes) ?>, + "productAttributes": <?= /* @noEscape */ $this->helper(Magento\Framework\Json\Helper\Data::class)->jsonEncode($attributes) ?>, "productUrl": "<?= /* @noEscape */ $block->getUrl('catalog/product/edit', ['id' => '%id%']) ?>", - "variations": <?= /* @noEscape */ $this->helper('Magento\Framework\Json\Helper\Data')->jsonEncode($productMatrix) ?>, + "variations": <?= /* @noEscape */ $this->helper(Magento\Framework\Json\Helper\Data::class)->jsonEncode($productMatrix) ?>, "currencySymbol": "<?= /* @noEscape */ $currencySymbol ?>", "attributeSetCreationUrl": "<?= /* @noEscape */ $block->getUrl('*/product_set/save') ?>" } diff --git a/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/product/configurable/affected-attribute-set-selector/form.phtml b/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/product/configurable/affected-attribute-set-selector/form.phtml index 6c993b243da23..6b30b3eba33b4 100644 --- a/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/product/configurable/affected-attribute-set-selector/form.phtml +++ b/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/product/configurable/affected-attribute-set-selector/form.phtml @@ -4,8 +4,6 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - /* @var $block \Magento\Framework\View\Element\Template */ ?> <div data-role="affected-attribute-set-selector" class="no-display affected-attribute-set"> diff --git a/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/product/configurable/affected-attribute-set-selector/js.phtml b/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/product/configurable/affected-attribute-set-selector/js.phtml index 0e0eb3464eaa6..cdb12b54e5e67 100644 --- a/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/product/configurable/affected-attribute-set-selector/js.phtml +++ b/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/product/configurable/affected-attribute-set-selector/js.phtml @@ -4,8 +4,6 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - /* @var $block \Magento\ConfigurableProduct\Block\Product\Configurable\AttributeSelector */ ?> <script> @@ -48,12 +46,12 @@ $form .modal({ - title: '<?= /* @escapeNotVerified */ __('Choose Affected Attribute Set') ?>', + title: '<?= $block->escapeJs(__('Choose Affected Attribute Set')) ?>', closed: function () { resetValidation(); }, buttons: [{ - text: '<?= /* @escapeNotVerified */ __('Confirm') ?>', + text: '<?= $block->escapeJs(__('Confirm')) ?>', attr: { 'data-action': 'confirm' }, @@ -77,12 +75,12 @@ $.ajax({ type: 'POST', - url: '<?= /* @escapeNotVerified */ $block->getAttributeSetCreationUrl() ?>', + url: '<?= $block->escapeUrl($block->getAttributeSetCreationUrl()) ?>', data: { gotoEdit: 1, attribute_set_name: $form.find('input[name=new-attribute-set-name]').val(), skeleton_set: $('#attribute_set_id').val(), - form_key: '<?= /* @escapeNotVerified */ $block->getFormKey() ?>', + form_key: '<?= $block->escapeJs($block->getFormKey()) ?>', return_session_messages_only: 1 }, dataType: 'json', @@ -101,8 +99,8 @@ return false; } },{ - text: '<?= /* @escapeNotVerified */ __('Cancel') ?>', - id: '<?= /* @escapeNotVerified */ $block->getJsId('close-button') ?>', + text: '<?= $block->escapeJs(__('Cancel')) ?>', + id: '<?= $block->escapeJs($block->getJsId('close-button')) ?>', 'class': 'action-close', click: function() { $form.modal('closeModal'); diff --git a/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/product/configurable/attribute-selector/js.phtml b/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/product/configurable/attribute-selector/js.phtml index 4246d8f53a79c..e6cf1e9c6870d 100644 --- a/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/product/configurable/attribute-selector/js.phtml +++ b/app/code/Magento/ConfigurableProduct/view/adminhtml/templates/product/configurable/attribute-selector/js.phtml @@ -3,16 +3,12 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - +// phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis /** @var $block \Magento\ConfigurableProduct\Block\Product\Configurable\AttributeSelector */ ?> <script> require(["jquery","mage/mage","mage/backend/suggest"], function($){ - var options = <?php - /* @escapeNotVerified */ echo $this->helper('Magento\Framework\Json\Helper\Data')->jsonEncode($block->getSuggestWidgetOptions()) - ?>; + var options = <?= /* @noEscape */ $this->helper(Magento\Framework\Json\Helper\Data::class)->jsonEncode($block->getSuggestWidgetOptions()) ?>; $('#configurable-attribute-selector') .mage('suggest', options) .on('suggestselect', function (event, ui) { diff --git a/app/code/Magento/ConfigurableProduct/view/base/templates/product/price/final_price.phtml b/app/code/Magento/ConfigurableProduct/view/base/templates/product/price/final_price.phtml index f020e4c2c9495..a6dc6c819989e 100644 --- a/app/code/Magento/ConfigurableProduct/view/base/templates/product/price/final_price.phtml +++ b/app/code/Magento/ConfigurableProduct/view/base/templates/product/price/final_price.phtml @@ -4,37 +4,28 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - -?> - -<?php /** @var \Magento\ConfigurableProduct\Pricing\Render\FinalPriceBox$block */ - /** @var \Magento\Framework\Pricing\Price\PriceInterface $priceModel */ $priceModel = $block->getPriceType('regular_price'); - /** @var \Magento\Framework\Pricing\Price\PriceInterface $finalPriceModel */ $finalPriceModel = $block->getPriceType('final_price'); $idSuffix = $block->getIdSuffix() ? $block->getIdSuffix() : ''; $schema = ($block->getZone() == 'item_view') ? true : false; ?> <span class="normal-price"> - <?php - $arguments = [ + <?= /* @noEscape */ $block->renderAmount($finalPriceModel->getAmount(), [ 'display_label' => __('As low as'), 'price_id' => $block->getPriceId('product-price-' . $idSuffix), 'price_type' => 'finalPrice', 'include_container' => true, 'schema' => $schema, - ]; - /* @noEscape */ echo $block->renderAmount($finalPriceModel->getAmount(), $arguments); - ?> + ]); +?> </span> -<?php if (!$block->isProductList() && $block->hasSpecialPrice()): ?> +<?php if (!$block->isProductList() && $block->hasSpecialPrice()) : ?> <span class="old-price sly-old-price no-display"> - <?php /* @escapeNotVerified */ echo $block->renderAmount($priceModel->getAmount(), [ + <?= /* @noEscape */ $block->renderAmount($priceModel->getAmount(), [ 'display_label' => __('Regular Price'), 'price_id' => $block->getPriceId('old-price-' . $idSuffix), 'price_type' => 'oldPrice', @@ -44,14 +35,14 @@ $schema = ($block->getZone() == 'item_view') ? true : false; </span> <?php endif; ?> -<?php if ($block->showMinimalPrice()): ?> - <?php if ($block->getUseLinkForAsLowAs()):?> - <a href="<?= /* @escapeNotVerified */ $block->getSaleableItem()->getProductUrl() ?>" class="minimal-price-link"> - <?= /* @escapeNotVerified */ $block->renderAmountMinimal() ?> +<?php if ($block->showMinimalPrice()) : ?> + <?php if ($block->getUseLinkForAsLowAs()) :?> + <a href="<?= $block->escapeUrl($block->getSaleableItem()->getProductUrl()) ?>" class="minimal-price-link"> + <?= /* @noEscape */ $block->renderAmountMinimal() ?> </a> - <?php else:?> + <?php else :?> <span class="minimal-price-link"> - <?= /* @escapeNotVerified */ $block->renderAmountMinimal() ?> + <?= /* @noEscape */ $block->renderAmountMinimal() ?> </span> <?php endif?> <?php endif; ?> diff --git a/app/code/Magento/ConfigurableProduct/view/base/templates/product/price/tier_price.phtml b/app/code/Magento/ConfigurableProduct/view/base/templates/product/price/tier_price.phtml index 325ee1d5d79b3..c68419b955e6d 100644 --- a/app/code/Magento/ConfigurableProduct/view/base/templates/product/price/tier_price.phtml +++ b/app/code/Magento/ConfigurableProduct/view/base/templates/product/price/tier_price.phtml @@ -3,7 +3,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - ?> <script type="text/x-magento-template" id="tier-prices-template"> <ul class="prices-tier items"> diff --git a/app/code/Magento/ConfigurableProduct/view/frontend/templates/js/components.phtml b/app/code/Magento/ConfigurableProduct/view/frontend/templates/js/components.phtml index bad5acc209b5f..5902a9f25cc4b 100644 --- a/app/code/Magento/ConfigurableProduct/view/frontend/templates/js/components.phtml +++ b/app/code/Magento/ConfigurableProduct/view/frontend/templates/js/components.phtml @@ -3,8 +3,5 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <?= $block->getChildHtml() ?> diff --git a/app/code/Magento/ConfigurableProduct/view/frontend/templates/product/view/type/options/configurable.phtml b/app/code/Magento/ConfigurableProduct/view/frontend/templates/product/view/type/options/configurable.phtml index f5ed067967547..f7db41225c970 100644 --- a/app/code/Magento/ConfigurableProduct/view/frontend/templates/product/view/type/options/configurable.phtml +++ b/app/code/Magento/ConfigurableProduct/view/frontend/templates/product/view/type/options/configurable.phtml @@ -3,9 +3,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <?php @@ -13,19 +10,19 @@ $_product = $block->getProduct(); $_attributes = $block->decorateArray($block->getAllowAttributes()); ?> -<?php if ($_product->isSaleable() && count($_attributes)):?> - <?php foreach ($_attributes as $_attribute): ?> +<?php if ($_product->isSaleable() && count($_attributes)) :?> + <?php foreach ($_attributes as $_attribute) : ?> <div class="field configurable required"> - <label class="label" for="attribute<?= /* @escapeNotVerified */ $_attribute->getAttributeId() ?>"> + <label class="label" for="attribute<?= $block->escapeHtmlAttr($_attribute->getAttributeId()) ?>"> <span><?= $block->escapeHtml($_attribute->getProductAttribute()->getStoreLabel()) ?></span> </label> <div class="control"> - <select name="super_attribute[<?= /* @escapeNotVerified */ $_attribute->getAttributeId() ?>]" - data-selector="super_attribute[<?= /* @escapeNotVerified */ $_attribute->getAttributeId() ?>]" + <select name="super_attribute[<?= $block->escapeHtmlAttr($_attribute->getAttributeId()) ?>]" + data-selector="super_attribute[<?= $block->escapeHtmlAttr($_attribute->getAttributeId()) ?>]" data-validate="{required:true}" - id="attribute<?= /* @escapeNotVerified */ $_attribute->getAttributeId() ?>" + id="attribute<?= $block->escapeHtmlAttr($_attribute->getAttributeId()) ?>" class="super-attribute-select"> - <option value=""><?= /* @escapeNotVerified */ __('Choose an Option...') ?></option> + <option value=""><?= $block->escapeHtml(__('Choose an Option...')) ?></option> </select> </div> </div> @@ -34,9 +31,11 @@ $_attributes = $block->decorateArray($block->getAllowAttributes()); { "#product_addtocart_form": { "configurable": { - "spConfig": <?= /* @escapeNotVerified */ $block->getJsonConfig() ?>, - "gallerySwitchStrategy": "<?php /* @escapeNotVerified */ echo $block->getVar('gallery_switch_strategy', - 'Magento_ConfigurableProduct') ?: 'replace'; ?>" + "spConfig": <?= /* @noEscape */ $block->getJsonConfig() ?>, + "gallerySwitchStrategy": "<?= $block->escapeJs($block->getVar( + 'gallery_switch_strategy', + 'Magento_ConfigurableProduct' + ) ?: 'replace'); ?>" } }, "*" : { diff --git a/app/code/Magento/Customer/Block/Adminhtml/Edit/Tab/Orders.php b/app/code/Magento/Customer/Block/Adminhtml/Edit/Tab/Orders.php index 2fb59ec767e8a..3d4ccb789dc72 100644 --- a/app/code/Magento/Customer/Block/Adminhtml/Edit/Tab/Orders.php +++ b/app/code/Magento/Customer/Block/Adminhtml/Edit/Tab/Orders.php @@ -107,7 +107,7 @@ protected function _prepareCollection() */ protected function _prepareColumns() { - $this->addColumn('increment_id', ['header' => __('Order'), 'width' => '100', 'index' => 'increment_id']); + $this->addColumn('increment_id', ['header' => __('Order #'), 'width' => '100', 'index' => 'increment_id']); $this->addColumn( 'created_at', @@ -140,7 +140,7 @@ protected function _prepareColumns() $this->addColumn( 'action', [ - 'header' => ' ', + 'header' => 'Action', 'filter' => false, 'sortable' => false, 'width' => '100px', diff --git a/app/code/Magento/Customer/Controller/Account/CreatePassword.php b/app/code/Magento/Customer/Controller/Account/CreatePassword.php index 124ac912a7ccf..d12ec57d3c339 100644 --- a/app/code/Magento/Customer/Controller/Account/CreatePassword.php +++ b/app/code/Magento/Customer/Controller/Account/CreatePassword.php @@ -3,13 +3,17 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Customer\Controller\Account; use Magento\Customer\Api\AccountManagementInterface; +use Magento\Customer\Model\ForgotPasswordToken\ConfirmCustomerByToken; use Magento\Customer\Model\Session; use Magento\Framework\App\Action\HttpGetActionInterface; use Magento\Framework\View\Result\PageFactory; use Magento\Framework\App\Action\Context; +use Magento\Framework\App\ObjectManager; /** * Class CreatePassword @@ -34,20 +38,30 @@ class CreatePassword extends \Magento\Customer\Controller\AbstractAccount implem protected $resultPageFactory; /** - * @param Context $context - * @param Session $customerSession - * @param PageFactory $resultPageFactory - * @param AccountManagementInterface $accountManagement + * @var \Magento\Customer\Model\ForgotPasswordToken\ConfirmCustomerByToken + */ + private $confirmByToken; + + /** + * @param \Magento\Framework\App\Action\Context $context + * @param \Magento\Customer\Model\Session $customerSession + * @param \Magento\Framework\View\Result\PageFactory $resultPageFactory + * @param \Magento\Customer\Api\AccountManagementInterface $accountManagement + * @param \Magento\Customer\Model\ForgotPasswordToken\ConfirmCustomerByToken $confirmByToken */ public function __construct( Context $context, Session $customerSession, PageFactory $resultPageFactory, - AccountManagementInterface $accountManagement + AccountManagementInterface $accountManagement, + ConfirmCustomerByToken $confirmByToken = null ) { $this->session = $customerSession; $this->resultPageFactory = $resultPageFactory; $this->accountManagement = $accountManagement; + $this->confirmByToken = $confirmByToken + ?? ObjectManager::getInstance()->get(ConfirmCustomerByToken::class); + parent::__construct($context); } @@ -67,6 +81,8 @@ public function execute() try { $this->accountManagement->validateResetPasswordLinkToken(null, $resetPasswordToken); + $this->confirmByToken->execute($resetPasswordToken); + if ($isDirectLink) { $this->session->setRpToken($resetPasswordToken); $resultRedirect = $this->resultRedirectFactory->create(); @@ -77,16 +93,17 @@ public function execute() /** @var \Magento\Framework\View\Result\Page $resultPage */ $resultPage = $this->resultPageFactory->create(); $resultPage->getLayout() - ->getBlock('resetPassword') - ->setResetPasswordLinkToken($resetPasswordToken); + ->getBlock('resetPassword') + ->setResetPasswordLinkToken($resetPasswordToken); return $resultPage; } } catch (\Exception $exception) { - $this->messageManager->addError(__('Your password reset link has expired.')); + $this->messageManager->addErrorMessage(__('Your password reset link has expired.')); /** @var \Magento\Framework\Controller\Result\Redirect $resultRedirect */ $resultRedirect = $this->resultRedirectFactory->create(); $resultRedirect->setPath('*/*/forgotpassword'); + return $resultRedirect; } } diff --git a/app/code/Magento/Customer/Controller/Account/CreatePost.php b/app/code/Magento/Customer/Controller/Account/CreatePost.php index 79a575add7347..4c9c25b5f33d9 100644 --- a/app/code/Magento/Customer/Controller/Account/CreatePost.php +++ b/app/code/Magento/Customer/Controller/Account/CreatePost.php @@ -5,6 +5,7 @@ */ namespace Magento\Customer\Controller\Account; +use Magento\Customer\Api\CustomerRepositoryInterface as CustomerRepository; use Magento\Framework\App\Action\HttpPostActionInterface as HttpPostActionInterface; use Magento\Customer\Model\Account\Redirect as AccountRedirect; use Magento\Customer\Api\Data\AddressInterface; @@ -38,6 +39,8 @@ use Magento\Customer\Controller\AbstractAccount; /** + * Post create customer action + * * @SuppressWarnings(PHPMD.TooManyFields) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ @@ -133,6 +136,11 @@ class CreatePost extends AbstractAccount implements CsrfAwareActionInterface, Ht */ private $formKeyValidator; + /** + * @var CustomerRepository + */ + private $customerRepository; + /** * @param Context $context * @param Session $customerSession @@ -152,6 +160,7 @@ class CreatePost extends AbstractAccount implements CsrfAwareActionInterface, Ht * @param CustomerExtractor $customerExtractor * @param DataObjectHelper $dataObjectHelper * @param AccountRedirect $accountRedirect + * @param CustomerRepository $customerRepository * @param Validator $formKeyValidator * * @SuppressWarnings(PHPMD.ExcessiveParameterList) @@ -175,6 +184,7 @@ public function __construct( CustomerExtractor $customerExtractor, DataObjectHelper $dataObjectHelper, AccountRedirect $accountRedirect, + CustomerRepository $customerRepository, Validator $formKeyValidator = null ) { $this->session = $customerSession; @@ -195,6 +205,7 @@ public function __construct( $this->dataObjectHelper = $dataObjectHelper; $this->accountRedirect = $accountRedirect; $this->formKeyValidator = $formKeyValidator ?: ObjectManager::getInstance()->get(Validator::class); + $this->customerRepository = $customerRepository; parent::__construct($context); } @@ -328,34 +339,29 @@ public function execute() return $this->resultRedirectFactory->create() ->setUrl($this->_redirect->error($url)); } - $this->session->regenerateId(); - try { $address = $this->extractAddress(); $addresses = $address === null ? [] : [$address]; - $customer = $this->customerExtractor->extract('customer_account_create', $this->_request); $customer->setAddresses($addresses); - $password = $this->getRequest()->getParam('password'); $confirmation = $this->getRequest()->getParam('password_confirmation'); $redirectUrl = $this->session->getBeforeAuthUrl(); - $this->checkPasswordConfirmation($password, $confirmation); - $customer = $this->accountManagement ->createAccount($customer, $password, $redirectUrl); if ($this->getRequest()->getParam('is_subscribed', false)) { - $this->subscriberFactory->create()->subscribeCustomerById($customer->getId()); + $extensionAttributes = $customer->getExtensionAttributes(); + $extensionAttributes->setIsSubscribed(true); + $customer->setExtensionAttributes($extensionAttributes); + $this->customerRepository->save($customer); } - $this->_eventManager->dispatch( 'customer_register_success', ['account_controller' => $this, 'customer' => $customer] ); - $confirmationStatus = $this->accountManagement->getConfirmationStatus($customer->getId()); if ($confirmationStatus === AccountManagementInterface::ACCOUNT_CONFIRMATION_REQUIRED) { $email = $this->customerUrl->getEmailConfirmationUrl($customer->getEmail()); diff --git a/app/code/Magento/Customer/Model/AccountManagement.php b/app/code/Magento/Customer/Model/AccountManagement.php index 250d190f8fae7..15d98af86b72e 100644 --- a/app/code/Magento/Customer/Model/AccountManagement.php +++ b/app/code/Magento/Customer/Model/AccountManagement.php @@ -17,8 +17,10 @@ use Magento\Customer\Model\Config\Share as ConfigShare; use Magento\Customer\Model\Customer as CustomerModel; use Magento\Customer\Model\Customer\CredentialsValidator; +use Magento\Customer\Model\ForgotPasswordToken\GetCustomerByToken; use Magento\Customer\Model\Metadata\Validator; use Magento\Customer\Model\ResourceModel\Visitor\CollectionFactory; +use Magento\Directory\Model\AllowedCountries; use Magento\Eav\Model\Validator\Attribute\Backend; use Magento\Framework\Api\ExtensibleDataObjectConverter; use Magento\Framework\Api\SearchCriteriaBuilder; @@ -43,7 +45,6 @@ use Magento\Framework\Intl\DateTimeFactory; use Magento\Framework\Mail\Template\TransportBuilder; use Magento\Framework\Math\Random; -use Magento\Framework\Phrase; use Magento\Framework\Reflection\DataObjectProcessor; use Magento\Framework\Registry; use Magento\Framework\Session\SaveHandlerInterface; @@ -339,6 +340,16 @@ class AccountManagement implements AccountManagementInterface */ private $addressRegistry; + /** + * @var AllowedCountries + */ + private $allowedCountriesReader; + + /** + * @var GetCustomerByToken + */ + private $getByToken; + /** * @param CustomerFactory $customerFactory * @param ManagerInterface $eventManager @@ -371,8 +382,12 @@ class AccountManagement implements AccountManagementInterface * @param CollectionFactory|null $visitorCollectionFactory * @param SearchCriteriaBuilder|null $searchCriteriaBuilder * @param AddressRegistry|null $addressRegistry + * @param GetCustomerByToken|null $getByToken + * @param AllowedCountries|null $allowedCountriesReader + * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.ExcessiveParameterList) * @SuppressWarnings(PHPMD.NPathComplexity) + * @SuppressWarnings(PHPMD.LongVariable) */ public function __construct( CustomerFactory $customerFactory, @@ -405,7 +420,9 @@ public function __construct( SaveHandlerInterface $saveHandler = null, CollectionFactory $visitorCollectionFactory = null, SearchCriteriaBuilder $searchCriteriaBuilder = null, - AddressRegistry $addressRegistry = null + AddressRegistry $addressRegistry = null, + GetCustomerByToken $getByToken = null, + AllowedCountries $allowedCountriesReader = null ) { $this->customerFactory = $customerFactory; $this->eventManager = $eventManager; @@ -430,21 +447,26 @@ public function __construct( $this->customerModel = $customerModel; $this->objectFactory = $objectFactory; $this->extensibleDataObjectConverter = $extensibleDataObjectConverter; + $objectManager = ObjectManager::getInstance(); $this->credentialsValidator = - $credentialsValidator ?: ObjectManager::getInstance()->get(CredentialsValidator::class); - $this->dateTimeFactory = $dateTimeFactory ?: ObjectManager::getInstance()->get(DateTimeFactory::class); - $this->accountConfirmation = $accountConfirmation ?: ObjectManager::getInstance() + $credentialsValidator ?: $objectManager->get(CredentialsValidator::class); + $this->dateTimeFactory = $dateTimeFactory ?: $objectManager->get(DateTimeFactory::class); + $this->accountConfirmation = $accountConfirmation ?: $objectManager ->get(AccountConfirmation::class); $this->sessionManager = $sessionManager - ?: ObjectManager::getInstance()->get(SessionManagerInterface::class); + ?: $objectManager->get(SessionManagerInterface::class); $this->saveHandler = $saveHandler - ?: ObjectManager::getInstance()->get(SaveHandlerInterface::class); + ?: $objectManager->get(SaveHandlerInterface::class); $this->visitorCollectionFactory = $visitorCollectionFactory - ?: ObjectManager::getInstance()->get(CollectionFactory::class); + ?: $objectManager->get(CollectionFactory::class); $this->searchCriteriaBuilder = $searchCriteriaBuilder - ?: ObjectManager::getInstance()->get(SearchCriteriaBuilder::class); + ?: $objectManager->get(SearchCriteriaBuilder::class); $this->addressRegistry = $addressRegistry - ?: ObjectManager::getInstance()->get(AddressRegistry::class); + ?: $objectManager->get(AddressRegistry::class); + $this->getByToken = $getByToken + ?: $objectManager->get(GetCustomerByToken::class); + $this->allowedCountriesReader = $allowedCountriesReader + ?: $objectManager->get(AllowedCountries::class); } /** @@ -510,8 +532,11 @@ public function activateById($customerId, $confirmationKey) * @param \Magento\Customer\Api\Data\CustomerInterface $customer * @param string $confirmationKey * @return \Magento\Customer\Api\Data\CustomerInterface - * @throws \Magento\Framework\Exception\State\InvalidTransitionException - * @throws \Magento\Framework\Exception\State\InputMismatchException + * @throws InputException + * @throws InputMismatchException + * @throws InvalidTransitionException + * @throws LocalizedException + * @throws NoSuchEntityException */ private function activateCustomer($customer, $confirmationKey) { @@ -554,6 +579,7 @@ public function authenticate($username, $password) } try { $this->getAuthentication()->authenticate($customerId, $password); + // phpcs:disable Magento2.Exceptions.ThrowCatch } catch (InvalidEmailOrPasswordException $e) { throw new InvalidEmailOrPasswordException(__('Invalid login or password.')); } @@ -618,42 +644,6 @@ public function initiatePasswordReset($email, $template, $websiteId = null) return false; } - /** - * Match a customer by their RP token. - * - * @param string $rpToken - * @throws ExpiredException - * @throws NoSuchEntityException - * @return CustomerInterface - * @throws LocalizedException - */ - private function matchCustomerByRpToken(string $rpToken): CustomerInterface - { - $this->searchCriteriaBuilder->addFilter( - 'rp_token', - $rpToken - ); - $this->searchCriteriaBuilder->setPageSize(1); - $found = $this->customerRepository->getList( - $this->searchCriteriaBuilder->create() - ); - if ($found->getTotalCount() > 1) { - //Failed to generated unique RP token - throw new ExpiredException( - new Phrase('Reset password token expired.') - ); - } - if ($found->getTotalCount() === 0) { - //Customer with such token not found. - throw NoSuchEntityException::singleField( - 'rp_token', - $rpToken - ); - } - //Unique customer found. - return $found->getItems()[0]; - } - /** * Handle not supported template * @@ -662,15 +652,17 @@ private function matchCustomerByRpToken(string $rpToken): CustomerInterface */ private function handleUnknownTemplate($template) { - throw new InputException(__( - 'Invalid value of "%value" provided for the %fieldName field. Possible values: %template1 or %template2.', - [ - 'value' => $template, - 'fieldName' => 'template', - 'template1' => AccountManagement::EMAIL_REMINDER, - 'template2' => AccountManagement::EMAIL_RESET - ] - )); + throw new InputException( + __( + 'Invalid value of "%value" provided for the %fieldName field. Possible values: %template1 or %template2.', + [ + 'value' => $template, + 'fieldName' => 'template', + 'template1' => AccountManagement::EMAIL_REMINDER, + 'template2' => AccountManagement::EMAIL_RESET + ] + ) + ); } /** @@ -679,7 +671,7 @@ private function handleUnknownTemplate($template) public function resetPassword($email, $resetToken, $newPassword) { if (!$email) { - $customer = $this->matchCustomerByRpToken($resetToken); + $customer = $this->getByToken->execute($resetToken); $email = $customer->getEmail(); } else { $customer = $this->customerRepository->get($email); @@ -818,6 +810,8 @@ public function getConfirmationStatus($customerId) /** * @inheritdoc + * + * @throws LocalizedException */ public function createAccount(CustomerInterface $customer, $password = null, $redirectUrl = '') { @@ -840,6 +834,8 @@ public function createAccount(CustomerInterface $customer, $password = null, $re /** * @inheritdoc + * + * @throws InputMismatchException * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) */ @@ -894,11 +890,15 @@ public function createAccountWithPasswordHash(CustomerInterface $customer, $hash throw new InputMismatchException( __('A customer with the same email address already exists in an associated website.') ); + // phpcs:disable Magento2.Exceptions.ThrowCatch } catch (LocalizedException $e) { throw $e; } try { foreach ($customerAddresses as $address) { + if (!$this->isAddressAllowedForWebsite($address, $customer->getStoreId())) { + continue; + } if ($address->getId()) { $newAddress = clone $address; $newAddress->setId(null); @@ -910,6 +910,7 @@ public function createAccountWithPasswordHash(CustomerInterface $customer, $hash } } $this->customerRegistry->remove($customer->getId()); + // phpcs:disable Magento2.Exceptions.ThrowCatch } catch (InputException $e) { $this->customerRepository->delete($customer); throw $e; @@ -970,6 +971,8 @@ protected function sendEmailConfirmation(CustomerInterface $customer, $redirectU /** * @inheritdoc + * + * @throws InvalidEmailOrPasswordException */ public function changePassword($email, $currentPassword, $newPassword) { @@ -983,6 +986,8 @@ public function changePassword($email, $currentPassword, $newPassword) /** * @inheritdoc + * + * @throws InvalidEmailOrPasswordException */ public function changePasswordById($customerId, $currentPassword, $newPassword) { @@ -1012,6 +1017,7 @@ private function changePasswordForCustomer($customer, $currentPassword, $newPass { try { $this->getAuthentication()->authenticate($customer->getId(), $currentPassword); + // phpcs:disable Magento2.Exceptions.ThrowCatch } catch (InvalidEmailOrPasswordException $e) { throw new InvalidEmailOrPasswordException( __("The password doesn't match this account. Verify the password and try again.") @@ -1071,6 +1077,7 @@ public function validate(CustomerInterface $customer) $result = $this->getEavValidator()->isValid($customerModel); if ($result === false && is_array($this->getEavValidator()->getMessages())) { return $validationResults->setIsValid(false)->setMessages( + // phpcs:ignore Magento2.Functions.DiscouragedFunction call_user_func_array( 'array_merge', $this->getEavValidator()->getMessages() @@ -1118,12 +1125,14 @@ public function isCustomerInStore($customerWebsiteId, $storeId) * * @param int $customerId * @param string $resetPasswordLinkToken + * * @return bool - * @throws \Magento\Framework\Exception\State\InputMismatchException If token is mismatched - * @throws \Magento\Framework\Exception\State\ExpiredException If token is expired - * @throws \Magento\Framework\Exception\InputException If token or customer id is invalid - * @throws \Magento\Framework\Exception\NoSuchEntityException If customer doesn't exist + * @throws ExpiredException If token is expired + * @throws InputException If token or customer id is invalid + * @throws InputMismatchException If token is mismatched * @throws LocalizedException + * @throws NoSuchEntityException If customer doesn't exist + * @SuppressWarnings(PHPMD.LongVariable) */ private function validateResetPasswordToken($customerId, $resetPasswordLinkToken) { @@ -1138,7 +1147,8 @@ private function validateResetPasswordToken($customerId, $resetPasswordLinkToken if ($customerId === null) { //Looking for the customer. - $customerId = $this->matchCustomerByRpToken($resetPasswordLinkToken) + $customerId = $this->getByToken + ->execute($resetPasswordLinkToken) ->getId(); } if (!is_string($resetPasswordLinkToken) || empty($resetPasswordLinkToken)) { @@ -1306,13 +1316,20 @@ protected function sendEmailTemplate( } $transport = $this->transportBuilder->setTemplateIdentifier($templateId) - ->setTemplateOptions(['area' => Area::AREA_FRONTEND, 'store' => $storeId]) + ->setTemplateOptions( + [ + 'area' => Area::AREA_FRONTEND, + 'store' => $storeId + ] + ) ->setTemplateVars($templateParams) - ->setFrom($this->scopeConfig->getValue( - $sender, - ScopeInterface::SCOPE_STORE, - $storeId - )) + ->setFrom( + $this->scopeConfig->getValue( + $sender, + ScopeInterface::SCOPE_STORE, + $storeId + ) + ) ->addTo($email, $this->customerViewHelper->getCustomerName($customer)) ->getTransport(); @@ -1606,4 +1623,18 @@ private function setIgnoreValidationFlag($customer) { $customer->setData('ignore_validation_flag', true); } + + /** + * Check is address allowed for store + * + * @param AddressInterface $address + * @param int|null $storeId + * @return bool + */ + private function isAddressAllowedForWebsite(AddressInterface $address, $storeId): bool + { + $allowedCountries = $this->allowedCountriesReader->getAllowedCountries(ScopeInterface::SCOPE_STORE, $storeId); + + return in_array($address->getCountryId(), $allowedCountries); + } } diff --git a/app/code/Magento/Customer/Model/Address/AbstractAddress.php b/app/code/Magento/Customer/Model/Address/AbstractAddress.php index d8d0646b30bb8..158461b4d9c17 100644 --- a/app/code/Magento/Customer/Model/Address/AbstractAddress.php +++ b/app/code/Magento/Customer/Model/Address/AbstractAddress.php @@ -375,6 +375,8 @@ public function getRegion() } } elseif (is_string($region)) { $this->setData('region', $region); + } elseif (!$regionId && is_array($region)) { + $this->setData('region', $regionId); } return $this->getData('region'); diff --git a/app/code/Magento/Customer/Model/Address/DataProvider.php b/app/code/Magento/Customer/Model/Address/DataProvider.php index e1dd68207cae5..e7f9fce021709 100644 --- a/app/code/Magento/Customer/Model/Address/DataProvider.php +++ b/app/code/Magento/Customer/Model/Address/DataProvider.php @@ -19,6 +19,7 @@ /** * Dataprovider of customer addresses for customer address grid. + * * @property \Magento\Customer\Model\ResourceModel\Address\Collection $collection */ class DataProvider extends \Magento\Ui\DataProvider\AbstractDataProvider @@ -222,8 +223,7 @@ private function getAttributesMeta(Type $entityType): array $meta[$attribute->getAttributeCode()] = $this->attributeMetadataResolver->getAttributesMeta( $attribute, $entityType, - $this->allowToShowHiddenAttributes, - $this->getRequestFieldName() + $this->allowToShowHiddenAttributes ); } $this->attributeMetadataResolver->processWebsiteMeta($meta); diff --git a/app/code/Magento/Customer/Model/AttributeMetadataResolver.php b/app/code/Magento/Customer/Model/AttributeMetadataResolver.php index c22cc9a4f23f4..979730eb1c9c2 100644 --- a/app/code/Magento/Customer/Model/AttributeMetadataResolver.php +++ b/app/code/Magento/Customer/Model/AttributeMetadataResolver.php @@ -101,15 +101,13 @@ public function __construct( * @param AbstractAttribute $attribute * @param Type $entityType * @param bool $allowToShowHiddenAttributes - * @param string $requestFieldName * @return array * @throws \Magento\Framework\Exception\LocalizedException */ public function getAttributesMeta( AbstractAttribute $attribute, Type $entityType, - bool $allowToShowHiddenAttributes, - string $requestFieldName + bool $allowToShowHiddenAttributes ): array { $meta = $this->modifyBooleanAttributeMeta($attribute); // use getDataUsingMethod, since some getters are defined and apply additional processing of returning value @@ -138,7 +136,6 @@ public function getAttributesMeta( $meta['arguments']['data']['config']['componentType'] = Field::NAME; $meta['arguments']['data']['config']['visible'] = $this->canShowAttribute( $attribute, - $requestFieldName, $allowToShowHiddenAttributes ); @@ -155,48 +152,16 @@ public function getAttributesMeta( * Detect can we show attribute on specific form or not * * @param AbstractAttribute $customerAttribute - * @param string $requestFieldName * @param bool $allowToShowHiddenAttributes * @return bool */ private function canShowAttribute( AbstractAttribute $customerAttribute, - string $requestFieldName, bool $allowToShowHiddenAttributes ) { - $userDefined = (bool)$customerAttribute->getIsUserDefined(); - if (!$userDefined) { - return $customerAttribute->getIsVisible(); - } - - $canShowOnForm = $this->canShowAttributeInForm($customerAttribute, $requestFieldName); - - return ($allowToShowHiddenAttributes && $canShowOnForm) || - (!$allowToShowHiddenAttributes && $canShowOnForm && $customerAttribute->getIsVisible()); - } - - /** - * Check whether the specific attribute can be shown in form: customer registration, customer edit, etc... - * - * @param AbstractAttribute $customerAttribute - * @param string $requestFieldName - * @return bool - */ - private function canShowAttributeInForm(AbstractAttribute $customerAttribute, string $requestFieldName): bool - { - $isRegistration = $this->context->getRequestParam($requestFieldName) === null; - - if ($customerAttribute->getEntityType()->getEntityTypeCode() === 'customer') { - return \is_array($customerAttribute->getUsedInForms()) && - ( - (\in_array('customer_account_create', $customerAttribute->getUsedInForms(), true) - && $isRegistration) || - (\in_array('customer_account_edit', $customerAttribute->getUsedInForms(), true) - && !$isRegistration) - ); - } - return \is_array($customerAttribute->getUsedInForms()) && - \in_array('customer_address_edit', $customerAttribute->getUsedInForms(), true); + return $allowToShowHiddenAttributes && (bool) $customerAttribute->getIsUserDefined() + ? true + : (bool) $customerAttribute->getIsVisible(); } /** diff --git a/app/code/Magento/Customer/Model/Customer/DataProvider.php b/app/code/Magento/Customer/Model/Customer/DataProvider.php index 5bad0a41aadc3..0b7c618e6a18b 100644 --- a/app/code/Magento/Customer/Model/Customer/DataProvider.php +++ b/app/code/Magento/Customer/Model/Customer/DataProvider.php @@ -307,45 +307,17 @@ protected function getAttributesMeta(Type $entityType) return $meta; } - /** - * Check whether the specific attribute can be shown in form: customer registration, customer edit, etc... - * - * @param Attribute $customerAttribute - * @return bool - */ - private function canShowAttributeInForm(AbstractAttribute $customerAttribute) - { - $isRegistration = $this->context->getRequestParam($this->getRequestFieldName()) === null; - - if ($customerAttribute->getEntityType()->getEntityTypeCode() === 'customer') { - return is_array($customerAttribute->getUsedInForms()) && - ( - (in_array('customer_account_create', $customerAttribute->getUsedInForms()) && $isRegistration) || - (in_array('customer_account_edit', $customerAttribute->getUsedInForms()) && !$isRegistration) - ); - } else { - return is_array($customerAttribute->getUsedInForms()) && - in_array('customer_address_edit', $customerAttribute->getUsedInForms()); - } - } - /** * Detect can we show attribute on specific form or not * * @param Attribute $customerAttribute * @return bool */ - private function canShowAttribute(AbstractAttribute $customerAttribute) + private function canShowAttribute(AbstractAttribute $customerAttribute): bool { - $userDefined = (bool) $customerAttribute->getIsUserDefined(); - if (!$userDefined) { - return $customerAttribute->getIsVisible(); - } - - $canShowOnForm = $this->canShowAttributeInForm($customerAttribute); - - return ($this->allowToShowHiddenAttributes && $canShowOnForm) || - (!$this->allowToShowHiddenAttributes && $canShowOnForm && $customerAttribute->getIsVisible()); + return $this->allowToShowHiddenAttributes && (bool) $customerAttribute->getIsUserDefined() + ? true + : (bool) $customerAttribute->getIsVisible(); } /** diff --git a/app/code/Magento/Customer/Model/Customer/DataProviderWithDefaultAddresses.php b/app/code/Magento/Customer/Model/Customer/DataProviderWithDefaultAddresses.php index 07b8681df91ac..6d18b881a69ff 100644 --- a/app/code/Magento/Customer/Model/Customer/DataProviderWithDefaultAddresses.php +++ b/app/code/Magento/Customer/Model/Customer/DataProviderWithDefaultAddresses.php @@ -188,8 +188,7 @@ private function getAttributesMeta(Type $entityType): array $meta[$attribute->getAttributeCode()] = $this->attributeMetadataResolver->getAttributesMeta( $attribute, $entityType, - $this->allowToShowHiddenAttributes, - $this->getRequestFieldName() + $this->allowToShowHiddenAttributes ); } $this->attributeMetadataResolver->processWebsiteMeta($meta); diff --git a/app/code/Magento/Customer/Model/CustomerAuthUpdate.php b/app/code/Magento/Customer/Model/CustomerAuthUpdate.php index 06de649524e71..bc9bffb6ffdf0 100644 --- a/app/code/Magento/Customer/Model/CustomerAuthUpdate.php +++ b/app/code/Magento/Customer/Model/CustomerAuthUpdate.php @@ -6,31 +6,43 @@ namespace Magento\Customer\Model; +use Magento\Customer\Model\ResourceModel\Customer as CustomerResourceModel; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\Exception\NoSuchEntityException; + /** * Customer Authentication update model. */ class CustomerAuthUpdate { /** - * @var \Magento\Customer\Model\CustomerRegistry + * @var CustomerRegistry */ protected $customerRegistry; /** - * @var \Magento\Customer\Model\ResourceModel\Customer + * @var CustomerResourceModel */ protected $customerResourceModel; /** - * @param \Magento\Customer\Model\CustomerRegistry $customerRegistry - * @param \Magento\Customer\Model\ResourceModel\Customer $customerResourceModel + * @var Customer + */ + private $customerModel; + + /** + * @param CustomerRegistry $customerRegistry + * @param CustomerResourceModel $customerResourceModel + * @param Customer|null $customerModel */ public function __construct( - \Magento\Customer\Model\CustomerRegistry $customerRegistry, - \Magento\Customer\Model\ResourceModel\Customer $customerResourceModel + CustomerRegistry $customerRegistry, + CustomerResourceModel $customerResourceModel, + Customer $customerModel = null ) { $this->customerRegistry = $customerRegistry; $this->customerResourceModel = $customerResourceModel; + $this->customerModel = $customerModel ?: ObjectManager::getInstance()->get(Customer::class); } /** @@ -38,21 +50,30 @@ public function __construct( * * @param int $customerId * @return $this + * @throws NoSuchEntityException */ public function saveAuth($customerId) { $customerSecure = $this->customerRegistry->retrieveSecureData($customerId); + $this->customerResourceModel->load($this->customerModel, $customerId); + $currentLockExpiresVal = $this->customerModel->getData('lock_expires'); + $newLockExpiresVal = $customerSecure->getData('lock_expires'); + $this->customerResourceModel->getConnection()->update( $this->customerResourceModel->getTable('customer_entity'), [ 'failures_num' => $customerSecure->getData('failures_num'), 'first_failure' => $customerSecure->getData('first_failure'), - 'lock_expires' => $customerSecure->getData('lock_expires'), + 'lock_expires' => $newLockExpiresVal, ], $this->customerResourceModel->getConnection()->quoteInto('entity_id = ?', $customerId) ); + if ($currentLockExpiresVal !== $newLockExpiresVal) { + $this->customerModel->reindex(); + } + return $this; } } diff --git a/app/code/Magento/Customer/Model/ForgotPasswordToken/ConfirmCustomerByToken.php b/app/code/Magento/Customer/Model/ForgotPasswordToken/ConfirmCustomerByToken.php new file mode 100644 index 0000000000000..6aadc814a4b9b --- /dev/null +++ b/app/code/Magento/Customer/Model/ForgotPasswordToken/ConfirmCustomerByToken.php @@ -0,0 +1,58 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Customer\Model\ForgotPasswordToken; + +use Magento\Customer\Api\CustomerRepositoryInterface; + +/** + * Confirm customer by reset password token + */ +class ConfirmCustomerByToken +{ + /** + * @var GetCustomerByToken + */ + private $getByToken; + + /** + * @var CustomerRepositoryInterface + */ + private $customerRepository; + + /** + * ConfirmByToken constructor. + * + * @param GetCustomerByToken $getByToken + * @param CustomerRepositoryInterface $customerRepository + */ + public function __construct( + GetCustomerByToken $getByToken, + CustomerRepositoryInterface $customerRepository + ) { + $this->getByToken = $getByToken; + $this->customerRepository = $customerRepository; + } + + /** + * Confirm customer account my rp_token + * + * @param string $resetPasswordToken + * + * @return void + * @throws \Magento\Framework\Exception\LocalizedException + */ + public function execute(string $resetPasswordToken): void + { + $customer = $this->getByToken->execute($resetPasswordToken); + if ($customer->getConfirmation()) { + $this->customerRepository->save( + $customer->setConfirmation(null) + ); + } + } +} diff --git a/app/code/Magento/Customer/Model/ForgotPasswordToken/GetCustomerByToken.php b/app/code/Magento/Customer/Model/ForgotPasswordToken/GetCustomerByToken.php new file mode 100644 index 0000000000000..09af4e296bd92 --- /dev/null +++ b/app/code/Magento/Customer/Model/ForgotPasswordToken/GetCustomerByToken.php @@ -0,0 +1,89 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Customer\Model\ForgotPasswordToken; + +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Api\Data\CustomerInterface; +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Exception\State\ExpiredException; +use Magento\Framework\Phrase; + +/** + * Get Customer By reset password token + * @SuppressWarnings(PHPMD.LongVariable) + */ +class GetCustomerByToken +{ + /** + * @var \Magento\Customer\Api\CustomerRepositoryInterface + */ + private $customerRepository; + + /** + * @var \Magento\Framework\Api\SearchCriteriaBuilder + */ + private $searchCriteriaBuilder; + + /** + * ForgotPassword constructor. + * + * @param \Magento\Customer\Api\CustomerRepositoryInterface $customerRepository + * @param \Magento\Framework\Api\SearchCriteriaBuilder $searchCriteriaBuilder + */ + public function __construct( + CustomerRepositoryInterface $customerRepository, + SearchCriteriaBuilder $searchCriteriaBuilder + ) { + $this->customerRepository = $customerRepository; + $this->searchCriteriaBuilder = $searchCriteriaBuilder; + } + + /** + * Get customer by rp_token + * + * @param string $resetPasswordToken + * + * @return \Magento\Customer\Api\Data\CustomerInterface + * @throws ExpiredException + * @throws NoSuchEntityException + * @throws \Magento\Framework\Exception\LocalizedException + */ + public function execute(string $resetPasswordToken):CustomerInterface + { + $this->searchCriteriaBuilder->addFilter( + 'rp_token', + $resetPasswordToken + ); + $this->searchCriteriaBuilder->setPageSize(1); + $found = $this->customerRepository->getList( + $this->searchCriteriaBuilder->create() + ); + + if ($found->getTotalCount() > 1) { + //Failed to generated unique RP token + throw new ExpiredException( + new Phrase('Reset password token expired.') + ); + } + if ($found->getTotalCount() === 0) { + //Customer with such token not found. + new NoSuchEntityException( + new Phrase( + 'No such entity with rp_token = %value', + [ + 'value' => $resetPasswordToken + ] + ) + ); + } + + //Unique customer found. + return $found->getItems()[0]; + } +} diff --git a/app/code/Magento/Customer/Model/ResourceModel/Customer.php b/app/code/Magento/Customer/Model/ResourceModel/Customer.php index 2eb1ef897e70e..94196df6fe093 100644 --- a/app/code/Magento/Customer/Model/ResourceModel/Customer.php +++ b/app/code/Magento/Customer/Model/ResourceModel/Customer.php @@ -95,9 +95,12 @@ protected function _getDefaultAttributes() /** * Check customer scope, email and confirmation key before saving * - * @param \Magento\Framework\DataObject $customer + * @param \Magento\Framework\DataObject|\Magento\Customer\Api\Data\CustomerInterface $customer + * * @return $this - * @throws \Magento\Framework\Exception\LocalizedException + * @throws AlreadyExistsException + * @throws ValidatorException + * @throws \Magento\Framework\Exception\NoSuchEntityException * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) */ @@ -141,9 +144,7 @@ protected function _beforeSave(\Magento\Framework\DataObject $customer) } // set confirmation key logic - if ($customer->getForceConfirmed() || $customer->getPasswordHash() == '') { - $customer->setConfirmation(null); - } elseif (!$customer->getId() && $customer->isConfirmationRequired()) { + if (!$customer->getId() && $customer->isConfirmationRequired()) { $customer->setConfirmation($customer->getRandomConfirmationKey()); } // remove customer confirmation key from database, if empty @@ -163,7 +164,7 @@ protected function _beforeSave(\Magento\Framework\DataObject $customer) * * @param \Magento\Customer\Model\Customer $customer * @return void - * @throws \Magento\Framework\Validator\Exception + * @throws ValidatorException */ protected function _validate($customer) { diff --git a/app/code/Magento/Customer/Model/ResourceModel/CustomerRepository.php b/app/code/Magento/Customer/Model/ResourceModel/CustomerRepository.php index 038526053b872..529b0e806972a 100644 --- a/app/code/Magento/Customer/Model/ResourceModel/CustomerRepository.php +++ b/app/code/Magento/Customer/Model/ResourceModel/CustomerRepository.php @@ -209,7 +209,9 @@ public function save(CustomerInterface $customer, $passwordHash = null) $customerModel->setId($customer->getId()); $storeId = $customerModel->getStoreId(); if ($storeId === null) { - $customerModel->setStoreId($this->storeManager->getStore()->getId()); + $customerModel->setStoreId( + $prevCustomerData ? $prevCustomerData->getStoreId() : $this->storeManager->getStore()->getId() + ); } // Need to use attribute set or future updates can cause data loss if (!$customerModel->getAttributeSetId()) { @@ -277,7 +279,6 @@ public function save(CustomerInterface $customer, $passwordHash = null) 'delegate_data' => $delegatedNewOperation ? $delegatedNewOperation->getAdditionalData() : [], ] ); - return $savedCustomer; } diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminAssertCustomerGroupOnCustomerFormActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminAssertCustomerGroupOnCustomerFormActionGroup.xml new file mode 100644 index 0000000000000..3112f2b3efee0 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminAssertCustomerGroupOnCustomerFormActionGroup.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminAssertCustomerGroupOnCustomerForm"> + <arguments> + <argument name="customerGroupName" type="string"/> + </arguments> + <amOnPage url="{{AdminNewCustomerPage.url}}" stepKey="amOnCustomerCreatePage"/> + <waitForElementVisible selector="{{AdminCustomerAccountInformationSection.group}}" stepKey="waitForElementVisible"/> + <see selector="{{AdminCustomerAccountInformationSection.group}}" userInput="{{customerGroupName}}" stepKey="assertCustomerGroupPresent"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminAssertCustomerGroupOnProductFormActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminAssertCustomerGroupOnProductFormActionGroup.xml new file mode 100644 index 0000000000000..0f6d6a5fcfd98 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminAssertCustomerGroupOnProductFormActionGroup.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminAssertCustomerGroupOnProductForm"> + <arguments> + <argument name="customerGroupName" type="string"/> + </arguments> + <amOnPage url="{{AdminProductCreatePage.url(AddToDefaultSet.attributeSetId, 'simple')}}" stepKey="amOnProductCreatePage"/> + <waitForElementVisible selector="{{AdminProductFormSection.advancedPricingLink}}" stepKey="waitForAdvancedPricingLinkVisible"/> + <click selector="{{AdminProductFormSection.advancedPricingLink}}" stepKey="clickOnAdvancedPricingButton"/> + <waitForElementVisible selector="{{AdminProductFormAdvancedPricingSection.customerGroupPriceAddButton}}" stepKey="waitForAddButtonVisible"/> + <click selector="{{AdminProductFormAdvancedPricingSection.customerGroupPriceAddButton}}" stepKey="clickAddButton"/> + <see selector="{{AdminProductFormAdvancedPricingSection.productTierPriceCustGroupSelect('0')}}" userInput="{{customerGroupName}}" stepKey="assertCustomerGroupPresent"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminAssertCustomerGroupPresentInGridActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminAssertCustomerGroupPresentInGridActionGroup.xml new file mode 100644 index 0000000000000..248c93a16def8 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminAssertCustomerGroupPresentInGridActionGroup.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminAssertCustomerGroupPresentInGrid" extends="AdminFilterCustomerGroupByNameActionGroup"> + <!--- Assume we are on admin customer group page. --> + <see selector="{{AdminDataGridTableSection.column('Group')}}" userInput="{{customerGroupName}}" after="clickApplyFiltersButton" stepKey="seeCustomerGroupNameInGrid"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminAssertErrorMessageCustomerGroupAlreadyExistsActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminAssertErrorMessageCustomerGroupAlreadyExistsActionGroup.xml new file mode 100644 index 0000000000000..5eb52630d906b --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminAssertErrorMessageCustomerGroupAlreadyExistsActionGroup.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminAssertErrorMessageCustomerGroupAlreadyExists" extends="AdminCreateCustomerGroupActionGroup"> + <remove keyForRemoval="seeCustomerGroupSaveMessage"/> + <waitForElementVisible selector="{{AdminMessagesSection.errorMessage}}" stepKey="waitForElementVisible"/> + <see selector="{{AdminMessagesSection.errorMessage}}" userInput="Customer Group already exists." stepKey="seeErrorMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminSaveCustomerAddressActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminSaveCustomerAddressActionGroup.xml new file mode 100644 index 0000000000000..e47aa8809f080 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AdminSaveCustomerAddressActionGroup.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminSaveCustomerAddressActionGroup"> + <click selector="{{StorefrontCustomerAddressFormSection.saveAddress}}" stepKey="saveCustomerAddress"/> + <see selector="{{AdminMessagesSection.successMessage}}" userInput="You saved the address." stepKey="seeSuccessMessage"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertCustomerLoggedInActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertCustomerLoggedInActionGroup.xml index d9da950fe7115..d2d4d86d7f964 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertCustomerLoggedInActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/AssertCustomerLoggedInActionGroup.xml @@ -12,6 +12,7 @@ <arguments> <argument name="customerFullName" type="string" /> </arguments> + <waitForPageLoad stepKey="waitForPageLoad"/> <see userInput="Welcome, {{customerFullName}}!" selector="{{StorefrontPanelHeaderSection.welcomeMessage}}" stepKey="verifyMessage" /> </actionGroup> </actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/FillNewCustomerAddressRequiredFieldsActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/FillNewCustomerAddressRequiredFieldsActionGroup.xml new file mode 100644 index 0000000000000..c533d1a6e55bb --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/FillNewCustomerAddressRequiredFieldsActionGroup.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="FillNewCustomerAddressRequiredFieldsActionGroup"> + <arguments> + <argument name="address" type="entity"/> + </arguments> + <fillField selector="{{StorefrontCustomerAddressFormSection.firstName}}" userInput="{{address.firstname}}" stepKey="fillFirstName"/> + <fillField selector="{{StorefrontCustomerAddressFormSection.lastName}}" userInput="{{address.lastname}}" stepKey="fillLastName"/> + <fillField selector="{{StorefrontCustomerAddressFormSection.phoneNumber}}" userInput="{{address.telephone}}" stepKey="fillPhoneNumber"/> + <fillField selector="{{StorefrontCustomerAddressFormSection.streetAddress}}" userInput="{{address.street[0]}}" stepKey="fillStreetAddress"/> + <fillField selector="{{StorefrontCustomerAddressFormSection.city}}" userInput="{{address.city}}" stepKey="fillCity"/> + <selectOption selector="{{StorefrontCustomerAddressFormSection.state}}" userInput="{{address.state}}" stepKey="selectState"/> + <fillField selector="{{StorefrontCustomerAddressFormSection.zip}}" userInput="{{address.postcode}}" stepKey="fillZip"/> + <selectOption selector="{{StorefrontCustomerAddressFormSection.country}}" userInput="{{address.country}}" stepKey="selectCountry"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/SignUpNewUserFromStorefrontActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/SignUpNewUserFromStorefrontActionGroup.xml index ef956293d367b..2e2b1892774c0 100644 --- a/app/code/Magento/Customer/Test/Mftf/ActionGroup/SignUpNewUserFromStorefrontActionGroup.xml +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/SignUpNewUserFromStorefrontActionGroup.xml @@ -57,7 +57,7 @@ <arguments> <argument name="Address"/> </arguments> - + <amOnPage url="customer/address/new/" stepKey="goToAddressPage"/> <waitForPageLoad stepKey="waitForAddressPage"/> <fillField stepKey="fillFirstName" selector="{{StorefrontCustomerAddressSection.firstName}}" userInput="{{Address.firstname}}"/> @@ -154,4 +154,4 @@ <waitForPageLoad stepKey="waitForRegistered" after="clickCreateAccountButton"/> <remove keyForRemoval="seeThankYouMessage"/> </actionGroup> -</actionGroups> \ No newline at end of file +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontAssertRegistrationPageFieldsActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontAssertRegistrationPageFieldsActionGroup.xml new file mode 100644 index 0000000000000..d76277d2e5e45 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontAssertRegistrationPageFieldsActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontAssertRegistrationPageFields"> + <seeInCurrentUrl url="{{StorefrontCustomerCreatePage.url}}" stepKey="seeCreateNewCustomerAccountPage"/> + <seeElement selector="{{StorefrontCustomerCreateFormSection.firstnameField}}" stepKey="seeFirstNameField"/> + <seeElement selector="{{StorefrontCustomerCreateFormSection.lastnameField}}" stepKey="seeFLastNameField"/> + <seeElement selector="{{StorefrontCustomerCreateFormSection.emailField}}" stepKey="seeEmailField"/> + <seeElement selector="{{StorefrontCustomerCreateFormSection.passwordField}}" stepKey="seePasswordField"/> + <seeElement selector="{{StorefrontCustomerCreateFormSection.confirmPasswordField}}" stepKey="seeConfirmPasswordField"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontFillCustomerLoginFormWithWrongPasswordActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontFillCustomerLoginFormWithWrongPasswordActionGroup.xml new file mode 100644 index 0000000000000..16d7fd197b52f --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontFillCustomerLoginFormWithWrongPasswordActionGroup.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontFillCustomerLoginFormWithWrongPasswordActionGroup" extends="StorefrontFillCustomerLoginFormActionGroup"> + <remove keyForRemoval="fillPassword"/> + <fillField userInput="{{customer.password}}_INCORRECT" selector="{{StorefrontCustomerSignInFormSection.passwordField}}" stepKey="fillPassword"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontRegisterCustomerFromOrderSuccessPageActionGroup.xml b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontRegisterCustomerFromOrderSuccessPageActionGroup.xml new file mode 100644 index 0000000000000..5e24592bf017f --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/ActionGroup/StorefrontRegisterCustomerFromOrderSuccessPageActionGroup.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontRegisterCustomerFromOrderSuccessPage"> + <arguments> + <argument name="customer" /> + </arguments> + <click selector="{{CheckoutSuccessRegisterSection.createAccountButton}}" stepKey="clickCreateAccountButton"/> + <fillField selector="{{StorefrontCustomerCreateFormSection.passwordField}}" userInput="{{customer.password}}" stepKey="typePassword"/> + <fillField selector="{{StorefrontCustomerCreateFormSection.confirmPasswordField}}" userInput="{{customer.password}}" stepKey="typeConfirmationPassword"/> + <click selector="{{StorefrontCustomerCreateFormSection.createAccountButton}}" stepKey="clickOnCreateAccount"/> + <see selector="{{StorefrontMessagesSection.success}}" userInput="Thank you for registering" stepKey="verifyAccountCreated"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml b/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml index 05c17c9fbb694..88f86e456e5bf 100755 --- a/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml +++ b/app/code/Magento/Customer/Test/Mftf/Data/AddressData.xml @@ -284,4 +284,18 @@ <data key="telephone">333-33-333-33</data> <data key="country">Germany</data> </entity> + <entity name="US_Address_California"> + <data key="firstname">John</data> + <data key="lastname">Doe</data> + <data key="company">Magento</data> + <array key="street"> + <item>6161 West Centinela Avenue</item> + </array> + <data key="city">Culver City</data> + <data key="country_id">United States</data> + <data key="country">United States</data> + <data key="state">California</data> + <data key="postcode">90230</data> + <data key="telephone">555-55-555-55</data> + </entity> </entities> diff --git a/app/code/Magento/Customer/Test/Mftf/Data/CustomerConfigData.xml b/app/code/Magento/Customer/Test/Mftf/Data/CustomerConfigData.xml index 11a47459ab7b3..870bd929eea9a 100644 --- a/app/code/Magento/Customer/Test/Mftf/Data/CustomerConfigData.xml +++ b/app/code/Magento/Customer/Test/Mftf/Data/CustomerConfigData.xml @@ -28,4 +28,17 @@ <entity name="CustomerAccountSharingInherit" type="account_share_scope_inherit"> <data key="inherit">true</data> </entity> + <entity name="StorefrontCustomerLockoutFailuresDefaultConfigData"> + <!-- Magento default value --> + <data key="path">customer/password/lockout_failures</data> + <data key="scope_id">0</data> + <data key="label">10</data> + <data key="value">10</data> + </entity> + <entity name="StorefrontCustomerLockoutFailures5ConfigData"> + <data key="path">customer/password/lockout_failures</data> + <data key="scope_id">0</data> + <data key="label">5</data> + <data key="value">5</data> + </entity> </entities> diff --git a/app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml b/app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml index 5904067aea639..6f8f354fd45c4 100644 --- a/app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml +++ b/app/code/Magento/Customer/Test/Mftf/Data/CustomerData.xml @@ -271,4 +271,14 @@ <requiredEntity type="address">US_Address_TX</requiredEntity> <requiredEntity type="address">US_Address_NY_Not_Default_Address</requiredEntity> </entity> + <entity name="John_Smith_Customer" type="customer"> + <data key="group_id">1</data> + <data key="email" unique="prefix">john.smith@example.com</data> + <data key="firstname">John</data> + <data key="lastname">Smith</data> + <data key="fullname">John Smith</data> + <data key="password">pwdTest123!</data> + <data key="store_id">0</data> + <data key="website_id">0</data> + </entity> </entities> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerActivitiesConfigureSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerActivitiesConfigureSection.xml index 5d7c4b1171558..cbe22fd26e402 100644 --- a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerActivitiesConfigureSection.xml +++ b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerActivitiesConfigureSection.xml @@ -9,7 +9,8 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminCustomerActivitiesConfigureSection"> - <element name="addAttribute" type="select" selector="[id*='attribute']" timeout="30"/> + <element name="addAttribute" type="select" selector="//select[contains(concat(' ',normalize-space(@class),' '),' super-attribute-select ')]" timeout="30"/> + <element name="dropdownProductSelection" type="select" selector="//option[contains(text(), '{{productName}}')]" parameterized="true" timeout="30"/> <element name="okButton" type="button" selector="//button[contains(concat(' ',normalize-space(@class),' '),' action-primary ')]" timeout="30"/> </section> </sections> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerActivitiesLastOrderedSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerActivitiesLastOrderedSection.xml new file mode 100644 index 0000000000000..22fa1b5bd21cb --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerActivitiesLastOrderedSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminCustomerActivitiesLastOrderedSection"> + <element name="addProductToOrder" type="text" selector="//div[@id='sidebar_data_reorder']//tr[td[.='{{productName}}']]//input[contains(@name,'add')]" parameterized="true" timeout="30"/> + </section> +</sections> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerActivitiesRecentlyViewedSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerActivitiesRecentlyViewedSection.xml new file mode 100644 index 0000000000000..b3a0151135491 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Section/AdminCustomerActivitiesRecentlyViewedSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminCustomerActivitiesRecentlyViewedSection"> + <element name="addToOrderConfigure" type="button" selector="//div[@id='sidebar_data_pviewed']//tr[td[contains(.,'{{productName}}')]]//a[contains(@class, 'icon-configure')]" parameterized="true" timeout="30"/> + </section> +</sections> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/AdminEditCustomerOrdersSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/AdminEditCustomerOrdersSection.xml index 89fed43184b84..fb3d1570848e4 100644 --- a/app/code/Magento/Customer/Test/Mftf/Section/AdminEditCustomerOrdersSection.xml +++ b/app/code/Magento/Customer/Test/Mftf/Section/AdminEditCustomerOrdersSection.xml @@ -10,5 +10,6 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminEditCustomerOrdersSection"> <element name="orderGrid" type="text" selector="#customer_orders_grid_table"/> + <element name="orderIdInGrid" type="text" selector="//td[contains(., '{{orderId}}')]" parameterized="true"/> </section> </sections> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerOrderSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerOrderSection.xml index 9308a214cd803..ec5141d84b1bd 100644 --- a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerOrderSection.xml +++ b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerOrderSection.xml @@ -13,6 +13,7 @@ <element name="productCustomOptions" type="text" selector="//strong[contains(@class, 'product-item-name') and normalize-space(.)='{{var1}}']/following-sibling::*[contains(@class, 'item-options')]/dt[normalize-space(.)='{{var2}}']/following-sibling::dd[normalize-space(.)='{{var3}}']" parameterized="true"/> <element name="productCustomOptionsFile" type="text" selector="//strong[contains(@class, 'product-item-name') and normalize-space(.)='{{var1}}']/following-sibling::*[contains(@class, 'item-options')]/dt[normalize-space(.)='{{var2}}']/following-sibling::dd[contains(.,'{{var3}}')]" parameterized="true"/> <element name="productCustomOptionsLink" type="text" selector="//strong[contains(@class, 'product-item-name') and normalize-space(.)='{{var1}}']/following-sibling::*[contains(@class, 'item-options')]/dt[normalize-space(.)='{{var2}}']/following-sibling::dd//a[text() = '{{var3}}']" parameterized="true"/> + <element name="status" type="text" selector="//td[contains(concat(' ',normalize-space(@class),' '),' col status ')]"/> <element name="viewOrder" type="button" selector="//td[contains(concat(' ',normalize-space(@class),' '),' col actions ')]/a[contains(concat(' ',normalize-space(@class),' '),' action view ')]"/> <element name="tabRefund" type="button" selector="//a[text()='Refunds']"/> <element name="grandTotalRefund" type="text" selector="td[data-th='Grand Total'] > strong > span.price"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerOrderViewSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerOrderViewSection.xml index f831aabddd4ee..09b79fe831188 100644 --- a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerOrderViewSection.xml +++ b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerOrderViewSection.xml @@ -16,5 +16,7 @@ <element name="printOrderLink" type="text" selector="a.action.print" timeout="30"/> <element name="shippingAddress" type="text" selector=".box.box-order-shipping-address"/> <element name="billingAddress" type="text" selector=".box.box-order-billing-address"/> + <element name="orderStatusInGrid" type="text" selector="//td[contains(.,'{{orderId}}')]/../td[contains(.,'{{status}}')]" parameterized="true"/> + <element name="pager" type="block" selector=".pager"/> </section> </sections> diff --git a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerSignInFormSection.xml b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerSignInFormSection.xml index 5a4aff383b996..b2c583e008acc 100644 --- a/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerSignInFormSection.xml +++ b/app/code/Magento/Customer/Test/Mftf/Section/StorefrontCustomerSignInFormSection.xml @@ -13,6 +13,7 @@ <element name="passwordField" type="input" selector="#pass"/> <element name="signInAccountButton" type="button" selector="#send2" timeout="30"/> <element name="forgotPasswordLink" type="button" selector=".action.remind" timeout="10"/> + <element name="customerLoginBlock" type="text" selector=".login-container .block.block-customer-login"/> </section> <section name="StorefrontCustomerSignInPopupFormSection"> <element name="errorMessage" type="input" selector="[data-ui-id='checkout-cart-validationmessages-message-error']"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerGroupAlreadyExistsTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerGroupAlreadyExistsTest.xml new file mode 100644 index 0000000000000..6b1c1f29f97fc --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateCustomerGroupAlreadyExistsTest.xml @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCreateCustomerGroupAlreadyExistsTest"> + <annotations> + <features value="Create customer group already exists"/> + <stories value="Create customer group"/> + <title value="Create customer group already exists"/> + <description value="Create customer group already exists"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-5302"/> + <group value="customer"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <actionGroup ref="logout" stepKey="logout"/> + </after> + <!-- Steps: 1. Log in to backend as admin user. + 2. Navigate to Stores > Other Settings > Customer Groups. + 3. Start to create new Customer Group. + 4. Fill in all data according to data set: Group Name "General", Tax Class "Retail customer" + 5. Click "Save Customer Group" button. --> + <!-- Assert "Customer Group already exists." error message displayed --> + <actionGroup ref="AdminAssertErrorMessageCustomerGroupAlreadyExists" stepKey="seeErrorMessageCustomerGroupAlreadyExists"> + <argument name="groupName" value="{{GeneralCustomerGroup.code}}"/> + <argument name="taxClass" value="{{GeneralCustomerGroup.tax_class_name}}"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateRetailCustomerGroupTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateRetailCustomerGroupTest.xml new file mode 100644 index 0000000000000..4f1d88ffe99f5 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateRetailCustomerGroupTest.xml @@ -0,0 +1,71 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCreateRetailCustomerGroupTest"> + <annotations> + <features value="Create retail customer group"/> + <stories value="Create customer group"/> + <title value="Create retail customer group"/> + <description value="Create retail customer group"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-5301"/> + <group value="customer"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <actionGroup ref="AdminDeleteCustomerGroupActionGroup" stepKey="deleteCustomerGroup"> + <argument name="customerGroupName" value="{{CustomCustomerGroup.code}}"/> + </actionGroup> + <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearFilters"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + <!-- Steps: 1. Log in to backend as admin user. + 2. Navigate to Stores > Other Settings > Customer Groups. + 3. Start to create new Customer Group. + 4. Fill in all data according to data set. Tax Class - "Retail customer" + 5. Click "Save Customer Group" button. --> + <!-- Assert "You saved the customer group." success message displayed --> + <!-- Assert created Customer Group displayed In Grid --> + <actionGroup ref="AdminCreateCustomerGroupActionGroup" stepKey="createCustomerGroup"> + <argument name="groupName" value="{{CustomCustomerGroup.code}}"/> + <argument name="taxClass" value="{{CustomCustomerGroup.tax_class_name}}"/> + </actionGroup> + <actionGroup ref="AdminAssertCustomerGroupPresentInGrid" stepKey="assertCustomerGroupDisplayedInGrid"> + <argument name="customerGroupName" value="{{CustomCustomerGroup.code}}"/> + </actionGroup> + + <!-- 6. Go to Catalog -> Products -> click "Add Product" button -> click "Advanced Pricing" link -> Customer Group Price -> click "Add" button --> + <!-- Assert: Customer Group Displayed On Product Form --> + <actionGroup ref="AdminAssertCustomerGroupOnProductForm" stepKey="assertCustomerGroupDisplayedOnProductForm"> + <argument name="customerGroupName" value="{{CustomCustomerGroup.code}}"/> + </actionGroup> + + <!-- 7. Go to Customers -> All Customers -> click "Add New Customer" button --> + <!-- Assert created Customer Group displayed On Customer Form --> + <actionGroup ref="AdminAssertCustomerGroupOnCustomerForm" stepKey="assertCustomerGroupDisplayedOnCustomerForm"> + <argument name="customerGroupName" value="{{CustomCustomerGroup.code}}"/> + </actionGroup> + + <!-- 8. Go to Marketing - Catalog Price Rule - click "Add New Rule" button --> + <!-- Assert created Customer Group displayed On Catalog Price Rule Form --> + <actionGroup ref="AdminAssertCustomerGroupOnCatalogPriceRuleForm" stepKey="assertCustomerGroupDisplayedOnCatalogPriceRuleForm"> + <argument name="customerGroupName" value="{{CustomCustomerGroup.code}}"/> + </actionGroup> + + <!-- 9. Go to Marketing - Cart Price Rule - click "Add New Rule" button --> + <!-- Assert created Customer Group displayed On Cart Price Rule Form --> + <actionGroup ref="AdminAssertCustomerGroupOnCartPriceRuleForm" stepKey="assertCustomerGroupDisplayedOnCartPriceRuleForm"> + <argument name="customerGroupName" value="{{CustomCustomerGroup.code}}"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateTaxClassCustomerGroupTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateTaxClassCustomerGroupTest.xml new file mode 100644 index 0000000000000..7d54ede7c1612 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminCreateTaxClassCustomerGroupTest.xml @@ -0,0 +1,58 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCreateTaxClassCustomerGroupTest"> + <annotations> + <features value="Create tax class customer group"/> + <stories value="Create customer group"/> + <title value="Create tax class customer group"/> + <description value="Create tax class customer group"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-5303"/> + <group value="customer"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <!-- Create Tax Class "Customer tax class"--> + <createData entity="customerTaxClass" stepKey="createCustomerTaxClass"/> + <getData entity="customerTaxClass" stepKey="customerTaxClassData"> + <requiredEntity createDataKey="createCustomerTaxClass"/> + </getData> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <actionGroup ref="AdminDeleteCustomerGroupActionGroup" stepKey="deleteCustomerGroup"> + <argument name="customerGroupName" value="{{CustomCustomerGroup.code}}"/> + </actionGroup> + <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearFilters"/> + <deleteData createDataKey="createCustomerTaxClass" stepKey="deleteCustomerTaxClass"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + <!-- Steps: 1. Log in to backend as admin user. + 2. Navigate to Stores > Other Settings > Customer Groups. + 3. Start to create new Customer Group. + 4. Fill in all data according to data set: Tax Class "Customer tax class" + 5. Click "Save Customer Group" button. --> + <!-- Assert "You saved the customer group." success message displayed --> + <!-- Assert created Customer Group displayed In Grid --> + <actionGroup ref="AdminCreateCustomerGroupActionGroup" stepKey="createNewCustomerGroup"> + <argument name="groupName" value="{{CustomCustomerGroup.code}}"/> + <argument name="taxClass" value="$$customerTaxClassData.class_name$$"/> + </actionGroup> + <actionGroup ref="AdminAssertCustomerGroupPresentInGrid" stepKey="assertCustomerGroupDisplayedInGrid"> + <argument name="customerGroupName" value="{{CustomCustomerGroup.code}}"/> + </actionGroup> + <!-- 6. Go to Customers -> All Customers -> click "Add New Customer" button --> + <!-- Assert created Customer Group displayed On Customer Form --> + <actionGroup ref="AdminAssertCustomerGroupOnCustomerForm" stepKey="assertCustomerGroupDisplayedOnCustomerForm"> + <argument name="customerGroupName" value="{{CustomCustomerGroup.code}}"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AdminExactMatchSearchInCustomerGridTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AdminExactMatchSearchInCustomerGridTest.xml new file mode 100644 index 0000000000000..6a7aeab78bcde --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Test/AdminExactMatchSearchInCustomerGridTest.xml @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminExactMatchSearchInCustomerGridTest"> + <annotations> + <features value="Customer"/> + <stories value="Customer Search"/> + <title value="Admin customer grid exact match searching"/> + <description value="Admin customer grid exact match searching with quotes in keyword"/> + <severity value="MAJOR"/> + <testCaseId value="MC-16335"/> + <useCaseId value="MAGETWO-99605"/> + <group value="customer"/> + </annotations> + <before> + <createData entity="Simple_US_Customer" stepKey="createFirstCustomer"/> + <createData entity="Simple_US_Customer" stepKey="createSecondCustomer"> + <field key="firstname">"Jane Doe"</field> + </createData> + <actionGroup ref="LoginAsAdmin" stepKey="login"/> + </before> + <after> + <deleteData createDataKey="createFirstCustomer" stepKey="deleteFirstCustomer"/> + <deleteData createDataKey="createSecondCustomer" stepKey="deleteSecondCustomer"/> + <amOnPage url="{{AdminCustomerPage.url}}" stepKey="openCustomersGridPage"/> + <actionGroup ref="AdminResetFilterInCustomerAddressGrid" stepKey="clearCustomerGridFilter"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + <!--Step 1: Go to Customers > All Customers--> + <amOnPage url="{{AdminCustomerPage.url}}" stepKey="openCustomersGridPage"/> + <!--Step 2: On Customers grid page search customer by keyword with quotes--> + <actionGroup ref="searchAdminDataGridByKeyword" stepKey="searchCustomer"> + <argument name="keyword" value="$$createSecondCustomer.firstname$$"/> + </actionGroup> + <!--Step 3: Check if customer is placed in a first row and clear grid filter--> + <actionGroup ref="AdminAssertCustomerInCustomersGrid" stepKey="checkCustomerInGrid"> + <argument name="text" value="$$createSecondCustomer.fullname$$"/> + <argument name="row" value="1"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/AllowedCountriesRestrictionApplyOnBackendTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/AllowedCountriesRestrictionApplyOnBackendTest.xml index 11a6fbff8a405..6de03e225ae08 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/AllowedCountriesRestrictionApplyOnBackendTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/AllowedCountriesRestrictionApplyOnBackendTest.xml @@ -9,10 +9,10 @@ <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AllowedCountriesRestrictionApplyOnBackendTest"> <annotations> + <features value="Customer"/> <stories value="Country filter"/> <title value="Country filter on Customers page when allowed countries restriction for a default website is applied"/> <description value="Country filter on Customers page when allowed countries restriction for a default website is applied"/> - <features value="Customer"/> <severity value="MAJOR"/> <testCaseId value="MC-6441"/> <useCaseId value="MAGETWO-91523"/> @@ -114,6 +114,6 @@ <waitForPageLoad stepKey="waitForCustomersGrid"/> <click selector="{{AdminDataGridHeaderSection.filters}}" stepKey="openFiltersSectionOnCustomersGrid"/> <executeJS function="var len = document.querySelectorAll('{{AdminCustomerFiltersSection.countryOptions}}').length; return len-1;" stepKey="countriesAmount2"/> - <assertEquals expected='($countriesAmount)' expectedType="integer" actual="($countriesAmount2)" stepKey="assertCountryAmounts"/> + <assertEquals expected="($countriesAmount)" actual="($countriesAmount2)" stepKey="assertCountryAmounts"/> </test> </tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/ChangeCustomerGroupTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/ChangeCustomerGroupTest.xml index fb083f39ad387..e35a1ad61dc7c 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/ChangeCustomerGroupTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/ChangeCustomerGroupTest.xml @@ -67,6 +67,9 @@ <stories value="Change Customer Group"/> <group value="customer"/> <group value="mtf_migrated"/> + <skip> + <issueId value="MC-17140"/> + </skip> </annotations> <remove keyForRemoval="filterCustomer"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/DeleteCustomerGroupTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/DeleteCustomerGroupTest.xml index a085a67167f60..b19966e0102b6 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/DeleteCustomerGroupTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/DeleteCustomerGroupTest.xml @@ -10,18 +10,20 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="DeleteCustomerGroupTest"> <annotations> - <title value="Delete customer group entity test"/> - <description value="Delete a customer group"/> + <features value="Customer"/> <stories value="Delete Customer Group"/> - <testCaseId value="MC-14590" /> + <title value="Delete Customer Group in Admin Panel"/> + <description value="Admin should be able to delete a Customer Group"/> + <testCaseId value="MC-14590"/> + <severity value="MAJOR"/> <group value="customers"/> <group value="mtf_migrated"/> </annotations> <before> - <createData entity="CustomCustomerGroup" stepKey="customerGroup" /> + <createData entity="CustomCustomerGroup" stepKey="customerGroup"/> <createData entity="UsCustomerAssignedToNewCustomerGroup" stepKey="customer"> - <requiredEntity createDataKey="customerGroup" /> + <requiredEntity createDataKey="customerGroup"/> </createData> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> </before> @@ -35,21 +37,21 @@ <argument name="customerGroupName" value="$$customerGroup.code$$"/> </actionGroup> <actionGroup ref="AssertCustomerGroupNotInGridActionGroup" stepKey="assertCustomerGroupNotInGrid"> - <argument name="customerGroup" value="$$customerGroup$$" /> + <argument name="customerGroup" value="$$customerGroup$$"/> </actionGroup> <actionGroup ref="AdminOpenCustomerEditPageActionGroup" stepKey="openCustomerEditPage"> - <argument name="customerId" value="$$customer.id$$" /> + <argument name="customerId" value="$$customer.id$$"/> </actionGroup> <actionGroup ref="AssertCustomerGroupOnCustomerFormActionGroup" stepKey="assertCustomerGroupOnCustomerForm"> - <argument name="customerGroup" value="GeneralCustomerGroup" /> + <argument name="customerGroup" value="GeneralCustomerGroup"/> </actionGroup> - - <actionGroup ref="AdminOpenNewProductFormPageActionGroup" stepKey="openNewProductForm" /> - + + <actionGroup ref="AdminOpenNewProductFormPageActionGroup" stepKey="openNewProductForm"/> + <actionGroup ref="AssertCustomerGroupNotOnProductFormActionGroup" stepKey="assertCustomerGroupNotOnProductForm"> - <argument name="customerGroup" value="$$customerGroup$$" /> + <argument name="customerGroup" value="$$customerGroup$$"/> </actionGroup> </test> </tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml index 901018c2fd074..2b24233e8b072 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml @@ -17,6 +17,9 @@ <description value="New user signup and browses catalog, searches for product, adds product to cart, adds product to wishlist, compares products, uses coupon code and checks out."/> <severity value="CRITICAL"/> <testCaseId value="MAGETWO-87653"/> + <skip> + <issueId value="MC-17140"/> + </skip> </annotations> <before> <resetCookie userInput="PHPSESSID" stepKey="resetCookieForCart"/> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontCheckTaxAddingValidVATIdTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontCheckTaxAddingValidVATIdTest.xml index ab805193854b0..0cba9159dd5ac 100644 --- a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontCheckTaxAddingValidVATIdTest.xml +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontCheckTaxAddingValidVATIdTest.xml @@ -17,6 +17,9 @@ <severity value="MAJOR"/> <testCaseId value="MAGETWO-95028"/> <group value="customer"/> + <skip> + <issueId value="MC-16684"/> + </skip> </annotations> <before> <!--Log In--> @@ -83,6 +86,7 @@ <!--Add a product to the cart--> <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addProductToCart"/> <waitForPageLoad stepKey="waitForAddProductToCart"/> + <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessage"/> <!--Proceed to checkout--> <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="GoToCheckoutFromMinicartActionGroup"/> <!-- Click next button to open payment section --> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontCreateExistingCustomerTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontCreateExistingCustomerTest.xml new file mode 100644 index 0000000000000..952ac235d92a4 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontCreateExistingCustomerTest.xml @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontCreateExistingCustomerTest"> + <annotations> + <features value="Customer"/> + <stories value="Customer Registration"/> + <title value="Attempt to register customer on storefront with existing email"/> + <description value="Attempt to register customer on storefront with existing email"/> + <testCaseId value="MC-10907"/> + <severity value="MAJOR"/> + <group value="customers"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <createData entity="Simple_US_Customer" stepKey="customer"/> + </before> + <after> + <deleteData createDataKey="customer" stepKey="deleteCustomer"/> + </after> + + <actionGroup ref="StorefrontOpenCustomerAccountCreatePageActionGroup" stepKey="openCreateAccountPage"/> + <actionGroup ref="StorefrontFillCustomerAccountCreationFormActionGroup" stepKey="fillCreateAccountForm"> + <argument name="customer" value="$$customer$$"/> + </actionGroup> + <actionGroup ref="StorefrontClickCreateAnAccountCustomerAccountCreationFormActionGroup" stepKey="submitCreateAccountForm"/> + <actionGroup ref="AssertMessageCustomerCreateAccountActionGroup" stepKey="seeErrorMessage"> + <argument name="messageType" value="error"/> + <argument name="message" value="There is already an account with this email address."/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Customer/Test/Mftf/Test/StorefrontLockCustomerOnLoginPageTest.xml b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontLockCustomerOnLoginPageTest.xml new file mode 100644 index 0000000000000..c69c4dd071e38 --- /dev/null +++ b/app/code/Magento/Customer/Test/Mftf/Test/StorefrontLockCustomerOnLoginPageTest.xml @@ -0,0 +1,92 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontLockCustomerOnLoginPageTest"> + <annotations> + <features value="Customer"/> + <stories value="Lock Customer entering incorrect login credentials"/> + <title value="Lock customer on Storefront with after many attempts to log in with incorrect credentials"/> + <description value="Lock customer on Storefront with after many attempts to log in with incorrect credentials"/> + <testCaseId value="MC-14388"/> + <severity value="CRITICAL"/> + <group value="customer"/> + <group value="security"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <magentoCLI command="config:set {{StorefrontCustomerCaptchaDisableConfigData.path}} {{StorefrontCustomerCaptchaDisableConfigData.value}}" stepKey="disableCaptcha"/> + <magentoCLI command="config:set {{StorefrontCustomerLockoutFailures5ConfigData.path}} {{StorefrontCustomerLockoutFailures5ConfigData.value}}" stepKey="setInvalidAttemptsCountConfigTo5"/> + <createData stepKey="customer" entity="Simple_US_Customer"/> + </before> + <after> + <magentoCLI command="config:set {{StorefrontCustomerCaptchaEnableConfigData.path}} {{StorefrontCustomerCaptchaEnableConfigData.value}}" stepKey="enableCaptcha"/> + <magentoCLI command="config:set {{StorefrontCustomerLockoutFailuresDefaultConfigData.path}} {{StorefrontCustomerLockoutFailuresDefaultConfigData.value}}" stepKey="revertInvalidAttemptsCountConfig"/> + <deleteData stepKey="deleteCustomer" createDataKey="customer"/> + </after> + + <actionGroup ref="StorefrontOpenCustomerLoginPageActionGroup" stepKey="goToSignInPage"/> + + <!-- Perform 5 attempts to log in with invalid credentials --> + <actionGroup ref="StorefrontFillCustomerLoginFormWithWrongPasswordActionGroup" stepKey="fillLoginFormFirstAttempt"> + <argument name="customer" value="$$customer$$"/> + </actionGroup> + <actionGroup ref="StorefrontClickSignOnCustomerLoginFormActionGroup" stepKey="clickSignInAccountButtonFirstAttempt"/> + <actionGroup ref="AssertMessageCustomerLoginActionGroup" stepKey="seeErrorMessageAfterFirstAttempt"> + <argument name="messageType" value="error"/> + <argument name="message" value="The account sign-in was incorrect or your account is disabled temporarily. Please wait and try again later"/> + </actionGroup> + + <actionGroup ref="StorefrontFillCustomerLoginFormWithWrongPasswordActionGroup" stepKey="fillLoginFormSecondAttempt"> + <argument name="customer" value="$$customer$$"/> + </actionGroup> + <actionGroup ref="StorefrontClickSignOnCustomerLoginFormActionGroup" stepKey="clickSignInAccountButtonSecondAttempt"/> + <actionGroup ref="AssertMessageCustomerLoginActionGroup" stepKey="seeErrorMessageAfterSecondAttempt"> + <argument name="messageType" value="error"/> + <argument name="message" value="The account sign-in was incorrect or your account is disabled temporarily. Please wait and try again later"/> + </actionGroup> + + <actionGroup ref="StorefrontFillCustomerLoginFormWithWrongPasswordActionGroup" stepKey="fillLoginFormThirdAttempt"> + <argument name="customer" value="$$customer$$"/> + </actionGroup> + <actionGroup ref="StorefrontClickSignOnCustomerLoginFormActionGroup" stepKey="clickSignInAccountButtonThirdAttempt"/> + <actionGroup ref="AssertMessageCustomerLoginActionGroup" stepKey="seeErrorMessageAfterThirdAttempt"> + <argument name="messageType" value="error"/> + <argument name="message" value="The account sign-in was incorrect or your account is disabled temporarily. Please wait and try again later"/> + </actionGroup> + + <actionGroup ref="StorefrontFillCustomerLoginFormWithWrongPasswordActionGroup" stepKey="fillLoginFormFourthAttempt"> + <argument name="customer" value="$$customer$$"/> + </actionGroup> + <actionGroup ref="StorefrontClickSignOnCustomerLoginFormActionGroup" stepKey="clickSignInAccountButtonFourthAttempt"/> + <actionGroup ref="AssertMessageCustomerLoginActionGroup" stepKey="seeErrorMessageAfterFourthAttempt"> + <argument name="messageType" value="error"/> + <argument name="message" value="The account sign-in was incorrect or your account is disabled temporarily. Please wait and try again later"/> + </actionGroup> + + <actionGroup ref="StorefrontFillCustomerLoginFormWithWrongPasswordActionGroup" stepKey="fillLoginFormFifthAttempt"> + <argument name="customer" value="$$customer$$"/> + </actionGroup> + <actionGroup ref="StorefrontClickSignOnCustomerLoginFormActionGroup" stepKey="clickSignInAccountButtonFifthAttempt"/> + <actionGroup ref="AssertMessageCustomerLoginActionGroup" stepKey="seeErrorMessageAfterFifthAttempt"> + <argument name="messageType" value="error"/> + <argument name="message" value="The account sign-in was incorrect or your account is disabled temporarily. Please wait and try again later"/> + </actionGroup> + + <!-- Make sure that the customer is locked --> + <actionGroup ref="StorefrontFillCustomerLoginFormActionGroup" stepKey="fillLoginFormWithCorrectCredentials"> + <argument name="customer" value="$$customer$$"/> + </actionGroup> + <actionGroup ref="StorefrontClickSignOnCustomerLoginFormActionGroup" stepKey="clickSignInAccountButtonWithCorrectCredentials"/> + <actionGroup ref="AssertMessageCustomerLoginActionGroup" stepKey="seeLockoutErrorMessage"> + <argument name="messageType" value="error"/> + <argument name="message" value="The account sign-in was incorrect or your account is disabled temporarily. Please wait and try again later."/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Customer/Test/Unit/Model/AccountManagementTest.php b/app/code/Magento/Customer/Test/Unit/Model/AccountManagementTest.php index 209a9b077a307..5eda0c52c1db2 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/AccountManagementTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/AccountManagementTest.php @@ -12,6 +12,7 @@ use Magento\Customer\Model\AuthenticationInterface; use Magento\Customer\Model\Data\Customer; use Magento\Customer\Model\EmailNotificationInterface; +use Magento\Directory\Model\AllowedCountries; use Magento\Framework\Api\SearchCriteriaBuilder; use Magento\Framework\App\Area; use Magento\Framework\Exception\NoSuchEntityException; @@ -155,6 +156,11 @@ class AccountManagementTest extends \PHPUnit\Framework\TestCase */ private $searchCriteriaBuilderMock; + /** + * @var AllowedCountries|\PHPUnit_Framework_MockObject_MockObject + */ + private $allowedCountriesReader; + /** * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ @@ -193,6 +199,7 @@ protected function setUp() $this->extensibleDataObjectConverter = $this->createMock( \Magento\Framework\Api\ExtensibleDataObjectConverter::class ); + $this->allowedCountriesReader = $this->createMock(AllowedCountries::class); $this->authenticationMock = $this->getMockBuilder(AuthenticationInterface::class) ->disableOriginalConstructor() ->getMock(); @@ -256,6 +263,7 @@ protected function setUp() 'visitorCollectionFactory' => $this->visitorCollectionFactory, 'searchCriteriaBuilder' => $this->searchCriteriaBuilderMock, 'addressRegistry' => $this->addressRegistryMock, + 'allowedCountriesReader' => $this->allowedCountriesReader, ] ); $this->objectManagerHelper->setBackwardCompatibleProperty( @@ -551,7 +559,14 @@ public function testCreateAccountWithPasswordHashWithAddressException() ->expects($this->once()) ->method('delete') ->with($customer); - + $this->allowedCountriesReader + ->expects($this->atLeastOnce()) + ->method('getAllowedCountries') + ->willReturn(['US' => 'US']); + $address + ->expects($this->atLeastOnce()) + ->method('getCountryId') + ->willReturn('US'); $this->accountManagement->createAccountWithPasswordHash($customer, $hash); } @@ -725,6 +740,14 @@ public function testCreateAccountWithoutPassword() $this->emailNotificationMock->expects($this->once()) ->method('newAccount') ->willReturnSelf(); + $this->allowedCountriesReader + ->expects($this->atLeastOnce()) + ->method('getAllowedCountries') + ->willReturn(['US' => 'US']); + $address + ->expects($this->atLeastOnce()) + ->method('getCountryId') + ->willReturn('US'); $this->accountManagement->createAccount($customer); } @@ -970,6 +993,14 @@ public function testCreateAccountWithPassword() $this->emailNotificationMock->expects($this->once()) ->method('newAccount') ->willReturnSelf(); + $this->allowedCountriesReader + ->expects($this->atLeastOnce()) + ->method('getAllowedCountries') + ->willReturn(['US' => 'US']); + $address + ->expects($this->atLeastOnce()) + ->method('getCountryId') + ->willReturn('US'); $this->accountManagement->createAccount($customer, $password); } @@ -1951,6 +1982,14 @@ public function testCreateAccountWithPasswordHashWithCustomerAddresses() ->method('getWebsite') ->with($websiteId) ->willReturn($website); + $this->allowedCountriesReader + ->expects($this->atLeastOnce()) + ->method('getAllowedCountries') + ->willReturn(['US' => 'US']); + $existingAddress + ->expects($this->atLeastOnce()) + ->method('getCountryId') + ->willReturn('US'); $this->assertSame($customer, $this->accountManagement->createAccountWithPasswordHash($customer, $hash)); } @@ -2078,7 +2117,9 @@ public function testCreateAccountUnexpectedValueException(): void ->method('newAccount') ->willThrowException($exception); $this->logger->expects($this->once())->method('error')->with($exception); - + $this->allowedCountriesReader->expects($this->atLeastOnce()) + ->method('getAllowedCountries')->willReturn(['US' => 'US']); + $address->expects($this->atLeastOnce())->method('getCountryId')->willReturn('US'); $this->accountManagement->createAccount($customer); } diff --git a/app/code/Magento/Customer/Test/Unit/Model/Customer/DataProviderTest.php b/app/code/Magento/Customer/Test/Unit/Model/Customer/DataProviderTest.php index ac87e2e336e3d..3cbcbdf80b2a1 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/Customer/DataProviderTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/Customer/DataProviderTest.php @@ -1087,16 +1087,15 @@ public function testGetDataWithVisibleAttributesWithAccountEdit() $meta = $dataProvider->getMeta(); $this->assertNotEmpty($meta); - $this->assertEquals($this->getExpectationForVisibleAttributes(false), $meta); + $this->assertEquals($this->getExpectationForVisibleAttributes(), $meta); } /** * Retrieve all customer variations of attributes with all variations of visibility * - * @param bool $isRegistration * @return array */ - private function getCustomerAttributeExpectations($isRegistration) + private function getCustomerAttributeExpectations() { return [ self::ATTRIBUTE_CODE . "_1" => [ @@ -1106,7 +1105,7 @@ private function getCustomerAttributeExpectations($isRegistration) 'dataType' => 'frontend_input', 'formElement' => 'frontend_input', 'options' => 'test-options', - 'visible' => !$isRegistration, + 'visible' => true, 'required' => 'is_required', 'label' => __('frontend_label'), 'sortOrder' => 'sort_order', @@ -1143,7 +1142,7 @@ private function getCustomerAttributeExpectations($isRegistration) 'config' => [ 'dataType' => 'frontend_input', 'formElement' => 'frontend_input', - 'visible' => $isRegistration, + 'visible' => true, 'required' => 'is_required', 'label' => __('frontend_label'), 'sortOrder' => 'sort_order', @@ -1166,7 +1165,7 @@ private function getCustomerAttributeExpectations($isRegistration) 'config' => [ 'dataType' => 'frontend_input', 'formElement' => 'frontend_input', - 'visible' => $isRegistration, + 'visible' => true, 'required' => 'is_required', 'label' => __('frontend_label'), 'sortOrder' => 'sort_order', @@ -1189,14 +1188,13 @@ private function getCustomerAttributeExpectations($isRegistration) /** * Retrieve all variations of attributes with all variations of visibility * - * @param bool $isRegistration * @return array */ - private function getExpectationForVisibleAttributes($isRegistration = true) + private function getExpectationForVisibleAttributes() { return [ 'customer' => [ - 'children' => $this->getCustomerAttributeExpectations($isRegistration), + 'children' => $this->getCustomerAttributeExpectations(), ], 'address' => [ 'children' => [ diff --git a/app/code/Magento/Customer/Test/Unit/Model/CustomerAuthUpdateTest.php b/app/code/Magento/Customer/Test/Unit/Model/CustomerAuthUpdateTest.php index a1a243066bb7d..81a612c519f52 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/CustomerAuthUpdateTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/CustomerAuthUpdateTest.php @@ -5,7 +5,14 @@ */ namespace Magento\Customer\Test\Unit\Model; +use Magento\Customer\Model\Customer as CustomerModel; use Magento\Customer\Model\CustomerAuthUpdate; +use Magento\Customer\Model\CustomerRegistry; +use Magento\Customer\Model\Data\CustomerSecure; +use Magento\Customer\Model\ResourceModel\Customer as CustomerResourceModel; +use Magento\Framework\DB\Adapter\AdapterInterface; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; /** * Class CustomerAuthUpdateTest @@ -18,17 +25,22 @@ class CustomerAuthUpdateTest extends \PHPUnit\Framework\TestCase protected $model; /** - * @var \Magento\Customer\Model\CustomerRegistry|\PHPUnit_Framework_MockObject_MockObject + * @var CustomerRegistry|\PHPUnit_Framework_MockObject_MockObject */ protected $customerRegistry; /** - * @var \Magento\Customer\Model\ResourceModel\Customer|\PHPUnit_Framework_MockObject_MockObject + * @var CustomerResourceModel|\PHPUnit_Framework_MockObject_MockObject */ protected $customerResourceModel; /** - * @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager + * @var CustomerModel|\PHPUnit_Framework_MockObject_MockObject + */ + protected $customerModel; + + /** + * @var ObjectManager */ protected $objectManager; @@ -37,32 +49,36 @@ class CustomerAuthUpdateTest extends \PHPUnit\Framework\TestCase */ protected function setUp() { - $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->objectManager = new ObjectManager($this); $this->customerRegistry = - $this->createMock(\Magento\Customer\Model\CustomerRegistry::class); + $this->createMock(CustomerRegistry::class); $this->customerResourceModel = - $this->createMock(\Magento\Customer\Model\ResourceModel\Customer::class); + $this->createMock(CustomerResourceModel::class); + $this->customerModel = + $this->createMock(CustomerModel::class); $this->model = $this->objectManager->getObject( - \Magento\Customer\Model\CustomerAuthUpdate::class, + CustomerAuthUpdate::class, [ 'customerRegistry' => $this->customerRegistry, 'customerResourceModel' => $this->customerResourceModel, + 'customerModel' => $this->customerModel ] ); } /** * test SaveAuth + * @throws NoSuchEntityException */ public function testSaveAuth() { $customerId = 1; - $customerSecureMock = $this->createMock(\Magento\Customer\Model\Data\CustomerSecure::class); + $customerSecureMock = $this->createMock(CustomerSecure::class); - $dbAdapter = $this->createMock(\Magento\Framework\DB\Adapter\AdapterInterface::class); + $dbAdapter = $this->createMock(AdapterInterface::class); $this->customerRegistry->expects($this->once()) ->method('retrieveSecureData') @@ -98,6 +114,9 @@ public function testSaveAuth() $customerId ); + $this->customerModel->expects($this->once()) + ->method('reindex'); + $this->model->saveAuth($customerId); } } diff --git a/app/code/Magento/Customer/Test/Unit/Model/ResourceModel/CustomerRepositoryTest.php b/app/code/Magento/Customer/Test/Unit/Model/ResourceModel/CustomerRepositoryTest.php index 05953b09b8c04..8032399e14881 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/ResourceModel/CustomerRepositoryTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/ResourceModel/CustomerRepositoryTest.php @@ -107,7 +107,7 @@ protected function setUp() $this->createMock(\Magento\Customer\Model\ResourceModel\Customer::class); $this->customerRegistry = $this->createMock(\Magento\Customer\Model\CustomerRegistry::class); $this->dataObjectHelper = $this->createMock(\Magento\Framework\Api\DataObjectHelper::class); - $this->customerFactory = + $this->customerFactory = $this->createPartialMock(\Magento\Customer\Model\CustomerFactory::class, ['create']); $this->customerSecureFactory = $this->createPartialMock( \Magento\Customer\Model\Data\CustomerSecureFactory::class, @@ -193,9 +193,10 @@ protected function setUp() public function testSave() { $customerId = 1; - $storeId = 2; - $customerModel = $this->createPartialMock(\Magento\Customer\Model\Customer::class, [ + $customerModel = $this->createPartialMock( + \Magento\Customer\Model\Customer::class, + [ 'getId', 'setId', 'setStoreId', @@ -210,7 +211,8 @@ public function testSave() 'setFirstFailure', 'setLockExpires', 'save', - ]); + ] + ); $origCustomer = $this->customer; @@ -229,14 +231,17 @@ public function testSave() 'setAddresses' ] ); - $customerSecureData = $this->createPartialMock(\Magento\Customer\Model\Data\CustomerSecure::class, [ - 'getRpToken', - 'getRpTokenCreatedAt', - 'getPasswordHash', - 'getFailuresNum', - 'getFirstFailure', - 'getLockExpires', - ]); + $customerSecureData = $this->createPartialMock( + \Magento\Customer\Model\Data\CustomerSecure::class, + [ + 'getRpToken', + 'getRpTokenCreatedAt', + 'getPasswordHash', + 'getFailuresNum', + 'getFirstFailure', + 'getLockExpires', + ] + ); $this->customer->expects($this->atLeastOnce()) ->method('getId') ->willReturn($customerId); @@ -268,17 +273,6 @@ public function testSave() $customerModel->expects($this->once()) ->method('getStoreId') ->willReturn(null); - $store = $this->createMock(\Magento\Store\Model\Store::class); - $store->expects($this->once()) - ->method('getId') - ->willReturn($storeId); - $this->storeManager - ->expects($this->once()) - ->method('getStore') - ->willReturn($store); - $customerModel->expects($this->once()) - ->method('setStoreId') - ->with($storeId); $customerModel->expects($this->once()) ->method('setId') ->with($customerId); @@ -310,16 +304,20 @@ public function testSave() $customerModel->expects($this->once()) ->method('setRpToken') - ->willReturnMap([ + ->willReturnMap( + [ ['rpToken', $customerModel], [null, $customerModel], - ]); + ] + ); $customerModel->expects($this->once()) ->method('setRpTokenCreatedAt') - ->willReturnMap([ + ->willReturnMap( + [ ['rpTokenCreatedAt', $customerModel], [null, $customerModel], - ]); + ] + ); $customerModel->expects($this->once()) ->method('setPasswordHash') @@ -371,32 +369,37 @@ public function testSave() public function testSaveWithPasswordHash() { $customerId = 1; - $storeId = 2; $passwordHash = 'ukfa4sdfa56s5df02asdf4rt'; - $customerSecureData = $this->createPartialMock(\Magento\Customer\Model\Data\CustomerSecure::class, [ - 'getRpToken', - 'getRpTokenCreatedAt', - 'getPasswordHash', - 'getFailuresNum', - 'getFirstFailure', - 'getLockExpires', - ]); + $customerSecureData = $this->createPartialMock( + \Magento\Customer\Model\Data\CustomerSecure::class, + [ + 'getRpToken', + 'getRpTokenCreatedAt', + 'getPasswordHash', + 'getFailuresNum', + 'getFirstFailure', + 'getLockExpires', + ] + ); $origCustomer = $this->customer; - $customerModel = $this->createPartialMock(\Magento\Customer\Model\Customer::class, [ - 'getId', - 'setId', - 'setStoreId', - 'getStoreId', - 'getAttributeSetId', - 'setAttributeSetId', - 'setRpToken', - 'setRpTokenCreatedAt', - 'getDataModel', - 'setPasswordHash', - 'save', - ]); + $customerModel = $this->createPartialMock( + \Magento\Customer\Model\Customer::class, + [ + 'getId', + 'setId', + 'setStoreId', + 'getStoreId', + 'getAttributeSetId', + 'setAttributeSetId', + 'setRpToken', + 'setRpTokenCreatedAt', + 'getDataModel', + 'setPasswordHash', + 'save', + ] + ); $customerAttributesMetaData = $this->getMockForAbstractClass( \Magento\Framework\Api\CustomAttributesDataInterface::class, [], @@ -447,7 +450,6 @@ public function testSaveWithPasswordHash() $customerSecureData->expects($this->once()) ->method('getLockExpires') ->willReturn('lockExpires'); - $this->customer->expects($this->atLeastOnce()) ->method('getId') ->willReturn($customerId); @@ -477,20 +479,6 @@ public function testSaveWithPasswordHash() ->method('create') ->with(['data' => ['customerData']]) ->willReturn($customerModel); - $customerModel->expects($this->once()) - ->method('getStoreId') - ->willReturn(null); - $store = $this->createMock(\Magento\Store\Model\Store::class); - $store->expects($this->once()) - ->method('getId') - ->willReturn($storeId); - $this->storeManager - ->expects($this->once()) - ->method('getStore') - ->willReturn($store); - $customerModel->expects($this->once()) - ->method('setStoreId') - ->with($storeId); $customerModel->expects($this->once()) ->method('setId') ->with($customerId); diff --git a/app/code/Magento/Customer/i18n/en_US.csv b/app/code/Magento/Customer/i18n/en_US.csv index 578267984f985..e1c68f3d81e9d 100644 --- a/app/code/Magento/Customer/i18n/en_US.csv +++ b/app/code/Magento/Customer/i18n/en_US.csv @@ -47,7 +47,7 @@ Sending,Sending Paused,Paused View,View Unknown,Unknown -Order,Order +"Order #","Order #" Purchased,Purchased "Bill-to Name","Bill-to Name" "Ship-to Name","Ship-to Name" diff --git a/app/code/Magento/Customer/view/frontend/layout/customer_account_forgotpassword.xml b/app/code/Magento/Customer/view/frontend/layout/customer_account_forgotpassword.xml index 9a701c14a0307..24cede5f0232a 100644 --- a/app/code/Magento/Customer/view/frontend/layout/customer_account_forgotpassword.xml +++ b/app/code/Magento/Customer/view/frontend/layout/customer_account_forgotpassword.xml @@ -7,7 +7,7 @@ --> <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="1column" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> <head> - <title>Forgot Your Password + Forgot Your Password? diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/IsEmailAvailable.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/IsEmailAvailable.php index ddf1aec275ece..7e13a40cc8ae8 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Resolver/IsEmailAvailable.php +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/IsEmailAvailable.php @@ -44,7 +44,7 @@ public function resolve( array $args = null ) { if (!isset($args['email']) || empty($args['email'])) { - throw new GraphQlInputException(__('"Email should be specified')); + throw new GraphQlInputException(__('Email must be specified')); } try { diff --git a/app/code/Magento/CustomerGraphQl/Model/Resolver/UpdateCustomerAddress.php b/app/code/Magento/CustomerGraphQl/Model/Resolver/UpdateCustomerAddress.php index bf41b7ddd10c9..36d7d66199319 100644 --- a/app/code/Magento/CustomerGraphQl/Model/Resolver/UpdateCustomerAddress.php +++ b/app/code/Magento/CustomerGraphQl/Model/Resolver/UpdateCustomerAddress.php @@ -70,11 +70,11 @@ public function resolve( array $args = null ) { if (!isset($args['id']) || empty($args['id'])) { - throw new GraphQlInputException(__('Address "id" value should be specified')); + throw new GraphQlInputException(__('Address "id" value must be specified')); } if (!isset($args['input']) || !is_array($args['input']) || empty($args['input'])) { - throw new GraphQlInputException(__('"input" value should be specified')); + throw new GraphQlInputException(__('"input" value must be specified')); } $customer = $this->getCustomer->execute($context); diff --git a/app/code/Magento/Deploy/Process/Queue.php b/app/code/Magento/Deploy/Process/Queue.php index fd7aad44e0a5b..2f2a149239990 100644 --- a/app/code/Magento/Deploy/Process/Queue.php +++ b/app/code/Magento/Deploy/Process/Queue.php @@ -10,9 +10,9 @@ use Magento\Deploy\Package\Package; use Magento\Deploy\Service\DeployPackage; use Magento\Framework\App\ResourceConnection; -use Psr\Log\LoggerInterface; use Magento\Framework\App\State as AppState; use Magento\Framework\Locale\ResolverInterface as LocaleResolver; +use Psr\Log\LoggerInterface; /** * Deployment Queue @@ -165,6 +165,7 @@ public function process() $packages = $this->packages; while (count($packages) && $this->checkTimeout()) { foreach ($packages as $name => $packageJob) { + // Unsets each member of $packages array (passed by reference) as each is executed $this->assertAndExecute($name, $packages, $packageJob); } $this->logger->info('.'); @@ -224,12 +225,8 @@ private function assertAndExecute($name, array & $packages, array $packageJob) * @param bool $dependenciesNotFinished * @return void */ - private function executePackage( - Package $package, - string $name, - array &$packages, - bool $dependenciesNotFinished - ) { + private function executePackage(Package $package, string $name, array &$packages, bool $dependenciesNotFinished) + { if (!$dependenciesNotFinished && !$this->isDeployed($package) && ($this->maxProcesses < 2 || (count($this->inProgress) < $this->maxProcesses)) @@ -338,14 +335,41 @@ private function isDeployed(Package $package) { if ($this->isCanBeParalleled()) { if ($package->getState() === null) { + $pid = $this->getPid($package); + + // When $pid comes back as null the child process for this package has not yet started; prevents both + // hanging until timeout expires (which was behaviour in 2.2.x) and the type error from strict_types + if ($pid === null) { + return false; + } + // phpcs:ignore Magento2.Functions.DiscouragedFunction - $pid = pcntl_waitpid($this->getPid($package), $status, WNOHANG); - if ($pid === $this->getPid($package)) { + $result = pcntl_waitpid($pid, $status, WNOHANG); + if ($result === $pid) { $package->setState(Package::STATE_COMPLETED); + // phpcs:ignore Magento2.Functions.DiscouragedFunction + $exitStatus = pcntl_wexitstatus($status); + + $this->logger->info( + "Exited: " . $package->getPath() . "(status: $exitStatus)", + [ + 'process' => $package->getPath(), + 'status' => $exitStatus, + ] + ); unset($this->inProgress[$package->getPath()]); // phpcs:ignore Magento2.Functions.DiscouragedFunction return pcntl_wexitstatus($status) === 0; + } elseif ($result === -1) { + // phpcs:ignore Magento2.Functions.DiscouragedFunction + $errno = pcntl_errno(); + // phpcs:ignore Magento2.Functions.DiscouragedFunction + $strerror = pcntl_strerror($errno); + + throw new \RuntimeException( + "Error encountered checking child process status (PID: $pid): $strerror (errno: $errno)" + ); } return false; } @@ -361,7 +385,7 @@ private function isDeployed(Package $package) */ private function getPid(Package $package) { - return isset($this->processIds[$package->getPath()]) ?? null; + return $this->processIds[$package->getPath()] ?? null; } /** @@ -380,15 +404,30 @@ private function checkTimeout() * Protect against zombie process * * @throws \RuntimeException + * @SuppressWarnings(PHPMD.UnusedLocalVariable) * @return void */ public function __destruct() { foreach ($this->inProgress as $package) { + $pid = $this->getPid($package); + $this->logger->info( + "Reaping child process: {$package->getPath()} (PID: $pid)", + [ + 'process' => $package->getPath(), + 'pid' => $pid, + ] + ); + // phpcs:ignore Magento2.Functions.DiscouragedFunction - if (pcntl_waitpid($this->getPid($package), $status) === -1) { + if (pcntl_waitpid($pid, $status) === -1) { + // phpcs:ignore Magento2.Functions.DiscouragedFunction + $errno = pcntl_errno(); + // phpcs:ignore Magento2.Functions.DiscouragedFunction + $strerror = pcntl_strerror($errno); + throw new \RuntimeException( - 'Error while waiting for package deployed: ' . $this->getPid($package) . '; Status: ' . $status + "Error encountered waiting for child process (PID: $pid): $strerror (errno: $errno)" ); } } diff --git a/app/code/Magento/Deploy/Service/DeployPackage.php b/app/code/Magento/Deploy/Service/DeployPackage.php index 3b7b5b77a0793..52cb6c6075749 100644 --- a/app/code/Magento/Deploy/Service/DeployPackage.php +++ b/app/code/Magento/Deploy/Service/DeployPackage.php @@ -107,6 +107,8 @@ function () use ($package, $options, $skipLogging) { } /** + * Execute package deploy procedure when area already emulated + * * @param Package $package * @param array $options * @param bool $skipLogging @@ -136,7 +138,9 @@ public function deployEmulated(Package $package, array $options, $skipLogging = $this->errorsCount++; $this->logger->critical($errorMessage); } catch (\Exception $exception) { - $this->logger->critical($exception->getTraceAsString()); + $this->logger->critical( + 'Compilation from source ' . $file->getSourcePath() . ' failed' . PHP_EOL . (string)$exception + ); $this->errorsCount++; } } @@ -219,7 +223,9 @@ private function checkIfCanCopy(PackageFile $file, Package $package, Package $pa private function checkFileSkip($filePath, array $options) { if ($filePath !== '.') { + // phpcs:ignore Magento2.Functions.DiscouragedFunction $ext = strtolower(pathinfo($filePath, PATHINFO_EXTENSION)); + // phpcs:ignore Magento2.Functions.DiscouragedFunction $basename = pathinfo($filePath, PATHINFO_BASENAME); if ($ext === 'less' && strpos($basename, '_') === 0) { return true; diff --git a/app/code/Magento/Directory/Block/Data.php b/app/code/Magento/Directory/Block/Data.php index 333e9e03706b9..d4c46469e4771 100644 --- a/app/code/Magento/Directory/Block/Data.php +++ b/app/code/Magento/Directory/Block/Data.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Directory\Block; /** @@ -173,10 +175,33 @@ public function getRegionCollection() * Returns region html select * * @return string + * @deprecated + * @see getRegionSelect() method for more configurations */ public function getRegionHtmlSelect() { + return $this->getRegionSelect(); + } + + /** + * Returns region html select + * + * @param null|int $value + * @param string $name + * @param string $id + * @param string $title + * @return string + */ + public function getRegionSelect( + ?int $value = null, + string $name = 'region', + string $id = 'state', + string $title = 'State/Province' + ): string { \Magento\Framework\Profiler::start('TEST: ' . __METHOD__, ['group' => 'TEST', 'method' => __METHOD__]); + if ($value === null) { + $value = (int)$this->getRegionId(); + } $cacheKey = 'DIRECTORY_REGION_SELECT_STORE' . $this->_storeManager->getStore()->getId(); $cache = $this->_configCacheType->load($cacheKey); if ($cache) { @@ -188,15 +213,15 @@ public function getRegionHtmlSelect() $html = $this->getLayout()->createBlock( \Magento\Framework\View\Element\Html\Select::class )->setName( - 'region' + $name )->setTitle( - __('State/Province') + __($title) )->setId( - 'state' + $id )->setClass( 'required-entry validate-state' )->setValue( - (int)$this->getRegionId() + $value )->setOptions( $options )->getHtml(); diff --git a/app/code/Magento/Directory/Model/ResourceModel/Country/Collection.php b/app/code/Magento/Directory/Model/ResourceModel/Country/Collection.php index 4ec34a3842fa2..d84b11fc6cece 100644 --- a/app/code/Magento/Directory/Model/ResourceModel/Country/Collection.php +++ b/app/code/Magento/Directory/Model/ResourceModel/Country/Collection.php @@ -126,24 +126,6 @@ public function __construct( */ protected $_foregroundCountries = []; - /** - * Add top destinition countries to head of option array - * - * @param string $emptyLabel - * @param array $options - * @return array - */ - private function addForegroundCountriesToOptionArray($emptyLabel, $options) - { - if ($emptyLabel !== false && count($this->_foregroundCountries) !== 0 && - count($options) === count($this->_foregroundCountries) - ) { - $options[] = ['value' => '', 'label' => $emptyLabel]; - return $options; - } - return $options; - } - /** * Define main table * @@ -269,24 +251,20 @@ public function addCountryIdFilter($countryId) public function toOptionArray($emptyLabel = ' ') { $options = $this->_toOptionArray('country_id', 'name', ['title' => 'iso2_code']); - $sort = []; - foreach ($options as $data) { - $name = (string)$this->_localeLists->getCountryTranslation($data['value']); - if (!empty($name)) { - $sort[$name] = $data['value']; - } - } + $sort = $this->getSort($options); + $this->_arrayUtils->ksortMultibyte($sort, $this->_localeResolver->getLocale()); foreach (array_reverse($this->_foregroundCountries) as $foregroundCountry) { $name = array_search($foregroundCountry, $sort); - unset($sort[$name]); - $sort = [$name => $foregroundCountry] + $sort; + if ($name) { + unset($sort[$name]); + $sort = [$name => $foregroundCountry] + $sort; + } } $isRegionVisible = (bool)$this->helperData->isShowNonRequiredState(); $options = []; foreach ($sort as $label => $value) { - $options = $this->addForegroundCountriesToOptionArray($emptyLabel, $options); $option = ['value' => $value, 'label' => $label]; if ($this->helperData->isRegionRequired($value)) { $option['is_region_required'] = true; @@ -366,4 +344,23 @@ public function getCountriesWithRequiredStates() } return $countries; } + + /** + * Get sort + * + * @param array $options + * @return array + */ + private function getSort(array $options): array + { + $sort = []; + foreach ($options as $data) { + $name = (string)$this->_localeLists->getCountryTranslation($data['value']); + if (!empty($name)) { + $sort[$name] = $data['value']; + } + } + + return $sort; + } } diff --git a/app/code/Magento/Directory/Test/Unit/Model/ResourceModel/Country/CollectionTest.php b/app/code/Magento/Directory/Test/Unit/Model/ResourceModel/Country/CollectionTest.php index 8eb4ad78fbe5c..f5c6c5eeb53a6 100644 --- a/app/code/Magento/Directory/Test/Unit/Model/ResourceModel/Country/CollectionTest.php +++ b/app/code/Magento/Directory/Test/Unit/Model/ResourceModel/Country/CollectionTest.php @@ -99,8 +99,7 @@ public function testToOptionArray($optionsArray, $emptyLabel, $foregroundCountri $this->_model->setForegroundCountries($foregroundCountries); $result = $this->_model->toOptionArray($emptyLabel); - $this->assertCount(count($optionsArray) + (int)(!empty($emptyLabel) && !empty($foregroundCountries)) + - (int)(!empty($emptyLabel)), $result); + $this->assertCount(count($optionsArray) + (int)(!empty($emptyLabel)), $result); foreach ($expectedResults as $index => $expectedResult) { $this->assertEquals($expectedResult, $result[$index]['label']); } @@ -121,8 +120,10 @@ public function toOptionArrayDataProvider() [$optionsArray, false, [], ['AD', 'US', 'ES', 'BZ']], [$optionsArray, false, 'US', ['US', 'AD', 'ES', 'BZ']], [$optionsArray, false, ['US', 'BZ'], ['US', 'BZ', 'AD', 'ES']], - [$optionsArray, ' ', 'US', [' ', 'US', ' ', 'AD', 'ES', 'BZ']], - [$optionsArray, ' ', [], [' ', 'AD', 'US', 'ES', 'BZ']] + [$optionsArray, ' ', 'US', [' ', 'US', 'AD', 'ES', 'BZ']], + [$optionsArray, ' ', [], [' ', 'AD', 'US', 'ES', 'BZ']], + [$optionsArray, ' ', 'UA', [' ', 'AD', 'US', 'ES', 'BZ']], + [$optionsArray, ' ', ['AF', 'UA'], [' ', 'AD', 'US', 'ES', 'BZ']], ]; } } diff --git a/app/code/Magento/Downloadable/Observer/UpdateLinkPurchasedObserver.php b/app/code/Magento/Downloadable/Observer/UpdateLinkPurchasedObserver.php index db391ccda6866..223a3ad66b3d3 100644 --- a/app/code/Magento/Downloadable/Observer/UpdateLinkPurchasedObserver.php +++ b/app/code/Magento/Downloadable/Observer/UpdateLinkPurchasedObserver.php @@ -7,6 +7,10 @@ namespace Magento\Downloadable\Observer; +use Magento\Downloadable\Model\ResourceModel\Link\Purchased\Collection as PurchasedCollection; +use Magento\Downloadable\Model\ResourceModel\Link\Purchased\CollectionFactory; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\Event\Observer; use Magento\Framework\Event\ObserverInterface; /** @@ -16,65 +20,65 @@ class UpdateLinkPurchasedObserver implements ObserverInterface { /** * Core store config - * @var \Magento\Framework\App\Config\ScopeConfigInterface + * + * @var ScopeConfigInterface */ private $scopeConfig; /** - * @var \Magento\Downloadable\Model\ResourceModel\Link\Purchased\CollectionFactory - */ - private $purchasedFactory; - - /** - * @var \Magento\Framework\DataObject\Copy + * Purchased links collection factory + * + * @var CollectionFactory */ - private $objectCopyService; + private $purchasedCollectionFactory; /** - * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig - * @param \Magento\Downloadable\Model\ResourceModel\Link\Purchased\CollectionFactory $purchasedFactory - * @param \Magento\Framework\DataObject\Copy $objectCopyService + * @param ScopeConfigInterface $scopeConfig + * @param CollectionFactory $purchasedCollectionFactory */ public function __construct( - \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, - \Magento\Downloadable\Model\ResourceModel\Link\Purchased\CollectionFactory $purchasedFactory, - \Magento\Framework\DataObject\Copy $objectCopyService + ScopeConfigInterface $scopeConfig, + CollectionFactory $purchasedCollectionFactory ) { $this->scopeConfig = $scopeConfig; - $this->purchasedFactory = $purchasedFactory; - $this->objectCopyService = $objectCopyService; + $this->purchasedCollectionFactory = $purchasedCollectionFactory; } /** - * Re-save order data after order update. + * Link customer_id to downloadable link purchased after update order * - * @param \Magento\Framework\Event\Observer $observer + * @param Observer $observer * @return $this */ - public function execute(\Magento\Framework\Event\Observer $observer) + public function execute(Observer $observer) { $order = $observer->getEvent()->getOrder(); - - if (!$order->getId()) { - //order not saved in the database + $orderId = $order->getId(); + $customerId = $order->getCustomerId(); + if (!$orderId || !$customerId) { return $this; } + $purchasedLinksCollection = $this->getPurchasedCollection((int)$orderId); + foreach ($purchasedLinksCollection as $linkPurchased) { + $linkPurchased->setCustomerId($customerId)->save(); + } - $purchasedLinks = $this->purchasedFactory->create()->addFieldToFilter( + return $this; + } + + /** + * Get purchased collection by order id + * + * @param int $orderId + * @return PurchasedCollection + */ + private function getPurchasedCollection(int $orderId): PurchasedCollection + { + $purchasedCollection = $this->purchasedCollectionFactory->create()->addFieldToFilter( 'order_id', - ['eq' => $order->getId()] + ['eq' => $orderId] ); - foreach ($purchasedLinks as $linkPurchased) { - $this->objectCopyService->copyFieldsetToTarget( - \downloadable_sales_copy_order::class, - 'to_downloadable', - $order, - $linkPurchased - ); - $linkPurchased->save(); - } - - return $this; + return $purchasedCollection; } } diff --git a/app/code/Magento/Downloadable/Test/Mftf/ActionGroup/StorefrontAssertDownloadableProductIsPresentInCustomerAccountActionGroup.xml b/app/code/Magento/Downloadable/Test/Mftf/ActionGroup/StorefrontAssertDownloadableProductIsPresentInCustomerAccountActionGroup.xml new file mode 100644 index 0000000000000..2e4afbd803205 --- /dev/null +++ b/app/code/Magento/Downloadable/Test/Mftf/ActionGroup/StorefrontAssertDownloadableProductIsPresentInCustomerAccountActionGroup.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + diff --git a/app/code/Magento/Downloadable/Test/Mftf/Data/CatalogConfigData.xml b/app/code/Magento/Downloadable/Test/Mftf/Data/CatalogConfigData.xml new file mode 100644 index 0000000000000..8bb81f9c7579d --- /dev/null +++ b/app/code/Magento/Downloadable/Test/Mftf/Data/CatalogConfigData.xml @@ -0,0 +1,21 @@ + + + + + + catalog/downloadable/disable_guest_checkout + 0 + 0 + + + catalog/downloadable/disable_guest_checkout + 0 + 1 + + diff --git a/app/code/Magento/Downloadable/Test/Mftf/Page/StorefrontCustomerDownloadableProductsPage.xml b/app/code/Magento/Downloadable/Test/Mftf/Page/StorefrontCustomerDownloadableProductsPage.xml new file mode 100644 index 0000000000000..eafb37111fda2 --- /dev/null +++ b/app/code/Magento/Downloadable/Test/Mftf/Page/StorefrontCustomerDownloadableProductsPage.xml @@ -0,0 +1,13 @@ + + + + +
+ + diff --git a/app/code/Magento/Downloadable/Test/Mftf/Section/StorefrontCustomerDownloadableProductsSection.xml b/app/code/Magento/Downloadable/Test/Mftf/Section/StorefrontCustomerDownloadableProductsSection.xml new file mode 100644 index 0000000000000..d45a774077ba0 --- /dev/null +++ b/app/code/Magento/Downloadable/Test/Mftf/Section/StorefrontCustomerDownloadableProductsSection.xml @@ -0,0 +1,14 @@ + + + + +
+ +
+
diff --git a/app/code/Magento/Downloadable/Test/Mftf/Test/LinkDownloadableProductFromGuestToCustomerTest.xml b/app/code/Magento/Downloadable/Test/Mftf/Test/LinkDownloadableProductFromGuestToCustomerTest.xml new file mode 100644 index 0000000000000..b960d15b2fdf1 --- /dev/null +++ b/app/code/Magento/Downloadable/Test/Mftf/Test/LinkDownloadableProductFromGuestToCustomerTest.xml @@ -0,0 +1,67 @@ + + + + + + + + + <description value="Verify that in 'My Downloadable Products' section in customer account user can see products."/> + <severity value="AVERAGE"/> + <useCaseId value="MAGETWO-98656"/> + <testCaseId value="MC-16011"/> + </annotations> + <before> + <magentoCLI command="config:set {{EnableGuestCheckoutWithDownloadableItems.path}} {{EnableGuestCheckoutWithDownloadableItems.value}}" stepKey="enableGuestCheckoutWithDownloadableItems" /> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="DownloadableProductWithOneLink" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="downloadableLink1" stepKey="addDownloadableLink"> + <requiredEntity createDataKey="createProduct"/> + </createData> + </before> + <after> + <magentoCLI command="config:set {{DisableGuestCheckoutWithDownloadableItems.path}} {{DisableGuestCheckoutWithDownloadableItems.value}}" stepKey="disableGuestCheckoutWithDownloadableItems" /> + <deleteData stepKey="deleteProduct" createDataKey="createProduct"/> + <deleteData stepKey="deleteCategory" createDataKey="createCategory"/> + </after> + <!--Step 1: Go to Storefront as Guest--> + <amOnPage url="{{StorefrontHomePage.url}}" stepKey="amOnStorefrontPage"/> + <!--Step 2: Add downloadable product to shopping cart--> + <amOnPage url="{{StorefrontProductPage.url($$createProduct.custom_attributes[url_key]$$)}}" stepKey="amOnStorefrontProductPage"/> + <actionGroup ref="StorefrontAddProductToCartActionGroup" stepKey="addProductToCart"> + <argument name="product" value="$$createProduct$$"/> + <argument name="productCount" value="1"/> + </actionGroup> + <!--Step 3: Go to checkout--> + <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="goToCheckoutFromMinicart" /> + <!--Step 4: Select Check/Money Order payment, fill required fields and click Update and Place Order--> + <actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyOrderPayment"/> + <actionGroup ref="GuestCheckoutSelectPaymentAndFillNewBillingAddressActionGroup" stepKey="changeAddress"> + <argument name="customerVar" value="Simple_US_Customer_NY"/> + <argument name="customerAddressVar" value="US_Address_NY"/> + <argument name="paymentMethod" value="Check / Money order"/> + </actionGroup> + <click selector="{{CheckoutShippingSection.updateAddress}}" stepKey="saveAddress"/> + <waitForPageLoad stepKey="waitUpdateAddress"/> + <actionGroup ref="CheckoutPlaceOrderActionGroup" stepKey="placeOrder"> + <argument name="orderNumberMessage" value="CONST.successGuestCheckoutOrderNumberMessage"/> + <argument name="emailYouMessage" value="CONST.successCheckoutEmailYouMessage" /> + </actionGroup> + <!--Step 5: Create customer account after placing order--> + <actionGroup ref="StorefrontRegisterCustomerFromOrderSuccessPage" stepKey="createCustomerAfterPlaceOrder"> + <argument name="customer" value="CustomerEntityOne"/> + </actionGroup> + <!--Step 6: Go To My Account -> My Downloadable Products and check if downloadable product link exist--> + <actionGroup ref="StorefrontAssertDownloadableProductIsPresentInCustomerAccount" stepKey="seeStorefontMyDownloadableProductsProductName"> + <argument name="product" value="$$createProduct$$"/> + </actionGroup> + </test> +</tests> diff --git a/app/code/Magento/Downloadable/etc/db_schema.xml b/app/code/Magento/Downloadable/etc/db_schema.xml index 89d47644661a5..ccbefa4fb3992 100644 --- a/app/code/Magento/Downloadable/etc/db_schema.xml +++ b/app/code/Magento/Downloadable/etc/db_schema.xml @@ -42,7 +42,7 @@ comment="Link ID"/> <column xsi:type="smallint" name="website_id" padding="5" unsigned="true" nullable="false" identity="false" default="0" comment="Website ID"/> - <column xsi:type="decimal" name="price" scale="4" precision="12" unsigned="false" nullable="false" default="0" + <column xsi:type="decimal" name="price" scale="6" precision="20" unsigned="false" nullable="false" default="0" comment="Price"/> <constraint xsi:type="primary" referenceId="PRIMARY"> <column name="price_id"/> @@ -223,9 +223,9 @@ identity="false"/> <column xsi:type="smallint" name="website_id" padding="5" unsigned="true" nullable="false" identity="false" comment="Website ID"/> - <column xsi:type="decimal" name="min_price" scale="4" precision="12" unsigned="false" nullable="false" + <column xsi:type="decimal" name="min_price" scale="6" precision="20" unsigned="false" nullable="false" default="0" comment="Minimum price"/> - <column xsi:type="decimal" name="max_price" scale="4" precision="12" unsigned="false" nullable="false" + <column xsi:type="decimal" name="max_price" scale="6" precision="20" unsigned="false" nullable="false" default="0" comment="Maximum price"/> <constraint xsi:type="primary" referenceId="PRIMARY"> <column name="entity_id"/> @@ -241,9 +241,9 @@ identity="false"/> <column xsi:type="smallint" name="website_id" padding="5" unsigned="true" nullable="false" identity="false" comment="Website ID"/> - <column xsi:type="decimal" name="min_price" scale="4" precision="12" unsigned="false" nullable="false" + <column xsi:type="decimal" name="min_price" scale="6" precision="20" unsigned="false" nullable="false" default="0" comment="Minimum price"/> - <column xsi:type="decimal" name="max_price" scale="4" precision="12" unsigned="false" nullable="false" + <column xsi:type="decimal" name="max_price" scale="6" precision="20" unsigned="false" nullable="false" default="0" comment="Maximum price"/> <constraint xsi:type="primary" referenceId="PRIMARY"> <column name="entity_id"/> diff --git a/app/code/Magento/Downloadable/view/adminhtml/templates/product/composite/fieldset/downloadable.phtml b/app/code/Magento/Downloadable/view/adminhtml/templates/product/composite/fieldset/downloadable.phtml index c2338e30ecd3b..8d471a1e49e7f 100644 --- a/app/code/Magento/Downloadable/view/adminhtml/templates/product/composite/fieldset/downloadable.phtml +++ b/app/code/Magento/Downloadable/view/adminhtml/templates/product/composite/fieldset/downloadable.phtml @@ -3,59 +3,63 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - +// phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis // @deprecated -// @codingStandardsIgnoreFile - ?> <?php /* @var $block \Magento\Downloadable\Block\Adminhtml\Catalog\Product\Composite\Fieldset\Downloadable */ ?> <?php $_linksPurchasedSeparately = $block->getLinksPurchasedSeparately(); ?> -<?php $_skipSaleableCheck = $this->helper('Magento\Catalog\Helper\Product')->getSkipSaleableCheck(); ?> -<?php if (($block->getProduct()->isSaleable() || $_skipSaleableCheck) && $block->hasLinks()):?> +<?php $_skipSaleableCheck = $this->helper(Magento\Catalog\Helper\Product::class)->getSkipSaleableCheck(); ?> +<?php if (($block->getProduct()->isSaleable() || $_skipSaleableCheck) && $block->hasLinks()) :?> <fieldset id="catalog_product_composite_configure_fields_downloadable" class="fieldset admin__fieldset downloadable information<?= $block->getIsLastFieldset() ? ' last-fieldset' : '' ?>"> - <legend class="legend admin__legend"><span><?= /* @escapeNotVerified */ __('Downloadable Information') ?></span></legend><br /> + <legend class="legend admin__legend"> + <span><?= $block->escapeHtml(__('Downloadable Information')) ?></span> + </legend><br /> <?php $_links = $block->getLinks(); ?> <?php $_isRequired = $block->getLinkSelectionRequired(); ?> - <div class="field admin__field link<?php if ($_isRequired) echo ' required _required' ?>"> - <label class="label admin__field-label"><span><?= /* @escapeNotVerified */ $block->getLinksTitle() ?></span></label> + <div class="field admin__field link <?php if ($_isRequired) { echo ' required _required'; } ?>"> + <label class="label admin__field-label"><span><?= $block->escapeHtml($block->getLinksTitle()) ?></span></label> <div class="control admin__field-control" id="downloadable-links-list"> - <?php foreach ($_links as $_link): ?> + <?php foreach ($_links as $_link) : ?> <div class="nested admin__field-option"> - <?php if ($_linksPurchasedSeparately): ?> + <?php if ($_linksPurchasedSeparately) : ?> <input type="checkbox" - class="admin__control-checkbox checkbox<?php if ($_isRequired):?> validate-one-required-by-name<?php endif; ?> product downloadable link" - name="links[]" id="links_<?= /* @escapeNotVerified */ $_link->getId() ?>" - value="<?= /* @escapeNotVerified */ $_link->getId() ?>" <?= /* @escapeNotVerified */ $block->getLinkCheckedValue($_link) ?> - price="<?= /* @escapeNotVerified */ $block->getCurrencyPrice($_link->getPrice()) ?>"/> + class="admin__control-checkbox checkbox<?php if ($_isRequired) :?> validate-one-required-by-name<?php endif; ?> product downloadable link" + name="links[]" id="links_<?= $block->escapeHtmlAttr($_link->getId()) ?>" + value="<?= $block->escapeHtmlAttr($_link->getId()) ?>" + <?= $block->escapeHtml($block->getLinkCheckedValue($_link)) ?> + price="<?= $block->escapeHtmlAttr($block->getCurrencyPrice($_link->getPrice())) ?>"/> <?php endif; ?> - <label for="links_<?= /* @escapeNotVerified */ $_link->getId() ?>" class="label admin__field-label"> + <label for="links_<?= $block->escapeHtmlAttr($_link->getId()) ?>" class="label admin__field-label"> <?= $block->escapeHtml($_link->getTitle()) ?> - <?php if ($_link->getSampleFile() || $_link->getSampleUrl()): ?> -  (<a href="<?= /* @escapeNotVerified */ $block->getLinkSampleUrl($_link) ?>" <?= $block->getIsOpenInNewWindow()?'onclick="this.target=\'_blank\'"':'' ?>><?= /* @escapeNotVerified */ __('sample') ?></a>) + <?php if ($_link->getSampleFile() || $_link->getSampleUrl()) : ?> +  (<a href="<?= $block->escapeUrl($block->getLinkSampleUrl($_link)) ?>" + <?= /* @noEscape */ $block->getIsOpenInNewWindow() ? 'onclick="this.target=\'_blank\'"' : '' ?>> + <?= $block->escapeHtml(__('sample')) ?> + </a>) <?php endif; ?> - <?php if ($_linksPurchasedSeparately): ?> - <?= /* @escapeNotVerified */ $block->getFormattedLinkPrice($_link) ?> + <?php if ($_linksPurchasedSeparately) : ?> + <?= /* @noEscape */ $block->getFormattedLinkPrice($_link) ?> <br /> - <?= /* @escapeNotVerified */ $block->getLinkPrice($_link) ?> + <?= /* @noEscape */ $block->getLinkPrice($_link) ?> <?php endif; ?> </label> - <?php if ($_isRequired): ?> + <?php if ($_isRequired) : ?> <script> -require(['prototype'], function(){ + require(['prototype'], function(){ - //<![CDATA[ - $('links_<?= /* @escapeNotVerified */ $_link->getId() ?>').advaiceContainer = 'links-advice-container'; - $('links_<?= /* @escapeNotVerified */ $_link->getId() ?>').callbackFunction = 'validateDownloadableCallback'; - //]]> - -}); -</script> + //<![CDATA[ + $('links_<?= $block->escapeJs($_link->getId()) ?>').advaiceContainer = 'links-advice-container'; + $('links_<?= $block->escapeJs($_link->getId()) ?>').callbackFunction = 'validateDownloadableCallback'; + //]]> + + }); + </script> <?php endif; ?> </div> <?php endforeach; ?> - <?php if ($_isRequired): ?> + <?php if ($_isRequired) : ?> <span id="links-advice-container"></span> <?php endif;?> </div> diff --git a/app/code/Magento/Downloadable/view/adminhtml/templates/product/edit/downloadable.phtml b/app/code/Magento/Downloadable/view/adminhtml/templates/product/edit/downloadable.phtml index a4443edb08e69..36032d58ab252 100644 --- a/app/code/Magento/Downloadable/view/adminhtml/templates/product/edit/downloadable.phtml +++ b/app/code/Magento/Downloadable/view/adminhtml/templates/product/edit/downloadable.phtml @@ -5,8 +5,6 @@ */ // @deprecated -// @codingStandardsIgnoreFile - ?> <?php @@ -114,7 +112,7 @@ var uploaderTemplate = '<div class="no-display" id="[[idName]]-template">' + this.idName = idName; this.config = config; uploaderTemplate = new Template(this.uploaderText, this.uploaderSyntax); - <?php if (!$block->isReadonly()):?> + <?php if (!$block->isReadonly()) :?> Element.insert( elmContainer, {'top' : uploaderTemplate.evaluate({ @@ -204,8 +202,8 @@ var uploaderTemplate = '<div class="no-display" id="[[idName]]-template">' + }); </script> -<div data-tab-type="tab_content_downloadableInfo" id="<?= /* @escapeNotVerified */ $block->getId() ?>_content" - <?= /* @escapeNotVerified */ $block->getUiId('tab', 'content', $block->getId()) ?> +<div data-tab-type="tab_content_downloadableInfo" id="<?= $block->escapeHtmlAttr($block->getId()) ?>_content" + <?= $block->escapeHtml($block->getUiId('tab', 'content', $block->getId())) ?> > <div id="alert_messages_block"><?= $block->getMessageHtml() ?></div> <div class="admin__field admin__field-option admin__field-is-downloaodable"> @@ -221,11 +219,11 @@ var uploaderTemplate = '<div class="no-display" id="[[idName]]-template">' + <div style="display:none"> <div id="custom-advice-container"></div> </div> -<?php if ($block->isReadonly()): ?> +<?php if ($block->isReadonly()) : ?> <script> require(['prototype'], function(){ - $(<?= /* @escapeNotVerified */ $block->getContentTabId() ?>).select('input', 'select', 'textarea', 'button').each(function (item){ + $(<?= /* @noEscape */ $block->getContentTabId() ?>).select('input', 'select', 'textarea', 'button').each(function (item){ item.disabled = true; if (item.tagName.toLowerCase() == 'button') { item.addClassName('disabled'); diff --git a/app/code/Magento/Downloadable/view/adminhtml/templates/product/edit/downloadable/links.phtml b/app/code/Magento/Downloadable/view/adminhtml/templates/product/edit/downloadable/links.phtml index c86019d9cd20c..747ada71221f9 100644 --- a/app/code/Magento/Downloadable/view/adminhtml/templates/product/edit/downloadable/links.phtml +++ b/app/code/Magento/Downloadable/view/adminhtml/templates/product/edit/downloadable/links.phtml @@ -5,8 +5,6 @@ */ // @deprecated -// @codingStandardsIgnoreFile - ?> <?php @@ -17,23 +15,23 @@ <?php $_product = $block->getProduct()?> <?php $block->getConfigJson() ?> <fieldset class="admin__fieldset downloadable-form" data-ui-id="downloadable-links"> - <legend class="admin__legend"><span><?= /* @escapeNotVerified */ __('Links') ?></span></legend><br> - <p class="note"><?= /* @escapeNotVerified */ __('Add links to your product files here.') ?></p> - <div class="admin__field" <?= !$block->isSingleStoreMode() ? ' data-config-scope="' . __('[STORE VIEW]') . '"' : '' ?>> - <label class="admin__field-label" for="downloadable_links_title"><span><?= /* @escapeNotVerified */ __('Title') ?></span></label> + <legend class="admin__legend"><span><?= $block->escapeHtml(__('Links')) ?></span></legend><br> + <p class="note"><?= $block->escapeHtml(__('Add links to your product files here.')) ?></p> + <div class="admin__field" <?= $block->escapeHtml(!$block->isSingleStoreMode() ? ' data-config-scope="' . __('[STORE VIEW]') . '"' : '') ?>> + <label class="admin__field-label" for="downloadable_links_title"><span><?= $block->escapeHtml(__('Title')) ?></span></label> <div class="admin__field-control"> <input type="text" class="admin__control-text" id="downloadable_links_title" name="product[links_title]" value="<?= $block->escapeHtml($block->getLinksTitle()) ?>" <?= ($_product->getStoreId() && $block->getUsedDefault()) ? 'disabled="disabled"' : '' ?>> - <?php if ($_product->getStoreId()): ?> + <?php if ($_product->getStoreId()) : ?> <div class="admin__field admin__field-option"> <input id="link_title_default" class="admin__control-checkbox" type="checkbox" name="use_default[]" value="links_title" onclick="toggleValueElements(this, this.parentNode.parentNode)" <?= $block->getUsedDefault() ? 'checked="checked"' : '' ?> /> - <label class="admin__field-label" for="link_title_default"><span><?= /* @escapeNotVerified */ __('Use Default Value') ?></span></label> + <label class="admin__field-label" for="link_title_default"><span><?= $block->escapeHtml(__('Use Default Value')) ?></span></label> </div> <?php endif; ?> </div> </div> - <div class="admin__field" <?= !$block->isSingleStoreMode() ? ' data-config-scope="' . __('[GLOBAL]') . '"' : '' ?>> - <label class="admin__field-label" for="downloadable_link_purchase_type"><span><?= /* @escapeNotVerified */ __('Links can be purchased separately') ?></span></label> + <div class="admin__field" <?= $block->escapeHtml(!$block->isSingleStoreMode() ? ' data-config-scope="' . __('[GLOBAL]') . '"' : '') ?>> + <label class="admin__field-label" for="downloadable_link_purchase_type"><span><?= $block->escapeHtml(__('Links can be purchased separately')) ?></span></label> <div class="admin__field-control"> <div class="admin__field-control link-switcher" data-role="link-switcher"> <div class="admin__field-control-group"> @@ -41,9 +39,9 @@ <input type="radio" name="product[links_purchased_separately]" value="1" class="admin__control-radio" id="link-switcher1" - <?php if($block->isProductLinksCanBePurchasedSeparately()): ?> + <?php if ($block->isProductLinksCanBePurchasedSeparately()) : ?> checked="checked" - <?php endif; ?> + <?php endif; ?> > <label class="admin__field-label" for="link-switcher1"> <span>Yes</span> @@ -53,7 +51,7 @@ <input type="radio" name="product[links_purchased_separately]" value="0" class="admin__control-radio" id="link-switcher0" - <?php if(!$block->isProductLinksCanBePurchasedSeparately()): ?> + <?php if (!$block->isProductLinksCanBePurchasedSeparately()) : ?> checked="checked" <?php endif; ?> > @@ -71,15 +69,15 @@ <table class="admin__control-table"> <thead> <tr> - <th class="col-sort"><span><?= /* @escapeNotVerified */ __('Sort Order') ?></span></th> - <th class="col-title _required"><span><?= /* @escapeNotVerified */ __('Title') ?></span></th> + <th class="col-sort"><span><?= $block->escapeHtml(__('Sort Order')) ?></span></th> + <th class="col-title _required"><span><?= $block->escapeHtml(__('Title')) ?></span></th> <?php if ($block->getCanReadPrice() !== false) : ?> - <th class="col-price"><span><?= /* @escapeNotVerified */ __('Price') ?></span></th> + <th class="col-price"><span><?= $block->escapeHtml(__('Price')) ?></span></th> <?php endif; ?> - <th class="col-file"><span><?= /* @escapeNotVerified */ __('Attach File or Enter Link') ?></span></th> - <th class="col-sample"><span><?= /* @escapeNotVerified */ __('Sample') ?></span></th> - <th class="col-share"><span><?= /* @escapeNotVerified */ __('Shareable') ?></span></th> - <th class="col-limit"><span><?= /* @escapeNotVerified */ __('Max. Downloads') ?></span></th> + <th class="col-file"><span><?= $block->escapeHtml(__('Attach File or Enter Link')) ?></span></th> + <th class="col-sample"><span><?= $block->escapeHtml(__('Sample')) ?></span></th> + <th class="col-share"><span><?= $block->escapeHtml(__('Shareable')) ?></span></th> + <th class="col-limit"><span><?= $block->escapeHtml(__('Max. Downloads')) ?></span></th> <th class="col-actions"> </th> </tr> </thead> @@ -93,7 +91,7 @@ </table> </div> <div class="admin__field-note"> - <span><?= /* @escapeNotVerified */ __('Alphanumeric, dash and underscore characters are recommended for filenames. Improper characters are replaced with \'_\'.') ?></span> + <span><?= $block->escapeHtml(__('Alphanumeric, dash and underscore characters are recommended for filenames. Improper characters are replaced with \'_\'.')) ?></span> </div> </div> </div> @@ -115,16 +113,16 @@ require([ 'data.id' + ' %>][sort_order]" ' + 'value="<%- data.sort_order %>" class="input-text admin__control-text sort" />' + - '<span class="draggable-handle" title="<?= /* @escapeNotVerified */ $block->escapeHtml(__('Sort Variations')) ?>"></span>' + + '<span class="draggable-handle" title="<?= $block->escapeJs($block->escapeHtml(__('Sort Variations'))) ?>"></span>' + '</td>'+ '<td class="col-title">'+ '<input type="hidden" class="__delete__" name="downloadable[link][<%- data.id %>][is_delete]" value="" />'+ '<input type="hidden" name="downloadable[link][<%- data.id %>][link_id]" value="<%- data.link_id %>" />'+ '<input type="text" class="required-entry input-text admin__control-text" name="downloadable[link][<%- data.id %>][title]" value="<%- data.title %>" />'+ - <?php if($_product->getStoreId()): ?> + <?php if ($_product->getStoreId()) : ?> '<div class="admin__field admin__field-option">'+ '<input type="checkbox" id="downloadable_link_<%- data.id %>_title" name="downloadable[link][<%- data.id %>][use_default_title]" value="1" class="admin__control-checkbox" />'+ - '<label for="downloadable_link_<%- data.id %>_title" class="admin__field-label"><span><?= /* @escapeNotVerified */ __('Use Default Value') ?></span></label>'+ + '<label for="downloadable_link_<%- data.id %>_title" class="admin__field-label"><span><?= $block->escapeJs($block->escapeHtml(__('Use Default Value'))) ?></span></label>'+ '</div>' + <?php endif; ?> <?php if ($block->getCanReadPrice() == false) : ?> @@ -143,7 +141,7 @@ require([ <?php if ($_product->getStoreId() && $block->getIsPriceWebsiteScope()) : ?> '<div class="admin__field admin__field-option">'+ '<input type="checkbox" id="downloadable_link_<%- data.id %>_price" name="downloadable[link][<%- data.id %>][use_default_price]" value="1"<?php if ($block->getCanEditPrice() === false) : ?> disabled="disabled"<?php endif; ?> class="admin__control-checkbox" />'+ - '<label for="downloadable_link_<%- data.id %>_price" class="admin__field-label"><span><?= /* @escapeNotVerified */ __('Use Default Value') ?></span></label>' + + '<label for="downloadable_link_<%- data.id %>_price" class="admin__field-label"><span><?= $block->escapeJs($block->escapeHtml(__('Use Default Value'))) ?></span></label>' + '</div>' + <?php endif; ?> '</td>' + @@ -151,25 +149,25 @@ require([ '<td class="col-file">'+ '<div class="admin__field admin__field-option">'+ '<input type="radio" class="admin__control-radio validate-one-required-by-name" id="downloadable_link_<%- data.id %>_file_type" name="downloadable[link][<%- data.id %>][type]" value="file"<%- data.file_checked %> />' + - '<label for="downloadable_link_<%- data.id %>_file_type" class="admin__field-label"><span><?= /* @escapeNotVerified */ __('File') ?></span></label>'+ + '<label for="downloadable_link_<%- data.id %>_file_type" class="admin__field-label"><span><?= $block->escapeJs($block->escapeHtml(__('File'))) ?></span></label>'+ '<input type="hidden" class="validate-downloadable-file" id="downloadable_link_<%- data.id %>_file_save" name="downloadable[link][<%- data.id %>][file]" value="<%- data.file_save %>" />'+ '<div id="downloadable_link_<%- data.id %>_file" class="admin__field-uploader">'+ '<div id="downloadable_link_<%- data.id %>_file-old" class="file-row-info"></div>'+ '<div id="downloadable_link_<%- data.id %>_file-new" class="file-row-info new-file"></div>'+ '<div class="fileinput-button form-buttons">'+ - '<span><?= /* @escapeNotVerified */ __('Browse Files...') ?></span>' + - '<input id="downloadable_link_<%- data.id %>_file" type="file" name="<?= $block->escapeHtml($block->getFileFieldName('links')) ?>">' + + '<span><?= $block->escapeJs($block->escapeHtml(__('Browse Files...'))) ?></span>' + + '<input id="downloadable_link_<%- data.id %>_file" type="file" name="<?= $block->escapeJs($block->escapeHtmlAttr($block->getFileFieldName('links'))) ?>">' + '<script>' + - 'linksUploader("#downloadable_link_<%- data.id %>_file", "<?= $block->escapeUrl($block->getUploadUrl('links')) ?>"); ' + + 'linksUploader("#downloadable_link_<%- data.id %>_file", "<?= $block->escapeJs($block->escapeUrl($block->getUploadUrl('links'))) ?>"); ' + '</scr'+'ipt>'+ '</div>'+ '</div>'+ '</div>'+ '<div class="admin__field admin__field-option admin__field-file-url">'+ '<input type="radio" class="admin__control-radio validate-one-required-by-name" id="downloadable_link_<%- data.id %>_url_type" name="downloadable[link][<%- data.id %>][type]" value="url"<%- data.url_checked %> />' + - '<label for="downloadable_link_<%- data.id %>_url_type" class="admin__field-label"><span><?= /* @escapeNotVerified */ __('URL') ?></span></label>' + - '<input type="text" class="validate-downloadable-url validate-url admin__control-text" name="downloadable[link][<%- data.id %>][link_url]" value="<%- data.link_url %>" placeholder="<?= /* @escapeNotVerified */ __('URL') ?>" />'+ + '<label for="downloadable_link_<%- data.id %>_url_type" class="admin__field-label"><span><?= $block->escapeJs($block->escapeHtml(__('URL'))) ?></span></label>' + + '<input type="text" class="validate-downloadable-url validate-url admin__control-text" name="downloadable[link][<%- data.id %>][link_url]" value="<%- data.link_url %>" placeholder="<?= $block->escapeJs($block->escapeHtmlAttr(__('URL'))) ?>" />'+ '</div>'+ '<div>'+ '<span id="downloadable_link_<%- data.id %>_link_container"></span>'+ @@ -178,16 +176,16 @@ require([ '<td class="col-sample">'+ '<div class="admin__field admin__field-option">'+ '<input type="radio" class="admin__control-radio" id="downloadable_link_<%- data.id %>_sample_file_type" name="downloadable[link][<%- data.id %>][sample][type]" value="file"<%- data.sample_file_checked %> />' + - '<label for="downloadable_link_<%- data.id %>_sample_file_type" class="admin__field-label"><span><?= /* @escapeNotVerified */ __('File') ?>:</span></label>'+ + '<label for="downloadable_link_<%- data.id %>_sample_file_type" class="admin__field-label"><span><?= $block->escapeJs($block->escapeHtml(__('File'))) ?>:</span></label>'+ '<input type="hidden" id="downloadable_link_<%- data.id %>_sample_file_save" name="downloadable[link][<%- data.id %>][sample][file]" value="<%- data.sample_file_save %>" class="validate-downloadable-file"/>'+ '<div id="downloadable_link_<%- data.id %>_sample_file" class="admin__field-uploader">'+ '<div id="downloadable_link_<%- data.id %>_sample_file-old" class="file-row-info"></div>'+ '<div id="downloadable_link_<%- data.id %>_sample_file-new" class="file-row-info new-file"></div>'+ '<div class="fileinput-button form-buttons">'+ - '<span><?= /* @escapeNotVerified */ __('Browse Files...') ?></span>' + - '<input id="downloadable_link_<%- data.id %>_sample_file" type="file" name="<?= $block->escapeHtml($block->getFileFieldName('link_samples'), '"') ?>">' + + '<span><?= $block->escapeJs($block->escapeHtml(__('Browse Files...'))) ?></span>' + + '<input id="downloadable_link_<%- data.id %>_sample_file" type="file" name="<?= $block->escapeJs($block->escapeHtmlAttr($block->getFileFieldName('link_samples'), '"')) ?>">' + '<script>'+ - 'linksUploader("#downloadable_link_<%- data.id %>_sample_file", "<?= $block->escapeUrl($block->getUploadUrl('link_samples')) ?>"); ' + + 'linksUploader("#downloadable_link_<%- data.id %>_sample_file", "<?= $block->escapeJs($block->escapeUrl($block->getUploadUrl('link_samples'))) ?>"); ' + '</scr'+'ipt>'+ '</div>'+ '</div>'+ @@ -195,8 +193,8 @@ require([ '<div class="admin__field admin__field-option admin__field-file-url">'+ '<input type="radio" class="admin__control-radio validate-one-required-by-name" id="downloadable_link_<%- data.id %>_sample_url_type" name="downloadable[link][<%- data.id %>][sample][type]" value="url"<%- data.sample_url_checked %> />' + - '<label for="downloadable_link_<%- data.id %>_sample_url_type" class="admin__field-label"><span><?= /* @escapeNotVerified */ __('URL') ?></span></label>'+ - '<input type="text" class="validate-downloadable-url validate-url admin__control-text" name="downloadable[link][<%- data.id %>][sample][url]" value="<%- data.sample_url %>" placeholder="<?= /* @escapeNotVerified */ __('URL') ?>" />'+ + '<label for="downloadable_link_<%- data.id %>_sample_url_type" class="admin__field-label"><span><?= $block->escapeJs($block->escapeHtml(__('URL'))) ?></span></label>'+ + '<input type="text" class="validate-downloadable-url validate-url admin__control-text" name="downloadable[link][<%- data.id %>][sample][url]" value="<%- data.sample_url %>" placeholder="<?= $block->escapeJs($block->escapeHtmlAttr(__('URL'))) ?>" />'+ '</div>'+ '<div>'+ '<span id="downloadable_link_<%- data.id %>_sample_container"></span>'+ @@ -204,20 +202,20 @@ require([ '</td>'+ '<td class="col-share">'+ '<select id="downloadable_link _<%- data.id %>_shareable" class="admin__control-select" name="downloadable[link][<%- data.id %>][is_shareable]">'+ - '<option value="1"><?= /* @escapeNotVerified */ __('Yes') ?></option>'+ - '<option value="0"><?= /* @escapeNotVerified */ __('No') ?></option>'+ - '<option value="2" selected="selected"><?= /* @escapeNotVerified */ __('Use config') ?></option>'+ + '<option value="1"><?= $block->escapeJs($block->escapeHtml(__('Yes'))) ?></option>'+ + '<option value="0"><?= $block->escapeJs($block->escapeHtml(__('No'))) ?></option>'+ + '<option value="2" selected="selected"><?= $block->escapeJs($block->escapeHtml(__('Use config'))) ?></option>'+ '</select>'+ '</td>'+ '<td class="col-limit">' + '<input type="text" id="downloadable_link_<%- data.id %>_downloads" name="downloadable[link][<%- data.id %>][number_of_downloads]" class="input-text validate-zero-or-greater admin__control-text downloads" value="<%- data.number_of_downloads %>" />'+ '<div class="admin__field admin__field-option">' + '<input type="checkbox" class="admin__control-checkbox" id="downloadable_link_<%- data.id %>_is_unlimited" name="downloadable[link][<%- data.id %>][is_unlimited]" value="1" <%- data.is_unlimited %> />' + - '<label for="downloadable_link_<%- data.id %>_is_unlimited" class="admin__field-label"><span><?= /* @escapeNotVerified */ __('Unlimited') ?></span></label>' + + '<label for="downloadable_link_<%- data.id %>_is_unlimited" class="admin__field-label"><span><?= $block->escapeJs($block->escapeHtml(__('Unlimited'))) ?></span></label>' + '</div>' + '</td>'+ '<td class="col-action">'+ - '<button id="downloadable_link_<%- data.id %>_delete_button" type="button" class="action-delete" title="<?= /* @escapeNotVerified */ $block->escapeHtml(__('Delete')) ?>"><span><?= /* @escapeNotVerified */ __('Delete') ?></span></button>'+ + '<button id="downloadable_link_<%- data.id %>_delete_button" type="button" class="action-delete" title="<?= $block->escapeJs($block->escapeHtmlAttr(__('Delete'))) ?>"><span><?= $block->escapeJs($block->escapeHtml(__('Delete'))) ?></span></button>'+ '</td>'+ '</tr>'; @@ -234,7 +232,7 @@ require([ data.link_id = 0; data.link_type = 'file'; data.sample_type = 'none'; - data.number_of_downloads = '<?= /* @escapeNotVerified */ $block->getConfigMaxDownloads() ?>'; + data.number_of_downloads = '<?= $block->escapeJs($block->getConfigMaxDownloads()) ?>'; data.sort_order = this.itemCount + 1; } @@ -325,7 +323,7 @@ require([ 'downloadable[link]['+data.id+'][sample]', data.sample_file_save, 'downloadable_link_'+data.id+'_sample_file', - <?= /* @escapeNotVerified */ $block->getConfigJson('link_samples') ?> + <?= /* @noEscape */ $block->getConfigJson('link_samples') ?> ); // link file new Downloadable.FileUploader( @@ -335,7 +333,7 @@ require([ 'downloadable[link]['+data.id+']', data.file_save, 'downloadable_link_'+data.id+'_file', - <?= /* @escapeNotVerified */ $block->getConfigJson() ?> + <?= /* @noEscape */ $block->getConfigJson() ?> ); linkFile = $('downloadable_link_'+data.id+'_file_type'); @@ -479,8 +477,8 @@ require([ Event.observe('add_link_item', 'click', linkItems.add.bind(linkItems)); } - <?php foreach ($block->getLinkData() as $item): ?> - linkItems.add(<?= /* @escapeNotVerified */ $item->toJson() ?>); + <?php foreach ($block->getLinkData() as $item) : ?> + linkItems.add(<?= /* @noEscape */ $item->toJson() ?>); <?php endforeach; ?> }); diff --git a/app/code/Magento/Downloadable/view/adminhtml/templates/product/edit/downloadable/samples.phtml b/app/code/Magento/Downloadable/view/adminhtml/templates/product/edit/downloadable/samples.phtml index 947d1d0b38bef..33c2afe827d03 100644 --- a/app/code/Magento/Downloadable/view/adminhtml/templates/product/edit/downloadable/samples.phtml +++ b/app/code/Magento/Downloadable/view/adminhtml/templates/product/edit/downloadable/samples.phtml @@ -5,8 +5,6 @@ */ // @deprecated -// @codingStandardsIgnoreFile - ?> <?php /** @@ -17,13 +15,13 @@ $_product = $block->getProduct(); $block->getConfigJson(); ?> <fieldset class="admin__fieldset downloadable-form" data-ui-id="downloadable-samples"> - <legend class="admin__legend"><span><?= /* @escapeNotVerified */ __('Samples') ?></span></legend><br> - <p class="note"><?= /* @escapeNotVerified */ __('Add product preview files here.') ?></p> - <div class="admin__field"<?= !$block->isSingleStoreMode() ? ' data-config-scope="' . __('[STORE VIEW]') . '"' : '' ?>> - <label class="admin__field-label" for="downloadable_samples_title"><span><?= /* @noEscape */ __('Title') ?></span></label> + <legend class="admin__legend"><span><?= $block->escapeHtml(__('Samples')) ?></span></legend><br> + <p class="note"><?= $block->escapeHtml(__('Add product preview files here.')) ?></p> + <div class="admin__field"<?= $block->escapeHtml(!$block->isSingleStoreMode() ? ' data-config-scope="' . __('[STORE VIEW]') . '"' : '') ?>> + <label class="admin__field-label" for="downloadable_samples_title"><span><?= $block->escapeHtml(__('Title')) ?></span></label> <div class="admin__field-control"> <input type="text" class="admin__control-text" id="downloadable_samples_title" name="product[samples_title]" value="<?= $block->escapeHtml($block->getSamplesTitle()) ?>" <?= /* @noEscape */ ($_product->getStoreId() && $block->getUsedDefault()) ? 'disabled="disabled"' : '' ?>> - <?php if ($_product->getStoreId()): ?> + <?php if ($_product->getStoreId()) : ?> <div class="admin__field admin__field-option"> <input id="sample_title_default" class="admin__control-checkbox" type="checkbox" name="use_default[]" value="samples_title" onclick="toggleValueElements(this, this.parentNode.parentNode)" <?= /* @noEscape */ $block->getUsedDefault() ? 'checked="checked"' : '' ?> /> <label class="admin__field-label" for="sample_title_default"><span>Use Default Value</span></label> @@ -37,9 +35,9 @@ $block->getConfigJson(); <table class="admin__control-table"> <thead> <tr> - <th class="col-sort"><span><?= /* @noEscape */ __('Sort Order') ?></span></th> - <th class="_required col-title"><span><?= /* @noEscape */ __('Title') ?></span></th> - <th class="col-file"><span><?= /* @noEscape */ __('Attach File or Enter Link') ?></span></th> + <th class="col-sort"><span><?= $block->escapeHtml(__('Sort Order')) ?></span></th> + <th class="_required col-title"><span><?= $block->escapeHtml(__('Title')) ?></span></th> + <th class="col-file"><span><?= $block->escapeHtml(__('Attach File or Enter Link')) ?></span></th> <th class="col-actions"> </th> </tr> </thead> @@ -71,30 +69,30 @@ require([ var sampleTemplate = '<tr>'+ '<td class="col-sort" data-role="draggable-handle">' + '<input data-container="link-order" type="hidden" name="downloadable[sample][<%- data.id %>][sort_order]" value="<%- data.sort_order %>" class="sort" />' + - '<span class="draggable-handle" title="<?= /* @escapeNotVerified */ $block->escapeHtml(__('Sort Variations')) ?>"></span>' + + '<span class="draggable-handle" title="<?= $block->escapeJs($block->escapeHtmlAttr(__('Sort Variations'))) ?>"></span>' + '</td>'+ '<td class="col-title">'+ '<input type="hidden" class="__delete__" name="downloadable[sample][<%- data.id %>][is_delete]" value="" />'+ '<input type="hidden" name="downloadable[sample][<%- data.id %>][sample_id]" value="<%- data.sample_id %>" />'+ '<input type="text" class="required-entry input-text admin__control-text" name="downloadable[sample][<%- data.id %>][title]" value="<%- data.title %>" />'+ - <?php if($_product->getStoreId()): ?> + <?php if ($_product->getStoreId()) : ?> '<div class="admin__field admin__field-option">'+ '<input type="checkbox" id="downloadable_sample_<%- data.id %>_title" name="downloadable[sample][<%- data.id %>][use_default_title]" value="1" class="admin__control-checkbox" />'+ - '<label for="downloadable_link_<%- data.id %>_price" class="admin__field-label"><span><?= /* @escapeNotVerified */ __('Use Default Value') ?></span></label>'+ + '<label for="downloadable_link_<%- data.id %>_price" class="admin__field-label"><span><?= $block->escapeJs($block->escapeHtml(__('Use Default Value'))) ?></span></label>'+ '</div>' + <?php endif; ?> '</td>'+ '<td class="col-file">'+ '<div class="admin__field admin__field-option">'+ '<input type="radio" class="admin__control-radio validate-one-required-by-name" id="downloadable_sample_<%- data.id %>_file_type" name="downloadable[sample][<%- data.id %>][type]" value="file"<%- data.file_checked %> />' + - '<label for="downloadable_sample_<%- data.id %>_file_type" class="admin__field-label"><span><?= /* @escapeNotVerified */ __('File') ?>:</span></label>'+ + '<label for="downloadable_sample_<%- data.id %>_file_type" class="admin__field-label"><span><?= $block->escapeJs($block->escapeHtml(__('File'))) ?>:</span></label>'+ '<input type="hidden" class="validate-downloadable-file" id="downloadable_sample_<%- data.id %>_file_save" name="downloadable[sample][<%- data.id %>][file]" value="<%- data.file_save %>" />'+ '<div id="downloadable_sample_<%- data.id %>_file" class="admin__field-uploader">'+ '<div id="downloadable_sample_<%- data.id %>_file-old" class="file-row-info"></div>'+ '<div id="downloadable_sample_<%- data.id %>_file-new" class="file-row-info new-file"></div>'+ '<div class="fileinput-button">'+ - '<span><?= /* @noEscape */ __('Browse Files...') ?></span>' + - '<input id="downloadable_sample_<%- data.id %>_file" type="file" name="<?= /* @noEscape */ $block->getConfig()->getFileField() ?>" data-url="<?= $block->escapeHtml($block->getConfig()->getUrl()) ?>">' + + '<span><?= $block->escapeJs($block->escapeHtml(__('Browse Files...'))) ?></span>' + + '<input id="downloadable_sample_<%- data.id %>_file" type="file" name="<?= /* @noEscape */ $block->getConfig()->getFileField() ?>" data-url="<?= /* @noEscape */ $block->getConfig()->getUrl() ?>">' + '<script>' + '/*<![CDATA[*/' + 'sampleUploader("#downloadable_sample_<%- data.id %>_file"); ' + @@ -105,15 +103,15 @@ require([ '</div>'+ '<div class="admin__field admin__field-option admin__field-file-url">'+ '<input type="radio" class="admin__control-radio validate-one-required-by-name" id="downloadable_sample_<%- data.id %>_url_type" name="downloadable[sample][<%- data.id %>][type]" value="url"<%- data.url_checked %> />' + - '<label for="downloadable_sample_<%- data.id %>_url_type" class="admin__field-label"><span><?= /* @escapeNotVerified */ __('URL') ?></span></label>' + - '<input type="text" class="validate-downloadable-url validate-url admin__control-text" name="downloadable[sample][<%- data.id %>][sample_url]" value="<%- data.sample_url %>" placeholder="<?= /* @escapeNotVerified */ __('URL') ?>" />'+ + '<label for="downloadable_sample_<%- data.id %>_url_type" class="admin__field-label"><span><?= $block->escapeJs($block->escapeHtml(__('URL'))) ?></span></label>' + + '<input type="text" class="validate-downloadable-url validate-url admin__control-text" name="downloadable[sample][<%- data.id %>][sample_url]" value="<%- data.sample_url %>" placeholder="<?= $block->escapeJs($block->escapeHtmlAttr(__('URL'))) ?>" />'+ '</div>'+ '<div>'+ '<span id="downloadable_sample_<%- data.id %>_container"></span>'+ '</div>'+ '</td>'+ '<td class="col-actions">'+ - '<button type="button" class="action-delete" title="<?= /* @escapeNotVerified */ $block->escapeHtml(__('Delete')) ?>"><span><?= /* @escapeNotVerified */ __('Delete') ?></span></button>'+ + '<button type="button" class="action-delete" title="<?= $block->escapeJs($block->escapeHtmlAttr(__('Delete'))) ?>"><span><?= $block->escapeJs($block->escapeHtml(__('Delete'))) ?></span></button>'+ '</td>'+ '</tr>'; sampleItems = { @@ -289,7 +287,7 @@ require([ Event.observe('add_sample_item', 'click', sampleItems.add.bind(sampleItems)); } - <?php foreach ($block->getSampleData() as $item): ?> + <?php foreach ($block->getSampleData() as $item) : ?> sampleItems.add(<?= /* @noEscape */ $item->toJson() ?>); <?php endforeach; ?> diff --git a/app/code/Magento/Downloadable/view/adminhtml/templates/sales/items/column/downloadable/creditmemo/name.phtml b/app/code/Magento/Downloadable/view/adminhtml/templates/sales/items/column/downloadable/creditmemo/name.phtml index 7438275bf5874..94c8405c718a8 100644 --- a/app/code/Magento/Downloadable/view/adminhtml/templates/sales/items/column/downloadable/creditmemo/name.phtml +++ b/app/code/Magento/Downloadable/view/adminhtml/templates/sales/items/column/downloadable/creditmemo/name.phtml @@ -3,44 +3,42 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - +// phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis ?> -<?php if ($_item = $block->getItem()): ?> - <div class="product-title"><?= /* @escapeNotVerified */ $_item->getName() ?></div> - <div><strong><?= /* @escapeNotVerified */ __('SKU') ?>:</strong> <?= implode('<br />', $this->helper('Magento\Catalog\Helper\Data')->splitSku($block->escapeHtml($block->getSku()))) ?></div> - <?php if ($block->getOrderOptions()): ?> +<?php if ($_item = $block->getItem()) : ?> + <div class="product-title"><?= $block->escapeHtml($_item->getName()) ?></div> + <div><strong><?= $block->escapeHtml(__('SKU')) ?>:</strong> <?= /* @noEscape */ implode('<br />', $this->helper(Magento\Catalog\Helper\Data::class)->splitSku($block->getSku())) ?></div> + <?php if ($block->getOrderOptions()) : ?> <dl class="item-options"> - <?php foreach ($block->getOrderOptions() as $_option): ?> + <?php foreach ($block->getOrderOptions() as $_option) : ?> <dt><?= $block->escapeHtml($_option['label']) ?></dt> <dd> - <?php if (isset($_option['custom_view']) && $_option['custom_view']): ?> - <?= /* @escapeNotVerified */ $_option['value'] ?> - <?php else: ?> - <?= $block->truncateString($_option['value'], 55, '', $_remainder) ?> - <?php if ($_remainder):?> - ... <span id="<?= /* @escapeNotVerified */ $_id = 'id' . uniqid() ?>"><?= /* @escapeNotVerified */ $_remainder ?></span> + <?php if (isset($_option['custom_view']) && $_option['custom_view']) : ?> + <?= $block->escapeHtml($_option['value']) ?> + <?php else : ?> + <?= $block->escapeHtml($block->truncateString($_option['value'], 55, '', $_remainder)) ?> + <?php if ($_remainder) :?> + ... <span id="<?= $block->escapeHtmlAttr($_id = 'id' . uniqid()) ?>"><?= $block->escapeHtml($_remainder) ?></span> <script> -require(['prototype'], function(){ - - $('<?= /* @escapeNotVerified */ $_id ?>').hide(); - $('<?= /* @escapeNotVerified */ $_id ?>').up().observe('mouseover', function(){$('<?= /* @escapeNotVerified */ $_id ?>').show();}); - $('<?= /* @escapeNotVerified */ $_id ?>').up().observe('mouseout', function(){$('<?= /* @escapeNotVerified */ $_id ?>').hide();}); + require(['prototype'], function(){ + <?php $escapedId = $block->escapeJs($_id) ?> + $('<?= /* @noEscape */ $escapedId ?>').hide(); + $('<?= /* @noEscape */ $escapedId ?>').up().observe('mouseover', function(){$('<?= /* @noEscape */ $escapedId ?>').show();}); + $('<?= /* @noEscape */ $escapedId ?>').up().observe('mouseout', function(){$('<?= /* @noEscape */ $escapedId ?>').hide();}); -}); -</script> + }); + </script> <?php endif;?> <?php endif;?> </dd> <?php endforeach; ?> </dl> <?php endif; ?> - <?php if ($block->getLinks()): ?> + <?php if ($block->getLinks()) : ?> <dl class="item-options"> <dt><?= $block->escapeHtml($block->getLinksTitle()) ?></dt> - <?php foreach ($block->getLinks()->getPurchasedItems() as $_link): ?> + <?php foreach ($block->getLinks()->getPurchasedItems() as $_link) : ?> <dd><?= $block->escapeHtml($_link->getLinkTitle()) ?></dd> <?php endforeach; ?> </dl> diff --git a/app/code/Magento/Downloadable/view/adminhtml/templates/sales/items/column/downloadable/invoice/name.phtml b/app/code/Magento/Downloadable/view/adminhtml/templates/sales/items/column/downloadable/invoice/name.phtml index b00c4b4b45392..9a45066f64d15 100644 --- a/app/code/Magento/Downloadable/view/adminhtml/templates/sales/items/column/downloadable/invoice/name.phtml +++ b/app/code/Magento/Downloadable/view/adminhtml/templates/sales/items/column/downloadable/invoice/name.phtml @@ -3,45 +3,42 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - +// phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis ?> -<?php if ($_item = $block->getItem()): ?> - <div class="product-title"><?= /* @escapeNotVerified */ $_item->getName() ?></div> - <div><strong><?= /* @escapeNotVerified */ __('SKU') ?>:</strong> <?= implode('<br />', $this->helper('Magento\Catalog\Helper\Data')->splitSku($block->escapeHtml($block->getSku()))) ?></div> - <?php if ($block->getOrderOptions()): ?> +<?php if ($_item = $block->getItem()) : ?> + <div class="product-title"><?= $block->escapeHtml($_item->getName()) ?></div> + <div><strong><?= $block->escapeHtml(__('SKU')) ?>:</strong> <?= /* @noEscape */ implode('<br />', $this->helper(Magento\Catalog\Helper\Data::class)->splitSku($block->getSku())) ?></div> + <?php if ($block->getOrderOptions()) : ?> <dl class="item-options"> - <?php foreach ($block->getOrderOptions() as $_option): ?> + <?php foreach ($block->getOrderOptions() as $_option) : ?> <dt><?= $block->escapeHtml($_option['label']) ?></dt> <dd> - <?php if (isset($_option['custom_view']) && $_option['custom_view']): ?> - <?= /* @escapeNotVerified */ $_option['value'] ?> - <?php else: ?> - <?= $block->truncateString($_option['value'], 55, '', $_remainder) ?> - <?php if ($_remainder):?> - ... <span id="<?= /* @escapeNotVerified */ $_id = 'id' . uniqid() ?>"><?= /* @escapeNotVerified */ $_remainder ?></span> + <?php if (isset($_option['custom_view']) && $_option['custom_view']) : ?> + <?= $block->escapeHtml($_option['value']) ?> + <?php else : ?> + <?= $block->escapeHtml($block->truncateString($_option['value'], 55, '', $_remainder)) ?> + <?php if ($_remainder) :?> + ... <span id="<?= $block->escapeHtmlAttr($_id = 'id' . uniqid()) ?>"><?= $block->escapeHtml($_remainder) ?></span> <script> -require(['prototype'], function(){ - - $('<?= /* @escapeNotVerified */ $_id ?>').hide(); - $('<?= /* @escapeNotVerified */ $_id ?>').up().observe('mouseover', function(){$('<?= /* @escapeNotVerified */ $_id ?>').show();}); - $('<?= /* @escapeNotVerified */ $_id ?>').up().observe('mouseout', function(){$('<?= /* @escapeNotVerified */ $_id ?>').hide();}); - -}); -</script> + require(['prototype'], function(){ + <?php $escapedId = $block->escapeJs($_id) ?> + $('<?= /* @noEscape */ $escapedId ?>').hide(); + $('<?= /* @noEscape */ $escapedId ?>').up().observe('mouseover', function(){$('<?= /* @noEscape */ $escapedId ?>').show();}); + $('<?= /* @noEscape */ $escapedId ?>').up().observe('mouseout', function(){$('<?= /* @noEscape */ $escapedId ?>').hide();}); + }); + </script> <?php endif;?> <?php endif;?> </dd> <?php endforeach; ?> </dl> <?php endif; ?> - <?php if ($block->getLinks()): ?> + <?php if ($block->getLinks()) : ?> <dl class="item-options"> <dt><?= $block->escapeHtml($block->getLinksTitle()) ?></dt> - <?php foreach ($block->getLinks()->getPurchasedItems() as $_link): ?> - <dd><?= $block->escapeHtml($_link->getLinkTitle()) ?> (<?= /* @escapeNotVerified */ $_link->getNumberOfDownloadsBought() ? $_link->getNumberOfDownloadsBought() : __('Unlimited') ?>)</dd> + <?php foreach ($block->getLinks()->getPurchasedItems() as $_link) : ?> + <dd><?= $block->escapeHtml($_link->getLinkTitle()) ?> (<?= $block->escapeHtml($_link->getNumberOfDownloadsBought() ? $_link->getNumberOfDownloadsBought() : __('Unlimited')) ?>)</dd> <?php endforeach; ?> </dl> <?php endif; ?> diff --git a/app/code/Magento/Downloadable/view/adminhtml/templates/sales/items/column/downloadable/name.phtml b/app/code/Magento/Downloadable/view/adminhtml/templates/sales/items/column/downloadable/name.phtml index 9e6a73263bf25..b5fe7b3385630 100644 --- a/app/code/Magento/Downloadable/view/adminhtml/templates/sales/items/column/downloadable/name.phtml +++ b/app/code/Magento/Downloadable/view/adminhtml/templates/sales/items/column/downloadable/name.phtml @@ -3,48 +3,45 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - +// phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis ?> -<?php if ($_item = $block->getItem()): ?> - <div class="product-title"><?= /* @escapeNotVerified */ $_item->getName() ?></div> +<?php if ($_item = $block->getItem()) : ?> + <div class="product-title"><?= $block->escapeHtml($_item->getName()) ?></div> <div class="product-sku-block"> - <span><?= /* @escapeNotVerified */ __('SKU') ?>:</span> - <?= implode('<br />', $this->helper('Magento\Catalog\Helper\Data')->splitSku($block->escapeHtml($block->getSku()))) ?> + <span><?= $block->escapeHtml(__('SKU')) ?>:</span> + <?= /* @noEscape */ implode('<br />', $this->helper(Magento\Catalog\Helper\Data::class)->splitSku($block->getSku())) ?> </div> - <?php if ($block->getOrderOptions()): ?> + <?php if ($block->getOrderOptions()) : ?> <dl class="item-options"> - <?php foreach ($block->getOrderOptions() as $_option): ?> + <?php foreach ($block->getOrderOptions() as $_option) : ?> <dt><?= $block->escapeHtml($_option['label']) ?>:</dt> <dd> - <?php if (isset($_option['custom_view']) && $_option['custom_view']): ?> - <?= /* @escapeNotVerified */ $_option['value'] ?> - <?php else: ?> - <?= $block->truncateString($_option['value'], 55, '', $_remainder) ?> - <?php if ($_remainder):?> - ... <span id="<?= /* @escapeNotVerified */ $_id = 'id' . uniqid() ?>"><?= /* @escapeNotVerified */ $_remainder ?></span> + <?php if (isset($_option['custom_view']) && $_option['custom_view']) : ?> + <?= $block->escapeHtml($_option['value']) ?> + <?php else : ?> + <?= $block->escapeHtml($block->truncateString($_option['value'], 55, '', $_remainder)) ?> + <?php if ($_remainder) :?> + ... <span id="<?= $block->escapeHtmlAttr($_id = 'id' . uniqid()) ?>"><?= $block->escapeHtml($_remainder) ?></span> <script> -require(['prototype'], function(){ - - $('<?= /* @escapeNotVerified */ $_id ?>').hide(); - $('<?= /* @escapeNotVerified */ $_id ?>').up().observe('mouseover', function(){$('<?= /* @escapeNotVerified */ $_id ?>').show();}); - $('<?= /* @escapeNotVerified */ $_id ?>').up().observe('mouseout', function(){$('<?= /* @escapeNotVerified */ $_id ?>').hide();}); - -}); -</script> + require(['prototype'], function(){ + <?php $escapedId = $block->escapeJs($_id) ?> + $('<?= /* @noEscape */ $escapedId ?>').hide(); + $('<?= /* @noEscape */ $escapedId ?>').up().observe('mouseover', function(){$('<?= /* @noEscape */ $escapedId ?>').show();}); + $('<?= /* @noEscape */ $escapedId ?>').up().observe('mouseout', function(){$('<?= /* @noEscape */ $escapedId ?>').hide();}); + }); + </script> <?php endif;?> <?php endif;?> </dd> <?php endforeach; ?> </dl> <?php endif; ?> - <?php if ($block->getLinks()): ?> + <?php if ($block->getLinks()) : ?> <dl class="item-options"> <dt><?= $block->escapeHtml($block->getLinksTitle()) ?>:</dt> - <?php foreach ($block->getLinks()->getPurchasedItems() as $_link): ?> - <dd><?= $block->escapeHtml($_link->getLinkTitle()) ?> (<?= /* @escapeNotVerified */ $_link->getNumberOfDownloadsUsed() . ' / ' . ($_link->getNumberOfDownloadsBought() ? $_link->getNumberOfDownloadsBought() : __('U')) ?>)</dd> + <?php foreach ($block->getLinks()->getPurchasedItems() as $_link) : ?> + <dd><?= $block->escapeHtml($_link->getLinkTitle()) ?> (<?= $block->escapeHtml($_link->getNumberOfDownloadsUsed() . ' / ' . ($_link->getNumberOfDownloadsBought() ? $_link->getNumberOfDownloadsBought() : __('U'))) ?>)</dd> <?php endforeach; ?> </dl> <?php endif; ?> diff --git a/app/code/Magento/Downloadable/view/frontend/templates/catalog/product/links.phtml b/app/code/Magento/Downloadable/view/frontend/templates/catalog/product/links.phtml index 5548279a1118b..cb924e6b9ac96 100644 --- a/app/code/Magento/Downloadable/view/frontend/templates/catalog/product/links.phtml +++ b/app/code/Magento/Downloadable/view/frontend/templates/catalog/product/links.phtml @@ -3,61 +3,58 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <?php /* @var $block \Magento\Downloadable\Block\Catalog\Product\Links */ ?> <?php $_linksPurchasedSeparately = $block->getLinksPurchasedSeparately(); ?> -<?php if ($block->getProduct()->isSaleable() && $block->hasLinks()):?> +<?php if ($block->getProduct()->isSaleable() && $block->hasLinks()) :?> <?php $_links = $block->getLinks(); ?> <?php $_linksLength = 0; ?> <?php $_isRequired = $block->getLinkSelectionRequired(); ?> <legend class="legend links-title"><span><?= $block->escapeHtml($block->getLinksTitle()) ?></span></legend><br> - <div class="field downloads<?php if ($_isRequired) echo ' required' ?><?php if (!$_linksPurchasedSeparately) echo ' downloads-no-separately' ?>"> + <div class="field downloads<?php if ($_isRequired) { echo ' required'; } ?><?php if (!$_linksPurchasedSeparately) { echo ' downloads-no-separately'; } ?>"> <label class="label"><span><?= $block->escapeHtml($block->getLinksTitle()) ?></span></label> <div class="control" id="downloadable-links-list" data-mage-init='{"downloadable":{ "linkElement":"input:checkbox[value]", "allElements":"#links_all", - "config":<?= /* @escapeNotVerified */ $block->getJsonConfig() ?>} + "config":<?= $block->escapeHtmlAttr($block->getJsonConfig()) ?>} }' data-container-for="downloadable-links"> - <?php foreach ($_links as $_link): ?> + <?php foreach ($_links as $_link) : ?> <?php $_linksLength++;?> <div class="field choice" data-role="link"> - <?php if ($_linksPurchasedSeparately): ?> + <?php if ($_linksPurchasedSeparately) : ?> <input type="checkbox" - <?php if ($_isRequired): ?>data-validate="{'validate-one-checkbox-required-by-name':'downloadable-links-list'}" <?php endif; ?> + <?php if ($_isRequired) : ?>data-validate="{'validate-one-checkbox-required-by-name':'downloadable-links-list'}" <?php endif; ?> name="links[]" - id="links_<?= /* @escapeNotVerified */ $_link->getId() ?>" - value="<?= /* @escapeNotVerified */ $_link->getId() ?>" <?= /* @escapeNotVerified */ $block->getLinkCheckedValue($_link) ?> /> + id="links_<?= $block->escapeHtmlAttr($_link->getId()) ?>" + value="<?= $block->escapeHtmlAttr($_link->getId()) ?>" <?= $block->escapeHtml($block->getLinkCheckedValue($_link)) ?> /> <?php endif; ?> - <label class="label" for="links_<?= /* @escapeNotVerified */ $_link->getId() ?>"> + <label class="label" for="links_<?= $block->escapeHtmlAttr($_link->getId()) ?>"> <span><?= $block->escapeHtml($_link->getTitle()) ?></span> - <?php if ($_link->getSampleFile() || $_link->getSampleUrl()): ?> + <?php if ($_link->getSampleFile() || $_link->getSampleUrl()) : ?> <a class="sample link" href="<?= $block->escapeUrl($block->getLinkSampleUrl($_link)) ?>" <?= $block->getIsOpenInNewWindow() ? 'target="_blank"' : '' ?>> - <?= /* @escapeNotVerified */ __('sample') ?> + <?= $block->escapeHtml(__('sample')) ?> </a> <?php endif; ?> - <?php if ($_linksPurchasedSeparately): ?> - <?= /* @escapeNotVerified */ $block->getLinkPrice($_link) ?> + <?php if ($_linksPurchasedSeparately) : ?> + <?= /* @noEscape */ $block->getLinkPrice($_link) ?> <?php endif; ?> </label> </div> <?php endforeach; ?> - <?php if ($_linksPurchasedSeparately && $_linksLength > 1): ?> + <?php if ($_linksPurchasedSeparately && $_linksLength > 1) : ?> <div class="field choice downloads-all"> <input type="checkbox" - data-notchecked="<?= /* @escapeNotVerified */ __('Select all') ?>" - data-checked="<?= /* @escapeNotVerified */ __('Unselect all') ?>" + data-notchecked="<?= $block->escapeHtmlAttr(__('Select all')) ?>" + data-checked="<?= $block->escapeHtmlAttr(__('Unselect all')) ?>" id="links_all" /> - <label class="label" for="links_all"><span><?= /* @escapeNotVerified */ __('Select all') ?></span></label> + <label class="label" for="links_all"><span><?= $block->escapeHtml(__('Select all')) ?></span></label> </div> <?php endif; ?> </div> - <?php if ($_isRequired): ?> + <?php if ($_isRequired) : ?> <span id="links-advice-container"></span> <?php endif;?> </div> diff --git a/app/code/Magento/Downloadable/view/frontend/templates/catalog/product/samples.phtml b/app/code/Magento/Downloadable/view/frontend/templates/catalog/product/samples.phtml index c6b2281196e69..3eea3803a0513 100644 --- a/app/code/Magento/Downloadable/view/frontend/templates/catalog/product/samples.phtml +++ b/app/code/Magento/Downloadable/view/frontend/templates/catalog/product/samples.phtml @@ -4,8 +4,6 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - /** * Downloadable product links * @@ -13,11 +11,11 @@ */ ?> -<?php if ($block->hasSamples()): ?> +<?php if ($block->hasSamples()) : ?> <dl class="items samples"> <dt class="item-title samples-item-title"><?= $block->escapeHtml($block->getSamplesTitle()) ?></dt> <?php $_samples = $block->getSamples() ?> - <?php foreach ($_samples as $_sample): ?> + <?php foreach ($_samples as $_sample) : ?> <dd class="item samples-item"> <a href="<?= $block->escapeHtml($block->getSampleUrl($_sample)) ?>" <?= $block->getIsOpenInNewWindow() ? 'onclick="this.target=\'_blank\'"' : '' ?> class="item-link samples-item-link"> diff --git a/app/code/Magento/Downloadable/view/frontend/templates/catalog/product/type.phtml b/app/code/Magento/Downloadable/view/frontend/templates/catalog/product/type.phtml index d4afdf5f81fe2..ba6d9e0abec71 100644 --- a/app/code/Magento/Downloadable/view/frontend/templates/catalog/product/type.phtml +++ b/app/code/Magento/Downloadable/view/frontend/templates/catalog/product/type.phtml @@ -4,8 +4,6 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - /** * Downloadable product type * @@ -14,13 +12,13 @@ ?> <?php $_product = $block->getProduct() ?> -<?php if ($_product->getIsSalable()): ?> - <div class="stock available" title="<?= /* @escapeNotVerified */ __('Availability') ?>"> - <span><?= /* @escapeNotVerified */ __('In stock') ?></span> +<?php if ($_product->getIsSalable()) : ?> + <div class="stock available" title="<?= $block->escapeHtmlAttr(__('Availability')) ?>"> + <span><?= $block->escapeHtml(__('In stock')) ?></span> </div> -<?php else: ?> - <div class="stock unavailable" title="<?= /* @escapeNotVerified */ __('Availability') ?>"> - <span><?= /* @escapeNotVerified */ __('Out of stock') ?></span> +<?php else : ?> + <div class="stock unavailable" title="<?= $block->escapeHtmlAttr(__('Availability')) ?>"> + <span><?= $block->escapeHtml(__('Out of stock')) ?></span> </div> <?php endif; ?> <?= $block->getChildHtml() ?> diff --git a/app/code/Magento/Downloadable/view/frontend/templates/checkout/success.phtml b/app/code/Magento/Downloadable/view/frontend/templates/checkout/success.phtml index a32d854d3b47f..ad631b0ebd095 100644 --- a/app/code/Magento/Downloadable/view/frontend/templates/checkout/success.phtml +++ b/app/code/Magento/Downloadable/view/frontend/templates/checkout/success.phtml @@ -3,10 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> -<?php if ($block->getOrderHasDownloadable()): ?> - <?= /* @escapeNotVerified */ __('Go to <a href="%1">My Downloadable Products</a>', $block->getDownloadableProductsUrl()) ?> +<?php if ($block->getOrderHasDownloadable()) : ?> + <?= /* @noEscape */ __('Go to <a href="%1">My Downloadable Products</a>', $block->escapeUrl($block->getDownloadableProductsUrl())) ?> <?php endif; ?> diff --git a/app/code/Magento/Downloadable/view/frontend/templates/customer/products/list.phtml b/app/code/Magento/Downloadable/view/frontend/templates/customer/products/list.phtml index 08cadb2d78f11..d040714aa9aef 100644 --- a/app/code/Magento/Downloadable/view/frontend/templates/customer/products/list.phtml +++ b/app/code/Magento/Downloadable/view/frontend/templates/customer/products/list.phtml @@ -3,9 +3,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <?php /** @@ -13,55 +10,55 @@ */ ?> <?php $_items = $block->getItems(); ?> -<?php if (count($_items)): ?> +<?php if (count($_items)) : ?> <div class="table-wrapper downloadable-products"> <table id="my-downloadable-products-table" class="data table table-downloadable-products"> - <caption class="table-caption"><?= /* @escapeNotVerified */ __('Downloadable Products') ?></caption> + <caption class="table-caption"><?= $block->escapeHtml(__('Downloadable Products')) ?></caption> <thead> <tr> - <th scope="col" class="col id"><?= /* @escapeNotVerified */ __('Order #') ?></th> - <th scope="col" class="col date"><?= /* @escapeNotVerified */ __('Date') ?></th> - <th scope="col" class="col title"><?= /* @escapeNotVerified */ __('Title') ?></th> - <th scope="col" class="col status"><?= /* @escapeNotVerified */ __('Status') ?></th> - <th scope="col" class="col remaining"><?= /* @escapeNotVerified */ __('Remaining Downloads') ?></th> + <th scope="col" class="col id"><?= $block->escapeHtml(__('Order #')) ?></th> + <th scope="col" class="col date"><?= $block->escapeHtml(__('Date')) ?></th> + <th scope="col" class="col title"><?= $block->escapeHtml(__('Title')) ?></th> + <th scope="col" class="col status"><?= $block->escapeHtml(__('Status')) ?></th> + <th scope="col" class="col remaining"><?= $block->escapeHtml(__('Remaining Downloads')) ?></th> </tr> </thead> <tbody> - <?php foreach ($_items as $_item): ?> + <?php foreach ($_items as $_item) : ?> <tr> <td data-th="<?= $block->escapeHtml(__('Order #')) ?>" class="col id"> - <a href="<?= /* @escapeNotVerified */ $block->getOrderViewUrl($_item->getPurchased()->getOrderId()) ?>" + <a href="<?= $block->escapeUrl($block->getOrderViewUrl($_item->getPurchased()->getOrderId())) ?>" title="<?= $block->escapeHtml(__('View Order')) ?>"> - <?= /* @escapeNotVerified */ $_item->getPurchased()->getOrderIncrementId() ?> + <?= $block->escapeHtml($_item->getPurchased()->getOrderIncrementId()) ?> </a> </td> - <td data-th="<?= $block->escapeHtml(__('Date')) ?>" class="col date"><?= /* @escapeNotVerified */ $block->formatDate($_item->getPurchased()->getCreatedAt()) ?></td> + <td data-th="<?= $block->escapeHtml(__('Date')) ?>" class="col date"><?= $block->escapeHtml($block->formatDate($_item->getPurchased()->getCreatedAt())) ?></td> <td data-th="<?= $block->escapeHtml(__('Title')) ?>" class="col title"> <strong class="product-name"><?= $block->escapeHtml($_item->getPurchased()->getProductName()) ?></strong> - <?php if ($_item->getStatus() == \Magento\Downloadable\Model\Link\Purchased\Item::LINK_STATUS_AVAILABLE): ?> - <a href="<?= /* @escapeNotVerified */ $block->getDownloadUrl($_item) ?>" title="<?= $block->escapeHtml(__('Start Download')) ?>" class="action download" <?= $block->getIsOpenInNewWindow() ? 'onclick="this.target=\'_blank\'"' : '' ?>><?= $block->escapeHtml($_item->getLinkTitle()) ?></a> + <?php if ($_item->getStatus() == \Magento\Downloadable\Model\Link\Purchased\Item::LINK_STATUS_AVAILABLE) : ?> + <a href="<?= $block->escapeUrl($block->getDownloadUrl($_item)) ?>" title="<?= $block->escapeHtmlAttr(__('Start Download')) ?>" class="action download" <?= /* @noEscape */ $block->getIsOpenInNewWindow() ? 'onclick="this.target=\'_blank\'"' : '' ?>><?= $block->escapeHtml($_item->getLinkTitle()) ?></a> <?php endif; ?> </td> - <td data-th="<?= $block->escapeHtml(__('Status')) ?>" class="col status"><?= /* @escapeNotVerified */ __(ucfirst($_item->getStatus())) ?></td> - <td data-th="<?= $block->escapeHtml(__('Remaining Downloads')) ?>" class="col remaining"><?= /* @escapeNotVerified */ $block->getRemainingDownloads($_item) ?></td> + <td data-th="<?= $block->escapeHtml(__('Status')) ?>" class="col status"><?= $block->escapeHtml(__(ucfirst($_item->getStatus()))) ?></td> + <td data-th="<?= $block->escapeHtml(__('Remaining Downloads')) ?>" class="col remaining"><?= $block->escapeHtml($block->getRemainingDownloads($_item)) ?></td> </tr> <?php endforeach; ?> </tbody> </table> </div> - <?php if ($block->getChildHtml('pager')): ?> + <?php if ($block->getChildHtml('pager')) : ?> <div class="toolbar downloadable-products-toolbar bottom"> <?= $block->getChildHtml('pager') ?> </div> <?php endif; ?> -<?php else: ?> - <div class="message info empty"><span><?= /* @escapeNotVerified */ __('You have not purchased any downloadable products yet.') ?></span></div> +<?php else : ?> + <div class="message info empty"><span><?= $block->escapeHtml(__('You have not purchased any downloadable products yet.')) ?></span></div> <?php endif; ?> <div class="actions-toolbar"> <div class="secondary"> <a href="<?= $block->escapeUrl($block->getBackUrl()) ?>" class="action back"> - <span><?= /* @escapeNotVerified */ __('Back') ?></span> + <span><?= $block->escapeHtml(__('Back')) ?></span> </a> </div> </div> diff --git a/app/code/Magento/Downloadable/view/frontend/templates/email/order/items/creditmemo/downloadable.phtml b/app/code/Magento/Downloadable/view/frontend/templates/email/order/items/creditmemo/downloadable.phtml index 4882074b60074..78c9f712dfaab 100644 --- a/app/code/Magento/Downloadable/view/frontend/templates/email/order/items/creditmemo/downloadable.phtml +++ b/app/code/Magento/Downloadable/view/frontend/templates/email/order/items/creditmemo/downloadable.phtml @@ -3,9 +3,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <?php /** @var $block \Magento\Downloadable\Block\Sales\Order\Email\Items\Downloadable */ ?> <?php $_item = $block->getItem() ?> @@ -13,19 +10,19 @@ <tr> <td class="item-info has-extra"> <p class="product-name"><?= $block->escapeHtml($_item->getName()) ?></p> - <p class="sku"><?= /* @escapeNotVerified */ __('SKU') ?>: <?= $block->escapeHtml($block->getSku($_item)) ?></p> - <?php if ($block->getItemOptions()): ?> + <p class="sku"><?= $block->escapeHtml(__('SKU')) ?>: <?= $block->escapeHtml($block->getSku($_item)) ?></p> + <?php if ($block->getItemOptions()) : ?> <dl> - <?php foreach ($block->getItemOptions() as $option): ?> - <dt><strong><em><?= /* @escapeNotVerified */ $option['label'] ?></em></strong></dt> - <dd><?= /* @escapeNotVerified */ $option['value'] ?></dd> + <?php foreach ($block->getItemOptions() as $option) : ?> + <dt><strong><em><?= $block->escapeHtml($option['label']) ?></em></strong></dt> + <dd><?= $block->escapeHtml($option['value']) ?></dd> <?php endforeach; ?> </dl> <?php endif; ?> - <?php if ($links = $block->getLinks()->getPurchasedItems()): ?> + <?php if ($links = $block->getLinks()->getPurchasedItems()) : ?> <dl> - <dt><strong><em><?= /* @escapeNotVerified */ $block->getLinksTitle() ?></em></strong></dt> - <?php foreach ($links as $link): ?> + <dt><strong><em><?= $block->escapeHtml($block->getLinksTitle()) ?></em></strong></dt> + <?php foreach ($links as $link) : ?> <dd> <?= $block->escapeHtml($link->getLinkTitle()) ?> </dd> @@ -34,8 +31,8 @@ <?php endif; ?> <?= $block->escapeHtml($_item->getDescription()) ?> </td> - <td class="item-qty"><?= /* @escapeNotVerified */ $_item->getQty() * 1 ?></td> + <td class="item-qty"><?= $block->escapeHtml($_item->getQty() * 1) ?></td> <td class="item-price"> - <?= /* @escapeNotVerified */ $block->getItemPrice($_item) ?> + <?= $block->escapeHtml($block->getItemPrice($_item)) ?> </td> </tr> diff --git a/app/code/Magento/Downloadable/view/frontend/templates/email/order/items/invoice/downloadable.phtml b/app/code/Magento/Downloadable/view/frontend/templates/email/order/items/invoice/downloadable.phtml index 1be4ac56bad3e..727bf156f368a 100644 --- a/app/code/Magento/Downloadable/view/frontend/templates/email/order/items/invoice/downloadable.phtml +++ b/app/code/Magento/Downloadable/view/frontend/templates/email/order/items/invoice/downloadable.phtml @@ -3,9 +3,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <?php /** @var $block \Magento\Downloadable\Block\Sales\Order\Email\Items\Downloadable */ ?> <?php $_item = $block->getItem() ?> @@ -13,30 +10,30 @@ <tr> <td class="item-info has-extra"> <p class="product-name"><?= $block->escapeHtml($_item->getName()) ?></p> - <p class="sku"><?= /* @escapeNotVerified */ __('SKU') ?>: <?= $block->escapeHtml($block->getSku($_item)) ?></p> - <?php if ($block->getItemOptions()): ?> + <p class="sku"><?= $block->escapeHtml(__('SKU')) ?>: <?= $block->escapeHtml($block->getSku($_item)) ?></p> + <?php if ($block->getItemOptions()) : ?> <dl> - <?php foreach ($block->getItemOptions() as $option): ?> - <dt><strong><em><?= /* @escapeNotVerified */ $option['label'] ?></em></strong></dt> - <dd><?= /* @escapeNotVerified */ $option['value'] ?></dd> + <?php foreach ($block->getItemOptions() as $option) : ?> + <dt><strong><em><?= $block->escapeHtml($option['label']) ?></em></strong></dt> + <dd><?= $block->escapeHtml($option['value']) ?></dd> <?php endforeach; ?> </dl> <?php endif; ?> - <?php if ($links = $block->getLinks()->getPurchasedItems()): ?> + <?php if ($links = $block->getLinks()->getPurchasedItems()) : ?> <dl> - <dt><strong><em><?= /* @escapeNotVerified */ $block->getLinksTitle() ?></em></strong></dt> - <?php foreach ($links as $link): ?> + <dt><strong><em><?= $block->escapeHtml($block->getLinksTitle()) ?></em></strong></dt> + <?php foreach ($links as $link) : ?> <dd> <?= $block->escapeHtml($link->getLinkTitle()) ?>  - (<a href="<?= /* @escapeNotVerified */ $block->getPurchasedLinkUrl($link) ?>"><?= /* @escapeNotVerified */ __('download') ?></a>) + (<a href="<?= $block->escapeUrl($block->getPurchasedLinkUrl($link)) ?>"><?= $block->escapeHtml(__('download')) ?></a>) </dd> <?php endforeach; ?> </dl> <?php endif; ?> <?= $block->escapeHtml($_item->getDescription()) ?> </td> - <td class="item-qty"><?= /* @escapeNotVerified */ $_item->getQty() * 1 ?></td> + <td class="item-qty"><?= $block->escapeHtml($_item->getQty() * 1) ?></td> <td class="item-price"> - <?= /* @escapeNotVerified */ $block->getItemPrice($_item) ?> + <?= $block->escapeHtml($block->getItemPrice($_item)) ?> </td> </tr> diff --git a/app/code/Magento/Downloadable/view/frontend/templates/email/order/items/order/downloadable.phtml b/app/code/Magento/Downloadable/view/frontend/templates/email/order/items/order/downloadable.phtml index 21b4ebc598917..9f9de6bf7d051 100644 --- a/app/code/Magento/Downloadable/view/frontend/templates/email/order/items/order/downloadable.phtml +++ b/app/code/Magento/Downloadable/view/frontend/templates/email/order/items/order/downloadable.phtml @@ -3,9 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - +// phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis ?> <?php /** @var $block \Magento\Downloadable\Block\Sales\Order\Email\Items\Order\Downloadable */ ?> <?php $_item = $block->getItem() ?> @@ -13,43 +11,43 @@ <tr> <td class="item-info has-extra"> <p class="product-name"><?= $block->escapeHtml($_item->getName()) ?></p> - <p class="sku"><?= /* @escapeNotVerified */ __('SKU') ?>: <?= $block->escapeHtml($block->getSku($_item)) ?></p> - <?php if ($block->getItemOptions()): ?> + <p class="sku"><?= $block->escapeHtml(__('SKU')) ?>: <?= $block->escapeHtml($block->getSku($_item)) ?></p> + <?php if ($block->getItemOptions()) : ?> <dl> - <?php foreach ($block->getItemOptions() as $option): ?> - <dt><strong><em><?= /* @escapeNotVerified */ $option['label'] ?></em></strong></dt> - <dd><?= /* @escapeNotVerified */ $option['value'] ?></dd> + <?php foreach ($block->getItemOptions() as $option) : ?> + <dt><strong><em><?= $block->escapeHtml($option['label']) ?></em></strong></dt> + <dd><?= $block->escapeHtml($option['value']) ?></dd> <?php endforeach; ?> </dl> <?php endif; ?> - <?php if ($links = $block->getLinks()->getPurchasedItems()): ?> + <?php if ($links = $block->getLinks()->getPurchasedItems()) : ?> <dl> - <dt><strong><em><?= /* @escapeNotVerified */ $block->getLinksTitle() ?></em></strong></dt> - <?php foreach ($links as $link): ?> + <dt><strong><em><?= $block->escapeHtml($block->getLinksTitle()) ?></em></strong></dt> + <?php foreach ($links as $link) : ?> <dd> <?= $block->escapeHtml($link->getLinkTitle()) ?>  - (<a href="<?= /* @escapeNotVerified */ $block->getPurchasedLinkUrl($link) ?>"><?= /* @escapeNotVerified */ __('download') ?></a>) + (<a href="<?= $block->escapeUrl($block->getPurchasedLinkUrl($link)) ?>"><?= $block->escapeHtml(__('download')) ?></a>) </dd> <?php endforeach; ?> </dl> <?php endif; ?> <?= $block->escapeHtml($_item->getDescription()) ?> - <?php if ($_item->getGiftMessageId() && $_giftMessage = $this->helper('Magento\GiftMessage\Helper\Message')->getGiftMessage($_item->getGiftMessageId())): ?> + <?php if ($_item->getGiftMessageId() && $_giftMessage = $this->helper(Magento\GiftMessage\Helper\Message::class)->getGiftMessage($_item->getGiftMessageId())) : ?> <table class="message-gift"> <tr> <td> - <h3><?= /* @escapeNotVerified */ __('Gift Message') ?></h3> - <strong><?= /* @escapeNotVerified */ __('From:') ?></strong> <?= $block->escapeHtml($_giftMessage->getSender()) ?> - <br /><strong><?= /* @escapeNotVerified */ __('To:') ?></strong> <?= $block->escapeHtml($_giftMessage->getRecipient()) ?> - <br /><strong><?= /* @escapeNotVerified */ __('Message:') ?></strong> + <h3><?= $block->escapeHtml(__('Gift Message')) ?></h3> + <strong><?= $block->escapeHtml(__('From:')) ?></strong> <?= $block->escapeHtml($_giftMessage->getSender()) ?> + <br /><strong><?= $block->escapeHtml(__('To:')) ?></strong> <?= $block->escapeHtml($_giftMessage->getRecipient()) ?> + <br /><strong><?= $block->escapeHtml(__('Message:')) ?></strong> <br /><?= $block->escapeHtml($_giftMessage->getMessage()) ?> </td> </tr> </table> <?php endif; ?> </td> - <td class="item-qty"><?= /* @escapeNotVerified */ $_item->getQtyOrdered() * 1 ?></td> + <td class="item-qty"><?= $block->escapeHtml($_item->getQtyOrdered() * 1) ?></td> <td class="item-price"> - <?= /* @escapeNotVerified */ $block->getItemPrice($_item) ?> + <?= $block->escapeHtml($block->getItemPrice($_item)) ?> </td> </tr> diff --git a/app/code/Magento/Downloadable/view/frontend/templates/js/components.phtml b/app/code/Magento/Downloadable/view/frontend/templates/js/components.phtml index bad5acc209b5f..5902a9f25cc4b 100644 --- a/app/code/Magento/Downloadable/view/frontend/templates/js/components.phtml +++ b/app/code/Magento/Downloadable/view/frontend/templates/js/components.phtml @@ -3,8 +3,5 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <?= $block->getChildHtml() ?> diff --git a/app/code/Magento/Downloadable/view/frontend/templates/sales/order/creditmemo/items/renderer/downloadable.phtml b/app/code/Magento/Downloadable/view/frontend/templates/sales/order/creditmemo/items/renderer/downloadable.phtml index 49422e4d2e483..2d263635718da 100644 --- a/app/code/Magento/Downloadable/view/frontend/templates/sales/order/creditmemo/items/renderer/downloadable.phtml +++ b/app/code/Magento/Downloadable/view/frontend/templates/sales/order/creditmemo/items/renderer/downloadable.phtml @@ -4,43 +4,41 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - /** @var $block \Magento\Downloadable\Block\Sales\Order\Item\Renderer\Downloadable */ ?> <?php $_item = $block->getItem() ?> <?php $_order = $block->getItem()->getOrderItem()->getOrder() ?> -<tr class="border" id="order-item-row-<?= /* @escapeNotVerified */ $_item->getId() ?>"> +<tr class="border" id="order-item-row-<?= $block->escapeHtmlAttr($_item->getId()) ?>"> <td class="col name" data-th="<?= $block->escapeHtml(__('Product Name')) ?>"> <strong class="product name product-item-name"><?= $block->escapeHtml($_item->getName()) ?></strong> - <?php if ($_options = $block->getItemOptions()): ?> + <?php if ($_options = $block->getItemOptions()) : ?> <dl class="item-options links"> <?php foreach ($_options as $_option) : ?> <dt><?= $block->escapeHtml($_option['label']) ?></dt> - <?php if (!$block->getPrintStatus()): ?> + <?php if (!$block->getPrintStatus()) : ?> <?php $_formatedOptionValue = $block->getFormatedOptionValue($_option) ?> - <dd<?php if (isset($_formatedOptionValue['full_view'])): ?> class="tooltip wrapper"<?php endif; ?>> - <?= /* @escapeNotVerified */ $_formatedOptionValue['value'] ?> - <?php if (isset($_formatedOptionValue['full_view'])): ?> + <dd<?php if (isset($_formatedOptionValue['full_view'])) : ?> class="tooltip wrapper"<?php endif; ?>> + <?= /* @noEscape */ $_formatedOptionValue['value'] ?> + <?php if (isset($_formatedOptionValue['full_view'])) : ?> <div class="tooltip content"> <dl class="item options"> <dt><?= $block->escapeHtml($_option['label']) ?></dt> - <dd><?= /* @escapeNotVerified */ $_formatedOptionValue['full_view'] ?></dd> + <dd><?= /* @noEscape */ $_formatedOptionValue['full_view'] ?></dd> </dl> </div> <?php endif; ?> </dd> - <?php else: ?> + <?php else : ?> <dd><?= $block->escapeHtml((isset($_option['print_value']) ? $_option['print_value'] : $_option['value'])) ?></dd> <?php endif; ?> <?php endforeach; ?> </dl> <?php endif; ?> <?php /* downloadable */?> - <?php if ($links = $block->getLinks()): ?> + <?php if ($links = $block->getLinks()) : ?> <dl class="item-options links"> <dt><?= $block->escapeHtml($block->getLinksTitle()) ?></dt> - <?php foreach ($links->getPurchasedItems() as $link): ?> + <?php foreach ($links->getPurchasedItems() as $link) : ?> <dd><?= $block->escapeHtml($link->getLinkTitle()) ?></dd> <?php endforeach; ?> </dl> @@ -53,15 +51,15 @@ <?php endif; ?> <?= $block->escapeHtml($_item->getDescription()) ?> </td> - <td class="col sku" data-th="<?= $block->escapeHtml(__('SKU')) ?>"><?= /* @escapeNotVerified */ $block->prepareSku($block->getSku()) ?></td> + <td class="col sku" data-th="<?= $block->escapeHtml(__('SKU')) ?>"><?= /* @noEscape */ $block->prepareSku($block->getSku()) ?></td> <td class="col price" data-th="<?= $block->escapeHtml(__('Price')) ?>"> <?= $block->getItemPriceHtml() ?> </td> - <td class="col qty" data-th="<?= $block->escapeHtml(__('Qty')) ?>"><?= /* @escapeNotVerified */ $_item->getQty()*1 ?></td> + <td class="col qty" data-th="<?= $block->escapeHtml(__('Qty')) ?>"><?= $block->escapeHtml($_item->getQty()*1) ?></td> <td class="col subtotal" data-th="<?= $block->escapeHtml(__('Subtotal')) ?>"> <?= $block->getItemRowTotalHtml() ?> </td> - <td class="col discount" data-th="<?= $block->escapeHtml(__('Discount Amount')) ?>"><?= /* @escapeNotVerified */ $_order->formatPrice(-$_item->getDiscountAmount()) ?></td> + <td class="col discount" data-th="<?= $block->escapeHtml(__('Discount Amount')) ?>"><?= /* @noEscape */ $_order->formatPrice(-$_item->getDiscountAmount()) ?></td> <td class="col total" data-th="<?= $block->escapeHtml(__('Row Total')) ?>"> <?= $block->getItemRowTotalAfterDiscountHtml() ?> </td> diff --git a/app/code/Magento/Downloadable/view/frontend/templates/sales/order/invoice/items/renderer/downloadable.phtml b/app/code/Magento/Downloadable/view/frontend/templates/sales/order/invoice/items/renderer/downloadable.phtml index 202f123a331b0..6e86dc5bf8785 100644 --- a/app/code/Magento/Downloadable/view/frontend/templates/sales/order/invoice/items/renderer/downloadable.phtml +++ b/app/code/Magento/Downloadable/view/frontend/templates/sales/order/invoice/items/renderer/downloadable.phtml @@ -4,43 +4,41 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - /** @var $block \Magento\Downloadable\Block\Sales\Order\Item\Renderer\Downloadable */ ?> <?php $_item = $block->getItem() ?> <?php $_order = $block->getItem()->getOrderItem()->getOrder() ?> -<tr id="order-item-row-<?= /* @escapeNotVerified */ $_item->getId() ?>"> +<tr id="order-item-row-<?= $block->escapeHtmlAttr($_item->getId()) ?>"> <td class="col name" data-th="<?= $block->escapeHtml(__('Product Name')) ?>"> <strong class="product name product-item-name"><?= $block->escapeHtml($_item->getName()) ?></strong> - <?php if ($_options = $block->getItemOptions()): ?> + <?php if ($_options = $block->getItemOptions()) : ?> <dl class="item-options links"> <?php foreach ($_options as $_option) : ?> <dt><?= $block->escapeHtml($_option['label']) ?></dt> - <?php if (!$block->getPrintStatus()): ?> + <?php if (!$block->getPrintStatus()) : ?> <?php $_formatedOptionValue = $block->getFormatedOptionValue($_option) ?> - <dd<?php if (isset($_formatedOptionValue['full_view'])): ?> class="tooltip wrapper"<?php endif; ?>> - <?= /* @escapeNotVerified */ $_formatedOptionValue['value'] ?> - <?php if (isset($_formatedOptionValue['full_view'])): ?> + <dd<?php if (isset($_formatedOptionValue['full_view'])) : ?> class="tooltip wrapper"<?php endif; ?>> + <?= /* @noEscape */ $_formatedOptionValue['value'] ?> + <?php if (isset($_formatedOptionValue['full_view'])) : ?> <div class="tooltip content"> <dl class="item options"> <dt><?= $block->escapeHtml($_option['label']) ?></dt> - <dd><?= /* @escapeNotVerified */ $_formatedOptionValue['full_view'] ?></dd> + <dd><?= /* @noEscape */ $_formatedOptionValue['full_view'] ?></dd> </dl> </div> <?php endif; ?> </dd> - <?php else: ?> + <?php else : ?> <dd><?= $block->escapeHtml((isset($_option['print_value']) ? $_option['print_value'] : $_option['value'])) ?></dd> <?php endif; ?> <?php endforeach; ?> </dl> <?php endif; ?> <?php /* downloadable */ ?> - <?php if ($links = $block->getLinks()): ?> + <?php if ($links = $block->getLinks()) : ?> <dl class="item-options links"> <dt><?= $block->escapeHtml($block->getLinksTitle()) ?></dt> - <?php foreach ($links->getPurchasedItems() as $link): ?> + <?php foreach ($links->getPurchasedItems() as $link) : ?> <dd><?= $block->escapeHtml($link->getLinkTitle()) ?></dd> <?php endforeach; ?> </dl> @@ -52,12 +50,12 @@ <?php endif; ?> <?= $block->escapeHtml($_item->getDescription()) ?> </td> - <td class="col sku" data-th="<?= $block->escapeHtml(__('SKU')) ?>"><?= /* @escapeNotVerified */ $block->prepareSku($block->getSku()) ?></td> + <td class="col sku" data-th="<?= $block->escapeHtml(__('SKU')) ?>"><?= /* @noEscape */ $block->prepareSku($block->getSku()) ?></td> <td class="col price" data-th="<?= $block->escapeHtml(__('Price')) ?>"> <?= $block->getItemPriceHtml() ?> </td> <td class="col qty" data-th="<?= $block->escapeHtml(__('Qty Invoiced')) ?>"> - <span class="qty summary" data-label="<?= $block->escapeHtml(__('Qty Invoiced')) ?>"><?= /* @escapeNotVerified */ $_item->getQty()*1 ?></span> + <span class="qty summary" data-label="<?= $block->escapeHtml(__('Qty Invoiced')) ?>"><?= $block->escapeHtml($_item->getQty()*1) ?></span> </td> <td class="col subtotal" data-th="<?= $block->escapeHtml(__('Subtotal')) ?>"> <?= $block->getItemRowTotalHtml() ?> diff --git a/app/code/Magento/Downloadable/view/frontend/templates/sales/order/items/renderer/downloadable.phtml b/app/code/Magento/Downloadable/view/frontend/templates/sales/order/items/renderer/downloadable.phtml index c10bb0db591b4..4caedd3e2d231 100644 --- a/app/code/Magento/Downloadable/view/frontend/templates/sales/order/items/renderer/downloadable.phtml +++ b/app/code/Magento/Downloadable/view/frontend/templates/sales/order/items/renderer/downloadable.phtml @@ -4,44 +4,42 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - /** @var $block \Magento\Downloadable\Block\Sales\Order\Item\Renderer\Downloadable */ ?> <?php $_item = $block->getItem() ?> -<tr id="order-item-row-<?= /* @escapeNotVerified */ $_item->getId() ?>"> +<tr id="order-item-row-<?= $block->escapeHtmlAttr($_item->getId()) ?>"> <td class="col name" data-th="<?= $block->escapeHtml(__('Product Name')) ?>"> <strong class="product name product-item-name"><?= $block->escapeHtml($_item->getName()) ?></strong> - <?php if ($_options = $block->getItemOptions()): ?> + <?php if ($_options = $block->getItemOptions()) : ?> <dl class="item-options links"> <?php foreach ($_options as $_option) : ?> <dt><?= $block->escapeHtml($_option['label']) ?></dt> - <?php if (!$block->getPrintStatus()): ?> + <?php if (!$block->getPrintStatus()) : ?> <?php $_formatedOptionValue = $block->getFormatedOptionValue($_option) ?> - <dd<?php if (isset($_formatedOptionValue['full_view'])): ?> class="tooltip wrapper"<?php endif; ?>> - <?= /* @escapeNotVerified */ $_formatedOptionValue['value'] ?> - <?php if (isset($_formatedOptionValue['full_view'])): ?> + <dd<?php if (isset($_formatedOptionValue['full_view'])) : ?> class="tooltip wrapper"<?php endif; ?>> + <?= /* @noEscape */ $_formatedOptionValue['value'] ?> + <?php if (isset($_formatedOptionValue['full_view'])) : ?> <div class="tooltip content"> <dl class="item options"> <dt><?= $block->escapeHtml($_option['label']) ?></dt> - <dd><?= /* @escapeNotVerified */ $_formatedOptionValue['full_view'] ?></dd> + <dd><?= /* @noEscape */ $_formatedOptionValue['full_view'] ?></dd> </dl> </div> <?php endif; ?> </dd> - <?php else: ?> + <?php else : ?> <dd> - <?= nl2br($block->escapeHtml((isset($_option['print_value']) ? $_option['print_value'] : $_option['value']))) ?> + <?= /* @noEscape */ nl2br((isset($_option['print_value']) ? $_option['print_value'] : $_option['value'])) ?> </dd> <?php endif; ?> <?php endforeach; ?> </dl> <?php endif; ?> <?php /* downloadable */ ?> - <?php if ($links = $block->getLinks()): ?> + <?php if ($links = $block->getLinks()) : ?> <dl class="item-options links"> <dt><?= $block->escapeHtml($block->getLinksTitle()) ?></dt> - <?php foreach ($links->getPurchasedItems() as $link): ?> + <?php foreach ($links->getPurchasedItems() as $link) : ?> <dd><?= $block->escapeHtml($link->getLinkTitle()) ?></dd> <?php endforeach; ?> </dl> @@ -53,34 +51,34 @@ <?php endif; ?> <?= $block->escapeHtml($_item->getDescription()) ?> </td> - <td class="col sku" data-th="<?= $block->escapeHtml(__('SKU')) ?>"><?= /* @escapeNotVerified */ $block->prepareSku($block->getSku()) ?></td> + <td class="col sku" data-th="<?= $block->escapeHtml(__('SKU')) ?>"><?= /* @noEscape */ $block->prepareSku($block->getSku()) ?></td> <td class="col price" data-th="<?= $block->escapeHtml(__('Price')) ?>"> <?= $block->getItemPriceHtml() ?> </td> <td class="col qty" data-th="<?= $block->escapeHtml(__('Qty')) ?>"> <ul class="items-qty"> - <?php if ($block->getItem()->getQtyOrdered() > 0): ?> + <?php if ($block->getItem()->getQtyOrdered() > 0) : ?> <li class="item"> - <span class="title"><?= /* @escapeNotVerified */ __('Ordered') ?></span> - <span class="content"><?= /* @escapeNotVerified */ $block->getItem()->getQtyOrdered()*1 ?></span> + <span class="title"><?= $block->escapeHtml(__('Ordered')) ?></span> + <span class="content"><?= $block->escapeHtml($block->getItem()->getQtyOrdered()*1) ?></span> </li> <?php endif; ?> - <?php if ($block->getItem()->getQtyShipped() > 0): ?> + <?php if ($block->getItem()->getQtyShipped() > 0) : ?> <li class="item"> - <span class="title"><?= /* @escapeNotVerified */ __('Shipped') ?></span> - <span class="content"><?= /* @escapeNotVerified */ $block->getItem()->getQtyShipped() * 1 ?></span> + <span class="title"><?= $block->escapeHtml(__('Shipped')) ?></span> + <span class="content"><?= $block->escapeHtml($block->getItem()->getQtyShipped() * 1) ?></span> </li> <?php endif; ?> - <?php if ($block->getItem()->getQtyCanceled() > 0): ?> + <?php if ($block->getItem()->getQtyCanceled() > 0) : ?> <li class="item"> - <span class="title"><?= /* @escapeNotVerified */ __('Canceled') ?></span> - <span class="content"><?= /* @escapeNotVerified */ $block->getItem()->getQtyCanceled()*1 ?></span> + <span class="title"><?= $block->escapeHtml(__('Canceled')) ?></span> + <span class="content"><?= $block->escapeHtml($block->getItem()->getQtyCanceled()*1) ?></span> </li> <?php endif; ?> - <?php if ($block->getItem()->getQtyRefunded() > 0): ?> + <?php if ($block->getItem()->getQtyRefunded() > 0) : ?> <li class="item"> - <span class="title"><?= /* @escapeNotVerified */ __('Refunded') ?></span> - <span class="content"><?= /* @escapeNotVerified */ $block->getItem()->getQtyRefunded()*1 ?></span> + <span class="title"><?= $block->escapeHtml(__('Refunded')) ?></span> + <span class="content"><?= $block->escapeHtml($block->getItem()->getQtyRefunded()*1) ?></span> </li> <?php endif; ?> </ul> diff --git a/app/code/Magento/Eav/Model/Validator/Attribute/Code.php b/app/code/Magento/Eav/Model/Validator/Attribute/Code.php index f3ee37721b8ce..6447507ea175a 100644 --- a/app/code/Magento/Eav/Model/Validator/Attribute/Code.php +++ b/app/code/Magento/Eav/Model/Validator/Attribute/Code.php @@ -7,6 +7,7 @@ namespace Magento\Eav\Model\Validator\Attribute; +use Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\AbstractModifier; use Magento\Eav\Model\Entity\Attribute; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Validator\AbstractValidator; @@ -65,6 +66,16 @@ public function isValid($attributeCode): bool ); } + /** + * Check attribute_code for prohibited prefix + */ + if (strpos($attributeCode, AbstractModifier::CONTAINER_PREFIX) === 0) { + $errorMessages[] = __( + '"%1" prefix is reserved by the system and cannot be used in attribute code names.', + AbstractModifier::CONTAINER_PREFIX + ); + } + $this->_addMessages($errorMessages); return !$this->hasMessages(); diff --git a/app/code/Magento/Eav/Test/Unit/Model/Entity/Collection/AbstractCollectionTest.php b/app/code/Magento/Eav/Test/Unit/Model/Entity/Collection/AbstractCollectionTest.php index ab5b40c56685c..bc4ed7d4bd9e4 100644 --- a/app/code/Magento/Eav/Test/Unit/Model/Entity/Collection/AbstractCollectionTest.php +++ b/app/code/Magento/Eav/Test/Unit/Model/Entity/Collection/AbstractCollectionTest.php @@ -5,9 +5,6 @@ */ namespace Magento\Eav\Test\Unit\Model\Entity\Collection; -use Magento\Framework\Data\Collection\Db\FetchStrategyInterface; -use Magento\Framework\Model\ResourceModel\ResourceModelPoolInterface; - /** * AbstractCollection test * @@ -31,7 +28,7 @@ class AbstractCollectionTest extends \PHPUnit\Framework\TestCase protected $loggerMock; /** - * @var FetchStrategyInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Data\Collection\Db\FetchStrategyInterface|\PHPUnit_Framework_MockObject_MockObject */ protected $fetchStrategyMock; @@ -61,7 +58,7 @@ class AbstractCollectionTest extends \PHPUnit\Framework\TestCase protected $resourceHelperMock; /** - * @var ResourceModelPoolInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Framework\Validator\UniversalFactory|\PHPUnit_Framework_MockObject_MockObject */ protected $validatorFactoryMock; diff --git a/app/code/Magento/Eav/Test/Unit/Model/Validator/Attribute/CodeTest.php b/app/code/Magento/Eav/Test/Unit/Model/Validator/Attribute/CodeTest.php index 9db290bcba3e1..7216fad0ae70b 100644 --- a/app/code/Magento/Eav/Test/Unit/Model/Validator/Attribute/CodeTest.php +++ b/app/code/Magento/Eav/Test/Unit/Model/Validator/Attribute/CodeTest.php @@ -61,7 +61,10 @@ public function isValidDataProvider(): array ], [ 'more_than_60_chars_more_than_60_chars_more_than_60_chars_more', false - ] + ], [ + 'container_attribute', + false, + ], ]; } } diff --git a/app/code/Magento/Elasticsearch/etc/search_engine.xml b/app/code/Magento/Elasticsearch/etc/search_engine.xml index 34b6432b15c03..51af3038b9c8d 100644 --- a/app/code/Magento/Elasticsearch/etc/search_engine.xml +++ b/app/code/Magento/Elasticsearch/etc/search_engine.xml @@ -9,4 +9,7 @@ <engine name="elasticsearch"> <feature name="synonyms" support="true" /> </engine> + <engine name="elasticsearch5"> + <feature name="synonyms" support="true" /> + </engine> </engines> diff --git a/app/code/Magento/Elasticsearch6/etc/search_engine.xml b/app/code/Magento/Elasticsearch6/etc/search_engine.xml new file mode 100644 index 0000000000000..40bc69bfd8298 --- /dev/null +++ b/app/code/Magento/Elasticsearch6/etc/search_engine.xml @@ -0,0 +1,12 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<engines xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Search/etc/search_engine.xsd"> + <engine name="elasticsearch6"> + <feature name="synonyms" support="true" /> + </engine> +</engines> diff --git a/app/code/Magento/Email/Model/AbstractTemplate.php b/app/code/Magento/Email/Model/AbstractTemplate.php index a6ecdaf24ebbb..5eae1d1462184 100644 --- a/app/code/Magento/Email/Model/AbstractTemplate.php +++ b/app/code/Magento/Email/Model/AbstractTemplate.php @@ -14,10 +14,12 @@ use Magento\Store\Model\Information as StoreInformation; use Magento\Store\Model\ScopeInterface; use Magento\Store\Model\Store; +use Magento\MediaStorage\Helper\File\Storage\Database; /** - * Template model class + * Template model class. * + * phpcs:disable Magento2.Classes.AbstractApi * @author Magento Core Team <core@magentocommerce.com> * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @SuppressWarnings(PHPMD.TooManyFields) @@ -163,6 +165,11 @@ abstract class AbstractTemplate extends AbstractModel implements TemplateTypesIn */ private $urlModel; + /** + * @var Database + */ + private $fileStorageDatabase; + /** * @param \Magento\Framework\Model\Context $context * @param \Magento\Framework\View\DesignInterface $design @@ -177,6 +184,7 @@ abstract class AbstractTemplate extends AbstractModel implements TemplateTypesIn * @param \Magento\Framework\Filter\FilterManager $filterManager * @param \Magento\Framework\UrlInterface $urlModel * @param array $data + * @param Database $fileStorageDatabase * * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ @@ -193,7 +201,8 @@ public function __construct( \Magento\Email\Model\TemplateFactory $templateFactory, \Magento\Framework\Filter\FilterManager $filterManager, \Magento\Framework\UrlInterface $urlModel, - array $data = [] + array $data = [], + Database $fileStorageDatabase = null ) { $this->design = $design; $this->area = isset($data['area']) ? $data['area'] : null; @@ -207,6 +216,8 @@ public function __construct( $this->templateFactory = $templateFactory; $this->filterManager = $filterManager; $this->urlModel = $urlModel; + $this->fileStorageDatabase = $fileStorageDatabase ?: + \Magento\Framework\App\ObjectManager::getInstance()->get(Database::class); parent::__construct($context, $registry, null, null, $data); } @@ -394,6 +405,11 @@ protected function getLogoUrl($store) if ($fileName) { $uploadDir = \Magento\Email\Model\Design\Backend\Logo::UPLOAD_DIR; $mediaDirectory = $this->filesystem->getDirectoryRead(DirectoryList::MEDIA); + if ($this->fileStorageDatabase->checkDbUsage() && + !$mediaDirectory->isFile($uploadDir . '/' . $fileName) + ) { + $this->fileStorageDatabase->saveFileToFilesystem($uploadDir . '/' . $fileName); + } if ($mediaDirectory->isFile($uploadDir . '/' . $fileName)) { return $this->storeManager->getStore()->getBaseUrl( \Magento\Framework\UrlInterface::URL_TYPE_MEDIA @@ -490,7 +506,6 @@ protected function addEmailVariables($variables, $storeId) /** * Apply design config so that emails are processed within the context of the appropriate area/store/theme. - * Can be called multiple times without issue. * * @return bool */ @@ -664,8 +679,7 @@ public function getTemplateFilter() } /** - * Save current design config and replace with design config from specified store - * Event is not dispatched. + * Save current design config and replace with design config from specified store. Event is not dispatched. * * @param null|bool|int|string $storeId * @param string $area diff --git a/app/code/Magento/Email/Test/Unit/Model/BackendTemplateTest.php b/app/code/Magento/Email/Test/Unit/Model/BackendTemplateTest.php index 31a04b0b2bbd0..1ceccd4414cc0 100644 --- a/app/code/Magento/Email/Test/Unit/Model/BackendTemplateTest.php +++ b/app/code/Magento/Email/Test/Unit/Model/BackendTemplateTest.php @@ -12,6 +12,9 @@ use Magento\Email\Model\BackendTemplate; use Magento\Framework\ObjectManagerInterface; +/** + * Tests for adminhtml email template model. + */ class BackendTemplateTest extends \PHPUnit\Framework\TestCase { /** @@ -46,6 +49,11 @@ class BackendTemplateTest extends \PHPUnit\Framework\TestCase */ private $serializerMock; + /** + * @var \Magento\MediaStorage\Helper\File\Storage\Database|\PHPUnit_Framework_MockObject_MockObject + */ + private $databaseHelperMock; + protected function setUp() { $helper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); @@ -56,6 +64,7 @@ protected function setUp() $this->structureMock = $this->createMock(\Magento\Config\Model\Config\Structure::class); $this->structureMock->expects($this->any())->method('getFieldPathsByAttribute')->willReturn(['path' => 'test']); + $this->databaseHelperMock = $this->createMock(\Magento\MediaStorage\Helper\File\Storage\Database::class); $this->resourceModelMock = $this->createMock(\Magento\Email\Model\ResourceModel\Template::class); $this->resourceModelMock->expects($this->any()) ->method('getSystemConfigByPathsAndTemplateId') @@ -64,8 +73,18 @@ protected function setUp() $objectManagerMock = $this->createMock(\Magento\Framework\ObjectManagerInterface::class); $objectManagerMock->expects($this->any()) ->method('get') - ->with(\Magento\Email\Model\ResourceModel\Template::class) - ->will($this->returnValue($this->resourceModelMock)); + ->willReturnCallback( + function ($value) { + switch ($value) { + case \Magento\MediaStorage\Helper\File\Storage\Database::class: + return ($this->databaseHelperMock); + case \Magento\Email\Model\ResourceModel\Template::class: + return ($this->resourceModelMock); + default: + return(null); + } + } + ); \Magento\Framework\App\ObjectManager::setInstance($objectManagerMock); diff --git a/app/code/Magento/Email/composer.json b/app/code/Magento/Email/composer.json index 1011b16f8537d..e887adef1fbc9 100644 --- a/app/code/Magento/Email/composer.json +++ b/app/code/Magento/Email/composer.json @@ -12,6 +12,7 @@ "magento/module-config": "*", "magento/module-store": "*", "magento/module-theme": "*", + "magento/module-media-storage": "*", "magento/module-variable": "*" }, "suggest": { diff --git a/app/code/Magento/GiftMessage/view/adminhtml/templates/giftoptionsform.phtml b/app/code/Magento/GiftMessage/view/adminhtml/templates/giftoptionsform.phtml index 15e87b00622ac..264fdab0ade8d 100644 --- a/app/code/Magento/GiftMessage/view/adminhtml/templates/giftoptionsform.phtml +++ b/app/code/Magento/GiftMessage/view/adminhtml/templates/giftoptionsform.phtml @@ -3,15 +3,12 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> -<?php if ($block->canDisplayGiftmessageForm()): ?> +<?php if ($block->canDisplayGiftmessageForm()) : ?> <div id="gift_options_giftmessage" class="giftcard-form giftcard-send-form fieldset admin__fieldset"> <div class="field admin__field"> - <label class="admin__field-label" for="current_item_giftmessage_sender"><?= /* @escapeNotVerified */ __('From') ?></label> + <label class="admin__field-label" for="current_item_giftmessage_sender"><?= $block->escapeHtml(__('From')) ?></label> <div class="control admin__field-control"> <input type="text" class="input-text admin__control-text" name="current_item_giftmessage_sender" @@ -19,7 +16,7 @@ </div> </div> <div class="field admin__field"> - <label class="admin__field-label" for="current_item_giftmessage_recipient"><?= /* @escapeNotVerified */ __('To') ?></label> + <label class="admin__field-label" for="current_item_giftmessage_recipient"><?= $block->escapeHtml(__('To')) ?></label> <div class="control admin__field-control"> <input type="text" class="input-text admin__control-text" @@ -28,7 +25,7 @@ </div> </div> <div class="field admin__field"> - <label class="admin__field-label" for="current_item_giftmessage_message"><?= /* @escapeNotVerified */ __('Message') ?></label> + <label class="admin__field-label" for="current_item_giftmessage_message"><?= $block->escapeHtml(__('Message')) ?></label> <div class="control admin__field-control"> <textarea class="textarea admin__control-textarea" cols="15" diff --git a/app/code/Magento/GiftMessage/view/adminhtml/templates/popup.phtml b/app/code/Magento/GiftMessage/view/adminhtml/templates/popup.phtml index c15d717c2291a..908d6c91bfb5f 100644 --- a/app/code/Magento/GiftMessage/view/adminhtml/templates/popup.phtml +++ b/app/code/Magento/GiftMessage/view/adminhtml/templates/popup.phtml @@ -3,9 +3,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <?php if ($block->getChildHtml()) :?> @@ -15,8 +12,8 @@ <?= $block->getChildHtml() ?> </div> <div class="ui-dialog-buttonset"> - <button type="button" class="action-close" id="gift_options_cancel_button"><span><?= /* @escapeNotVerified */ __('Cancel') ?></span></button> - <button type="button" class="action-primary" id="gift_options_ok_button"><span><?= /* @escapeNotVerified */ __('OK') ?></span></button> + <button type="button" class="action-close" id="gift_options_cancel_button"><span><?= $block->escapeHtml(__('Cancel')) ?></span></button> + <button type="button" class="action-primary" id="gift_options_ok_button"><span><?= $block->escapeHtml(__('OK')) ?></span></button> </div> </div> </div> diff --git a/app/code/Magento/GiftMessage/view/adminhtml/templates/sales/order/create/giftoptions.phtml b/app/code/Magento/GiftMessage/view/adminhtml/templates/sales/order/create/giftoptions.phtml index 377b870ce450a..1833ae0d2e339 100644 --- a/app/code/Magento/GiftMessage/view/adminhtml/templates/sales/order/create/giftoptions.phtml +++ b/app/code/Magento/GiftMessage/view/adminhtml/templates/sales/order/create/giftoptions.phtml @@ -3,29 +3,26 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <?php $_item = $block->getItem() ?> -<?php if ($_item): ?> +<?php if ($_item) : ?> <?php $_childHtml = trim($block->getChildHtml('', false));?> - <?php if ($_childHtml): ?> + <?php if ($_childHtml) : ?> <tr class="row-gift-options"> <td colspan="7"> - <a class="action-link" href="#" id="gift_options_link_<?= /* @escapeNotVerified */ $_item->getId() ?>"><?= /* @escapeNotVerified */ __('Gift Options') ?></a> + <a class="action-link" href="#" id="gift_options_link_<?= (int) $_item->getId() ?>"><?= $block->escapeHtml(__('Gift Options')) ?></a> <script> require([ "Magento_Sales/order/giftoptions_tooltip" ], function(){ - giftOptionsTooltip.addTargetLink('gift_options_link_<?= /* @escapeNotVerified */ $_item->getId() ?>', <?= /* @escapeNotVerified */ $_item->getId() ?>); + giftOptionsTooltip.addTargetLink('gift_options_link_<?= (int) $_item->getId() ?>', <?= (int) $_item->getId() ?>); }); </script> - <div id="gift_options_data_<?= /* @escapeNotVerified */ $_item->getId() ?>"> - <?= /* @escapeNotVerified */ $_childHtml ?> + <div id="gift_options_data_<?= (int) $_item->getId() ?>"> + <?= /* @noEscape */ $_childHtml ?> </div> </td> </tr> diff --git a/app/code/Magento/GiftMessage/view/adminhtml/templates/sales/order/create/items.phtml b/app/code/Magento/GiftMessage/view/adminhtml/templates/sales/order/create/items.phtml index 5b0c61375f8c6..edd08fdddcfb1 100644 --- a/app/code/Magento/GiftMessage/view/adminhtml/templates/sales/order/create/items.phtml +++ b/app/code/Magento/GiftMessage/view/adminhtml/templates/sales/order/create/items.phtml @@ -3,21 +3,18 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> -<?php if ($block->canDisplayGiftMessage()): ?> +<?php if ($block->canDisplayGiftMessage()) : ?> <div class="no-display"> - <div id="gift-message-form-data-<?= /* @escapeNotVerified */ $block->getItem()->getId() ?>"> + <div id="gift-message-form-data-<?= (int) $block->getItem()->getId() ?>"> <?= $block->getFormHtml() ?> </div> - <?php if ($block->getMessageText()): ?> + <?php if ($block->getMessageText()) : ?> <div class="gift-options-tooltip-content"> - <div><strong><?= /* @escapeNotVerified */ __('Gift Message') ?></strong>:</div> - <div><?= /* @escapeNotVerified */ $block->getMessageText() ?></div> + <div><strong><?= $block->escapeHtml(__('Gift Message')) ?></strong>:</div> + <div><?= /* @noEscape */ $block->getMessageText() ?></div> </div> <?php endif; ?> </div> diff --git a/app/code/Magento/GiftMessage/view/adminhtml/templates/sales/order/view/giftoptions.phtml b/app/code/Magento/GiftMessage/view/adminhtml/templates/sales/order/view/giftoptions.phtml index 47b5957fd2765..60a6e1b222b17 100644 --- a/app/code/Magento/GiftMessage/view/adminhtml/templates/sales/order/view/giftoptions.phtml +++ b/app/code/Magento/GiftMessage/view/adminhtml/templates/sales/order/view/giftoptions.phtml @@ -3,29 +3,29 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <?php $_childHtml = trim($block->getChildHtml('', false)); ?> -<?php if ($_childHtml): ?> -<?php $_item = $block->getItem() ?> -<tr> - <td colspan="10" class="last"> - <a class="action-link" href="#" id="gift_options_link_<?= /* @escapeNotVerified */ $_item->getId() ?>"><?= /* @escapeNotVerified */ __('Gift Options') ?></a> - <script> -require([ - "Magento_Sales/order/giftoptions_tooltip" -], function(){ - - giftOptionsTooltip.addTargetLink('gift_options_link_<?= /* @escapeNotVerified */ $_item->getId() ?>', <?= /* @escapeNotVerified */ $_item->getId() ?>); - -}); -</script> - <div id="gift_options_data_<?= /* @escapeNotVerified */ $_item->getId() ?>"> - <?= /* @escapeNotVerified */ $_childHtml ?> - </div> - </td> -</tr> +<?php if ($_childHtml) : ?> + <?php $_item = $block->getItem() ?> + <tr> + <td colspan="10" class="last"> + <a class="action-link" href="#" id="gift_options_link_<?= (int) $_item->getId() ?>"> + <?= $block->escapeHtml(__('Gift Options')) ?> + </a> + <script> + require([ + "Magento_Sales/order/giftoptions_tooltip" + ], function(){ + giftOptionsTooltip.addTargetLink( + 'gift_options_link_<?= (int) ($_item->getId()) ?>', + <?= (int) $_item->getId() ?> + ); + }); + </script> + <div id="gift_options_data_<?= (int) $_item->getId() ?>"> + <?= /* @noEscape */ $_childHtml ?> + </div> + </td> + </tr> <?php endif ?> diff --git a/app/code/Magento/GiftMessage/view/adminhtml/templates/sales/order/view/items.phtml b/app/code/Magento/GiftMessage/view/adminhtml/templates/sales/order/view/items.phtml index 5680f1ef98a5d..915603e3d7146 100644 --- a/app/code/Magento/GiftMessage/view/adminhtml/templates/sales/order/view/items.phtml +++ b/app/code/Magento/GiftMessage/view/adminhtml/templates/sales/order/view/items.phtml @@ -3,24 +3,34 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> -<?php if ($block->canDisplayGiftmessage()): ?> -<div id="gift-message-form-data-<?= /* @escapeNotVerified */ $block->getItem()->getId() ?>" class="no-display"> - <form id="<?= /* @escapeNotVerified */ $block->getFieldId('form') ?>" action="<?= /* @escapeNotVerified */ $block->getSaveUrl() ?>"> - <input type="hidden" id="<?= /* @escapeNotVerified */ $block->getFieldId('type') ?>" name="<?= /* @escapeNotVerified */ $block->getFieldName('type') ?>" value="order_item" /> - <input type="hidden" id="<?= /* @escapeNotVerified */ $block->getFieldId('sender') ?>" name="<?= /* @escapeNotVerified */ $block->getFieldName('sender') ?>" value="<?= /* @escapeNotVerified */ $block->getSender() ?>" /> - <input type="hidden" id="<?= /* @escapeNotVerified */ $block->getFieldId('recipient') ?>" name="<?= /* @escapeNotVerified */ $block->getFieldName('recipient') ?>" value="<?= /* @escapeNotVerified */ $block->getRecipient() ?>" /> - <input type="hidden" id="<?= /* @escapeNotVerified */ $block->getFieldId('message') ?>" name="<?= /* @escapeNotVerified */ $block->getFieldName('message') ?>" value="<?= /* @escapeNotVerified */ $block->getMessageText() ?>" /> +<?php if ($block->canDisplayGiftmessage()) : ?> +<div id="gift-message-form-data-<?= (int) $block->getItem()->getId() ?>" class="no-display"> + <form id="<?= $block->escapeHtmlAttr($block->getFieldId('form')) ?>" + action="<?= $block->escapeUrl($block->getSaveUrl()) ?>"> + <input type="hidden" + id="<?= $block->escapeHtmlAttr($block->getFieldId('type')) ?>" + name="<?= $block->escapeHtmlAttr($block->getFieldName('type')) ?>" + value="order_item" /> + <input type="hidden" + id="<?= $block->escapeHtmlAttr($block->getFieldId('sender')) ?>" + name="<?= $block->escapeHtmlAttr($block->getFieldName('sender')) ?>" + value="<?= $block->escapeHtmlAttr($block->getSender()) ?>" /> + <input type="hidden" + id="<?= $block->escapeHtmlAttr($block->getFieldId('recipient')) ?>" + name="<?= $block->escapeHtmlAttr($block->getFieldName('recipient')) ?>" + value="<?= $block->escapeHtmlAttr($block->getRecipient()) ?>" /> + <input type="hidden" + id="<?= $block->escapeHtmlAttr($block->getFieldId('message')) ?>" + name="<?= $block->escapeHtmlAttr($block->getFieldName('message')) ?>" + value="<?= $block->escapeHtmlAttr($block->getMessageText()) ?>" /> </form> - <?php if ($block->getMessageText()): ?> + <?php if ($block->getMessageText()) : ?> <div class="gift-options-tooltip-content"> - <div><strong><?= /* @escapeNotVerified */ __('Gift Message') ?></strong>: </div> - <div><?= /* @escapeNotVerified */ $block->getMessageText() ?></div> + <div><strong><?= $block->escapeHtml(__('Gift Message')) ?></strong>: </div> + <div><?= /* @noEscape */ $block->getMessageText() ?></div> </div> <?php endif; ?> </div> diff --git a/app/code/Magento/GiftMessage/view/frontend/templates/cart/gift_options.phtml b/app/code/Magento/GiftMessage/view/frontend/templates/cart/gift_options.phtml index 0211c5464315c..4d8a054e67fc5 100644 --- a/app/code/Magento/GiftMessage/view/frontend/templates/cart/gift_options.phtml +++ b/app/code/Magento/GiftMessage/view/frontend/templates/cart/gift_options.phtml @@ -9,11 +9,11 @@ <script type="text/x-magento-init"> { "#gift-options-cart": { - "Magento_Ui/js/core/app": <?= /* @escapeNotVerified */ $block->getJsLayout() ?> + "Magento_Ui/js/core/app": <?= /* @noEscape */ $block->getJsLayout() ?> } } </script> <script> - window.giftOptionsConfig = <?= /* @escapeNotVerified */ $block->getGiftOptionsConfigJson() ?>; + window.giftOptionsConfig = <?= /* @noEscape */ $block->getGiftOptionsConfigJson() ?>; </script> </div> diff --git a/app/code/Magento/GiftMessage/view/frontend/templates/cart/item/renderer/actions/gift_options.phtml b/app/code/Magento/GiftMessage/view/frontend/templates/cart/item/renderer/actions/gift_options.phtml index f98ec0714671a..6fcad0faaa694 100644 --- a/app/code/Magento/GiftMessage/view/frontend/templates/cart/item/renderer/actions/gift_options.phtml +++ b/app/code/Magento/GiftMessage/view/frontend/templates/cart/item/renderer/actions/gift_options.phtml @@ -4,19 +4,17 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - /** @var $block \Magento\GiftMessage\Block\Cart\Item\Renderer\Actions\GiftOptions */ ?> -<?php if (!$block->isVirtual()): ?> - <div id="gift-options-cart-item-<?= /* @escapeNotVerified */ $block->getItem()->getId() ?>" - data-bind="scope:'giftOptionsCartItem-<?= /* @escapeNotVerified */ $block->getItem()->getId() ?>'" +<?php if (!$block->isVirtual()) : ?> + <div id="gift-options-cart-item-<?= (int) $block->getItem()->getId() ?>" + data-bind="scope:'giftOptionsCartItem-<?= (int) $block->getItem()->getId() ?>'" class="gift-options-cart-item"> <!-- ko template: getTemplate() --><!-- /ko --> <script type="text/x-magento-init"> { - "#gift-options-cart-item-<?= /* @escapeNotVerified */ $block->getItem()->getId() ?>": { - "Magento_Ui/js/core/app": <?= /* @escapeNotVerified */ $block->getJsLayout() ?> + "#gift-options-cart-item-<?= (int) $block->getItem()->getId() ?>": { + "Magento_Ui/js/core/app": <?= /* @noEscape */ $block->getJsLayout() ?> } } </script> diff --git a/app/code/Magento/GiftMessage/view/frontend/templates/inline.phtml b/app/code/Magento/GiftMessage/view/frontend/templates/inline.phtml index 640ef1ba16486..cb462a630e3a6 100644 --- a/app/code/Magento/GiftMessage/view/frontend/templates/inline.phtml +++ b/app/code/Magento/GiftMessage/view/frontend/templates/inline.phtml @@ -3,61 +3,62 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - +//phpcs:disable Squiz.ControlStructures.ControlSignature +//phpcs:disable Squiz.WhiteSpace.ScopeClosingBrace +//phpcs:disable PSR2.ControlStructures.SwitchDeclaration +//phpcs:disable Magento2.Files.LineLength.MaxExceeded ?> <?php $_giftMessage = false; ?> -<?php switch ($block->getCheckoutType()): case 'onepage_checkout': ?> +<?php switch ($block->getCheckoutType()) : case 'onepage_checkout': ?> <fieldset class="fieldset gift-message"> - <legend class="legend"><span><?= /* @escapeNotVerified */ __('Do you have any gift items in your order?') ?></span></legend><br> + <legend class="legend"><span><?= $block->escapeHtml(__('Do you have any gift items in your order?')) ?></span></legend><br> - <div class="field choice" id="add-gift-options-<?= /* @escapeNotVerified */ $block->getEntity()->getId() ?>"> + <div class="field choice" id="add-gift-options-<?= (int) $block->getEntity()->getId() ?>"> <input type="checkbox" name="allow_gift_options" id="allow_gift_options" data-mage-init='{"giftOptions":{}}' value="1" data-selector='{"id":"#allow-gift-options-container"}'<?php if ($block->getItemsHasMesssages() || $block->getEntityHasMessage()): ?> checked="checked"<?php endif; ?> class="checkbox" /> - <label for="allow_gift_options" class="label"><span><?= /* @escapeNotVerified */ __('Add Gift Options') ?></span></label> + <label for="allow_gift_options" class="label"><span><?= $block->escapeHtml(__('Add Gift Options')) ?></span></label> </div> <dl class="options-items" id="allow-gift-options-container"> <?php if ($block->isMessagesAvailable()): ?> - <dt id="add-gift-options-for-order-<?= /* @escapeNotVerified */ $block->getEntity()->getId() ?>" class="order-title"> + <dt id="add-gift-options-for-order-<?= (int) $block->getEntity()->getId() ?>" class="order-title"> <div class="field choice"> <input type="checkbox" name="allow_gift_messages_for_order" id="allow_gift_options_for_order" data-mage-init='{"giftOptions":{}}' value="1" data-selector='{"id":"#allow-gift-options-for-order-container"}'<?php if ($block->getEntityHasMessage()): ?> checked="checked"<?php endif; ?> class="checkbox" /> - <label for="allow_gift_options_for_order" class="label"><span><?= /* @escapeNotVerified */ __('Gift Options for the Entire Order') ?></span></label> + <label for="allow_gift_options_for_order" class="label"><span><?= $block->escapeHtml(__('Gift Options for the Entire Order')) ?></span></label> </div> </dt> <dd id="allow-gift-options-for-order-container" class="order-options"> - <div class="options-order-container" id="options-order-container-<?= /* @escapeNotVerified */ $block->getEntity()->getId() ?>"></div> + <div class="options-order-container" id="options-order-container-<?= (int) $block->getEntity()->getId() ?>"></div> <button class="action action-gift" data-mage-init='{"toggleAdvanced": {"selectorsToggleClass":"hidden", "toggleContainers":"#allow-gift-messages-for-order-container"}}'> - <span><?= /* @escapeNotVerified */ __('Gift Message') ?></span> + <span><?= $block->escapeHtml(__('Gift Message')) ?></span> </button> <div id="allow-gift-messages-for-order-container" class="gift-messages-order hidden"> <fieldset class="fieldset"> - <p><?= /* @escapeNotVerified */ __('Leave this box blank if you don\'t want to leave a gift message for the entire order.') ?></p> + <p><?= $block->escapeHtml(__('Leave this box blank if you don\'t want to leave a gift message for the entire order.')) ?></p> <div class="field from"> - <label for="gift-message-whole-from" class="label"><span><?= /* @escapeNotVerified */ __('From') ?></span></label> + <label for="gift-message-whole-from" class="label"><span><?= $block->escapeHtml(__('From')) ?></span></label> <div class="control"> - <input type="text" name="giftmessage[quote][<?= /* @escapeNotVerified */ $block->getEntity()->getId() ?>][from]" id="gift-message-whole-from" title="<?= /* @escapeNotVerified */ __('From') ?>" value="<?= /* @escapeNotVerified */ $block->getEscaped($block->getMessage()->getSender(), $block->getDefaultFrom()) ?>" class="input-text"> + <input type="text" name="giftmessage[quote][<?= (int) $block->getEntity()->getId() ?>][from]" id="gift-message-whole-from" title="<?= $block->escapeHtmlAttr(__('From')) ?>" value="<?= /* @noEscape */ $block->getEscaped($block->getMessage()->getSender(), $block->getDefaultFrom()) ?>" class="input-text"> </div> </div> <div class="field to"> - <label for="gift-message-whole-to" class="label"><span><?= /* @escapeNotVerified */ __('To') ?></span></label> + <label for="gift-message-whole-to" class="label"><span><?= $block->escapeHtml(__('To')) ?></span></label> <div class="control"> - <input type="text" name="giftmessage[quote][<?= /* @escapeNotVerified */ $block->getEntity()->getId() ?>][to]" id="gift-message-whole-to" title="<?= /* @escapeNotVerified */ __('To') ?>" value="<?= /* @escapeNotVerified */ $block->getEscaped($block->getMessage()->getRecipient(), $block->getDefaultTo()) ?>" class="input-text"> + <input type="text" name="giftmessage[quote][<?= (int) $block->getEntity()->getId() ?>][to]" id="gift-message-whole-to" title="<?= $block->escapeHtmlAttr(__('To')) ?>" value="<?= /* @noEscape */ $block->getEscaped($block->getMessage()->getRecipient(), $block->getDefaultTo()) ?>" class="input-text"> </div> </div> <div class="field text"> - <label for="gift-message-whole-message" class="label"><span><?= /* @escapeNotVerified */ __('Message') ?></span></label> + <label for="gift-message-whole-message" class="label"><span><?= $block->escapeHtml(__('Message')) ?></span></label> <div class="control"> - <textarea id="gift-message-whole-message" class="input-text" name="giftmessage[quote][<?= /* @escapeNotVerified */ $block->getEntity()->getId() ?>][message]" title="<?= /* @escapeNotVerified */ __('Message') ?>" rows="5" cols="10"><?= /* @escapeNotVerified */ $block->getEscaped($block->getMessage()->getMessage()) ?></textarea> + <textarea id="gift-message-whole-message" class="input-text" name="giftmessage[quote][<?= (int) $block->getEntity()->getId() ?>][message]" title="<?= $block->escapeHtmlAttr(__('Message')) ?>" rows="5" cols="10"><?= /* @noEscape */ $block->getEscaped($block->getMessage()->getMessage()) ?></textarea> </div> </div> </fieldset> <script> require(['jquery'], function(jQuery){ - jQuery('#add-gift-options-<?= /* @escapeNotVerified */ $block->getEntity()->getId() ?>') - .add('#add-gift-options-for-order-<?= /* @escapeNotVerified */ $block->getEntity()->getId() ?>') + jQuery('#add-gift-options-<?= (int) $block->getEntity()->getId() ?>') + .add('#add-gift-options-for-order-<?= (int) $block->getEntity()->getId() ?>') .removeClass('hidden'); }); </script> @@ -65,21 +66,21 @@ </dd> <?php endif ?> <?php if ($block->isItemsAvailable()): ?> - <dt id="add-gift-options-for-items-<?= /* @escapeNotVerified */ $block->getEntity()->getId() ?>" class="order-title individual"> + <dt id="add-gift-options-for-items-<?= (int) $block->getEntity()->getId() ?>" class="order-title individual"> <div class="field choice"> <input type="checkbox" name="allow_gift_options_for_items" id="allow_gift_options_for_items" data-mage-init='{"giftOptions":{}}' value="1" data-selector='{"id":"#allow-gift-options-for-items-container"}'<?php if ($block->getItemsHasMesssages()): ?> checked="checked"<?php endif; ?> class="checkbox" /> - <label for="allow_gift_options_for_items" class="label"><span><?= /* @escapeNotVerified */ __('Gift Options for Individual Items') ?></span></label> + <label for="allow_gift_options_for_items" class="label"><span><?= $block->escapeHtml(__('Gift Options for Individual Items')) ?></span></label> </div> </dt> <dd id="allow-gift-options-for-items-container" class="order-options individual"> <ol class="items"> <?php foreach ($block->getItems() as $_index => $_item): ?> - <?php $_product = $_item->getProduct() ?> + <?php $_product = $_item->getProduct() ?> <li class="item"> <div class="product"> <div class="number"> - <?= /* @escapeNotVerified */ __('<span>Item %1</span> of %2', $_index+1, $block->countItems()) ?> + <?= $block->escapeHtml(__('<span>Item %1</span> of %2', $_index+1, $block->countItems()), ['span']) ?> </div> <div class="img photo container"> <?= $block->getImage($_product, 'gift_messages_checkout_thumbnail')->toHtml() ?> @@ -87,36 +88,36 @@ <strong class="product name"><?= $block->escapeHtml($_product->getName()) ?></strong> </div> <div class="options"> - <div class="options-items-container" id="options-items-container-<?= /* @escapeNotVerified */ $block->getEntity()->getId() ?>-<?= /* @escapeNotVerified */ $_item->getId() ?>"></div> + <div class="options-items-container" id="options-items-container-<?= (int) $block->getEntity()->getId() ?>-<?= (int) $_item->getId() ?>"></div> <?php if ($block->isItemMessagesAvailable($_item)): ?> <button class="action action-gift" - data-mage-init='{"toggleAdvanced": {"selectorsToggleClass":"hidden", "toggleContainers":"#gift-messages-for-item-container-<?= /* @escapeNotVerified */ $_item->getId() ?>"}}'> - <span><?= /* @escapeNotVerified */ __('Gift Message') ?></span> + data-mage-init='{"toggleAdvanced": {"selectorsToggleClass":"hidden", "toggleContainers":"#gift-messages-for-item-container-<?= (int) $_item->getId() ?>"}}'> + <span><?= $block->escapeHtml(__('Gift Message')) ?></span> </button> - <div id="gift-messages-for-item-container-<?= /* @escapeNotVerified */ $_item->getId() ?>" class="block message hidden"> + <div id="gift-messages-for-item-container-<?= (int) $_item->getId() ?>" class="block message hidden"> <fieldset class="fieldset"> - <p><?= /* @escapeNotVerified */ __('Leave a box blank if you don\'t want to add a gift message for that item.') ?></p> + <p><?= $block->escapeHtml(__('Leave a box blank if you don\'t want to add a gift message for that item.')) ?></p> <div class="field from"> - <label for="gift-message-<?= /* @escapeNotVerified */ $_item->getId() ?>-from" class="label"><span><?= /* @escapeNotVerified */ __('From') ?></span></label> + <label for="gift-message-<?= (int) $_item->getId() ?>-from" class="label"><span><?= $block->escapeHtml(__('From')) ?></span></label> <div class="control"> - <input type="text" name="giftmessage[quote_item][<?= /* @escapeNotVerified */ $_item->getId() ?>][from]" id="gift-message-<?= /* @escapeNotVerified */ $_item->getId() ?>-from" title="<?= /* @escapeNotVerified */ __('From') ?>" value="<?= /* @escapeNotVerified */ $block->getEscaped($block->getMessage($_item)->getSender(), $block->getDefaultFrom()) ?>" class="input-text"> + <input type="text" name="giftmessage[quote_item][<?= (int) $_item->getId() ?>][from]" id="gift-message-<?= (int) $_item->getId() ?>-from" title="<?= $block->escapeHtmlAttr(__('From')) ?>" value="<?= /* @noEscape */ $block->getEscaped($block->getMessage($_item)->getSender(), $block->getDefaultFrom()) ?>" class="input-text"> </div> </div> <div class="field to"> - <label for="gift-message-<?= /* @escapeNotVerified */ $_item->getId() ?>-to" class="label"><span><?= /* @escapeNotVerified */ __('To') ?></span></label> + <label for="gift-message-<?= (int) $_item->getId() ?>-to" class="label"><span><?= $block->escapeHtmlAttr(__('To')) ?></span></label> <div class="control"> - <input type="text" name="giftmessage[quote_item][<?= /* @escapeNotVerified */ $_item->getId() ?>][to]" id="gift-message-<?= /* @escapeNotVerified */ $_item->getId() ?>-to" title="<?= /* @escapeNotVerified */ __('To') ?>" value="<?= /* @escapeNotVerified */ $block->getEscaped($block->getMessage($_item)->getRecipient(), $block->getDefaultTo()) ?>" class="input-text"> + <input type="text" name="giftmessage[quote_item][<?= (int) $_item->getId() ?>][to]" id="gift-message-<?= (int) $_item->getId() ?>-to" title="<?= $block->escapeHtmlAttr(__('To')) ?>" value="<?= /* @noEscape */ $block->getEscaped($block->getMessage($_item)->getRecipient(), $block->getDefaultTo()) ?>" class="input-text"> </div> </div> <div class="field text"> - <label for="gift-message-<?= /* @escapeNotVerified */ $_item->getId() ?>-message" class="label"><span><?= /* @escapeNotVerified */ __('Message') ?></span></label> + <label for="gift-message-<?= (int) $_item->getId() ?>-message" class="label"><span><?= $block->escapeHtml(__('Message')) ?></span></label> <div class="control"> - <textarea id="gift-message-<?= /* @escapeNotVerified */ $_item->getId() ?>-message" class="input-text giftmessage-area" name="giftmessage[quote_item][<?= /* @escapeNotVerified */ $_item->getId() ?>][message]" title="<?= /* @escapeNotVerified */ __('Message') ?>" rows="5" cols="40"><?= /* @escapeNotVerified */ $block->getEscaped($block->getMessage($_item)->getMessage()) ?></textarea> + <textarea id="gift-message-<?= (int) $_item->getId() ?>-message" class="input-text giftmessage-area" name="giftmessage[quote_item][<?= (int) $_item->getId() ?>][message]" title="<?= $block->escapeHtmlAttr(__('Message')) ?>" rows="5" cols="40"><?= /* @noEscape */ $block->getEscaped($block->getMessage($_item)->getMessage()) ?></textarea> </div> </div> </fieldset> </div> - <?php endif; ?> + <?php endif; ?> </div> </li> <?php endforeach; ?> @@ -124,13 +125,13 @@ </dd> <script> require(['jquery'], function(jQuery){ - jQuery('#add-gift-options-<?= /* @escapeNotVerified */ $block->getEntity()->getId() ?>') - .add('#add-gift-options-for-items-<?= /* @escapeNotVerified */ $block->getEntity()->getId() ?>') + jQuery('#add-gift-options-<?= (int) $block->getEntity()->getId() ?>') + .add('#add-gift-options-for-items-<?= (int) $block->getEntity()->getId() ?>') .removeClass('hidden'); }); </script> <?php endif; ?> - <dt class="extra-options-container" id="extra-options-container-<?= /* @escapeNotVerified */ $block->getEntity()->getId() ?>"></dt> + <dt class="extra-options-container" id="extra-options-container-<?= (int) $block->getEntity()->getId() ?>"></dt> </dl> </fieldset> <script type="text/x-magento-init"> @@ -140,127 +141,125 @@ } } </script> -<?php break; ?> +<?php break; +case 'multishipping_address': ?> + <fieldset id="add-gift-options-<?= (int) $block->getEntity()->getId() ?>" class="fieldset gift-message"> + <legend class="legend"><span><?= $block->escapeHtml(__('Do you have any gift items in your order?')) ?></span></legend><br> -<?php case 'multishipping_address': ?> - <fieldset id="add-gift-options-<?= /* @escapeNotVerified */ $block->getEntity()->getId() ?>" class="fieldset gift-message"> - <legend class="legend"><span><?= /* @escapeNotVerified */ __('Do you have any gift items in your order?') ?></span></legend><br> - - <div class="field choice" id="add-gift-options-<?= /* @escapeNotVerified */ $block->getEntity()->getId() ?>"> - <input type="checkbox" name="allow_gift_options_<?= /* @escapeNotVerified */ $block->getEntity()->getId() ?>" id="allow_gift_options_<?= /* @escapeNotVerified */ $block->getEntity()->getId() ?>" data-mage-init='{"giftOptions":{}}' value="1" data-selector='{"id":"#allow-gift-options-container-<?= /* @escapeNotVerified */ $block->getEntity()->getId() ?>"}'<?php if ($block->getItemsHasMesssages() || $block->getEntityHasMessage()): ?> checked="checked"<?php endif; ?> class="checkbox" /> - <label for="allow_gift_options_<?= /* @escapeNotVerified */ $block->getEntity()->getId() ?>" class="label"><span><?= /* @escapeNotVerified */ __('Add Gift Options') ?></span></label> + <div class="field choice" id="add-gift-options-<?= (int) $block->getEntity()->getId() ?>"> + <input type="checkbox" name="allow_gift_options_<?= (int) $block->getEntity()->getId() ?>" id="allow_gift_options_<?= (int) $block->getEntity()->getId() ?>" data-mage-init='{"giftOptions":{}}' value="1" data-selector='{"id":"#allow-gift-options-container-<?= (int) $block->getEntity()->getId() ?>"}'<?php if ($block->getItemsHasMesssages() || $block->getEntityHasMessage()): ?> checked="checked"<?php endif; ?> class="checkbox" /> + <label for="allow_gift_options_<?= (int) $block->getEntity()->getId() ?>" class="label"><span><?= $block->escapeHtml(__('Add Gift Options')) ?></span></label> </div> - <dl class="options-items" id="allow-gift-options-container-<?= /* @escapeNotVerified */ $block->getEntity()->getId() ?>"> + <dl class="options-items" id="allow-gift-options-container-<?= (int) $block->getEntity()->getId() ?>"> <?php if ($block->isMessagesOrderAvailable() || $block->isMessagesAvailable()): ?> - <dt id="add-gift-options-for-order-<?= /* @escapeNotVerified */ $block->getEntity()->getId() ?>" class="order-title"> + <dt id="add-gift-options-for-order-<?= (int) $block->getEntity()->getId() ?>" class="order-title"> <div class="field choice"> - <input type="checkbox" name="allow_gift_options_for_order_<?= /* @escapeNotVerified */ $block->getEntity()->getId() ?>" id="allow_gift_options_for_order_<?= /* @escapeNotVerified */ $block->getEntity()->getId() ?>" data-mage-init='{"giftOptions":{}}' value="1" data-selector='{"id":"#allow-gift-options-for-order-container-<?= /* @escapeNotVerified */ $block->getEntity()->getId() ?>"}'<?php if ($block->getEntityHasMessage()): ?> checked="checked"<?php endif; ?> class="checkbox" /> - <label for="allow_gift_options_for_order_<?= /* @escapeNotVerified */ $block->getEntity()->getId() ?>" class="label"><span><?= /* @escapeNotVerified */ __('Add Gift Options for the Entire Order') ?></span></label> + <input type="checkbox" name="allow_gift_options_for_order_<?= (int) $block->getEntity()->getId() ?>" id="allow_gift_options_for_order_<?= (int) $block->getEntity()->getId() ?>" data-mage-init='{"giftOptions":{}}' value="1" data-selector='{"id":"#allow-gift-options-for-order-container-<?= (int) $block->getEntity()->getId() ?>"}'<?php if ($block->getEntityHasMessage()): ?> checked="checked"<?php endif; ?> class="checkbox" /> + <label for="allow_gift_options_for_order_<?= (int) $block->getEntity()->getId() ?>" class="label"><span><?= $block->escapeHtml(__('Add Gift Options for the Entire Order')) ?></span></label> </div> </dt> - <dd id="allow-gift-options-for-order-container-<?= /* @escapeNotVerified */ $block->getEntity()->getId() ?>" class="order-options"> - <div class="options-order-container" id="options-order-container-<?= /* @escapeNotVerified */ $block->getEntity()->getId() ?>"></div> + <dd id="allow-gift-options-for-order-container-<?= (int) $block->getEntity()->getId() ?>" class="order-options"> + <div class="options-order-container" id="options-order-container-<?= (int) $block->getEntity()->getId() ?>"></div> <?php if ($block->isMessagesAvailable()): ?> <?php $_giftMessage = true; ?> <button class="action action-gift" - data-mage-init='{"toggleAdvanced": {"selectorsToggleClass":"hidden", "toggleContainers":"#gift-messages-for-order-container-<?= /* @escapeNotVerified */ $block->getEntity()->getId() ?>"}}'> - <span><?= /* @escapeNotVerified */ __('Gift Message') ?></span> + data-mage-init='{"toggleAdvanced": {"selectorsToggleClass":"hidden", "toggleContainers":"#gift-messages-for-order-container-<?= (int) $block->getEntity()->getId() ?>"}}'> + <span><?= $block->escapeHtml(__('Gift Message')) ?></span> </button> - <div id="gift-messages-for-order-container-<?= /* @escapeNotVerified */ $block->getEntity()->getId() ?>" class="gift-messages-order hidden"> + <div id="gift-messages-for-order-container-<?= (int) $block->getEntity()->getId() ?>" class="gift-messages-order hidden"> <fieldset class="fieldset"> - <p><?= /* @escapeNotVerified */ __('You can leave this box blank if you don\'t want to add a gift message for this address.') ?></p> + <p><?= $block->escapeHtml(__('You can leave this box blank if you don\'t want to add a gift message for this address.')) ?></p> <div class="field from"> - <label for="gift-message-<?= /* @escapeNotVerified */ $block->getEntity()->getId() ?>-from" class="label"><span><?= /* @escapeNotVerified */ __('From') ?></span></label> + <label for="gift-message-<?= (int) $block->getEntity()->getId() ?>-from" class="label"><span><?= $block->escapeHtml(__('From')) ?></span></label> <div class="control"> - <input type="text" name="giftmessage[quote_address][<?= /* @escapeNotVerified */ $block->getEntity()->getId() ?>][from]" id="gift-message-<?= /* @escapeNotVerified */ $block->getEntity()->getId() ?>-from" title="<?= /* @escapeNotVerified */ __('From') ?>" value="<?= /* @escapeNotVerified */ $block->getEscaped($block->getMessage()->getSender(), $block->getDefaultFrom()) ?>" class="input-text"> + <input type="text" name="giftmessage[quote_address][<?= (int) $block->getEntity()->getId() ?>][from]" id="gift-message-<?= (int) $block->getEntity()->getId() ?>-from" title="<?= $block->escapeHtmlAttr(__('From')) ?>" value="<?= /* @noEscape */ $block->getEscaped($block->getMessage()->getSender(), $block->getDefaultFrom()) ?>" class="input-text"> </div> </div> <div class="field to"> - <label for="gift-message-<?= /* @escapeNotVerified */ $block->getEntity()->getId() ?>-to" class="label"><span><?= /* @escapeNotVerified */ __('To') ?></span></label> + <label for="gift-message-<?= (int) $block->getEntity()->getId() ?>-to" class="label"><span><?= $block->escapeHtml(__('To')) ?></span></label> <div class="control"> - <input type="text" name="giftmessage[quote_address][<?= /* @escapeNotVerified */ $block->getEntity()->getId() ?>][to]" id="gift-message-<?= /* @escapeNotVerified */ $block->getEntity()->getId() ?>-to" title="<?= /* @escapeNotVerified */ __('To') ?>" value="<?= /* @escapeNotVerified */ $block->getEscaped($block->getMessage()->getRecipient(), $block->getDefaultTo()) ?>" class="input-text"> + <input type="text" name="giftmessage[quote_address][<?= (int) $block->getEntity()->getId() ?>][to]" id="gift-message-<?= (int) $block->getEntity()->getId() ?>-to" title="<?= $block->escapeHtmlAttr(__('To')) ?>" value="<?= /* @noEscape */ $block->getEscaped($block->getMessage()->getRecipient(), $block->getDefaultTo()) ?>" class="input-text"> </div> </div> <div class="field text"> - <label for="gift-message-<?= /* @escapeNotVerified */ $block->getEntity()->getId() ?>-message" class="label"><span><?= /* @escapeNotVerified */ __('Message') ?></span></label> + <label for="gift-message-<?= (int) $block->getEntity()->getId() ?>-message" class="label"><span><?= $block->escapeHtml(__('Message')) ?></span></label> <div class="control"> - <textarea id="gift-message-<?= /* @escapeNotVerified */ $block->getEntity()->getId() ?>-message" class="input-text" name="giftmessage[quote_address][<?= /* @escapeNotVerified */ $block->getEntity()->getId() ?>][message]" title="<?= /* @escapeNotVerified */ __('Message') ?>" rows="5" cols="40"><?= /* @escapeNotVerified */ $block->getEscaped($block->getMessage()->getMessage()) ?></textarea> + <textarea id="gift-message-<?= (int) $block->getEntity()->getId() ?>-message" class="input-text" name="giftmessage[quote_address][<?= (int) $block->getEntity()->getId() ?>][message]" title="<?= $block->escapeHtmlAttr(__('Message')) ?>" rows="5" cols="40"><?= /* @noEscape */ $block->getEscaped($block->getMessage()->getMessage()) ?></textarea> </div> </div> </fieldset> </div> - <?php endif; ?> + <?php endif; ?> </dd> <?php endif; ?> <?php if ($block->isItemsAvailable()): ?> - <dt id="add-gift-options-for-items-<?= /* @escapeNotVerified */ $block->getEntity()->getId() ?>" class="order-title individual"> + <dt id="add-gift-options-for-items-<?= (int) $block->getEntity()->getId() ?>" class="order-title individual"> <div class="field choice"> - <input type="checkbox" name="allow_gift_options_for_items_<?= /* @escapeNotVerified */ $block->getEntity()->getId() ?>" id="allow_gift_options_for_items_<?= /* @escapeNotVerified */ $block->getEntity()->getId() ?>" data-mage-init='{"giftOptions":{}}' value="1" data-selector='{"id":"#allow-gift-options-for-items-container-<?= /* @escapeNotVerified */ $block->getEntity()->getId() ?>"}'<?php if ($block->getItemsHasMesssages()): ?> checked="checked"<?php endif; ?> class="checkbox" /> - <label for="allow_gift_options_for_items_<?= /* @escapeNotVerified */ $block->getEntity()->getId() ?>" class="label"><span><?= /* @escapeNotVerified */ __('Add Gift Options for Individual Items') ?></span></label> + <input type="checkbox" name="allow_gift_options_for_items_<?= (int) $block->getEntity()->getId() ?>" id="allow_gift_options_for_items_<?= (int) $block->getEntity()->getId() ?>" data-mage-init='{"giftOptions":{}}' value="1" data-selector='{"id":"#allow-gift-options-for-items-container-<?= (int) $block->getEntity()->getId() ?>"}'<?php if ($block->getItemsHasMesssages()): ?> checked="checked"<?php endif; ?> class="checkbox" /> + <label for="allow_gift_options_for_items_<?= (int) $block->getEntity()->getId() ?>" class="label"><span><?= $block->escapeHtml(__('Add Gift Options for Individual Items')) ?></span></label> </div> </dt> - <dd id="allow-gift-options-for-items-container-<?= /* @escapeNotVerified */ $block->getEntity()->getId() ?>" class="order-options individual"> - <ol class="items"> - <?php foreach ($block->getItems() as $_index => $_item): ?> - <?php $_product = $_item->getProduct() ?> - <li class="item"> - <div class="product"> - <div class="number"><?= /* @escapeNotVerified */ __('<span>Item %1</span> of %2', $_index+1, $block->countItems()) ?></div> - <div class="img photo container"> - <?= $block->getImage($_product, 'gift_messages_checkout_thumbnail')->toHtml() ?> - </div> - <strong class="product-name"><?= $block->escapeHtml($_product->getName()) ?></strong> - </div> - <div class="options"> - <div class="options-items-container" id="options-items-container-<?= /* @escapeNotVerified */ $block->getEntity()->getId() ?>-<?= /* @escapeNotVerified */ $_item->getId() ?>"></div> - <input type="hidden" name="giftoptions[quote_address_item][<?= /* @escapeNotVerified */ $_item->getId() ?>][address]" value="<?= /* @escapeNotVerified */ $block->getEntity()->getId() ?>" /> - - <?php if ($block->isItemMessagesAvailable($_item)): ?> - <?php $_giftMessage = true; ?> - <button class="action action-gift" - data-mage-init='{"toggleAdvanced": {"selectorsToggleClass":"hidden", "toggleContainers":"#gift-messages-for-item-container-<?= /* @escapeNotVerified */ $_item->getId() ?>"}}'> - <span><?= /* @escapeNotVerified */ __('Gift Message') ?></span> - </button> - <div id="gift-messages-for-item-container-<?= /* @escapeNotVerified */ $_item->getId() ?>" class="block message hidden"> - <fieldset class="fieldset"> - <p><?= /* @escapeNotVerified */ __('You can leave this box blank if you don\'t want to add a gift message for the item.') ?></p> - <input type="hidden" name="giftmessage[quote_address_item][<?= /* @escapeNotVerified */ $_item->getId() ?>][address]" value="<?= /* @escapeNotVerified */ $block->getEntity()->getId() ?>" /> - <div class="field from"> - <label for="gift-message-<?= /* @escapeNotVerified */ $_item->getId() ?>-from" class="label"><span><?= /* @escapeNotVerified */ __('From') ?></span></label> - <div class="control"> - <input type="text" name="giftmessage[quote_address_item][<?= /* @escapeNotVerified */ $_item->getId() ?>][from]" id="gift-message-<?= /* @escapeNotVerified */ $_item->getId() ?>-from" title="<?= /* @escapeNotVerified */ __('From') ?>" value="<?= /* @escapeNotVerified */ $block->getEscaped($block->getMessage($_item)->getSender(), $block->getDefaultFrom()) ?>" class="input-text"> - </div> - </div> - <div class="field to"> - <label for="gift-message-<?= /* @escapeNotVerified */ $_item->getId() ?>-to" class="label"><span><?= /* @escapeNotVerified */ __('To') ?></span></label> + <dd id="allow-gift-options-for-items-container-<?= (int) $block->getEntity()->getId() ?>" class="order-options individual"> + <ol class="items"> + <?php foreach ($block->getItems() as $_index => $_item): ?> + <?php $_product = $_item->getProduct() ?> + <li class="item"> + <div class="product"> + <div class="number"><?= $block->escapeHtml(__('<span>Item %1</span> of %2', $_index+1, $block->countItems()), ['span']) ?></div> + <div class="img photo container"> + <?= $block->getImage($_product, 'gift_messages_checkout_thumbnail')->toHtml() ?> + </div> + <strong class="product-name"><?= $block->escapeHtml($_product->getName()) ?></strong> + </div> + <div class="options"> + <div class="options-items-container" id="options-items-container-<?= (int) $block->getEntity()->getId() ?>-<?= (int) $_item->getId() ?>"></div> + <input type="hidden" name="giftoptions[quote_address_item][<?= (int) $_item->getId() ?>][address]" value="<?= (int) $block->getEntity()->getId() ?>" /> + <?php if ($block->isItemMessagesAvailable($_item)): ?> + <?php $_giftMessage = true; ?> + <button class="action action-gift" + data-mage-init='{"toggleAdvanced": {"selectorsToggleClass":"hidden", "toggleContainers":"#gift-messages-for-item-container-<?= (int) $_item->getId() ?>"}}'> + <span><?= $block->escapeHtml(__('Gift Message')) ?></span> + </button> + <div id="gift-messages-for-item-container-<?= (int) $_item->getId() ?>" class="block message hidden"> + <fieldset class="fieldset"> + <p><?= $block->escapeHtml(__('You can leave this box blank if you don\'t want to add a gift message for the item.')) ?></p> + <input type="hidden" name="giftmessage[quote_address_item][<?= (int) $_item->getId() ?>][address]" value="<?= (int) $block->getEntity()->getId() ?>" /> + <div class="field from"> + <label for="gift-message-<?= (int) $_item->getId() ?>-from" class="label"><span><?= $block->escapeHtml(__('From')) ?></span></label> <div class="control"> - <input type="text" name="giftmessage[quote_address_item][<?= /* @escapeNotVerified */ $_item->getId() ?>][to]" id="gift-message-<?= /* @escapeNotVerified */ $_item->getId() ?>-to" title="<?= /* @escapeNotVerified */ __('To') ?>" value="<?= /* @escapeNotVerified */ $block->getEscaped($block->getMessage($_item)->getRecipient(), $block->getDefaultTo()) ?>" class="input-text"> + <input type="text" name="giftmessage[quote_address_item][<?= (int) $_item->getId() ?>][from]" id="gift-message-<?= (int) $_item->getId() ?>-from" title="<?= $block->escapeHtmlAttr(__('From')) ?>" value="<?= /* @noEscape */ $block->getEscaped($block->getMessage($_item)->getSender(), $block->getDefaultFrom()) ?>" class="input-text"> </div> </div> - <div class="field text"> - <label for="gift-message-<?= /* @escapeNotVerified */ $_item->getId() ?>-message" class="label"><span><?= /* @escapeNotVerified */ __('Message') ?></span></label> - <div class="control"> - <textarea id="gift-message-<?= /* @escapeNotVerified */ $_item->getId() ?>-message" class="input-text giftmessage-area" name="giftmessage[quote_address_item][<?= /* @escapeNotVerified */ $_item->getId() ?>][message]" title="<?= /* @escapeNotVerified */ __('Message') ?>" rows="5" cols="10"><?= /* @escapeNotVerified */ $block->getEscaped($block->getMessage($_item)->getMessage()) ?></textarea> - </div> - </div> - </fieldset> - </div> - <?php endif; ?> - </div> - </li> - <?php endforeach; ?> - </ol> + <div class="field to"> + <label for="gift-message-<?= (int) $_item->getId() ?>-to" class="label"><span><?= $block->escapeHtml(__('To')) ?></span></label> + <div class="control"> + <input type="text" name="giftmessage[quote_address_item][<?= (int) $_item->getId() ?>][to]" id="gift-message-<?= (int) $_item->getId() ?>-to" title="<?= $block->escapeHtmlAttr(__('To')) ?>" value="<?= /* @noEscape */ $block->getEscaped($block->getMessage($_item)->getRecipient(), $block->getDefaultTo()) ?>" class="input-text"> + </div> + </div> + <div class="field text"> + <label for="gift-message-<?= (int) $_item->getId() ?>-message" class="label"><span><?= $block->escapeHtml(__('Message')) ?></span></label> + <div class="control"> + <textarea id="gift-message-<?= (int) $_item->getId() ?>-message" class="input-text giftmessage-area" name="giftmessage[quote_address_item][<?= (int) $_item->getId() ?>][message]" title="<?= $block->escapeHtmlAttr(__('Message')) ?>" rows="5" cols="10"><?= /* @noEscape */ $block->getEscaped($block->getMessage($_item)->getMessage()) ?></textarea> + </div> + </div> + </fieldset> + </div> + <?php endif; ?> + </div> + </li> + <?php endforeach; ?> + </ol> </dd> <?php endif; ?> - <dt class="extra-options-container" id="extra-options-container-<?= /* @escapeNotVerified */ $block->getEntity()->getId() ?>"></dt> + <dt class="extra-options-container" id="extra-options-container-<?= (int) $block->getEntity()->getId() ?>"></dt> </dl> </fieldset> <script type="text/x-magento-init"> { - "#allow_gift_options_<?= /* @escapeNotVerified */ $block->getEntity()->getId() ?>, #allow_gift_options_for_order_<?= /* @escapeNotVerified */ $block->getEntity()->getId() ?>, #allow_gift_options_for_items_<?= /* @escapeNotVerified */ $block->getEntity()->getId() ?>": { + "#allow_gift_options_<?= (int) $block->getEntity()->getId() ?>, #allow_gift_options_for_order_<?= (int) $block->getEntity()->getId() ?>, #allow_gift_options_for_items_<?= (int) $block->getEntity()->getId() ?>": { "giftOptions": {} } } diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/ActionGroup/AdminGroupedProductActionGroup.xml b/app/code/Magento/GroupedProduct/Test/Mftf/ActionGroup/AdminGroupedProductActionGroup.xml index 9360ed6e42792..e246612bc6c99 100644 --- a/app/code/Magento/GroupedProduct/Test/Mftf/ActionGroup/AdminGroupedProductActionGroup.xml +++ b/app/code/Magento/GroupedProduct/Test/Mftf/ActionGroup/AdminGroupedProductActionGroup.xml @@ -52,4 +52,22 @@ </arguments> <fillField selector="{{AdminAddedProductsToGroupGrid.inputByProductName(productName)}}" userInput="{{qty}}" stepKey="fillDefaultQtyForLinkedProduct"/> </actionGroup> + + <!-- Assign Specified Product To Grouped Product --> + <!-- Assumes web client is on grouped product edit page --> + <actionGroup name="AdminAssignProductToGroup"> + <arguments> + <argument name="product"/> + </arguments> + + <scrollTo selector="{{AdminProductFormGroupedProductsSection.toggleGroupedProduct}}" x="0" y="-100" stepKey="scrollToGroupedSection"/> + <conditionalClick selector="{{AdminProductFormGroupedProductsSection.toggleGroupedProduct}}" dependentSelector="{{AdminProductFormGroupedProductsSection.addProductsToGroup}}" visible="false" stepKey="openGroupedProductsSection"/> + <click selector="{{AdminProductFormGroupedProductsSection.addProductsToGroup}}" stepKey="clickAddProductsToGroup"/> + <conditionalClick selector="{{AdminAddProductsToGroupPanel.clearFilters}}" dependentSelector="{{AdminAddProductsToGroupPanel.clearFilters}}" visible="true" stepKey="clearExistingFilters"/> + <click selector="{{AdminAddProductsToGroupPanel.filters}}" stepKey="showFiltersPanel"/> + <fillField userInput="{{product.name}}" selector="{{AdminAddProductsToGroupPanel.nameFilter}}" stepKey="fillNameFilter"/> + <click selector="{{AdminAddProductsToGroupPanel.applyFilters}}" stepKey="clickApplyFilters"/> + <click selector="{{AdminAddProductsToGroupPanel.firstCheckbox}}" stepKey="selectProduct"/> + <click selector="{{AdminAddProductsToGroupPanel.addSelectedProducts}}" stepKey="clickAddSelectedGroupProducts"/> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/ActionGroup/AdminOrderGroupedProductActionGroup.xml b/app/code/Magento/GroupedProduct/Test/Mftf/ActionGroup/AdminOrderGroupedProductActionGroup.xml new file mode 100644 index 0000000000000..7028d1c8a6f76 --- /dev/null +++ b/app/code/Magento/GroupedProduct/Test/Mftf/ActionGroup/AdminOrderGroupedProductActionGroup.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminOrderConfigureGroupedProduct"> + <arguments> + <argument name="productSku" type="string" defaultValue="{{SimpleProduct.sku}}"/> + <argument name="productQty" type="string" defaultValue="1"/> + </arguments> + <click selector="{{AdminOrderFormItemsOrderedSection.configureButtonBySku}}" stepKey="clickConfigure"/> + <waitForPageLoad stepKey="waitForConfigurePageLoad"/> + <fillField selector="{{AdminOrderFormGroupedProductSection.optionQty(productSku)}}" userInput="{{productQty}}" stepKey="fillOptionQuantity"/> + <click selector="{{AdminOrderFormConfigureProductSection.ok}}" stepKey="clickOk"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/ActionGroup/AssertLinkPresenceOnGroupedProductPageActionGroup.xml b/app/code/Magento/GroupedProduct/Test/Mftf/ActionGroup/AssertLinkPresenceOnGroupedProductPageActionGroup.xml new file mode 100644 index 0000000000000..bce78f8bf9961 --- /dev/null +++ b/app/code/Magento/GroupedProduct/Test/Mftf/ActionGroup/AssertLinkPresenceOnGroupedProductPageActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <!-- Check for the product link. --> + <!-- Assumes web client is on Grouped Product Page --> + <actionGroup name="AssertLinkPresenceOnGroupedProductPage"> + <arguments> + <argument name="productName" type="string"/> + </arguments> + <see selector="{{StorefrontProductInfoMainSection.groupedProductsTable}}" userInput="{{productName}}" stepKey="seeFirstStagedGroupedProduct"/> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/GroupedProduct/Test/Mftf/Section/AdminAddProductsToGroupPanelSection.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Section/AdminAddProductsToGroupPanelSection.xml index e2c4286135d2e..f71bb49aea10a 100644 --- a/app/code/Magento/GroupedProduct/Test/Mftf/Section/AdminAddProductsToGroupPanelSection.xml +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Section/AdminAddProductsToGroupPanelSection.xml @@ -15,6 +15,7 @@ <element name="nameFilter" type="input" selector=".product_form_product_form_grouped_grouped_products_modal [name='name']"/> <element name="firstCheckbox" type="input" selector="tr[data-repeat-index='0'] .admin__control-checkbox"/> <element name="nThCheckbox" type="input" selector="tr[data-repeat-index='{{n}}'] .admin__control-checkbox" parameterized="true"/> + <element name="clearFilters" type="button" selector=".product_form_product_form_grouped_grouped_products_modal [data-action='grid-filter-reset']" timeout="30"/> </section> <section name="AdminAddedProductsToGroupGrid"> diff --git a/app/code/Magento/Theme/Test/Mftf/Section/StorefrontFooterSection.xml b/app/code/Magento/GroupedProduct/Test/Mftf/Section/StorefrontProductInfoMainSection.xml similarity index 66% rename from app/code/Magento/Theme/Test/Mftf/Section/StorefrontFooterSection.xml rename to app/code/Magento/GroupedProduct/Test/Mftf/Section/StorefrontProductInfoMainSection.xml index d9854b7a80b9b..45d8e63343734 100644 --- a/app/code/Magento/Theme/Test/Mftf/Section/StorefrontFooterSection.xml +++ b/app/code/Magento/GroupedProduct/Test/Mftf/Section/StorefrontProductInfoMainSection.xml @@ -8,6 +8,7 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> - <section name="StorefrontFooterSection"> + <section name="StorefrontProductInfoMainSection"> + <element name="groupedProductsTable" type="text" selector="#super-product-table .product-item-name"/> </section> </sections> diff --git a/app/code/Magento/GroupedProduct/view/adminhtml/templates/catalog/product/composite/fieldset/grouped.phtml b/app/code/Magento/GroupedProduct/view/adminhtml/templates/catalog/product/composite/fieldset/grouped.phtml index 6bd2c4e3c67f9..7c6fa50070315 100644 --- a/app/code/Magento/GroupedProduct/view/adminhtml/templates/catalog/product/composite/fieldset/grouped.phtml +++ b/app/code/Magento/GroupedProduct/view/adminhtml/templates/catalog/product/composite/fieldset/grouped.phtml @@ -3,73 +3,71 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - - ?> +// phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis +?> <?php /* @var $block \Magento\GroupedProduct\Block\Adminhtml\Product\Composite\Fieldset\Grouped */ ?> -<?php $_skipSaleableCheck = $this->helper('Magento\Catalog\Helper\Product')->getSkipSaleableCheck(); ?> +<?php $_skipSaleableCheck = $this->helper(Magento\Catalog\Helper\Product::class)->getSkipSaleableCheck(); ?> <div id="catalog_product_composite_configure_fields_grouped" class="<?= $block->getIsLastFieldset() ? 'last-fieldset' : '' ?>"> - <h4><?= /* @escapeNotVerified */ __('Associated Products') ?></h4> + <h4><?= $block->escapeHtml(__('Associated Products')) ?></h4> <div class="product-options"> <?php $_product = $block->getProduct(); ?> <?php $block->setPreconfiguredValue(); ?> <?php $_associatedProducts = $block->getAssociatedProducts(); ?> <?php $_hasAssociatedProducts = count($_associatedProducts) > 0; ?> - <?php if ((!$_product->isAvailable() && !$_skipSaleableCheck) || !$_hasAssociatedProducts): ?> - <p class="availability out-of-stock"><?= /* @escapeNotVerified */ __('Availability:') ?> <span><?= /* @escapeNotVerified */ __('Out of stock') ?></span></p> + <?php if ((!$_product->isAvailable() && !$_skipSaleableCheck) || !$_hasAssociatedProducts) : ?> + <p class="availability out-of-stock"><?= $block->escapeHtml(__('Availability:')) ?> <span><?= $block->escapeHtml(__('Out of stock')) ?></span></p> <?php endif; ?> <table class="data-table admin__table-primary grouped-items-table" id="super-product-table"> <thead> <tr class="headings"> - <th class="col-id"><?= /* @escapeNotVerified */ __('ID') ?></th> - <th class="col-sku"><?= /* @escapeNotVerified */ __('SKU') ?></th> - <th class="col-name"><?= /* @escapeNotVerified */ __('Product Name') ?></th> - <?php if ($block->getCanShowProductPrice($_product)): ?> - <th class="col-price"><?= /* @escapeNotVerified */ __('Price') ?></th> + <th class="col-id"><?= $block->escapeHtml(__('ID')) ?></th> + <th class="col-sku"><?= $block->escapeHtml(__('SKU')) ?></th> + <th class="col-name"><?= $block->escapeHtml(__('Product Name')) ?></th> + <?php if ($block->getCanShowProductPrice($_product)) : ?> + <th class="col-price"><?= $block->escapeHtml(__('Price')) ?></th> <?php endif; ?> - <?php if ($_product->isSaleable() || $_skipSaleableCheck): ?> - <th class="col-qty"><?= /* @escapeNotVerified */ __('Qty') ?></th> + <?php if ($_product->isSaleable() || $_skipSaleableCheck) : ?> + <th class="col-qty"><?= $block->escapeHtml(__('Qty')) ?></th> <?php endif; ?> </tr> </thead> <tbody> - <?php if ($_hasAssociatedProducts): ?> + <?php if ($_hasAssociatedProducts) : ?> <?php $i = 0 ?> - <?php foreach ($_associatedProducts as $_item): ?> - <tr class="<?= /* @escapeNotVerified */ (++$i % 2) ? 'even' : 'odd' ?>"> - <td class="col-id"><?= /* @escapeNotVerified */ $_item->getId() ?></td> + <?php foreach ($_associatedProducts as $_item) : ?> + <tr class="<?= $block->escapeHtmlAttr((++$i % 2) ? 'even' : 'odd') ?>"> + <td class="col-id"><?= $block->escapeHtml($_item->getId()) ?></td> <td class="col-sku"><?= $block->escapeHtml($_item->getSku()) ?></td> <td class="col-name"><?= $block->escapeHtml($_item->getName()) ?></td> - <?php if ($block->getCanShowProductPrice($_product)): ?> + <?php if ($block->getCanShowProductPrice($_product)) : ?> <td class="col-price"> - <?php if ($block->getCanShowProductPrice($_item)): ?> - <?= /* @escapeNotVerified */ $block->getProductPrice($_item) ?> + <?php if ($block->getCanShowProductPrice($_item)) : ?> + <?= /* @noEscape */ $block->getProductPrice($_item) ?> <?php endif; ?> </td> <?php endif; ?> - <?php if ($_product->isSaleable() || $_skipSaleableCheck): ?> + <?php if ($_product->isSaleable() || $_skipSaleableCheck) : ?> <td class="col-qty"> - <?php if ($_item->isSaleable() || $_skipSaleableCheck) : ?> + <?php if ($_item->isSaleable() || $_skipSaleableCheck) : ?> <input type="text" - name="super_group[<?= /* @escapeNotVerified */ $_item->getId() ?>]" - id="super_group[<?= /* @escapeNotVerified */ $_item->getId() ?>]" + name="super_group[<?= $block->escapeHtmlAttr($_item->getId()) ?>]" + id="super_group[<?= $block->escapeHtmlAttr($_item->getId()) ?>]" maxlength="12" - value="<?= /* @escapeNotVerified */ $_item->getQty()*1 ?>" - title="<?= /* @escapeNotVerified */ __('Qty') ?>" + value="<?= $block->escapeHtmlAttr($_item->getQty()*1) ?>" + title="<?= $block->escapeHtmlAttr(__('Qty')) ?>" class="input-text admin__control-text qty" /> - <input type="hidden" value="1" price="<?= /* @escapeNotVerified */ $block->getCurrencyPrice($_item->getPrice()) ?>" qtyId="super_group[<?= /* @escapeNotVerified */ $_item->getId() ?>]" /> - <?php else: ?> - <p class="availability out-of-stock"><span><?= /* @escapeNotVerified */ __('Out of stock') ?></span></p> + <input type="hidden" value="1" price="<?= $block->escapeHtmlAttr($block->getCurrencyPrice($_item->getPrice())) ?>" qtyId="super_group[<?= $block->escapeHtmlAttr($_item->getId()) ?>]" /> + <?php else : ?> + <p class="availability out-of-stock"><span><?= $block->escapeHtml(__('Out of stock')) ?></span></p> <?php endif; ?> </td> <?php endif; ?> </tr> <?php endforeach; ?> - <?php else: ?> + <?php else : ?> <tr> - <td class="empty-text" colspan="<?php if ($_product->isSaleable() || $_skipSaleableCheck): ?>4<?php else : ?>3<?php endif; ?>"><?= /* @escapeNotVerified */ __('No options of this product are available.') ?></td> + <td class="empty-text" colspan="<?php if ($_product->isSaleable() || $_skipSaleableCheck) : ?>4<?php else : ?>3<?php endif; ?>"><?= $block->escapeHtml(__('No options of this product are available.')) ?></td> </tr> <?php endif; ?> </tbody> diff --git a/app/code/Magento/GroupedProduct/view/adminhtml/templates/product/grouped/grouped.phtml b/app/code/Magento/GroupedProduct/view/adminhtml/templates/product/grouped/grouped.phtml index 2c5a24195dd3a..ac6161d940a3a 100644 --- a/app/code/Magento/GroupedProduct/view/adminhtml/templates/product/grouped/grouped.phtml +++ b/app/code/Magento/GroupedProduct/view/adminhtml/templates/product/grouped/grouped.phtml @@ -4,17 +4,15 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - /** @var $block \Magento\Framework\View\Element\Template */ /** @var $block \Magento\Framework\View\Element\Template */ $_gridPopupBlock = $block->getChildBlock('catalog.product.group.grid.popup.container')->getChildBlock('grid'); $_gridPopupBlock->setRowClickCallback('function(){}'); ?> -<div id="grouped-product-container" data-mage-init='{"groupedProduct":{"gridPopup":"[data-grid-id=<?= /* @escapeNotVerified */ $_gridPopupBlock->getId() ?>]"}}'> +<div id="grouped-product-container" data-mage-init='{"groupedProduct":{"gridPopup":"[data-grid-id=<?= $block->escapeHtmlAttr($_gridPopupBlock->getId()) ?>]"}}'> <div class="no-products-message"> - <?= /* @escapeNotVerified */ __('There are no grouped products.') ?> + <?= $block->escapeHtml(__('There are no grouped products.')) ?> </div> <?= $block->getChildHtml('list') ?> <div data-role="add-product-dialog" class="no-display"> diff --git a/app/code/Magento/GroupedProduct/view/adminhtml/templates/product/grouped/list.phtml b/app/code/Magento/GroupedProduct/view/adminhtml/templates/product/grouped/list.phtml index c931304fcaeac..0872afcc8b3c7 100644 --- a/app/code/Magento/GroupedProduct/view/adminhtml/templates/product/grouped/list.phtml +++ b/app/code/Magento/GroupedProduct/view/adminhtml/templates/product/grouped/list.phtml @@ -3,7 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile +// phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis /* @var $block \Magento\GroupedProduct\Block\Product\Grouped\AssociatedProducts\ListAssociatedProducts */ ?> <script type="text/x-magento-template" id="group-product-template"> @@ -56,16 +56,16 @@ <span></span> </th> <th data-column="name" class="no-link col-name"> - <span><?= /* @escapeNotVerified */ __('Name') ?></span> + <span><?= $block->escapeHtml(__('Name')) ?></span> </th> <th data-column="sku" class="no-link col-sku"> - <span><?= /* @escapeNotVerified */ __('SKU') ?></span> + <span><?= $block->escapeHtml(__('SKU')) ?></span> </th> <th data-column="price" class="no-link col-price"> - <span><?= /* @escapeNotVerified */ __('Price') ?></span> + <span><?= $block->escapeHtml(__('Price')) ?></span> </th> <th data-column="qty" class="no-link col-qty"> - <span><?= /* @escapeNotVerified */ __('Default Qty') ?></span> + <span><?= $block->escapeHtml(__('Default Qty')) ?></span> </th> <th data-column="actions" class="last no-link col-actions"> <span></span> diff --git a/app/code/Magento/GroupedProduct/view/base/templates/product/price/final_price.phtml b/app/code/Magento/GroupedProduct/view/base/templates/product/price/final_price.phtml index d9238e9794d7e..c9c03b9f214d9 100644 --- a/app/code/Magento/GroupedProduct/view/base/templates/product/price/final_price.phtml +++ b/app/code/Magento/GroupedProduct/view/base/templates/product/price/final_price.phtml @@ -4,8 +4,6 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - /** * Template for displaying grouped product price */ @@ -27,9 +25,9 @@ if ($minProduct) { } ?> <div class="price-box"> - <?php if ($minProduct && \Magento\Framework\Pricing\Render::ZONE_ITEM_VIEW != $block->getZone()): ?> + <?php if ($minProduct && \Magento\Framework\Pricing\Render::ZONE_ITEM_VIEW != $block->getZone()) : ?> <p class="minimal-price"> - <span class="price-label"><?= /* @escapeNotVerified */ __('Starting at') ?></span><?= $amountRender->toHtml() ?> + <span class="price-label"><?= $block->escapeHtml(__('Starting at')) ?></span><?= $amountRender->toHtml() ?> </p> <?php endif ?> </div> diff --git a/app/code/Magento/GroupedProduct/view/frontend/templates/product/view/type/default.phtml b/app/code/Magento/GroupedProduct/view/frontend/templates/product/view/type/default.phtml index 89434ac7b13cd..b1697bfb012b9 100644 --- a/app/code/Magento/GroupedProduct/view/frontend/templates/product/view/type/default.phtml +++ b/app/code/Magento/GroupedProduct/view/frontend/templates/product/view/type/default.phtml @@ -3,23 +3,20 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <?php /* @var $block \Magento\Catalog\Block\Product\View\AbstractView */?> <?php $_product = $block->getProduct() ?> <?php $_associatedProducts = $block->getAssociatedProducts(); ?> <?php $_hasAssociatedProducts = count($_associatedProducts) > 0; ?> -<?php if ($block->displayProductStockStatus()): ?> - <?php if ($_product->isAvailable() && $_hasAssociatedProducts): ?> - <div class="stock available" title="<?= /* @escapeNotVerified */ __('Availability') ?>"> - <span><?= /* @escapeNotVerified */ __('In stock') ?></span> +<?php if ($block->displayProductStockStatus()) : ?> + <?php if ($_product->isAvailable() && $_hasAssociatedProducts) : ?> + <div class="stock available" title="<?= $block->escapeHtmlAttr(__('Availability')) ?>"> + <span><?= $block->escapeHtml(__('In stock')) ?></span> </div> - <?php else: ?> - <div class="stock unavailable" title="<?= /* @escapeNotVerified */ __('Availability') ?>"> - <span><?= /* @escapeNotVerified */ __('Out of stock') ?></span> + <?php else : ?> + <div class="stock unavailable" title="<?= $block->escapeHtmlAttr(__('Availability')) ?>"> + <span><?= $block->escapeHtml(__('Out of stock')) ?></span> </div> <?php endif; ?> <?php endif; ?> diff --git a/app/code/Magento/GroupedProduct/view/frontend/templates/product/view/type/grouped.phtml b/app/code/Magento/GroupedProduct/view/frontend/templates/product/view/type/grouped.phtml index 0be71f20a3822..0257d87a2d9ee 100644 --- a/app/code/Magento/GroupedProduct/view/frontend/templates/product/view/type/grouped.phtml +++ b/app/code/Magento/GroupedProduct/view/frontend/templates/product/view/type/grouped.phtml @@ -4,8 +4,6 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - /** * Grouped product data template * @@ -22,55 +20,55 @@ <table class="table data grouped" id="super-product-table" data-mage-init='{ "Magento_GroupedProduct/js/product-ids-resolver": {} }'> - <caption class="table-caption"><?= /* @escapeNotVerified */ __('Grouped product items') ?></caption> + <caption class="table-caption"><?= $block->escapeHtml(__('Grouped product items')) ?></caption> <thead> <tr> - <th class="col item" scope="col"><?= /* @escapeNotVerified */ __('Product Name') ?></th> - <?php if ($_product->isSaleable()): ?> - <th class="col qty" scope="col"><?= /* @escapeNotVerified */ __('Qty') ?></th> + <th class="col item" scope="col"><?= $block->escapeHtml(__('Product Name')) ?></th> + <?php if ($_product->isSaleable()) : ?> + <th class="col qty" scope="col"><?= $block->escapeHtml(__('Qty')) ?></th> <?php endif; ?> </tr> </thead> - <?php if ($_hasAssociatedProducts): ?> + <?php if ($_hasAssociatedProducts) : ?> <tbody> - <?php foreach ($_associatedProducts as $_item): ?> + <?php foreach ($_associatedProducts as $_item) : ?> <tr> <td data-th="<?= $block->escapeHtml(__('Product Name')) ?>" class="col item"> <strong class="product-item-name"><?= $block->escapeHtml($_item->getName()) ?></strong> - <?php if ($block->getCanShowProductPrice($_product)): ?> - <?php if ($block->getCanShowProductPrice($_item)): ?> - <?= /* @escapeNotVerified */ $block->getProductPrice($_item) ?> + <?php if ($block->getCanShowProductPrice($_product)) : ?> + <?php if ($block->getCanShowProductPrice($_item)) : ?> + <?= /* @noEscape */ $block->getProductPrice($_item) ?> + <?php endif; ?> <?php endif; ?> - <?php endif; ?> </td> - <?php if ($_product->isSaleable()): ?> + <?php if ($_product->isSaleable()) : ?> <td data-th="<?= $block->escapeHtml(__('Qty')) ?>" class="col qty"> - <?php if ($_item->isSaleable()) : ?> + <?php if ($_item->isSaleable()) : ?> <div class="control qty"> <input type="number" - name="super_group[<?= /* @escapeNotVerified */ $_item->getId() ?>]" - data-selector="super_group[<?= /* @escapeNotVerified */ $_item->getId() ?>]" - value="<?= /* @escapeNotVerified */ $_item->getQty() * 1 ?>" - title="<?= /* @escapeNotVerified */ __('Qty') ?>" + name="super_group[<?= $block->escapeHtmlAttr($_item->getId()) ?>]" + data-selector="super_group[<?= $block->escapeHtmlAttr($_item->getId()) ?>]" + value="<?= $block->escapeHtmlAttr($_item->getQty() * 1) ?>" + title="<?= $block->escapeHtmlAttr(__('Qty')) ?>" class="input-text qty" data-validate="{'validate-grouped-qty':'#super-product-table'}" data-errors-message-box="#validation-message-box"/> </div> - <?php else: ?> - <div class="stock unavailable" title="<?= /* @escapeNotVerified */ __('Availability') ?>"> - <span><?= /* @escapeNotVerified */ __('Out of stock') ?></span> + <?php else : ?> + <div class="stock unavailable" title="<?= $block->escapeHtmlAttr(__('Availability')) ?>"> + <span><?= $block->escapeHtml(__('Out of stock')) ?></span> </div> <?php endif; ?> </td> <?php endif; ?> </tr> - <?php if ($block->getCanShowProductPrice($_product) + <?php if ($block->getCanShowProductPrice($_product) && $block->getCanShowProductPrice($_item) && trim($block->getProductPriceHtml( $_item, \Magento\Catalog\Pricing\Price\TierPrice::PRICE_CODE - ))): ?> + ))) : ?> <tr class="row-tier-price"> <td colspan="2"> <?= $block->getProductPriceHtml( @@ -82,12 +80,12 @@ <?php endif; ?> <?php endforeach; ?> </tbody> - <?php else: ?> + <?php else : ?> <tbody> <tr> <td class="unavailable" - colspan="<?php if ($_product->isSaleable()): ?>4<?php else : ?>3<?php endif; ?>"> - <?= /* @escapeNotVerified */ __('No options of this product are available.') ?> + colspan="<?php if ($_product->isSaleable()) : ?>4<?php else : ?>3<?php endif; ?>"> + <?= $block->escapeHtml(__('No options of this product are available.')) ?> </td> </tr> </tbody> diff --git a/app/code/Magento/ImportExport/view/adminhtml/templates/busy.phtml b/app/code/Magento/ImportExport/view/adminhtml/templates/busy.phtml index 4f13b759efd87..fe1763d3e0286 100644 --- a/app/code/Magento/ImportExport/view/adminhtml/templates/busy.phtml +++ b/app/code/Magento/ImportExport/view/adminhtml/templates/busy.phtml @@ -6,11 +6,11 @@ ?> <div class="fieldset"> <div class="legend"> - <span><?= /* @escapeNotVerified */ __('Status') ?></span> + <span><?= $block->escapeHtml(__('Status')) ?></span> </div><br> <div class="messages"> <div class="message message-success success"> - <div><?= /* @escapeNotVerified */ $block->getStatusMessage() ?></div> + <div><?= $block->escapeHtml($block->getStatusMessage()) ?></div> </div> </div> </div> diff --git a/app/code/Magento/ImportExport/view/adminhtml/templates/export/form/after.phtml b/app/code/Magento/ImportExport/view/adminhtml/templates/export/form/after.phtml index 7e801bcd1d662..150f7dbeb1046 100644 --- a/app/code/Magento/ImportExport/view/adminhtml/templates/export/form/after.phtml +++ b/app/code/Magento/ImportExport/view/adminhtml/templates/export/form/after.phtml @@ -3,20 +3,18 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile ?> <fieldset class="admin__fieldset" id="export_filter_container" style="display:none;"> <legend class="admin__legend"> - <span><?= /* @escapeNotVerified */ __('Entity Attributes') ?></span> + <span><?= $block->escapeHtml(__('Entity Attributes')) ?></span> </legend> <br /> - <form id="export_filter_form" action="<?= /* @escapeNotVerified */ $block->getUrl('*/*/export') ?>" method="post"> - <input name="form_key" type="hidden" value="<?= /* @escapeNotVerified */ $block->getFormKey() ?>" /> + <form id="export_filter_form" action="<?= $block->escapeUrl($block->getUrl('*/*/export')) ?>" method="post"> + <input name="form_key" type="hidden" value="<?= /* @noEscape */ $block->getFormKey() ?>" /> <div id="export_filter_grid_container"><!-- --></div> </form> - <button class="action- scalable" type="button" onclick="getFile();"><span><?php - /* @escapeNotVerified */ echo __('Continue') + <button class="action- scalable" type="button" onclick="getFile();"><span><?= + $block->escapeHtml(__('Continue')) ?></span></button> </fieldset> <script> diff --git a/app/code/Magento/ImportExport/view/adminhtml/templates/export/form/before.phtml b/app/code/Magento/ImportExport/view/adminhtml/templates/export/form/before.phtml index fbdd394783608..26b241b999493 100644 --- a/app/code/Magento/ImportExport/view/adminhtml/templates/export/form/before.phtml +++ b/app/code/Magento/ImportExport/view/adminhtml/templates/export/form/before.phtml @@ -44,7 +44,7 @@ require([ */ getFilter: function() { if ($('entity') && $F('entity')) { - var url = "<?= /* @escapeNotVerified */ $block->getUrl('*/*/getFilter') ?>"; + var url = "<?= $block->escapeJs($block->escapeUrl($block->getUrl('*/*/getFilter'))) ?>"; var entity = $F('entity'); if (entity != this.previousGridEntity) { this.previousGridEntity = entity; diff --git a/app/code/Magento/ImportExport/view/adminhtml/templates/import/form/after.phtml b/app/code/Magento/ImportExport/view/adminhtml/templates/import/form/after.phtml index b6c67e4167aac..f629e6c9e9f59 100644 --- a/app/code/Magento/ImportExport/view/adminhtml/templates/import/form/after.phtml +++ b/app/code/Magento/ImportExport/view/adminhtml/templates/import/form/after.phtml @@ -3,13 +3,11 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile ?> <div class="entry-edit fieldset" id="import_validation_container" style="display:none;"> <div class="entry-edit-head legend"> <span class="icon-head head-edit-form fieldset-legend" - id="import_validation_container_header"><?= /* @escapeNotVerified */ __('Validation Results') ?></span> + id="import_validation_container_header"><?= $block->escapeHtml(__('Validation Results')) ?></span> </div><br> <div id="import_validation_messages" class="fieldset"><!-- --></div> </div> diff --git a/app/code/Magento/ImportExport/view/adminhtml/templates/import/form/before.phtml b/app/code/Magento/ImportExport/view/adminhtml/templates/import/form/before.phtml index 8a52f4ca88e75..628c1088a016c 100644 --- a/app/code/Magento/ImportExport/view/adminhtml/templates/import/form/before.phtml +++ b/app/code/Magento/ImportExport/view/adminhtml/templates/import/form/before.phtml @@ -3,8 +3,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile ?> <?php /** @var $block \Magento\ImportExport\Block\Adminhtml\Import\Edit\Before */ @@ -29,25 +27,27 @@ require([ * List of existing behavior sets * @type {Array} */ - uniqueBehaviors: <?= /* @escapeNotVerified */ $block->getUniqueBehaviors() ?>, + uniqueBehaviors: <?= /* @noEscape */ $block->getUniqueBehaviors() ?>, /** * Behaviour codes for import entities * @type {Array} */ - entityBehaviors: <?= /* @escapeNotVerified */ $block->getEntityBehaviors() ?>, + entityBehaviors: <?= /* @noEscape */ $block->getEntityBehaviors() ?>, /** * Behaviour notes for import entities * @type {Array} */ - entityBehaviorsNotes: <?= /* @escapeNotVerified */ $block->getEntityBehaviorsNotes() ?>, + entityBehaviorsNotes: <?= /* @noEscape */ $block->getEntityBehaviorsNotes() ?>, /** * Base url * @type {string} */ - sampleFilesBaseUrl: '<?= /* @escapeNotVerified */ $block->getUrl('*/*/download/', ['filename' => 'entity-name']) ?>', + sampleFilesBaseUrl: '<?= $block->escapeJs( + $block->escapeUrl($block->getUrl('*/*/download/', ['filename' => 'entity-name'])) + ) ?>', /** * Reset selected index diff --git a/app/code/Magento/ImportExport/view/adminhtml/templates/import/frame/result.phtml b/app/code/Magento/ImportExport/view/adminhtml/templates/import/frame/result.phtml index fbb9baa452ac8..57f521fba946f 100644 --- a/app/code/Magento/ImportExport/view/adminhtml/templates/import/frame/result.phtml +++ b/app/code/Magento/ImportExport/view/adminhtml/templates/import/frame/result.phtml @@ -6,6 +6,6 @@ ?> <script type='text/javascript'> //<![CDATA[ - top.varienImport.postToFrameComplete(<?= /* @escapeNotVerified */ $block->getResponseJson() ?>); + top.varienImport.postToFrameComplete(<?= /* @noEscape */ $block->getResponseJson() ?>); //]]> </script> diff --git a/app/code/Magento/Integration/ViewModel/JsonSerializer.php b/app/code/Magento/Integration/ViewModel/JsonSerializer.php new file mode 100644 index 0000000000000..3f1df35d34ec6 --- /dev/null +++ b/app/code/Magento/Integration/ViewModel/JsonSerializer.php @@ -0,0 +1,40 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\Integration\ViewModel; + +/** + * JsonSerializer + */ +class JsonSerializer implements \Magento\Framework\View\Element\Block\ArgumentInterface +{ + + /** + * @var \Magento\Framework\Serialize\Serializer\Json + */ + private $serializer; + + /** + * @param \Magento\Framework\Serialize\Serializer\Json $serializer + */ + public function __construct(\Magento\Framework\Serialize\Serializer\Json $serializer) + { + $this->serializer = $serializer; + } + + /** + * Returns serialized version of data + * + * @param array $data + * @return string + */ + public function serialize(array $data): string + { + return $this->serializer->serialize($data); + } +} diff --git a/app/code/Magento/Integration/view/adminhtml/templates/integration/activate/permissions.phtml b/app/code/Magento/Integration/view/adminhtml/templates/integration/activate/permissions.phtml index 3264b640c8840..d51ac9e41ba3e 100644 --- a/app/code/Magento/Integration/view/adminhtml/templates/integration/activate/permissions.phtml +++ b/app/code/Magento/Integration/view/adminhtml/templates/integration/activate/permissions.phtml @@ -7,10 +7,8 @@ * * @var \Magento\Backend\Block\Widget\Form\Container $block */ - -// @codingStandardsIgnoreFile ?> -<div><p><?= /* @escapeNotVerified */ __('The integration you selected asks you to approve access to the following:') ?></p></div> +<div><p><?= $block->escapeHtml(__('The integration you selected asks you to approve access to the following:')) ?></p></div> <div id="integration-activate-permissions-tabs"> <?= $block->getChildHtml('tabs') ?> </div> diff --git a/app/code/Magento/Integration/view/adminhtml/templates/integration/activate/permissions/tab/webapi.phtml b/app/code/Magento/Integration/view/adminhtml/templates/integration/activate/permissions/tab/webapi.phtml index 2a0ac30d67df7..1730509a65910 100644 --- a/app/code/Magento/Integration/view/adminhtml/templates/integration/activate/permissions/tab/webapi.phtml +++ b/app/code/Magento/Integration/view/adminhtml/templates/integration/activate/permissions/tab/webapi.phtml @@ -7,14 +7,11 @@ * * @var \Magento\Integration\Block\Adminhtml\Integration\Activate\Permissions\Tab\Webapi $block */ - -// @codingStandardsIgnoreFile - ?> <fieldset class="admin__fieldset form-inline entry-edit"> - <?php if ($block->isTreeEmpty()): ?> - <p class="empty"><?= /* @escapeNotVerified */ __('No permissions requested') ?></p> - <?php else: ?> + <?php if ($block->isTreeEmpty()) : ?> + <p class="empty"><?= $block->escapeHtml(__('No permissions requested')) ?></p> + <?php else : ?> <div class="field" data-role="tree-resources-container"> <div class="control"> <div id="resource-tree" class="tree x-tree" data-role="resource-tree"></div> @@ -22,7 +19,7 @@ </div> <?php endif ?> </fieldset> -<?php if (!$block->isTreeEmpty()): ?> +<?php if (!$block->isTreeEmpty()) : ?> <script> require(["jquery", "Magento_User/js/roles-tree"], function($){ $.widget('mage.rolesTree', $.mage.rolesTree, { @@ -35,8 +32,8 @@ }); $('[data-role="resource-tree"]').rolesTree({ - 'treeInitData': <?= /* @escapeNotVerified */ $block->getResourcesTreeJson() ?>, - 'treeInitSelectedData': <?= /* @escapeNotVerified */ $block->getSelectedResourcesJson() ?> + 'treeInitData': <?= /* @noEscape */ $block->getResourcesTreeJson() ?>, + 'treeInitSelectedData': <?= /* @noEscape */ $block->getSelectedResourcesJson() ?> }); }); </script> diff --git a/app/code/Magento/Integration/view/adminhtml/templates/integration/popup_container.phtml b/app/code/Magento/Integration/view/adminhtml/templates/integration/popup_container.phtml index ae16da1760f62..ef0a667d2de47 100644 --- a/app/code/Magento/Integration/view/adminhtml/templates/integration/popup_container.phtml +++ b/app/code/Magento/Integration/view/adminhtml/templates/integration/popup_container.phtml @@ -7,8 +7,6 @@ * * @var \Magento\Backend\Block\Template $block */ - -// @codingStandardsIgnoreFile ?> <script> require([ @@ -20,11 +18,34 @@ ], function ($, Confirm) { window.integration = new Integration( - '<?= /* @escapeNotVerified */ $block->getUrl('*/*/permissionsDialog', ['id' => ':id', 'reauthorize' => ':isReauthorize', '_escape_params' => false]) ?>', - '<?= /* @escapeNotVerified */ $block->getUrl('*/*/tokensDialog', ['id' => ':id', 'reauthorize' => ':isReauthorize', '_escape_params' => false]) ?>', - '<?= /* @escapeNotVerified */ $block->getUrl('*/*/tokensExchange', ['id' => ':id', 'reauthorize' => ':isReauthorize', '_escape_params' => false]) ?>', - '<?= /* @escapeNotVerified */ $block->getUrl('*/*') ?>', - '<?= /* @escapeNotVerified */ $block->getUrl('*/*/loginSuccessCallback') ?>' + '<?= $block->escapeUrl( + $block->getUrl( + '*/*/permissionsDialog', + ['id' => ':id', 'reauthorize' => ':isReauthorize', '_escape_params' => false] + ) + ) ?>', + '<?= $block->escapeUrl( + $block->getUrl( + '*/*/tokensDialog', + ['id' => ':id', 'reauthorize' => ':isReauthorize', '_escape_params' => false] + ) + ) ?>', + '<?= $block->escapeUrl( + $block->getUrl( + '*/*/tokensExchange', + ['id' => ':id', 'reauthorize' => ':isReauthorize', '_escape_params' => false] + ) + ) ?>', + '<?= $block->escapeUrl( + $block->getUrl( + '*/*' + ) + ) ?>', + '<?= $block->escapeUrl( + $block->getUrl( + '*/*/loginSuccessCallback' + ) + ) ?>' ); /** @@ -34,8 +55,8 @@ $('div#integrationGrid').on('click', 'button#delete', function (e) { new Confirm({ - title: '<?= /* @escapeNotVerified */ __('Are you sure?') ?>', - content: "<?= /* @escapeNotVerified */ __("Are you sure you want to delete this integration? You can't undo this action.") ?>", + title: '<?= $block->escapeHtml(__('Are you sure?')) ?>', + content: "<?= $block->escapeHtml(__("Are you sure you want to delete this integration? You can't undo this action.")) ?>", actions: { confirm: function () { $.mage.dataPost().postData({action: $(e.target).data('url'), data: {}}); @@ -48,4 +69,4 @@ }); </script> -<div id="integration-popup-container" style="display: none;"></div> \ No newline at end of file +<div id="integration-popup-container" style="display: none;"></div> diff --git a/app/code/Magento/Integration/view/adminhtml/templates/integration/tokens_exchange.phtml b/app/code/Magento/Integration/view/adminhtml/templates/integration/tokens_exchange.phtml index c8e4c914b7c82..e1d85ec5361c0 100644 --- a/app/code/Magento/Integration/view/adminhtml/templates/integration/tokens_exchange.phtml +++ b/app/code/Magento/Integration/view/adminhtml/templates/integration/tokens_exchange.phtml @@ -7,8 +7,5 @@ * * @var \Magento\Backend\Block\Template $block */ - -// @codingStandardsIgnoreFile - ?> -<div><p><?= /* @escapeNotVerified */ __("Please setup or sign in into your 3rd party account to complete setup of this integration.") ?></p></div> +<div><p><?= $block->escapeHtml(__("Please setup or sign in into your 3rd party account to complete setup of this integration.")) ?></p></div> diff --git a/app/code/Magento/Integration/view/adminhtml/templates/resourcetree.phtml b/app/code/Magento/Integration/view/adminhtml/templates/resourcetree.phtml index 00f78226b6ac5..7d6c27954d836 100644 --- a/app/code/Magento/Integration/view/adminhtml/templates/resourcetree.phtml +++ b/app/code/Magento/Integration/view/adminhtml/templates/resourcetree.phtml @@ -4,11 +4,6 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - -?> - -<?php /** @var $block \Magento\Integration\Block\Adminhtml\Integration\Edit\Tab\Webapi */ ?> @@ -16,32 +11,41 @@ <fieldset class="fieldset form-inline entry-edit"> <legend class="legend"> - <span><?= /* @escapeNotVerified */ __('Available APIs') ?></span> + <span><?= $block->escapeHtml(__('Available APIs')) ?></span> </legend><br /> <div class="field"> - <label class="label" for="all_resources"><span><?= /* @escapeNotVerified */ __('Resource Access') ?></span></label> + <label class="label" for="all_resources"><span><?= $block->escapeHtml(__('Resource Access')) ?></span></label> <div class="control"> - <select id="all_resources" name="all_resources" onchange="jQuery('[data-role=tree-resources-container]').toggle()" class="select"> - <option value="0" <?= ($block->isEverythingAllowed() ? '' : 'selected="selected"') ?>><?= /* @escapeNotVerified */ __('Custom') ?></option> - <option value="1" <?= ($block->isEverythingAllowed() ? 'selected="selected"' : '') ?>><?= /* @escapeNotVerified */ __('All') ?></option> + <select id="all_resources" name="all_resources" + onchange="jQuery('[data-role=tree-resources-container]').toggle()" class="select"> + <option value="0" <?= ($block->isEverythingAllowed() ? '' : 'selected="selected"') ?>> + <?= $block->escapeHtml(__('Custom')) ?> + </option> + <option value="1" <?= ($block->isEverythingAllowed() ? 'selected="selected"' : '') ?>> + <?= $block->escapeHtml(__('All')) ?> + </option> </select> </div> </div> - <div class="field<?php if ($block->isEverythingAllowed()):?> no-display<?php endif?>" data-role="tree-resources-container"> - <label class="label"><span><?= /* @escapeNotVerified */ __('Resources') ?></span></label> + <div class="field + <?php if ($block->isEverythingAllowed()) :?> + no-display + <?php endif ?>" + data-role="tree-resources-container"> + <label class="label"><span><?= $block->escapeHtml(__('Resources')) ?></span></label> <div class="control"> - <div class="tree x-tree" data-role="resource-tree" data-mage-init='<?php - echo $block->escapeHtml($this->helper('Magento\Framework\Json\Helper\Data')->jsonEncode([ - 'rolesTree' => [ - "treeInitData" => $block->getTree(), - "treeInitSelectedData" => $block->getSelectedResources(), - ], - ])); - ?>'></div> + <div class="tree x-tree" data-role="resource-tree" data-mage-init='<?= /* @noEscape */ + $block->getJsonSerializer()->serialize([ + 'rolesTree' => [ + "treeInitData" => $block->getTree(), + "treeInitSelectedData" => $block->getSelectedResources(), + ], + ]); ?>'> + </div> </div> </div> </fieldset> diff --git a/app/code/Magento/LayeredNavigation/Test/Mftf/Test/ShopByButtonInMobile.xml b/app/code/Magento/LayeredNavigation/Test/Mftf/Test/ShopByButtonInMobile.xml index 28b937f61ee95..721942f58f7cc 100644 --- a/app/code/Magento/LayeredNavigation/Test/Mftf/Test/ShopByButtonInMobile.xml +++ b/app/code/Magento/LayeredNavigation/Test/Mftf/Test/ShopByButtonInMobile.xml @@ -11,11 +11,11 @@ <test name="ShopByButtonInMobile"> <annotations> <features value="Layered Navigation"/> - <stories value="Shop By button is not working in a mobile theme"/> - <title value="Layered Navigation"/> - <description value="Storefront Shop By collapsible button works in a mobile theme"/> + <stories value="Storefront Shop By collapsible button in mobile themes"/> + <title value="Storefront Shop By collapsible button works in a mobile theme"/> + <description value="Storefront Shop By collapsible button should work in a mobile theme"/> <severity value="CRITICAL"/> - <testCaseId value="MAGETWO-94873"/> + <testCaseId value="MC-6092"/> <group value="LayeredNavigation"/> </annotations> <before> diff --git a/app/code/Magento/LayeredNavigation/view/frontend/templates/layer/filter.phtml b/app/code/Magento/LayeredNavigation/view/frontend/templates/layer/filter.phtml index fab89973d908b..cd04b346b16a6 100644 --- a/app/code/Magento/LayeredNavigation/view/frontend/templates/layer/filter.phtml +++ b/app/code/Magento/LayeredNavigation/view/frontend/templates/layer/filter.phtml @@ -4,8 +4,7 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - +// phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis ?> <?php /** @@ -16,21 +15,29 @@ ?> <ol class="items"> - <?php foreach ($filterItems as $filterItem): ?> + <?php foreach ($filterItems as $filterItem) : ?> <li class="item"> - <?php if ($filterItem->getCount() > 0): ?> + <?php if ($filterItem->getCount() > 0) : ?> <a href="<?= $block->escapeUrl($filterItem->getUrl()) ?>"> - <?= /* @escapeNotVerified */ $filterItem->getLabel() ?> - <?php if ($this->helper('\Magento\Catalog\Helper\Data')->shouldDisplayProductCountOnLayer()): ?> - <span class="count"><?= /* @escapeNotVerified */ $filterItem->getCount() ?><span class="filter-count-label"> - <?php if ($filterItem->getCount() == 1):?> <?= /* @escapeNotVerified */ __('item') ?><?php else:?> <?= /* @escapeNotVerified */ __('items') ?><?php endif;?></span></span> + <?= /* @noEscape */ $filterItem->getLabel() ?> + <?php if ($this->helper(\Magento\Catalog\Helper\Data::class)->shouldDisplayProductCountOnLayer()) : ?> + <span class="count"><?= /* @noEscape */ (int)$filterItem->getCount() ?><span class="filter-count-label"> + <?php if ($filterItem->getCount() == 1) : + ?> <?= $block->escapeHtml(__('item')) ?><?php + else : + ?> <?= $block->escapeHtml(__('item')) ?><?php + endif;?></span></span> <?php endif; ?> </a> - <?php else:?> - <?= /* @escapeNotVerified */ $filterItem->getLabel() ?> - <?php if ($this->helper('\Magento\Catalog\Helper\Data')->shouldDisplayProductCountOnLayer()): ?> - <span class="count"><?= /* @escapeNotVerified */ $filterItem->getCount() ?><span class="filter-count-label"> - <?php if ($filterItem->getCount() == 1):?><?= /* @escapeNotVerified */ __('item') ?><?php else:?><?= /* @escapeNotVerified */ __('items') ?><?php endif;?></span></span> + <?php else :?> + <?= /* @noEscape */ $filterItem->getLabel() ?> + <?php if ($this->helper(\Magento\Catalog\Helper\Data::class)->shouldDisplayProductCountOnLayer()) : ?> + <span class="count"><?= /* @noEscape */ (int)$filterItem->getCount() ?><span class="filter-count-label"> + <?php if ($filterItem->getCount() == 1) : + ?><?= $block->escapeHtml(__('items')) ?><?php + else : + ?><?= $block->escapeHtml(__('items')) ?><?php + endif;?></span></span> <?php endif; ?> <?php endif; ?> </li> diff --git a/app/code/Magento/LayeredNavigation/view/frontend/templates/layer/state.phtml b/app/code/Magento/LayeredNavigation/view/frontend/templates/layer/state.phtml index 603f2f93ff754..ebdb6f4d56547 100644 --- a/app/code/Magento/LayeredNavigation/view/frontend/templates/layer/state.phtml +++ b/app/code/Magento/LayeredNavigation/view/frontend/templates/layer/state.phtml @@ -3,9 +3,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <?php /** @@ -15,35 +12,35 @@ */ ?> <?php $_filters = $block->getActiveFilters() ?> -<?php if (!empty($_filters)): ?> +<?php if (!empty($_filters)) : ?> <div class="filter-current"> <strong class="block-subtitle filter-current-subtitle" role="heading" aria-level="2" - data-count="<?= count($_filters) ?>"><?= /* @escapeNotVerified */ __('Now Shopping by') ?></strong> + data-count="<?= /* @noEscape */ count($_filters) ?>"><?= $block->escapeHtml(__('Now Shopping by')) ?></strong> <ol class="items"> - <?php foreach ($_filters as $_filter): ?> + <?php foreach ($_filters as $_filter) : ?> <li class="item"> <span class="filter-label"><?= $block->escapeHtml(__($_filter->getName())) ?></span> - <span class="filter-value"><?= /* @escapeNotVerified */ $block->stripTags($_filter->getLabel()) ?></span> + <span class="filter-value"><?= $block->escapeHtml($block->stripTags($_filter->getLabel())) ?></span> <?php $clearLinkUrl = $_filter->getClearLinkUrl(); - $currentFilterName = $block->escapeHtml(__($_filter->getName())) . " " . $block->stripTags($_filter->getLabel()); - if ($clearLinkUrl): + $currentFilterName = $block->escapeHtmlAttr(__($_filter->getName()) . " " . $block->stripTags($_filter->getLabel())); + if ($clearLinkUrl) : ?> - <a class="action previous" href="<?= /* @escapeNotVerified */ $_filter->getRemoveUrl() ?>" - title="<?= /* @escapeNotVerified */ __('Previous') ?>"> - <span><?= /* @escapeNotVerified */ __('Previous') ?></span> + <a class="action previous" href="<?= $block->escapeUrl($_filter->getRemoveUrl()) ?>" + title="<?= $block->escapeHtmlAttr(__('Previous')) ?>"> + <span><?= $block->escapeHtml(__('Previous')) ?></span> </a> <a class="action remove" - title="<?= $block->escapeHtml($_filter->getFilter()->getClearLinkText()) ?>" - href="<?= /* @escapeNotVerified */ $clearLinkUrl ?>"> + title="<?= $block->escapeHtmlAttr($_filter->getFilter()->getClearLinkText()) ?>" + href="<?= $block->escapeUrl($clearLinkUrl) ?>"> <span><?= $block->escapeHtml($_filter->getFilter()->getClearLinkText()) ?></span> </a> - <?php else: ?> - <a class="action remove" href="<?= /* @escapeNotVerified */ $_filter->getRemoveUrl() ?>" - title="<?= /* @escapeNotVerified */ $block->escapeHtml(__('Remove')) . " " . $currentFilterName ?>"> - <span><?= /* @escapeNotVerified */ __('Remove This Item') ?></span> + <?php else : ?> + <a class="action remove" href="<?= $block->escapeUrl($_filter->getRemoveUrl()) ?>" + title="<?= /* @noEscape */ $block->escapeHtmlAttr(__('Remove')) . " " . $currentFilterName ?>"> + <span><?= $block->escapeHtml(__('Remove This Item')) ?></span> </a> <?php endif; ?> </li> diff --git a/app/code/Magento/LayeredNavigation/view/frontend/templates/layer/view.phtml b/app/code/Magento/LayeredNavigation/view/frontend/templates/layer/view.phtml index b2159d2ff08d4..9d915956ea694 100644 --- a/app/code/Magento/LayeredNavigation/view/frontend/templates/layer/view.phtml +++ b/app/code/Magento/LayeredNavigation/view/frontend/templates/layer/view.phtml @@ -3,9 +3,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <?php /** @@ -15,32 +12,33 @@ */ ?> -<?php if ($block->canShowBlock()): ?> +<?php if ($block->canShowBlock()) : ?> <div class="block filter"> <div class="block-title filter-title"> - <strong><?= /* @escapeNotVerified */ __('Shop By') ?></strong> + <strong><?= $block->escapeHtml(__('Shop By')) ?></strong> </div> <div class="block-content filter-content"> <?= $block->getChildHtml('state') ?> - <?php if ($block->getLayer()->getState()->getFilters()): ?> + <?php if ($block->getLayer()->getState()->getFilters()) : ?> <div class="block-actions filter-actions"> - <a href="<?= /* @escapeNotVerified */ $block->getClearUrl() ?>" class="action clear filter-clear"><span><?= /* @escapeNotVerified */ __('Clear All') ?></span></a> + <a href="<?= $block->escapeUrl($block->getClearUrl()) ?>" class="action clear filter-clear"><span><?= $block->escapeHtml(__('Clear All')) ?></span></a> </div> <?php endif; ?> <?php $wrapOptions = false; ?> - <?php foreach ($block->getFilters() as $filter): ?> - <?php if (!$wrapOptions): ?> - <strong role="heading" aria-level="2" class="block-subtitle filter-subtitle"><?= /* @escapeNotVerified */ __('Shopping Options') ?></strong> + <?php foreach ($block->getFilters() as $filter) : ?> + <?php if (!$wrapOptions) : ?> + <strong role="heading" aria-level="2" class="block-subtitle filter-subtitle"><?= $block->escapeHtml(__('Shopping Options')) ?></strong> <dl class="filter-options" id="narrow-by-list"> - <?php $wrapOptions = true; endif; ?> - <?php if ($filter->getItemsCount()): ?> + <?php $wrapOptions = true; + endif; ?> + <?php if ($filter->getItemsCount()) : ?> <dt role="heading" aria-level="3" class="filter-options-title"><?= $block->escapeHtml(__($filter->getName())) ?></dt> - <dd class="filter-options-content"><?= /* @escapeNotVerified */ $block->getChildBlock('renderer')->render($filter) ?></dd> + <dd class="filter-options-content"><?= /* @noEscape */ $block->getChildBlock('renderer')->render($filter) ?></dd> <?php endif; ?> <?php endforeach; ?> - <?php if ($wrapOptions): ?> + <?php if ($wrapOptions) : ?> </dl> <?php endif; ?> </div> diff --git a/app/code/Magento/Marketplace/view/adminhtml/templates/index.phtml b/app/code/Magento/Marketplace/view/adminhtml/templates/index.phtml index a37306bf1eed7..ac39d72388e6c 100644 --- a/app/code/Magento/Marketplace/view/adminhtml/templates/index.phtml +++ b/app/code/Magento/Marketplace/view/adminhtml/templates/index.phtml @@ -3,20 +3,20 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile ?> <section class="page-partners"> - <h2 class="page-sub-title"><?= /* @escapeNotVerified */ __('Platinum Partners') ?></h2> + <h2 class="page-sub-title"><?= $block->escapeHtml(__('Platinum Partners')) ?></h2> <p class="partners-description"> - <?php /* @escapeNotVerified */ - echo __( - 'Representing Magento\'s highest level of partner engagement, Magento Platinum Partners have established themselves as leaders and innovators of key products and services designed to help merchants and brands grow their business. ' . - 'Magento reserves the Platinum level for select trusted partners that are committed to offering integrations of commerce features, functions, and tools, as well as back-end systems and operations, to extend and enhance the power of the Magento commerce platform.' - ); ?> + <?= $block->escapeHtml(__( + 'Representing Magento\'s highest level of partner engagement, Magento Platinum Partners have established ' . + 'themselves as leaders and innovators of key products and services designed to help merchants and brands ' . + 'grow their business. Magento reserves the Platinum level for select trusted partners that are committed ' . + 'to offering integrations of commerce features, functions, and tools, as well as back-end systems and ' . + 'operations, to extend and enhance the power of the Magento commerce platform.' + )); ?> </p> - <h3 class="page-sub-sub-title"><?= /* @escapeNotVerified */ __('Featured Platinum Partners') ?></h3> + <h3 class="page-sub-sub-title"><?= $block->escapeHtml(__('Featured Platinum Partners')) ?></h3> <div data-role="partners-block" class="partners-block"> <div data-role="spinner" class="admin__data-grid-loading-mask"> <div class="spinner"> @@ -29,40 +29,40 @@ <div class="row row-gutter partners-footer"> <div class="col-m-5"> <div class="partners-search"> - <h2 class="page-sub-title"><?= /* @escapeNotVerified */ __('Partner search') ?></h2> + <h2 class="page-sub-title"><?= $block->escapeHtml(__('Partner search')) ?></h2> <p> - <?php /* @escapeNotVerified */ - echo __( - 'Magento has a thriving ecosystem of technology partners to help merchants and brands deliver the best possible customer experiences. ' . - 'They are recognized as experts in eCommerce, search, email marketing, payments, tax, fraud, optimization and analytics, fulfillment, and more. ' . - 'Visit the Magento Partner Directory to see all of our trusted partners.' - ); ?> + <?= $block->escapeHtml(__( + 'Magento has a thriving ecosystem of technology partners to help merchants and brands deliver ' . + 'the best possible customer experiences. They are recognized as experts in eCommerce, ' . + 'search, email marketing, payments, tax, fraud, optimization and analytics, fulfillment, ' . + 'and more. Visit the Magento Partner Directory to see all of our trusted partners.' + )); ?> </p> <a class="action-secondary" target="_blank" href="http://partners.magento.com/partner_locator/search.aspx"> - <?= /* @escapeNotVerified */ __('More Partners') ?> + <?= $block->escapeHtml(__('More Partners')) ?> </a> </div> </div> <div class="col-m-3"> <img class="magento-marketplace-logo" - src="<?php /* @escapeNotVerified */ echo $block - ->getViewFileUrl('Magento_Marketplace::partners/images/magento-marketplace.svg'); - ?>" + src="<?= $block->escapeUrl($block + ->getViewFileUrl('Magento_Marketplace::partners/images/magento-marketplace.svg')); ?>" alt="Partner"/> </div> <div class="col-m-4"> - <h2 class="page-sub-title"><?= /* @escapeNotVerified */ __('Magento Marketplace') ?></h2> + <h2 class="page-sub-title"><?= $block->escapeHtml(__('Magento Marketplace')) ?></h2> <p class="partner-description"> - <?php /* @escapeNotVerified */ echo __( + <?= $block->escapeHtml(__( 'Extensions and Themes are an essential component of the Magento Ecosystem. ' . - 'Please visit the Magento Marketplace to see the latest innovations that developers have created to enhance your Magento Store.' - ); ?> + 'Please visit the Magento Marketplace to see the latest innovations that developers have ' . + 'created to enhance your Magento Store.' + )); ?> </p> <a class="action-secondary" target="_blank" href="https://marketplace.magento.com/"> - <?= /* @escapeNotVerified */ __('Visit Magento Marketplaces') ?> + <?= $block->escapeHtml(__('Visit Magento Marketplaces')) ?> </a> </div> </div> @@ -73,8 +73,10 @@ { "*": { "Magento_Marketplace/default": { - "url": "<?= $block->getUrl('marketplace/partners/index', - ['_current' => true, 'block' => '', 'period' => '']) ?>" + "url": "<?= $block->escapeUrl($block->getUrl( + 'marketplace/partners/index', + ['_current' => true, 'block' => '', 'period' => ''] + )) ?>" } } } diff --git a/app/code/Magento/Marketplace/view/adminhtml/templates/partners.phtml b/app/code/Magento/Marketplace/view/adminhtml/templates/partners.phtml index b63bf9ebd50eb..924d6fd7410a5 100644 --- a/app/code/Magento/Marketplace/view/adminhtml/templates/partners.phtml +++ b/app/code/Magento/Marketplace/view/adminhtml/templates/partners.phtml @@ -3,9 +3,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <?php $partners = $block->getPartners(); @@ -22,17 +19,17 @@ $partners = $block->getPartners(); <?= $block->escapeHtml($partner['description']) ?> <br /> <a href="<?= $block->escapeHtml($partner['url_page']) ?>" target="_blank"> - <?= /* @escapeNotVerified */ __('Read More') ?> + <?= $block->escapeHtml(__('Read More')) ?> </a> <br /> <a href="<?= $block->escapeHtml($partner['url_partner_page']) ?>" target="_blank"> - <?= /* @escapeNotVerified */ __('Partner Page') ?> + <?= $block->escapeHtml(__('Partner Page')) ?> </a> </p> </div> <?php endforeach; ?> <?php else : ?> <p> - <?= /* @escapeNotVerified */ __('No partners were found') ?> + <?= $block->escapeHtml(__('No partners were found')) ?> </p> <?php endif; ?> diff --git a/app/code/Magento/MediaStorage/view/adminhtml/templates/system/config/system/storage/media/synchronize.phtml b/app/code/Magento/MediaStorage/view/adminhtml/templates/system/config/system/storage/media/synchronize.phtml index f3c301b490282..fd437161dfbb0 100644 --- a/app/code/Magento/MediaStorage/view/adminhtml/templates/system/config/system/storage/media/synchronize.phtml +++ b/app/code/Magento/MediaStorage/view/adminhtml/templates/system/config/system/storage/media/synchronize.phtml @@ -3,9 +3,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <?php /* @var $block \Magento\MediaStorage\Block\System\Config\System\Storage\Media\Synchronize */ ?> @@ -35,7 +32,7 @@ require([ ); <?php $syncStorageParams = $block->getSyncStorageParams() ?> - addAllowedStorage(<?= /* @escapeNotVerified */ $syncStorageParams['storage_type'] ?>, '<?= /* @escapeNotVerified */ $syncStorageParams['connection_name'] ?>'); + addAllowedStorage(<?= $block->escapeJs($syncStorageParams['storage_type']) ?>, '<?= $block->escapeJs($syncStorageParams['connection_name']) ?>'); defaultValues = []; defaultValues['system_media_storage_configuration_media_storage'] = $('system_media_storage_configuration_media_storage').value; @@ -93,7 +90,7 @@ require([ } var checkStatus = function() { - u = new Ajax.PeriodicalUpdater('', '<?= /* @escapeNotVerified */ $block->getAjaxStatusUpdateUrl() ?>', { + u = new Ajax.PeriodicalUpdater('', '<?= $block->escapeUrl($block->getAjaxStatusUpdateUrl()) ?>', { method: 'get', frequency: 5, loaderArea: false, @@ -103,7 +100,7 @@ require([ try { response = JSON.parse(transport.responseText); - if (response.state == '<?= /* @escapeNotVerified */ \Magento\MediaStorage\Model\File\Storage\Flag::STATE_RUNNING ?>' + if (response.state == '<?= /* @noEscape */ (int)\Magento\MediaStorage\Model\File\Storage\Flag::STATE_RUNNING ?>' && response.message ) { if ($('sync_span').hasClassName('no-display')) { @@ -115,12 +112,12 @@ require([ enableStorageSelection(); $('sync_span').addClassName('no-display'); - if (response.state == '<?= /* @escapeNotVerified */ \Magento\MediaStorage\Model\File\Storage\Flag::STATE_FINISHED ?>') { + if (response.state == '<?= /* @noEscape */ (int)\Magento\MediaStorage\Model\File\Storage\Flag::STATE_FINISHED ?>') { addAllowedStorage( $('system_media_storage_configuration_media_storage').value, $('system_media_storage_configuration_media_database').value ); - } else if (response.state == '<?= /* @escapeNotVerified */ \Magento\MediaStorage\Model\File\Storage\Flag::STATE_NOTIFIED ?>') { + } else if (response.state == '<?= /* @noEscape */ (int)\Magento\MediaStorage\Model\File\Storage\Flag::STATE_NOTIFIED ?>') { if (response.has_errors) { enableSyncButton(); } else { @@ -155,7 +152,7 @@ require([ connection: $('system_media_storage_configuration_media_database').value }; - new Ajax.Request('<?= /* @escapeNotVerified */ $block->getAjaxSyncUrl() ?>', { + new Ajax.Request('<?= $block->escapeUrl($block->getAjaxSyncUrl()) ?>', { parameters: params, loaderArea: false, asynchronous: true @@ -179,7 +176,7 @@ require([ <?= $block->getButtonHtml() ?> <span class="sync-indicator no-display" id="sync_span"> - <img alt="Synchronize" style="margin:0 5px" src="<?= /* @escapeNotVerified */ $block->getViewFileUrl('images/process_spinner.gif') ?>"/> + <img alt="Synchronize" style="margin:0 5px" src="<?= $block->escapeUrl($block->getViewFileUrl('images/process_spinner.gif')) ?>"/> <span id="sync_message_span"></span> </span> <input type="hidden" id="synchronize-validation-input" class="required-synchronize no-display"/> diff --git a/app/code/Magento/Msrp/Pricing/Price/MsrpPrice.php b/app/code/Magento/Msrp/Pricing/Price/MsrpPrice.php index 0bf36e7ce5d6b..6eb840e0c7140 100644 --- a/app/code/Magento/Msrp/Pricing/Price/MsrpPrice.php +++ b/app/code/Magento/Msrp/Pricing/Price/MsrpPrice.php @@ -93,6 +93,8 @@ public function canApplyMsrp(Product $product) } /** + * Check if is minimal price is less than the msrp. + * * @param Product $product * @return bool|float */ diff --git a/app/code/Magento/Msrp/Pricing/Render/PriceBox.php b/app/code/Magento/Msrp/Pricing/Render/PriceBox.php index 892c0bcb51244..709d8da871722 100644 --- a/app/code/Magento/Msrp/Pricing/Render/PriceBox.php +++ b/app/code/Magento/Msrp/Pricing/Render/PriceBox.php @@ -59,4 +59,16 @@ public function getMsrpPriceCalculator(): MsrpPriceCalculatorInterface { return $this->msrpPriceCalculator; } + + /** + * @inheritDoc + */ + public function getCacheKey() + { + return sprintf( + '%s-%s', + parent::getCacheKey(), + $this->getZone() + ); + } } diff --git a/app/code/Magento/Msrp/view/base/templates/product/price/msrp.phtml b/app/code/Magento/Msrp/view/base/templates/product/price/msrp.phtml index a428df57ab113..a77fe0fff91e3 100644 --- a/app/code/Magento/Msrp/view/base/templates/product/price/msrp.phtml +++ b/app/code/Magento/Msrp/view/base/templates/product/price/msrp.phtml @@ -105,4 +105,4 @@ $priceElementIdPrefix = $block->getPriceElementIdPrefix() ? $block->getPriceElem "productName": "<?= $block->escapeJs($block->escapeHtml($product->getName())) ?>", "closeButtonId": "#map-popup-close"}}'><span><?= /* @escapeNotVerified */ __("What's this?") ?></span> </a> -<?php endif; ?> \ No newline at end of file +<?php endif; ?> diff --git a/app/code/Magento/Multishipping/view/frontend/templates/checkout/address/select.phtml b/app/code/Magento/Multishipping/view/frontend/templates/checkout/address/select.phtml index ab49788d8dc1b..598cbf37d8c4d 100644 --- a/app/code/Magento/Multishipping/view/frontend/templates/checkout/address/select.phtml +++ b/app/code/Magento/Multishipping/view/frontend/templates/checkout/address/select.phtml @@ -4,21 +4,19 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - /** @var \Magento\Multishipping\Block\Checkout\Address\Select $block */ ?> <div class="multicheckout"> <div class="block block-billing"> - <?php foreach ($block->getAddress() as $address): ?> + <?php foreach ($block->getAddress() as $address) : ?> <div class="box box-billing-address"> <div class="box-content"> <address> <?= $block->getAddressAsHtml($address) ?> - <?php if ($block->isAddressDefaultBilling($address)): ?> + <?php if ($block->isAddressDefaultBilling($address)) : ?> <br /><strong><?= $block->escapeHtml(__('Default Billing')); ?></strong> <?php endif; ?> - <?php if ($block->isAddressDefaultShipping($address)): ?> + <?php if ($block->isAddressDefaultShipping($address)) : ?> <br /><strong><?= $block->escapeHtml(__('Default Shipping')); ?></strong> <?php endif; ?> </address> diff --git a/app/code/Magento/Multishipping/view/frontend/templates/checkout/addresses.phtml b/app/code/Magento/Multishipping/view/frontend/templates/checkout/addresses.phtml index a29013cc71722..faf08f77c02f3 100644 --- a/app/code/Magento/Multishipping/view/frontend/templates/checkout/addresses.phtml +++ b/app/code/Magento/Multishipping/view/frontend/templates/checkout/addresses.phtml @@ -4,7 +4,7 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile +// phpcs:disable Magento2.Files.LineLength ?> <?php @@ -43,7 +43,7 @@ </tr> </thead> <tbody> - <?php foreach ($block->getItems() as $_index => $_item): ?> + <?php foreach ($block->getItems() as $_index => $_item) : ?> <?php if ($_item->getQuoteItem()) : ?> <tr> <td class="col product" data-th="<?= $block->escapeHtml(__('Product')) ?>"> @@ -68,11 +68,11 @@ </div> </td> <td class="col address" data-th="<?= $block->escapeHtml(__('Send To')) ?>"> - <?php if ($_item->getProduct()->getIsVirtual()): ?> + <?php if ($_item->getProduct()->getIsVirtual()) : ?> <div class="applicable"> <?= $block->escapeHtml(__('A shipping selection is not applicable.')) ?> </div> - <?php else: ?> + <?php else : ?> <div class="field address"> <label for="ship_<?= $block->escapeHtml($_index) ?>_<?= $block->escapeHtml($_item->getQuoteItemId()) ?>_address" class="label"> @@ -102,10 +102,10 @@ <div class="primary"> <button type="submit" title="<?= $block->escapeHtml(__('Go to Shipping Information')) ?>" - class="action primary continue<?php if ($block->isContinueDisabled()):?> disabled<?php endif; ?>" + class="action primary continue<?= $block->isContinueDisabled() ? ' disabled' : '' ?>" data-role="can-continue" data-flag="1" - <?php if ($block->isContinueDisabled()):?> + <?php if ($block->isContinueDisabled()) : ?> disabled="disabled" <?php endif; ?>> <span><?= $block->escapeHtml(__('Go to Shipping Information')) ?></span> diff --git a/app/code/Magento/Multishipping/view/frontend/templates/checkout/billing.phtml b/app/code/Magento/Multishipping/view/frontend/templates/checkout/billing.phtml index 4354cfb7c1c3e..761c1f1a78423 100644 --- a/app/code/Magento/Multishipping/view/frontend/templates/checkout/billing.phtml +++ b/app/code/Magento/Multishipping/view/frontend/templates/checkout/billing.phtml @@ -55,10 +55,6 @@ <div class="box-content"> <address> <?= /* @noEscape */ $block->getCheckoutData()->getAddressHtml($block->getAddress()); ?> - <input type="hidden" - id="multishipping_billing_country_id" - value="<?= /* @noEscape */ $block->getAddress()->getCountryId(); ?>" - name="multishipping_billing_country_id"/> </address> </div> </div> @@ -132,7 +128,7 @@ </div> </div> <div class="actions-toolbar"> - <div class="primary"> + <div class="primary" id="parent-payment-continue"> <button id="payment-continue" type="button" class="action primary continue"> @@ -165,3 +161,31 @@ }); }); </script> + +<script> + //<![CDATA[ + require( + [ + 'Magento_Checkout/js/model/quote', + 'jquery', + 'domReady!' + ], function(quote, $) { + quote.billingAddress({ + city: '<?= /* @noEscape */ $block->getAddress()->getCity() ?>', + company: '<?= /* @noEscape */ $block->getAddress()->getCompany(); ?>', + countryId: '<?= /* @noEscape */ $block->getAddress()->getCountryId(); ?>', + customerAddressId: '<?= /* @noEscape */ $block->getAddress()->getCustomerAddressId(); ?>', + customerId: '<?= /* @noEscape */ $block->getAddress()->getCustomerId(); ?>', + fax: '<?= /* @noEscape */ $block->getAddress()->getFax(); ?>', + firstname: '<?= /* @noEscape */ $block->getAddress()->getFirstname(); ?>', + lastname: '<?= /* @noEscape */ $block->getAddress()->getLastname(); ?>', + postcode: '<?= /* @noEscape */ $block->getAddress()->getPostcode(); ?>', + regionId: '<?= /* @noEscape */ $block->getAddress()->getRegionId(); ?>', + regionCode: '<?= /* @noEscape */ $block->getAddress()->getRegionCode() ?>', + region: '<?= /* @noEscape */ $block->getAddress()->getRegion(); ?>', + street: <?= /* @noEscape */ json_encode($block->getAddress()->getStreet()); ?>, + telephone: '<?= /* @noEscape */ $block->getAddress()->getTelephone(); ?>' + }); + }); + //]]> +</script> diff --git a/app/code/Magento/Multishipping/view/frontend/templates/checkout/billing/items.phtml b/app/code/Magento/Multishipping/view/frontend/templates/checkout/billing/items.phtml index d1c39970518e6..2f89805a2a577 100644 --- a/app/code/Magento/Multishipping/view/frontend/templates/checkout/billing/items.phtml +++ b/app/code/Magento/Multishipping/view/frontend/templates/checkout/billing/items.phtml @@ -4,31 +4,29 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - ?> -<?php if ($block->getQuote()->hasVirtualItems()): ?> +<?php if ($block->getQuote()->hasVirtualItems()) : ?> <div class="block block-other"> <div class="block-title"> - <strong><?= /* @escapeNotVerified */ __('Other items in your order') ?></strong> - <a href="<?= /* @escapeNotVerified */ $block->getVirtualProductEditUrl() ?>" class="action edit"><span><?= /* @escapeNotVerified */ __('Edit Items') ?></span></a> + <strong><?= $block->escapeHtml(__('Other items in your order')) ?></strong> + <a href="<?= $block->escapeUrl($block->getVirtualProductEditUrl()) ?>" class="action edit"><span><?= $block->escapeHtml(__('Edit Items')) ?></span></a> </div> <div class="block-content"> - <p><?= /* @escapeNotVerified */ __('Shipping is not applicable.') ?></p> + <p><?= $block->escapeHtml(__('Shipping is not applicable.')) ?></p> <div class="table-wrapper"> <table class="items data table" id="unavailable-shipping-table"> - <caption class="table-caption"><?= /* @escapeNotVerified */ __('Other items in your order') ?></caption> + <caption class="table-caption"><?= $block->escapeHtml(__('Other items in your order')) ?></caption> <thead> <tr> - <th class="col item" scope="col"><?= /* @escapeNotVerified */ __('Product Name') ?></th> - <th class="col qty" scope="col"><?= /* @escapeNotVerified */ __('Qty') ?></th> + <th class="col item" scope="col"><?= $block->escapeHtml(__('Product Name')) ?></th> + <th class="col qty" scope="col"><?= $block->escapeHtml(__('Qty')) ?></th> </tr> </thead> <tbody> - <?php foreach ($block->getVirtualQuoteItems() as $_item): ?> + <?php foreach ($block->getVirtualQuoteItems() as $_item) : ?> <tr> <td class="col item" data-th="<?= $block->escapeHtml(__('Product Name')) ?>"><?= $block->getItemHtml($_item) ?></td> - <td class="col qty" data-th="<?= $block->escapeHtml(__('Qty')) ?>"><?= /* @escapeNotVerified */ $_item->getQty() ?></td> + <td class="col qty" data-th="<?= $block->escapeHtml(__('Qty')) ?>"><?= $block->escapeHtml($_item->getQty()) ?></td> </tr> <?php endforeach; ?> </tbody> diff --git a/app/code/Magento/Multishipping/view/frontend/templates/checkout/item/default.phtml b/app/code/Magento/Multishipping/view/frontend/templates/checkout/item/default.phtml index 1de8357db8986..51d964957c4d5 100644 --- a/app/code/Magento/Multishipping/view/frontend/templates/checkout/item/default.phtml +++ b/app/code/Magento/Multishipping/view/frontend/templates/checkout/item/default.phtml @@ -4,27 +4,26 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - +// phpcs:disable Magento2.Files.LineLength ?> -<strong class="product name product-item-name"><a href="<?= /* @escapeNotVerified */ $block->getProductUrl() ?>"><?= $block->escapeHtml($block->getProductName()) ?></a></strong> -<?php if ($_options = $block->getOptionList()): ?> +<strong class="product name product-item-name"><a href="<?= $block->escapeUrl($block->getProductUrl()) ?>"><?= $block->escapeHtml($block->getProductName()) ?></a></strong> +<?php if ($_options = $block->getOptionList()) : ?> <dl class="item-options"> <?php foreach ($_options as $_option) : ?> <?php $_formatedOptionValue = $block->getFormatedOptionValue($_option) ?> <dt><?= $block->escapeHtml($_option['label']) ?></dt> - <dd<?php if (isset($_formatedOptionValue['full_view'])): ?> class="tooltip wrapper"<?php endif; ?>> - <?= /* @escapeNotVerified */ $_formatedOptionValue['value'] ?> - <?php if (isset($_formatedOptionValue['full_view'])): ?> + <dd<?= (isset($_formatedOptionValue['full_view']) ? ' class="tooltip wrapper"' : '') ?> + <?= $block->escapeHtml($_formatedOptionValue['value']) ?> + <?php if (isset($_formatedOptionValue['full_view'])) : ?> <dl class="item options tooltip content"> <dt><?= $block->escapeHtml($_option['label']) ?></dt> - <dd><?= /* @escapeNotVerified */ $_formatedOptionValue['full_view'] ?></dd> + <dd><?= $block->escapeHtml($_formatedOptionValue['full_view']) ?></dd> </dl> <?php endif; ?> </dd> <?php endforeach; ?> </dl> <?php endif; ?> -<?php if ($addtInfoBlock = $block->getProductAdditionalInformationBlock()): ?> +<?php if ($addtInfoBlock = $block->getProductAdditionalInformationBlock()) : ?> <?= $addtInfoBlock->setItem($block->getItem())->toHtml() ?> <?php endif; ?> diff --git a/app/code/Magento/Multishipping/view/frontend/templates/checkout/link.phtml b/app/code/Magento/Multishipping/view/frontend/templates/checkout/link.phtml index ae65caf6983d0..15fe496ddd802 100644 --- a/app/code/Magento/Multishipping/view/frontend/templates/checkout/link.phtml +++ b/app/code/Magento/Multishipping/view/frontend/templates/checkout/link.phtml @@ -4,7 +4,5 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - ?> -<a class="action multicheckout" href="<?= /* @escapeNotVerified */ $block->getCheckoutUrl() ?>"><span><?= /* @escapeNotVerified */ __('Check Out with Multiple Addresses') ?></span></a> +<a class="action multicheckout" href="<?= $block->escapeUrl($block->getCheckoutUrl()) ?>"><span><?= $block->escapeHtml(__('Check Out with Multiple Addresses')) ?></span></a> diff --git a/app/code/Magento/Multishipping/view/frontend/templates/checkout/overview.phtml b/app/code/Magento/Multishipping/view/frontend/templates/checkout/overview.phtml index 4590b7c584085..5fff0d72e8000 100644 --- a/app/code/Magento/Multishipping/view/frontend/templates/checkout/overview.phtml +++ b/app/code/Magento/Multishipping/view/frontend/templates/checkout/overview.phtml @@ -4,6 +4,8 @@ * See COPYING.txt for license details. */ +// phpcs:disable Magento2.Templates.ThisInTemplate + /** @var \Magento\Multishipping\Block\Checkout\Overview $block */ ?> <?php $errors = $block->getCheckoutData()->getAddressErrors(); ?> diff --git a/app/code/Magento/Multishipping/view/frontend/templates/checkout/overview/item.phtml b/app/code/Magento/Multishipping/view/frontend/templates/checkout/overview/item.phtml index 5424f37efac07..3505cadc5ec71 100644 --- a/app/code/Magento/Multishipping/view/frontend/templates/checkout/overview/item.phtml +++ b/app/code/Magento/Multishipping/view/frontend/templates/checkout/overview/item.phtml @@ -4,7 +4,7 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile +// phpcs:disable Magento2.Files.LineLength, Magento2.Templates.ThisInTemplate ?> <?php @@ -22,14 +22,14 @@ </td> <td class="col price" data-th="<?= $block->escapeHtml(__('Price')) ?>"> <?php /* Including Tax */ ?> - <?php if ($this->helper('Magento\Tax\Helper\Data')->displayCartPriceInclTax() || $this->helper('Magento\Tax\Helper\Data')->displayCartBothPrices()): ?> + <?php if ($this->helper(Magento\Tax\Helper\Data::class)->displayCartPriceInclTax() || $this->helper(Magento\Tax\Helper\Data::class)->displayCartBothPrices()) : ?> <span class="price-including-tax" data-label="<?= $block->escapeHtml(__('Incl. Tax')) ?>"> <?= $block->getUnitPriceInclTaxHtml($_item) ?> </span> <?php endif; ?> <?php /* end Including Tax */ ?> <?php /* Excluding Tax */ ?> - <?php if ($this->helper('Magento\Tax\Helper\Data')->displayCartPriceExclTax() || $this->helper('Magento\Tax\Helper\Data')->displayCartBothPrices()): ?> + <?php if ($this->helper(Magento\Tax\Helper\Data::class)->displayCartPriceExclTax() || $this->helper(Magento\Tax\Helper\Data::class)->displayCartBothPrices()) : ?> <span class="price-excluding-tax" data-label="<?= $block->escapeHtml(__('Excl. Tax')) ?>"> <?= $block->getUnitPriceExclTaxHtml($_item) ?> </span> @@ -37,17 +37,17 @@ <?php /* end Excluding Tax */ ?> </td> - <td class="col qty" data-th="<?= $block->escapeHtml(__('Qty')) ?>"><?= /* @escapeNotVerified */ $_item->getQty()*1 ?></td> + <td class="col qty" data-th="<?= $block->escapeHtml(__('Qty')) ?>"><?= $_item->getQty() * 1 ?></td> <td class="col subtotal" data-th="<?= $block->escapeHtml(__('Subtotal')) ?>"> <?php /* Including Tax Subtotal */ ?> - <?php if ($this->helper('Magento\Tax\Helper\Data')->displayCartPriceInclTax() || $this->helper('Magento\Tax\Helper\Data')->displayCartBothPrices()): ?> + <?php if ($this->helper(Magento\Tax\Helper\Data::class)->displayCartPriceInclTax() || $this->helper(Magento\Tax\Helper\Data::class)->displayCartBothPrices()) : ?> <span class="price-including-tax" data-label="<?= $block->escapeHtml(__('Incl. Tax')) ?>"> <?= $block->getRowTotalInclTaxHtml($_item) ?> </span> <?php endif; ?> <?php /* end Including Tax Subtotal */ ?> <?php /* Excluding Tax Subtotal */ ?> - <?php if ($this->helper('Magento\Tax\Helper\Data')->displayCartPriceExclTax() || $this->helper('Magento\Tax\Helper\Data')->displayCartBothPrices()): ?> + <?php if ($this->helper(Magento\Tax\Helper\Data::class)->displayCartPriceExclTax() || $this->helper(Magento\Tax\Helper\Data::class)->displayCartBothPrices()) : ?> <span class="price-excluding-tax" data-label="<?= $block->escapeHtml(__('Excl. Tax')) ?>"> <?= $block->getRowTotalExclTaxHtml($_item) ?> </span> diff --git a/app/code/Magento/Multishipping/view/frontend/templates/checkout/shipping.phtml b/app/code/Magento/Multishipping/view/frontend/templates/checkout/shipping.phtml index f66a2093b0aff..af67c087ed24d 100644 --- a/app/code/Magento/Multishipping/view/frontend/templates/checkout/shipping.phtml +++ b/app/code/Magento/Multishipping/view/frontend/templates/checkout/shipping.phtml @@ -4,7 +4,7 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile +// phpcs:disable Magento2.Files.LineLength, Magento2.Templates.ThisInTemplate ?> <?php @@ -14,63 +14,68 @@ * @var $block \Magento\Multishipping\Block\Checkout\Shipping */ ?> -<form action="<?= /* @escapeNotVerified */ $block->getPostActionUrl() ?>" method="post" id="shipping_method_form" class="form multicheckout shipping"> - <?php foreach ($block->getAddresses() as $_index => $_address): ?> +<form action="<?= $block->escapeUrl($block->getPostActionUrl()) ?>" method="post" id="shipping_method_form" class="form multicheckout shipping"> + <?php foreach ($block->getAddresses() as $_index => $_address) : ?> <div class="block block-shipping"> - <div class="block-title"><strong><?= /* @escapeNotVerified */ __('Address %1 <span>of %2</span>', ($_index+1), $block->getAddressCount()) ?></strong></div> + <div class="block-title"><strong><?= $block->escapeHtml(__('Address %1 <span>of %2</span>', ($_index+1), $block->getAddressCount()), ['span']) ?></strong></div> <div class="block-content"> <div class="box box-shipping-address"> <strong class="box-title"> - <span><?= /* @escapeNotVerified */ __('Shipping To') ?></span> - <a href="<?= /* @escapeNotVerified */ $block->getAddressEditUrl($_address) ?>" class="action edit"><span><?= /* @escapeNotVerified */ __('Change') ?></span></a> + <span><?= $block->escapeHtml(__('Shipping To')) ?></span> + <a href="<?= $block->escapeUrl($block->getAddressEditUrl($_address)) ?>" class="action edit"> + <span><?= $block->escapeHtml(__('Change')) ?></span> + </a> </strong> <div class="box-content"> - <address><?= /* @escapeNotVerified */ $_address->format('html') ?></address> + <address><?= /* @noEscape */ $_address->format('html') ?></address> </div> </div> <div class="box box-shipping-method"> <strong class="box-title"> - <span><?= /* @escapeNotVerified */ __('Shipping Method') ?></span> + <span><?= $block->escapeHtml(__('Shipping Method')) ?></span> </strong> <div class="box-content"> - <?php if (!($_shippingRateGroups = $block->getShippingRates($_address))): ?> - <p><?= /* @escapeNotVerified */ __('Sorry, no quotes are available for this order right now.') ?></p> - <?php else: ?> + <?php if (!($_shippingRateGroups = $block->getShippingRates($_address))) : ?> + <p><?= $block->escapeHtml(__('Sorry, no quotes are available for this order right now.')) ?></p> + <?php else : ?> <dl class="items methods-shipping"> - <?php $_sole = count($_shippingRateGroups) == 1; foreach ($_shippingRateGroups as $code => $_rates): ?> + <?php $_sole = count($_shippingRateGroups) == 1; foreach ($_shippingRateGroups as $code => $_rates) : ?> <dt class="item-title"><?= $block->escapeHtml($block->getCarrierName($code)) ?></dt> <dd class="item-content"> <fieldset class="fieldset"> <legend class="legend"> <span><?= $block->escapeHtml($block->getCarrierName($code)) ?></span> </legend><br> - <?php $_sole = $_sole && count($_rates) == 1; foreach ($_rates as $_rate): ?> + <?php $_sole = $_sole && count($_rates) == 1; foreach ($_rates as $_rate) : ?> <div class="field choice"> - <?php if ($_rate->getErrorMessage()): ?> + <?php if ($_rate->getErrorMessage()) : ?> <strong><?= $block->escapeHtml($_rate->getCarrierTitle()) ?>: <?= $block->escapeHtml($_rate->getErrorMessage()) ?></strong> - <?php else: ?> + <?php else : ?> <div class="control"> <?php if ($_sole) : ?> - <input type="radio" name="shipping_method[<?= /* @escapeNotVerified */ $_address->getId() ?>]" value="<?= $block->escapeHtml($_rate->getCode()) ?>" id="s_method_<?= /* @escapeNotVerified */ $_address->getId() ?>_<?= /* @escapeNotVerified */ $_rate->getCode() ?>" class="radio solo method" checked="checked"/> - <?php else: ?> - <input type="radio" name="shipping_method[<?= /* @escapeNotVerified */ $_address->getId() ?>]" value="<?= /* @escapeNotVerified */ $_rate->getCode() ?>" id="s_method_<?= /* @escapeNotVerified */ $_address->getId() ?>_<?= /* @escapeNotVerified */ $_rate->getCode() ?>"<?php if($_rate->getCode()===$block->getAddressShippingMethod($_address)) echo ' checked="checked"' ?> class="radio" /> + <input type="radio" name="shipping_method[<?= (int) $_address->getId() ?>]" value="<?= $block->escapeHtmlAttr($_rate->getCode()) ?>" id="s_method_<?= (int) $_address->getId() ?>_<?= $block->escapeHtmlAttr($_rate->getCode()) ?>" class="radio solo method" checked="checked"/> + <?php else : ?> + <input type="radio" name="shipping_method[<?= (int) $_address->getId() ?>]" value="<?= $block->escapeHtmlAttr($_rate->getCode()) ?>" id="s_method_<?= (int) $_address->getId() ?>_<?= $block->escapeHtmlAttr($_rate->getCode()) ?>" <?= ($_rate->getCode()===$block->getAddressShippingMethod($_address)) ? ' checked="checked"' : '' ?> class="radio" /> <?php endif; ?> </div> - <label for="s_method_<?= /* @escapeNotVerified */ $_address->getId() ?>_<?= /* @escapeNotVerified */ $_rate->getCode() ?>"><?= $block->escapeHtml($_rate->getMethodTitle()) ?> - <?php $_excl = $block->getShippingPrice($_address, $_rate->getPrice(), $this->helper('Magento\Tax\Helper\Data')->displayShippingPriceIncludingTax()); ?> + <label for="s_method_<?= (int) $_address->getId() ?>_<?= $block->escapeHtmlAttr($_rate->getCode()) ?>"> + <?= $block->escapeHtml($_rate->getMethodTitle()) ?> + <?php $_excl = $block->getShippingPrice($_address, $_rate->getPrice(), $this->helper(Magento\Tax\Helper\Data::class)->displayShippingPriceIncludingTax()); ?> <?php $_incl = $block->getShippingPrice($_address, $_rate->getPrice(), true); ?> - <?php if ($this->helper('Magento\Tax\Helper\Data')->displayShippingBothPrices() && $_incl != $_excl): ?> - <span class="price-including-tax" data-label="<?= /* @escapeNotVerified */ __('Incl. Tax') ?>"> - <?php endif; ?> - <?= /* @escapeNotVerified */ $_incl ?> - <?php if ($this->helper('Magento\Tax\Helper\Data')->displayShippingBothPrices() && $_incl != $_excl): ?> + <?php if ($this->helper(Magento\Tax\Helper\Data::class)->displayShippingBothPrices() && $_incl != $_excl) : ?> + <span class="price-including-tax" data-label="<?= $block->escapeHtml(__('Incl. Tax')) ?>"> + <?php endif; ?> + <?= $block->escapeHtml($_incl, ['span']) ?> + <?php if ($this->helper(Magento\Tax\Helper\Data::class)->displayShippingBothPrices() && $_incl != $_excl) : ?> + </span> + <?php endif; ?> + <?php if ($this->helper(Magento\Tax\Helper\Data::class)->displayShippingBothPrices() && $_incl != $_excl) : ?> + <span class="price-excluding-tax" data-label="<?= $block->escapeHtml(__('Excl. Tax')) ?>"> + <?= $block->escapeHtml($_excl, ['span']) ?> </span> - <?php endif; ?> - <?php if ($this->helper('Magento\Tax\Helper\Data')->displayShippingBothPrices() && $_incl != $_excl): ?> - <span class="price-excluding-tax" data-label="<?= /* @escapeNotVerified */ __('Excl. Tax') ?>"><?= /* @escapeNotVerified */ $_excl ?></span> - <?php endif; ?> + <?php endif; ?> </label> - <?php endif ?> + <?php endif ?> </div> <?php endforeach; ?> </fieldset> @@ -78,29 +83,29 @@ <?php endforeach; ?> </dl> <?php endif; ?> - <?= /* @escapeNotVerified */ $block->getItemsBoxTextAfter($_address) ?> + <?= /* @noEscape */ $block->getItemsBoxTextAfter($_address) ?> </div> </div> <div class="box box-items"> <strong class="box-title"> - <span><?= /* @escapeNotVerified */ __('Items') ?></span> - <a href="<?= /* @escapeNotVerified */ $block->getItemsEditUrl($_address) ?>" class="action edit"><span><?= /* @escapeNotVerified */ __('Edit Items') ?></span></a> + <span><?= $block->escapeHtml(__('Items')) ?></span> + <a href="<?= $block->escapeUrl($block->getItemsEditUrl($_address)) ?>" class="action edit"><span><?= $block->escapeHtml(__('Edit Items')) ?></span></a> </strong> <div class="box-content"> <div class="table-wrapper"> - <table class="items data table" id="shipping-table-<?= /* @escapeNotVerified */ $_address->getId() ?>"> - <caption class="table-caption"><?= /* @escapeNotVerified */ __('Items') ?></caption> + <table class="items data table" id="shipping-table-<?= (int) $_address->getId() ?>"> + <caption class="table-caption"><?= $block->escapeHtml(__('Items')) ?></caption> <thead> <tr> - <th class="col item" scope="col"><?= /* @escapeNotVerified */ __('Product Name') ?></th> - <th class="col qty" scope="col"><?= /* @escapeNotVerified */ __('Qty') ?></th> + <th class="col item" scope="col"><?= $block->escapeHtml(__('Product Name')) ?></th> + <th class="col qty" scope="col"><?= $block->escapeHtml(__('Qty')) ?></th> </tr> </thead> <tbody> - <?php foreach ($block->getAddressItems($_address) as $_item): ?> + <?php foreach ($block->getAddressItems($_address) as $_item) : ?> <tr> - <td class="col item" data-th="<?= $block->escapeHtml(__('Product Name')) ?>"><?= $block->getItemHtml($_item->getQuoteItem()) ?></td> - <td class="col qty" data-th="<?= $block->escapeHtml(__('Qty')) ?>"><?= /* @escapeNotVerified */ $_item->getQty() ?></td> + <td class="col item" data-th="<?= $block->escapeHtmlAttr(__('Product Name')) ?>"><?= $block->getItemHtml($_item->getQuoteItem()) ?></td> + <td class="col qty" data-th="<?= $block->escapeHtmlAttr(__('Qty')) ?>"><?= $block->escapeHtml($_item->getQty()) ?></td> </tr> <?php endforeach; ?> </tbody> @@ -114,10 +119,10 @@ <?= $block->getChildHtml('checkout_billing_items') ?> <div class="actions-toolbar"> <div class="primary"> - <button class="action primary continue" type="submit"><span><?= /* @escapeNotVerified */ __('Continue to Billing Information') ?></span></button> + <button class="action primary continue" type="submit"><span><?= $block->escapeHtml(__('Continue to Billing Information')) ?></span></button> </div> <div class="secondary"> - <a href="<?= /* @escapeNotVerified */ $block->getBackUrl() ?>" class="action back"><span><?= /* @escapeNotVerified */ __('Back to Select Addresses') ?></span></a> + <a href="<?= $block->escapeUrl($block->getBackUrl()) ?>" class="action back"><span><?= $block->escapeHtml(__('Back to Select Addresses')) ?></span></a> </div> </div> </form> diff --git a/app/code/Magento/Multishipping/view/frontend/templates/checkout/state.phtml b/app/code/Magento/Multishipping/view/frontend/templates/checkout/state.phtml index bf520d639a58d..4d8b63e605732 100644 --- a/app/code/Magento/Multishipping/view/frontend/templates/checkout/state.phtml +++ b/app/code/Magento/Multishipping/view/frontend/templates/checkout/state.phtml @@ -4,7 +4,7 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile +// phpcs:disable Magento2.Files.LineLength ?> <?php @@ -15,7 +15,9 @@ */ ?> <ol class="block multicheckout progress items" id="checkout-progress-state"> - <?php foreach ($block->getSteps() as $_step): ?> - <li title="<?= /* @escapeNotVerified */ $_step->getLabel() ?>" class="item<?= ($_step->getIsActive()) ? ' active' : '' ?>"><span><?= /* @escapeNotVerified */ $_step->getLabel() ?></span></li> + <?php foreach ($block->getSteps() as $_step) : ?> + <li title="<?= $block->escapeHtmlAttr($_step->getLabel()) ?>" class="item<?= ($_step->getIsActive()) ? ' active' : '' ?>"> + <span><?= $block->escapeHtml($_step->getLabel()) ?></span> + </li> <?php endforeach; ?> </ol> diff --git a/app/code/Magento/Multishipping/view/frontend/templates/js/components.phtml b/app/code/Magento/Multishipping/view/frontend/templates/js/components.phtml index bad5acc209b5f..6cf15f4770150 100644 --- a/app/code/Magento/Multishipping/view/frontend/templates/js/components.phtml +++ b/app/code/Magento/Multishipping/view/frontend/templates/js/components.phtml @@ -4,7 +4,5 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - ?> <?= $block->getChildHtml() ?> diff --git a/app/code/Magento/Multishipping/view/frontend/templates/multishipping/item/default.phtml b/app/code/Magento/Multishipping/view/frontend/templates/multishipping/item/default.phtml index 79e6d02465847..9245aec92ace5 100644 --- a/app/code/Magento/Multishipping/view/frontend/templates/multishipping/item/default.phtml +++ b/app/code/Magento/Multishipping/view/frontend/templates/multishipping/item/default.phtml @@ -4,29 +4,28 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - +// phpcs:disable Magento2.Files.LineLength ?> <div class="product details"> - <strong class="product name"><a href="<?= /* @escapeNotVerified */ $block->getProductUrl() ?>"><?= $block->escapeHtml($block->getProductName()) ?></a></strong> - <?php if ($_options = $block->getOptionList()): ?> + <strong class="product name"><a href="<?= $block->escapeUrl($block->getProductUrl()) ?>"><?= $block->escapeHtml($block->getProductName()) ?></a></strong> + <?php if ($_options = $block->getOptionList()) : ?> <dl class="item options"> <?php foreach ($_options as $_option) : ?> <?php $_formatedOptionValue = $block->getFormatedOptionValue($_option) ?> <dt><?= $block->escapeHtml($_option['label']) ?></dt> - <dd<?php if (isset($_formatedOptionValue['full_view'])): ?> class="tooltip wrapper"<?php endif; ?>> - <?= /* @escapeNotVerified */ $_formatedOptionValue['value'] ?> - <?php if (isset($_formatedOptionValue['full_view'])): ?> + <dd<?= isset($_formatedOptionValue['full_view']) ? ' class="tooltip wrapper"' : '' ?>> + <?= $block->escapeHtml($_formatedOptionValue['value']) ?> + <?php if (isset($_formatedOptionValue['full_view'])) : ?> <dl class="item options tooltip content"> <dt><?= $block->escapeHtml($_option['label']) ?></dt> - <dd><?= /* @escapeNotVerified */ $_formatedOptionValue['full_view'] ?></dd> + <dd><?= $block->escapeHtml($_formatedOptionValue['full_view']) ?></dd> </dl> <?php endif; ?> </dd> <?php endforeach; ?> </dl> <?php endif; ?> - <?php if ($addtInfoBlock = $block->getProductAdditionalInformationBlock()): ?> + <?php if ($addtInfoBlock = $block->getProductAdditionalInformationBlock()) : ?> <?= $addtInfoBlock->setItem($block->getItem())->toHtml() ?> <?php endif; ?> </div> diff --git a/app/code/Magento/Newsletter/Block/Adminhtml/Problem.php b/app/code/Magento/Newsletter/Block/Adminhtml/Problem.php index 61a17d7ad5e51..0c66e46a850ee 100644 --- a/app/code/Magento/Newsletter/Block/Adminhtml/Problem.php +++ b/app/code/Magento/Newsletter/Block/Adminhtml/Problem.php @@ -41,7 +41,7 @@ public function __construct( } /** - * @return void + * @inheritDoc * @SuppressWarnings(PHPMD.UnusedLocalVariable) */ protected function _construct() @@ -83,7 +83,7 @@ protected function _prepareLayout() /** * Get the html element for unsubscribe button * - * @return $string + * @return string */ public function getUnsubscribeButtonHtml() { @@ -93,7 +93,7 @@ public function getUnsubscribeButtonHtml() /** * Get the html element for delete button * - * @return $string + * @return string */ public function getDeleteButtonHtml() { diff --git a/app/code/Magento/Newsletter/Controller/Adminhtml/Subscriber/MassDelete.php b/app/code/Magento/Newsletter/Controller/Adminhtml/Subscriber/MassDelete.php index 7f02e4ea13445..cdef44b2da757 100644 --- a/app/code/Magento/Newsletter/Controller/Adminhtml/Subscriber/MassDelete.php +++ b/app/code/Magento/Newsletter/Controller/Adminhtml/Subscriber/MassDelete.php @@ -6,22 +6,27 @@ */ namespace Magento\Newsletter\Controller\Adminhtml\Subscriber; -use Magento\Newsletter\Controller\Adminhtml\Subscriber; use Magento\Backend\App\Action\Context; +use Magento\Framework\App\Action\HttpPostActionInterface; +use Magento\Framework\App\ObjectManager; use Magento\Framework\App\Response\Http\FileFactory; +use Magento\Newsletter\Controller\Adminhtml\Subscriber; use Magento\Newsletter\Model\SubscriberFactory; -use Magento\Framework\App\ObjectManager; -class MassDelete extends Subscriber +/** + * Subscriber mass delete controller. + */ +class MassDelete extends Subscriber implements HttpPostActionInterface { /** * @var SubscriberFactory */ private $subscriberFactory; - + /** * @param Context $context * @param FileFactory $fileFactory + * @param SubscriberFactory|null $subscriberFactory */ public function __construct( Context $context, @@ -31,7 +36,7 @@ public function __construct( $this->subscriberFactory = $subscriberFactory ?: ObjectManager::getInstance()->get(SubscriberFactory::class); parent::__construct($context, $fileFactory); } - + /** * Delete one or more subscribers action * diff --git a/app/code/Magento/Newsletter/Controller/Manage/Save.php b/app/code/Magento/Newsletter/Controller/Manage/Save.php index 698c2d19aae68..d7d511e2d1906 100644 --- a/app/code/Magento/Newsletter/Controller/Manage/Save.php +++ b/app/code/Magento/Newsletter/Controller/Manage/Save.php @@ -3,11 +3,12 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Newsletter\Controller\Manage; use Magento\Customer\Api\CustomerRepositoryInterface as CustomerRepository; -use Magento\Customer\Model\Customer; +use Magento\Customer\Api\Data\CustomerInterface; use Magento\Framework\App\Action\HttpGetActionInterface; use Magento\Framework\App\Action\HttpPostActionInterface; use Magento\Newsletter\Model\Subscriber; @@ -65,7 +66,7 @@ public function __construct( /** * Save newsletter subscription preference action * - * @return void|null + * @return \Magento\Framework\App\ResponseInterface */ public function execute() { @@ -110,16 +111,16 @@ public function execute() $this->messageManager->addError(__('Something went wrong while saving your subscription.')); } } - $this->_redirect('customer/account/'); + return $this->_redirect('customer/account/'); } /** * Set ignore_validation_flag to skip unnecessary address and customer validation * - * @param Customer $customer + * @param CustomerInterface $customer * @return void */ - private function setIgnoreValidationFlag($customer) + private function setIgnoreValidationFlag(CustomerInterface $customer): void { $customer->setData('ignore_validation_flag', true); } diff --git a/app/code/Magento/Newsletter/Controller/Subscriber/Confirm.php b/app/code/Magento/Newsletter/Controller/Subscriber/Confirm.php index c27717f4c7793..6f566761b2f87 100644 --- a/app/code/Magento/Newsletter/Controller/Subscriber/Confirm.php +++ b/app/code/Magento/Newsletter/Controller/Subscriber/Confirm.php @@ -4,6 +4,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Newsletter\Controller\Subscriber; diff --git a/app/code/Magento/Newsletter/Model/Subscriber.php b/app/code/Magento/Newsletter/Model/Subscriber.php index e7e5d5f202811..117783495406a 100644 --- a/app/code/Magento/Newsletter/Model/Subscriber.php +++ b/app/code/Magento/Newsletter/Model/Subscriber.php @@ -7,11 +7,11 @@ use Magento\Customer\Api\AccountManagementInterface; use Magento\Customer\Api\CustomerRepositoryInterface; -use Magento\Framework\Exception\MailException; -use Magento\Framework\Exception\NoSuchEntityException; use Magento\Customer\Api\Data\CustomerInterfaceFactory; use Magento\Framework\Api\DataObjectHelper; use Magento\Framework\App\ObjectManager; +use Magento\Framework\Exception\MailException; +use Magento\Framework\Exception\NoSuchEntityException; /** * Subscriber model @@ -31,6 +31,7 @@ * @method int getSubscriberId() * @method Subscriber setSubscriberId(int $value) * + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @@ -402,6 +403,7 @@ public function loadByCustomerId($customerId) $this->setSubscriberConfirmCode($this->randomSequence()); $this->save(); } + // phpcs:ignore Magento2.CodeAnalysis.EmptyBlock } catch (NoSuchEntityException $e) { } return $this; @@ -493,7 +495,9 @@ public function subscribe($email) $this->sendConfirmationSuccessEmail(); } return $this->getStatus(); + // phpcs:ignore Magento2.Exceptions.ThrowCatch } catch (\Exception $e) { + // phpcs:ignore Magento2.Exceptions.DirectThrow throw new \Exception($e->getMessage()); } } @@ -559,7 +563,7 @@ public function updateSubscription($customerId) * * @param int $customerId * @param bool $subscribe indicates whether the customer should be subscribed or unsubscribed - * @return $this + * @return $this * * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) @@ -686,7 +690,7 @@ public function confirm($code) * Mark receiving subscriber of queue newsletter * * @param \Magento\Newsletter\Model\Queue $queue - * @return boolean + * @return Subscriber */ public function received(\Magento\Newsletter\Model\Queue $queue) { diff --git a/app/code/Magento/Newsletter/Test/Mftf/Test/AdminAddImageToWYSIWYGNewsletterTest.xml b/app/code/Magento/Newsletter/Test/Mftf/Test/AdminAddImageToWYSIWYGNewsletterTest.xml index f69f94dbd79e3..a343a20a6d57c 100644 --- a/app/code/Magento/Newsletter/Test/Mftf/Test/AdminAddImageToWYSIWYGNewsletterTest.xml +++ b/app/code/Magento/Newsletter/Test/Mftf/Test/AdminAddImageToWYSIWYGNewsletterTest.xml @@ -16,6 +16,9 @@ <description value="Admin should be able to add image to WYSIWYG content Newsletter"/> <severity value="CRITICAL"/> <testCaseId value="MAGETWO-84377"/> + <skip> + <issueId value="MC-17233"/> + </skip> </annotations> <before> <actionGroup ref="LoginActionGroup" stepKey="login"/> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Test/AdminAddVariableToWYSIWYGNewsletterTest.xml b/app/code/Magento/Newsletter/Test/Mftf/Test/AdminAddVariableToWYSIWYGNewsletterTest.xml index e3d73fb57333e..841d202d518ab 100644 --- a/app/code/Magento/Newsletter/Test/Mftf/Test/AdminAddVariableToWYSIWYGNewsletterTest.xml +++ b/app/code/Magento/Newsletter/Test/Mftf/Test/AdminAddVariableToWYSIWYGNewsletterTest.xml @@ -49,9 +49,9 @@ <waitForLoadingMaskToDisappear stepKey="waitForPageLoad3"/> <!--see Insert Variable button disabled--> <see selector="{{VariableSection.InsertVariableBtnDisabled}}" userInput="Insert Variable" stepKey="seeInsertWidgetDisabled" /> - <!--see Cancel button enabed--> + <!--see Cancel button enabled--> <see selector="{{VariableSection.CancelBtnEnabled}}" userInput="Cancel" stepKey="seeCancelBtnEnabled" /> - <!--see 4 colums--> + <!--see 4 columns--> <see selector="{{VariableSection.ColName('Select')}}" userInput="Select" stepKey="selectCol" /> <see selector="{{VariableSection.ColName('Variable Name')}}" userInput="Variable Name" stepKey="variableCol" /> <see selector="{{VariableSection.ColName('Type')}}" userInput="Type" stepKey="typeCol" /> diff --git a/app/code/Magento/Newsletter/Test/Mftf/Test/AdminAddWidgetToWYSIWYGNewsletterTest.xml b/app/code/Magento/Newsletter/Test/Mftf/Test/AdminAddWidgetToWYSIWYGNewsletterTest.xml index 50a6b74a67233..016f07b8a2f44 100644 --- a/app/code/Magento/Newsletter/Test/Mftf/Test/AdminAddWidgetToWYSIWYGNewsletterTest.xml +++ b/app/code/Magento/Newsletter/Test/Mftf/Test/AdminAddWidgetToWYSIWYGNewsletterTest.xml @@ -16,6 +16,9 @@ <description value="Admin should be able to add widget to WYSIWYG Editor Newsletter"/> <severity value="CRITICAL"/> <testCaseId value="MAGETWO-84682"/> + <skip> + <issueId value="MC-17140"/> + </skip> </annotations> <before> <actionGroup ref="LoginActionGroup" stepKey="login"/> diff --git a/app/code/Magento/OfflineShipping/Model/Carrier/Tablerate.php b/app/code/Magento/OfflineShipping/Model/Carrier/Tablerate.php index 373d64afc8cc3..46c46243fed5c 100644 --- a/app/code/Magento/OfflineShipping/Model/Carrier/Tablerate.php +++ b/app/code/Magento/OfflineShipping/Model/Carrier/Tablerate.php @@ -278,7 +278,7 @@ private function createShippingMethod($shippingPrice, $cost) /** @var \Magento\Quote\Model\Quote\Address\RateResult\Method $method */ $method = $this->_resultMethodFactory->create(); - $method->setCarrier('tablerate'); + $method->setCarrier($this->getCarrierCode()); $method->setCarrierTitle($this->getConfigData('title')); $method->setMethod('bestway'); diff --git a/app/code/Magento/Payment/Test/Mftf/Data/PaymentConfigData.xml b/app/code/Magento/Payment/Test/Mftf/Data/PaymentConfigData.xml index 706414fa86eb7..510199673da1a 100644 --- a/app/code/Magento/Payment/Test/Mftf/Data/PaymentConfigData.xml +++ b/app/code/Magento/Payment/Test/Mftf/Data/PaymentConfigData.xml @@ -32,16 +32,41 @@ <data key="label">No</data> <data key="value">0</data> </entity> - <entity name="EnablePaymentCashOnDeliveryData"> + <entity name="EnableZeroSubtotalCheckoutConfigData"> + <!--Default Data--> + <data key="path">payment/free/active</data> + <data key="scope_id">0</data> + <data key="label">Yes</data> + <data key="value">1</data> + </entity> + <entity name="DisableZeroSubtotalCheckoutConfigData"> + <data key="path">payment/free/active</data> + <data key="scope_id">0</data> + <data key="label">No</data> + <data key="value">0</data> + </entity> + <entity name="EnableCashOnDeliveryConfigData"> <data key="path">payment/cashondelivery/active</data> <data key="scope_id">0</data> <data key="label">Yes</data> <data key="value">1</data> </entity> - <entity name="DisablePaymentCashOnDeliveryData"> + <entity name="DisableCashOnDeliveryConfigData"> <data key="path">payment/cashondelivery/active</data> <data key="scope_id">0</data> <data key="label">No</data> <data key="value">0</data> </entity> + <entity name="EnablePurchaseOrderConfigData"> + <data key="path">payment/purchaseorder/active</data> + <data key="scope_id">0</data> + <data key="label">Yes</data> + <data key="value">1</data> + </entity> + <entity name="DisablePurchaseOrderConfigData"> + <data key="path">payment/purchaseorder/active</data> + <data key="scope_id">0</data> + <data key="label">No</data> + <data key="value">0</data> + </entity> </entities> \ No newline at end of file diff --git a/app/code/Magento/Payment/view/base/web/js/model/credit-card-validation/credit-card-number-validator.js b/app/code/Magento/Payment/view/base/web/js/model/credit-card-validation/credit-card-number-validator.js index 785b636d5832f..b459b3598a857 100644 --- a/app/code/Magento/Payment/view/base/web/js/model/credit-card-validation/credit-card-number-validator.js +++ b/app/code/Magento/Payment/view/base/web/js/model/credit-card-validation/credit-card-number-validator.js @@ -36,7 +36,7 @@ define([ return resultWrapper(null, false, false); } - value = value.replace(/|\s/g, ''); + value = value.replace(/\s+/g, ''); if (!/^\d*$/.test(value)) { return resultWrapper(null, false, false); diff --git a/app/code/Magento/Payment/view/frontend/web/js/view/payment/iframe.js b/app/code/Magento/Payment/view/frontend/web/js/view/payment/iframe.js index 5eba4fd89d338..1e352e4297131 100644 --- a/app/code/Magento/Payment/view/frontend/web/js/view/payment/iframe.js +++ b/app/code/Magento/Payment/view/frontend/web/js/view/payment/iframe.js @@ -114,8 +114,12 @@ define([ * @override */ placeOrder: function () { - if (this.validateHandler() && additionalValidators.validate()) { + var self = this; + if (this.validateHandler() && + additionalValidators.validate() && + this.isPlaceOrderActionAllowed() === true + ) { fullScreenLoader.startLoader(); this.isPlaceOrderActionAllowed(false); @@ -127,8 +131,15 @@ define([ method: this.getCode() } ) - ).done(this.done.bind(this)) - .fail(this.fail.bind(this)); + ).done( + this.done.bind(this) + ).fail( + this.fail.bind(this) + ).always( + function () { + self.isPlaceOrderActionAllowed(true); + } + ); this.initTimeoutHandler(); } @@ -192,7 +203,6 @@ define([ */ fail: function () { fullScreenLoader.stopLoader(); - this.isPlaceOrderActionAllowed(true); return this; }, diff --git a/app/code/Magento/Paypal/Controller/Billing/Agreement/Index.php b/app/code/Magento/Paypal/Controller/Billing/Agreement/Index.php index c086efd54166d..797c7d7a8f4fe 100644 --- a/app/code/Magento/Paypal/Controller/Billing/Agreement/Index.php +++ b/app/code/Magento/Paypal/Controller/Billing/Agreement/Index.php @@ -6,7 +6,12 @@ */ namespace Magento\Paypal\Controller\Billing\Agreement; -class Index extends \Magento\Paypal\Controller\Billing\Agreement +use Magento\Framework\App\Action\HttpGetActionInterface as HttpGetActionInterface; + +/** + * Index Controller. + */ +class Index extends \Magento\Paypal\Controller\Billing\Agreement implements HttpGetActionInterface { /** * View billing agreements @@ -16,7 +21,7 @@ class Index extends \Magento\Paypal\Controller\Billing\Agreement public function execute() { $this->_view->loadLayout(); - $this->_view->getPage()->getConfig()->getTitle()->prepend(__('Billing Agreements')); + $this->_view->getPage()->getConfig()->getTitle()->set(__('Billing Agreements')); $this->_view->renderLayout(); } } diff --git a/app/code/Magento/Paypal/Model/Cart.php b/app/code/Magento/Paypal/Model/Cart.php index cef17fab8d916..90bc2b5e632de 100644 --- a/app/code/Magento/Paypal/Model/Cart.php +++ b/app/code/Magento/Paypal/Model/Cart.php @@ -9,6 +9,7 @@ /** * PayPal-specific model for shopping cart items and totals + * * The main idea is to accommodate all possible totals into PayPal-compatible 4 totals and line items */ class Cart extends \Magento\Payment\Model\Cart @@ -179,7 +180,7 @@ protected function _applyDiscountTaxCompensationWorkaround( ) { $dataContainer = $salesEntity->getTaxContainer(); $this->addTax((double)$dataContainer->getBaseDiscountTaxCompensationAmount()); - $this->addTax((double)$dataContainer->getBaseShippingDiscountTaxCompensationAmount()); + $this->addTax((double)$dataContainer->getBaseShippingDiscountTaxCompensationAmnt()); } /** diff --git a/app/code/Magento/Paypal/Model/Express.php b/app/code/Magento/Paypal/Model/Express.php index e52a85da3e829..946c0fd4c66ca 100644 --- a/app/code/Magento/Paypal/Model/Express.php +++ b/app/code/Magento/Paypal/Model/Express.php @@ -17,10 +17,12 @@ use Magento\Store\Model\ScopeInterface; /** - * PayPal Express Module + * PayPal Express Module. + * * @method \Magento\Quote\Api\Data\PaymentMethodExtensionInterface getExtensionAttributes() * @SuppressWarnings(PHPMD.TooManyFields) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) */ class Express extends \Magento\Payment\Model\Method\AbstractMethod { @@ -179,6 +181,11 @@ class Express extends \Magento\Payment\Model\Method\AbstractMethod */ protected $transactionBuilder; + /** + * @var string + */ + private static $authorizationExpiredCode = 10601; + /** * @param \Magento\Framework\Model\Context $context * @param \Magento\Framework\Registry $registry @@ -269,6 +276,7 @@ protected function _setApiProcessableErrors() ApiProcessableException::API_MAXIMUM_AMOUNT_FILTER_DECLINE, ApiProcessableException::API_OTHER_FILTER_DECLINE, ApiProcessableException::API_ADDRESS_MATCH_FAIL, + self::$authorizationExpiredCode ] ); } @@ -540,7 +548,17 @@ public function refund(\Magento\Payment\Model\InfoInterface $payment, $amount) */ public function cancel(\Magento\Payment\Model\InfoInterface $payment) { - $this->void($payment); + try { + $this->void($payment); + } catch (ApiProcessableException $e) { + if ((int)$e->getCode() === self::$authorizationExpiredCode) { + $payment->setTransactionId(null); + $payment->setIsTransactionClosed(true); + $payment->setShouldCloseParentTransaction(true); + } else { + throw $e; + } + } return $this; } diff --git a/app/code/Magento/Paypal/Model/Payflow/Transparent.php b/app/code/Magento/Paypal/Model/Payflow/Transparent.php index c308731c69527..f90c8f3792428 100644 --- a/app/code/Magento/Paypal/Model/Payflow/Transparent.php +++ b/app/code/Magento/Paypal/Model/Payflow/Transparent.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Paypal\Model\Payflow; @@ -59,6 +60,11 @@ class Transparent extends Payflowpro implements TransparentInterface */ private $paymentExtensionFactory; + /** + * @var \Magento\Paypal\Model\CartFactory + */ + private $payPalCartFactory; + /** * @param \Magento\Framework\Model\Context $context * @param \Magento\Framework\Registry $registry @@ -76,6 +82,7 @@ class Transparent extends Payflowpro implements TransparentInterface * @param ResponseValidator $responseValidator * @param PaymentTokenInterfaceFactory $paymentTokenFactory * @param OrderPaymentExtensionInterfaceFactory $paymentExtensionFactory + * @param \Magento\Paypal\Model\CartFactory $payPalCartFactory * @param \Magento\Framework\Model\ResourceModel\AbstractResource $resource * @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection * @param array $data @@ -98,6 +105,7 @@ public function __construct( ResponseValidator $responseValidator, PaymentTokenInterfaceFactory $paymentTokenFactory, OrderPaymentExtensionInterfaceFactory $paymentExtensionFactory, + \Magento\Paypal\Model\CartFactory $payPalCartFactory, \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null, \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null, array $data = [] @@ -123,9 +131,12 @@ public function __construct( $this->responseValidator = $responseValidator; $this->paymentTokenFactory = $paymentTokenFactory; $this->paymentExtensionFactory = $paymentExtensionFactory; + $this->payPalCartFactory = $payPalCartFactory; } /** + * Gets response validator instance. + * * @return ResponseValidator */ public function getResponceValidator() @@ -162,13 +173,19 @@ public function authorize(InfoInterface $payment, $amount) $this->addRequestOrderInfo($request, $order); $request = $this->fillCustomerContacts($order, $request); + /** @var \Magento\Paypal\Model\Cart $payPalCart */ + $payPalCart = $this->payPalCartFactory->create(['salesModel' => $order]); + $payPalCart->getAmounts(); + $token = $payment->getAdditionalInformation(self::PNREF); $request->setData('trxtype', self::TRXTYPE_AUTH_ONLY); $request->setData('origid', $token); $request->setData('amt', $this->formatPrice($amount)); $request->setData('currency', $order->getBaseCurrencyCode()); - $request->setData('taxamt', $this->formatPrice($order->getBaseTaxAmount())); - $request->setData('freightamt', $this->formatPrice($order->getBaseShippingAmount())); + $request->setData('itemamt', $this->formatPrice($payPalCart->getSubtotal())); + $request->setData('taxamt', $this->formatPrice($payPalCart->getTax())); + $request->setData('freightamt', $this->formatPrice($payPalCart->getShipping())); + $request->setData('discount', $this->formatPrice($payPalCart->getDiscount())); $response = $this->postRequest($request, $this->getConfig()); $this->processErrors($response); @@ -178,6 +195,7 @@ public function authorize(InfoInterface $payment, $amount) } catch (LocalizedException $exception) { $payment->setParentTransactionId($response->getData(self::PNREF)); $this->void($payment); + // phpcs:ignore Magento2.Exceptions.ThrowCatch throw new LocalizedException(__("The payment couldn't be processed at this time. Please try again later.")); } @@ -200,10 +218,12 @@ public function getConfigInterface() } /** + * Creates vault payment token. + * * @param Payment $payment * @param string $token - * @throws LocalizedException * @return void + * @throws \Exception */ protected function createPaymentToken(Payment $payment, $token) { @@ -222,8 +242,11 @@ protected function createPaymentToken(Payment $payment, $token) } /** + * Generates CC expiration date by year and month provided in payment. + * * @param Payment $payment * @return string + * @throws \Exception */ private function getExpirationDate(Payment $payment) { @@ -242,6 +265,8 @@ private function getExpirationDate(Payment $payment) } /** + * Returns payment extension attributes instance. + * * @param Payment $payment * @return \Magento\Sales\Api\Data\OrderPaymentExtensionInterface */ diff --git a/app/code/Magento/Paypal/Model/Pro.php b/app/code/Magento/Paypal/Model/Pro.php index 5e9159bd2c243..4e0d45ccf3be1 100644 --- a/app/code/Magento/Paypal/Model/Pro.php +++ b/app/code/Magento/Paypal/Model/Pro.php @@ -8,10 +8,10 @@ use Magento\Paypal\Model\Api\AbstractApi; use Magento\Sales\Api\TransactionRepositoryInterface; -use Magento\Paypal\Model\Info; /** - * PayPal Website Payments Pro implementation for payment method instances + * PayPal Website Payments Pro implementation for payment method instances. + * * This model was created because right now PayPal Direct and PayPal Express payment methods cannot have same abstract */ class Pro @@ -147,7 +147,8 @@ public function getConfig() } /** - * API instance getter + * API instance getter. + * * Sets current store id to current config instance and passes it to API * * @return \Magento\Paypal\Model\Api\Nvp @@ -229,19 +230,22 @@ public function importPaymentInfo(\Magento\Framework\DataObject $from, \Magento\ public function void(\Magento\Framework\DataObject $payment) { $authTransactionId = $this->_getParentTransactionId($payment); - if ($authTransactionId) { - $api = $this->getApi(); - $api->setPayment($payment)->setAuthorizationId($authTransactionId)->callDoVoid(); - $this->importPaymentInfo($api, $payment); - } else { + if (empty($authTransactionId)) { throw new \Magento\Framework\Exception\LocalizedException( __('You need an authorization transaction to void.') ); } + + $api = $this->getApi(); + $api->setPayment($payment); + $api->setAuthorizationId($authTransactionId); + $api->callDoVoid(); + $this->importPaymentInfo($api, $payment); } /** - * Attempt to capture payment + * Attempt to capture payment. + * * Will return false if the payment is not supposed to be captured * * @param \Magento\Framework\DataObject $payment diff --git a/app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsConflictResolutionForPayPal.xml b/app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsConflictResolutionForPayPal.xml index b485fcb2a8f9a..a6e741f0151e0 100644 --- a/app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsConflictResolutionForPayPal.xml +++ b/app/code/Magento/Paypal/Test/Mftf/Test/AdminConfigPaymentsConflictResolutionForPayPal.xml @@ -14,8 +14,8 @@ <stories value="Payment methods"/> <title value="Conflict resolution for PayPal in United Kingdom"/> <description value="A popup should show when enabling different paypal solutions when one is already enabled for merchant country United Kingdom"/> - <severity value="Major"/> - <testCaseId value="MC-13146"/> + <severity value="MAJOR"/> + <testCaseId value="MC-16679"/> <group value="paypal"/> </annotations> <before> @@ -79,7 +79,7 @@ <stories value="Payment methods"/> <title value="Conflict resolution for PayPal in Japan"/> <description value="A popup should show when enabling different paypal solutions when one is already enabled for merchant country Japan"/> - <severity value="Major"/> + <severity value="MAJOR"/> <testCaseId value="MC-13146"/> <group value="paypal"/> </annotations> @@ -119,8 +119,8 @@ <stories value="Payment methods"/> <title value="Conflict resolution for PayPal in France"/> <description value="A popup should show when enabling different paypal solutions when one is already enabled for merchant country France"/> - <severity value="Major"/> - <testCaseId value="MC-13146"/> + <severity value="MAJOR"/> + <testCaseId value="MC-16675"/> <group value="paypal"/> </annotations> <selectOption selector="{{PaymentsConfigSection.merchantCountry}}" userInput="France" stepKey="setMerchantCountry"/> @@ -159,8 +159,8 @@ <stories value="Payment methods"/> <title value="Conflict resolution for PayPal in Hong Kong"/> <description value="A popup should show when enabling different paypal solutions when one is already enabled for merchant country Hong Kong"/> - <severity value="Major"/> - <testCaseId value="MC-13146"/> + <severity value="MAJOR"/> + <testCaseId value="MC-16676"/> <group value="paypal"/> </annotations> <selectOption selector="{{PaymentsConfigSection.merchantCountry}}" userInput="Hong Kong SAR China" stepKey="setMerchantCountry"/> @@ -199,8 +199,8 @@ <stories value="Payment methods"/> <title value="Conflict resolution for PayPal in Italy"/> <description value="A popup should show when enabling different paypal solutions when one is already enabled for merchant country Italy"/> - <severity value="Major"/> - <testCaseId value="MC-13146"/> + <severity value="MAJOR"/> + <testCaseId value="MC-16677"/> <group value="paypal"/> </annotations> <selectOption selector="{{PaymentsConfigSection.merchantCountry}}" userInput="Italy" stepKey="setMerchantCountry"/> @@ -239,8 +239,8 @@ <stories value="Payment methods"/> <title value="Conflict resolution for PayPal in Spain"/> <description value="A popup should show when enabling different paypal solutions when one is already enabled for merchant country Spain"/> - <severity value="Major"/> - <testCaseId value="MC-13146"/> + <severity value="MAJOR"/> + <testCaseId value="MC-16678"/> <group value="paypal"/> </annotations> <selectOption selector="{{PaymentsConfigSection.merchantCountry}}" userInput="Spain" stepKey="setMerchantCountry"/> diff --git a/app/code/Magento/Paypal/Test/Mftf/Test/PayPalSmartButtonInCheckoutPage.xml b/app/code/Magento/Paypal/Test/Mftf/Test/PayPalSmartButtonInCheckoutPage.xml index 1858ee130a347..079b46dc1b0cb 100644 --- a/app/code/Magento/Paypal/Test/Mftf/Test/PayPalSmartButtonInCheckoutPage.xml +++ b/app/code/Magento/Paypal/Test/Mftf/Test/PayPalSmartButtonInCheckoutPage.xml @@ -104,7 +104,7 @@ <executeJS function="jQuery('.zoid-component-frame.zoid-visible').attr('id', 'myIframe')" stepKey="clickOrderLink"/> <!--switch to iframe of PayPal group button--> <comment userInput="switch to iframe of PayPal group button" stepKey="commentSwitchToIframe"/> - <switchToIframe userInput="myIframe" stepKey="clickPrintOrderLink"/> + <switchToIFrame userInput="myIframe" stepKey="clickPrintOrderLink"/> <waitForElementVisible selector="{{CheckoutPaymentSection.PayPalBtn}}" stepKey="waitForPayPalBtn"/> <seeElement selector="{{PayPalButtonOnStorefront.label(PayPalLabel.credit)}}{{PayPalButtonOnStorefront.size(PayPalSize.medium)}}" stepKey="seeButtonInMediumSize"/> <seeElement selector="{{PayPalButtonOnStorefront.label(PayPalLabel.credit)}}{{PayPalButtonOnStorefront.shape(PayPalShape.pill)}}" stepKey="seeButtonInPillShape"/> diff --git a/app/code/Magento/Paypal/Test/Unit/Model/AbstractConfigTest.php b/app/code/Magento/Paypal/Test/Unit/Model/AbstractConfigTest.php index 6bb2173e06f8d..7c528e5718c3b 100644 --- a/app/code/Magento/Paypal/Test/Unit/Model/AbstractConfigTest.php +++ b/app/code/Magento/Paypal/Test/Unit/Model/AbstractConfigTest.php @@ -109,8 +109,8 @@ public function testGetValue($key, $method, $returnMap, $expectedValue) /** * - * @case #1 This conf parameters must return AbstractConfig::PAYMENT_ACTION_SALE (isWppApiAvailabe == false) - * @case #2 This conf parameters must return configValue (isWppApiAvailabe == true) + * @case #1 This conf parameters must return AbstractConfig::PAYMENT_ACTION_SALE (isWppApiAvailable == false) + * @case #2 This conf parameters must return configValue (isWppApiAvailable == true) * @case #3 This conf parameters must return configValue ($key != 'payment_action') * @case #4 This conf parameters must return configValue (configValue == 'Sale') * @case #5 This conf parameters must return configValue (shouldUseUnilateralPayments == false) diff --git a/app/code/Magento/Paypal/Test/Unit/Model/CartTest.php b/app/code/Magento/Paypal/Test/Unit/Model/CartTest.php index 28837727533d2..88e7ee152b5de 100644 --- a/app/code/Magento/Paypal/Test/Unit/Model/CartTest.php +++ b/app/code/Magento/Paypal/Test/Unit/Model/CartTest.php @@ -7,6 +7,9 @@ use Magento\Paypal\Model\Cart; +/** + * @see \Magento\Paypal\Model\Cart + */ class CartTest extends \PHPUnit\Framework\TestCase { /** @@ -70,7 +73,7 @@ protected function setUp() public function testInvalidGetAllItems($items) { $taxContainer = new \Magento\Framework\DataObject( - ['base_discount_tax_compensation_amount' => 0.2, 'base_shipping_discount_tax_compensation_amount' => 0.1] + ['base_discount_tax_compensation_amount' => 0.2, 'base_shipping_discount_tax_compensation_amnt' => 0.1] ); $this->_salesModel->expects($this->once())->method('getTaxContainer')->will($this->returnValue($taxContainer)); $this->_salesModel->expects($this->once())->method('getAllItems')->will($this->returnValue($items)); @@ -146,7 +149,7 @@ public function testInvalidTotalsGetAllItems($values, $transferDiscount) $this->assertEquals( $values['base_tax_amount'] + $values['base_discount_tax_compensation_amount'] + - $values['base_shipping_discount_tax_compensation_amount'], + $values['base_shipping_discount_tax_compensation_amnt'], $this->_model->getTax() ); $this->assertEquals($values['base_shipping_amount'], $this->_model->getShipping()); @@ -162,7 +165,7 @@ public function invalidTotalsGetAllItemsDataProvider() [ [ 'base_discount_tax_compensation_amount' => 0, - 'base_shipping_discount_tax_compensation_amount' => 0, + 'base_shipping_discount_tax_compensation_amnt' => 0, 'base_subtotal' => 0, 'base_tax_amount' => 0, 'base_shipping_amount' => 0, @@ -174,7 +177,7 @@ public function invalidTotalsGetAllItemsDataProvider() [ [ 'base_discount_tax_compensation_amount' => 1, - 'base_shipping_discount_tax_compensation_amount' => 2, + 'base_shipping_discount_tax_compensation_amnt' => 2, 'base_subtotal' => 3, 'base_tax_amount' => 4, 'base_shipping_amount' => 5, @@ -255,8 +258,8 @@ protected function _prepareInvalidModelData($values, $transferDiscount) [ 'base_discount_tax_compensation_amount' => $values['base_discount_tax_compensation_amount'], - 'base_shipping_discount_tax_compensation_amount' => - $values['base_shipping_discount_tax_compensation_amount'], + 'base_shipping_discount_tax_compensation_amnt' => + $values['base_shipping_discount_tax_compensation_amnt'], ] ); $expectedSubtotal = $values['base_subtotal']; diff --git a/app/code/Magento/Paypal/Test/Unit/Model/ExpressTest.php b/app/code/Magento/Paypal/Test/Unit/Model/ExpressTest.php index abedc573558f1..3b012220106e3 100644 --- a/app/code/Magento/Paypal/Test/Unit/Model/ExpressTest.php +++ b/app/code/Magento/Paypal/Test/Unit/Model/ExpressTest.php @@ -12,6 +12,7 @@ use Magento\Payment\Model\InfoInterface; use Magento\Payment\Observer\AbstractDataAssignObserver; use Magento\Paypal\Model\Api\Nvp; +use Magento\Paypal\Model\Api\ProcessableException; use Magento\Paypal\Model\Api\ProcessableException as ApiProcessableException; use Magento\Paypal\Model\Express; use Magento\Paypal\Model\Pro; @@ -19,7 +20,7 @@ use Magento\Sales\Model\Order; use Magento\Sales\Model\Order\Payment; use Magento\Sales\Model\Order\Payment\Transaction\BuilderInterface; -use \PHPUnit_Framework_MockObject_MockObject as MockObject; +use PHPUnit_Framework_MockObject_MockObject as MockObject; /** * Class ExpressTest @@ -27,10 +28,15 @@ */ class ExpressTest extends \PHPUnit\Framework\TestCase { + /** + * @var string + */ + private static $authorizationExpiredCode = 10601; + /** * @var array */ - protected $errorCodes = [ + private $errorCodes = [ ApiProcessableException::API_INTERNAL_ERROR, ApiProcessableException::API_UNABLE_PROCESS_PAYMENT_ERROR_CODE, ApiProcessableException::API_DO_EXPRESS_CHECKOUT_FAIL, @@ -40,7 +46,7 @@ class ExpressTest extends \PHPUnit\Framework\TestCase ApiProcessableException::API_COUNTRY_FILTER_DECLINE, ApiProcessableException::API_MAXIMUM_AMOUNT_FILTER_DECLINE, ApiProcessableException::API_OTHER_FILTER_DECLINE, - ApiProcessableException::API_ADDRESS_MATCH_FAIL + ApiProcessableException::API_ADDRESS_MATCH_FAIL, ]; /** @@ -80,6 +86,7 @@ class ExpressTest extends \PHPUnit\Framework\TestCase protected function setUp() { + $this->errorCodes[] = self::$authorizationExpiredCode; $this->checkoutSession = $this->createPartialMock( Session::class, ['getPaypalTransactionData', 'setPaypalTransactionData'] @@ -104,16 +111,20 @@ protected function setUp() ); $this->pro = $this->createPartialMock( Pro::class, - ['setMethod', 'getApi', 'importPaymentInfo', 'resetApi'] + ['setMethod', 'getApi', 'importPaymentInfo', 'resetApi', 'void'] ); $this->eventManager = $this->getMockBuilder(ManagerInterface::class) ->setMethods(['dispatch']) ->getMockForAbstractClass(); - $this->pro->expects($this->any())->method('getApi')->will($this->returnValue($this->nvp)); + $this->pro->method('getApi') + ->willReturn($this->nvp); $this->helper = new ObjectManager($this); } + /** + * Tests setting the list of processable errors. + */ public function testSetApiProcessableErrors() { $this->nvp->expects($this->once())->method('setProcessableErrors')->with($this->errorCodes); @@ -128,6 +139,32 @@ public function testSetApiProcessableErrors() ); } + /** + * Tests canceling order payment when expired authorization generates exception on a client. + */ + public function testCancelWithExpiredAuthorizationTransaction() + { + $this->pro->method('void') + ->willThrowException( + new ProcessableException(__('PayPal gateway has rejected request.'), null, 10601) + ); + + $this->model = $this->helper->getObject(Express::class, ['data' => [$this->pro]]); + /** @var Payment|MockObject $paymentModel */ + $paymentModel = $this->createMock(Payment::class); + $paymentModel->expects($this->once()) + ->method('setTransactionId') + ->with(null); + $paymentModel->expects($this->once()) + ->method('setIsTransactionClosed') + ->with(true); + $paymentModel->expects($this->once()) + ->method('setShouldCloseParentTransaction') + ->with(true); + + $this->model->cancel($paymentModel); + } + /** * Tests order payment action. * @@ -164,6 +201,11 @@ public function testOrder() static::assertEquals($this->model, $this->model->order($paymentModel, 12.3)); } + /** + * Tests data assigning. + * + * @throws \Magento\Framework\Exception\LocalizedException + */ public function testAssignData() { $transportValue = 'something'; diff --git a/app/code/Magento/Paypal/Test/Unit/Model/Payflow/TransparentTest.php b/app/code/Magento/Paypal/Test/Unit/Model/Payflow/TransparentTest.php index e6a994cba78c3..f6df35ae272b7 100644 --- a/app/code/Magento/Paypal/Test/Unit/Model/Payflow/TransparentTest.php +++ b/app/code/Magento/Paypal/Test/Unit/Model/Payflow/TransparentTest.php @@ -3,446 +3,298 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Paypal\Test\Unit\Model\Payflow; -use Magento\Paypal\Block\Payment\Info; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\DataObject; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use Magento\Payment\Model\Method\ConfigInterface as PaymentConfigInterface; +use Magento\Payment\Model\Method\ConfigInterfaceFactory as PaymentConfigInterfaceFactory; +use Magento\Paypal\Model\Cart as PayPalCart; +use Magento\Paypal\Model\CartFactory as PayPalCartFactory; +use Magento\Paypal\Model\Payflow\Service\Gateway as PayPalPayflowGateway; +use Magento\Paypal\Model\Payflow\Transparent as PayPalPayflowTransparent; use Magento\Paypal\Model\Payflowpro; -use Magento\Paypal\Model\Payflow\Transparent; +use Magento\Sales\Api\Data\OrderPaymentExtensionInterface; +use Magento\Sales\Api\Data\OrderPaymentExtensionInterfaceFactory as PaymentExtensionInterfaceFactory; +use Magento\Sales\Model\Order; +use Magento\Sales\Model\Order\Payment; +use Magento\Store\Api\Data\StoreInterface; +use Magento\Store\Model\ScopeInterface; +use Magento\Store\Model\StoreManagerInterface; use Magento\Vault\Api\Data\PaymentTokenInterface; -use Magento\Vault\Model\CreditCardTokenFactory; +use Magento\Vault\Api\Data\PaymentTokenInterfaceFactory; +use PHPUnit_Framework_MockObject_MockObject as MockObject; /** - * Class TransparentTest - * - * Test class for \Magento\Paypal\Model\Payflow\Transparent * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class TransparentTest extends \PHPUnit\Framework\TestCase { - /** @var Transparent|\PHPUnit_Framework_MockObject_MockObject */ - protected $object; - - /** @var \Magento\Paypal\Model\Payflow\Service\Gateway|\PHPUnit_Framework_MockObject_MockObject */ - protected $gatewayMock; - - /** @var \Magento\Store\Model\StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $storeManagerMock; - - /** @var \Magento\Payment\Model\Method\ConfigInterfaceFactory|\PHPUnit_Framework_MockObject_MockObject */ - protected $configFactoryMock; - - /** @var \Magento\Payment\Model\Method\ConfigInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $configMock; + /** + * @var PayPalPayflowTransparent + */ + private $subject; - /** @var \Magento\Framework\DataObject */ - protected $responseMock; + /** + * @var PaymentConfigInterface|MockObject + */ + private $paymentConfig; - /** @var \Magento\Sales\Model\Order\Payment\Info|\PHPUnit_Framework_MockObject_MockObject */ - protected $paymentMock; + /** + * @var PayPalPayflowGateway|MockObject + */ + private $payPalPayflowGateway; - /** @var \Magento\Framework\DataObject|\PHPUnit_Framework_MockObject_MockObject */ - protected $orderMock; + /** + * @var PaymentTokenInterface|MockObject + */ + private $paymentToken; - /** @var \Magento\Framework\DataObject|\PHPUnit_Framework_MockObject_MockObject */ - protected $addressBillingMock; + /** + * @var PayPalCart|MockObject + */ + private $payPalCart; - /** @var \Magento\Framework\DataObject|\PHPUnit_Framework_MockObject_MockObject */ - protected $addressShippingMock; + /** + * @var ScopeConfigInterface|MockObject + */ + private $scopeConfig; /** - * @var CreditCardTokenFactory|\PHPUnit_Framework_MockObject_MockObject + * @var Payment|MockObject */ - protected $paymentTokenFactory; + private $payment; /** - * @var \PHPUnit_Framework_MockObject_MockObject| - * \Magento\Paypal\Model\Payflow\Service\Response\Validator\ResponseValidator + * @var Order|MockObject */ - protected $responseValidator; + private $order; - protected function setUp() + public function setUp() { - $this->paymentMock = $this->getMockBuilder(\Magento\Sales\Model\Order\Payment::class) - ->setMethods([]) - ->disableOriginalConstructor() - ->getMock(); - - $this->paymentTokenFactory = $this->getMockBuilder(CreditCardTokenFactory::class) - ->disableOriginalConstructor() - ->setMethods(['create']) - ->getMock(); - - $this->gatewayMock = $this->getMockBuilder(\Magento\Paypal\Model\Payflow\Service\Gateway::class) - ->setMethods(['postRequest']) - ->disableOriginalConstructor() - ->getMock(); - $this->storeManagerMock = $this->getMockBuilder(\Magento\Store\Model\StoreManagerInterface::class) - ->setMethods(['getStore', 'getId']) - ->disableOriginalConstructor() - ->getMockForAbstractClass(); - $this->storeManagerMock->method('getStore') - ->willReturnSelf(); - $this->configMock = $this->getMockBuilder(\Magento\Paypal\Model\PayflowConfig::class) - ->disableOriginalConstructor() - ->getMock(); - $this->configFactoryMock = $this->getMockBuilder(\Magento\Payment\Model\Method\ConfigInterfaceFactory::class) - ->setMethods(['create']) - ->disableOriginalConstructor() - ->getMock(); - $this->configFactoryMock->method('create') - ->willReturn($this->configMock); - $this->responseMock = new \Magento\Framework\DataObject(); - $this->responseValidator = $this->getMockBuilder( - \Magento\Paypal\Model\Payflow\Service\Response\Validator\ResponseValidator::class - )->disableOriginalConstructor() - ->setMethods(['validate']) - ->getMock(); + $this->initPayment(); - $objectHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $this->object = $objectHelper->getObject( - \Magento\Paypal\Model\Payflow\Transparent::class, - [ - 'gateway' => $this->gatewayMock, - 'storeManager' => $this->storeManagerMock, - 'configFactory' => $this->configFactoryMock, - 'responseValidator' => $this->responseValidator, - 'paymentTokenFactory' => $this->paymentTokenFactory - ] - ); + $this->subject = (new ObjectManagerHelper($this)) + ->getObject( + PayPalPayflowTransparent::class, + [ + 'configFactory' => $this->getPaymentConfigInterfaceFactory(), + 'paymentExtensionFactory' => $this->getPaymentExtensionInterfaceFactory(), + 'storeManager' => $this->getStoreManager(), + 'gateway' => $this->getPayPalPayflowGateway(), + 'paymentTokenFactory' => $this->getPaymentTokenFactory(), + 'payPalCartFactory' => $this->getPayPalCartFactory(), + 'scopeConfig' => $this->getScopeConfig(), + ] + ); } /** - * Initializing a collection Mock for Authorize method + * Asserts that authorize request to Payflow gateway is valid. * - * @return void + * @dataProvider validAuthorizeRequestDataProvider + * @param DataObject $validAuthorizeRequest + * @throws \Magento\Framework\Exception\LocalizedException + * @throws \Magento\Framework\Exception\State\InvalidTransitionException */ - protected function initializationAuthorizeMock() + public function testValidAuthorizeRequest(DataObject $validAuthorizeRequest) { - $this->orderMock = $this->getMockBuilder(\Magento\Sales\Model\Order::class) - ->setMethods([ - 'getCustomerId', 'getBillingAddress', 'getShippingAddress', 'getCustomerEmail', - 'getId', 'getIncrementId', 'getBaseCurrencyCode' - ]) - ->disableOriginalConstructor() - ->getMock(); - $this->addressBillingMock = $this->getMockBuilder(\Magento\Framework\DataObject::class) - ->setMethods( + $this->scopeConfig->method('getValue') + ->willReturnMap( [ - 'getFirstname', - 'getLastname', - 'getStreet', - 'getCity', - 'getRegionCode', - 'getPostcode', - 'getCountryId' + ['payment/payflowpro/user', ScopeInterface::SCOPE_STORE, null, 'user'], + ['payment/payflowpro/vendor', ScopeInterface::SCOPE_STORE, null, 'vendor'], + ['payment/payflowpro/partner', ScopeInterface::SCOPE_STORE, null, 'partner'], + ['payment/payflowpro/pwd', ScopeInterface::SCOPE_STORE, null, 'pwd'], + ['payment/payflowpro/verbosity', ScopeInterface::SCOPE_STORE, null, 'verbosity'], ] - )->disableOriginalConstructor() - ->getMock(); - $this->addressShippingMock = $this->getMockBuilder(\Magento\Framework\DataObject::class) - ->setMethods( + ); + $this->paymentConfig->method('getBuildNotationCode')->willReturn('BUTTONSOURCE'); + $this->payment->method('getAdditionalInformation') + ->willReturnMap( [ - 'getFirstname', - 'getLastname', - 'getStreet', - 'getCity', - 'getRegionCode', - 'getPostcode', - 'getCountryId' + [Payflowpro::PNREF, 'XXXXXXXXXXXX'], ] - )->disableOriginalConstructor() - ->getMock(); - } + ); + $this->order->method('getIncrementId')->willReturn('000000001'); + $this->order->method('getBaseCurrencyCode')->willReturn('USD'); + $this->payPalCart->method('getSubtotal')->willReturn(5.00); + $this->payPalCart->method('getTax')->willReturn(5.00); + $this->payPalCart->method('getShipping')->willReturn(5.00); + $this->payPalCart->method('getDiscount')->willReturn(5.00); + + $this->payPalPayflowGateway->expects($this->once()) + ->method('postRequest') + ->with($this->equalTo($validAuthorizeRequest)); - /** - * Build data for request for operation Authorize - * - * @return void - */ - protected function buildRequestData() - { - $this->paymentMock->expects($this->once()) - ->method('getOrder') - ->willReturn($this->orderMock); - $this->orderMock->expects($this->once()) - ->method('getBaseCurrencyCode') - ->willReturn('USD'); - $this->orderMock->expects($this->once()) - ->method('getBillingAddress') - ->willReturn($this->addressBillingMock); - $this->orderMock->expects(static::once()) - ->method('getId') - ->willReturn(1); - $this->orderMock->expects(static::once()) - ->method('getIncrementId') - ->willReturn('0000001'); - $this->orderMock->expects($this->once()) - ->method('getShippingAddress') - ->willReturn($this->addressShippingMock); - $this->addressBillingMock->expects($this->once()) - ->method('getFirstname') - ->willReturn('Firstname'); - $this->addressBillingMock->expects($this->once()) - ->method('getLastname') - ->willReturn('Lastname'); - $this->addressBillingMock->expects($this->once()) - ->method('getStreet') - ->willReturn(['street-1', 'street-2']); - $this->addressBillingMock->expects($this->once()) - ->method('getCity') - ->willReturn('City'); - $this->addressBillingMock->expects($this->once()) - ->method('getRegionCode') - ->willReturn('RegionCode'); - $this->addressBillingMock->expects($this->once()) - ->method('getPostcode') - ->willReturn('Postcode'); - $this->addressBillingMock->expects($this->once()) - ->method('getCountryId') - ->willReturn('CountryId'); - $this->orderMock->expects($this->once()) - ->method('getCustomerEmail') - ->willReturn('customer@email.com'); - $this->addressShippingMock->expects($this->once()) - ->method('getFirstname') - ->willReturn('Firstname'); - $this->addressShippingMock->expects($this->once()) - ->method('getLastname') - ->willReturn('Lastname'); - $this->addressShippingMock->expects($this->once()) - ->method('getStreet') - ->willReturn(['street-1', 'street-2']); - $this->addressShippingMock->expects($this->once()) - ->method('getCity') - ->willReturn('City'); - $this->addressShippingMock->expects($this->once()) - ->method('getRegionCode') - ->willReturn('RegionCode'); - $this->addressShippingMock->expects($this->once()) - ->method('getPostcode') - ->willReturn('Postcode'); - $this->addressShippingMock->expects($this->once()) - ->method('getCountryId') - ->willReturn('CountryId'); + $this->subject->authorize($this->payment, 10); } /** - * @return \Magento\Framework\DataObject + * @return array */ - protected function crateVoidResponseMock() + public function validAuthorizeRequestDataProvider(): array { - $voidResponseMock = new \Magento\Framework\DataObject( + return [ [ - 'result_code' => Transparent::RESPONSE_CODE_APPROVED, - 'pnref' => 'test-pnref' + new DataObject( + [ + 'user' => 'user', + 'vendor' => 'vendor', + 'partner' => 'partner', + 'pwd' => 'pwd', + 'verbosity' => 'verbosity', + 'BUTTONSOURCE' => 'BUTTONSOURCE', + 'tender' => 'C', + 'custref' => '000000001', + 'invnum' => '000000001', + 'comment1' => '000000001', + 'trxtype' => 'A', + 'origid' => 'XXXXXXXXXXXX', + 'amt' => '10.00', + 'currency' => 'USD', + 'itemamt' => '5.00', + 'taxamt' => '5.00', + 'freightamt' => '5.00', + 'discount' => '5.00', + ] + ), ] - ); - - $this->responseMock->setData(Transparent::PNREF, 'test-pnref'); - - $this->paymentMock->expects($this->once()) - ->method('setParentTransactionId') - ->with('test-pnref'); - $this->paymentMock->expects($this->once()) - ->method('getParentTransactionId') - ->willReturn('test-pnref'); - $this->paymentMock->expects($this->once()) - ->method('setTransactionId') - ->with('test-pnref') - ->willReturnSelf(); - $this->paymentMock->expects($this->once()) - ->method('setIsTransactionClosed') - ->with(1) - ->willReturnSelf(); - $this->paymentMock->expects($this->once()) - ->method('setShouldCloseParentTransaction') - ->with(1); - - return $voidResponseMock; + ]; } /** - * @expectedException \Exception + * @return PaymentConfigInterfaceFactory|MockObject */ - public function testAuthorizeException() + private function getPaymentConfigInterfaceFactory() { - $this->initializationAuthorizeMock(); - $this->buildRequestData(); + $paymentConfigInterfaceFactory = $this->getMockBuilder(PaymentConfigInterfaceFactory::class) + ->setMethods(['create']) + ->disableOriginalConstructor() + ->getMock(); + $this->paymentConfig = $this->getMockBuilder(PaymentConfigInterface::class) + ->setMethods(['setStoreId', 'setMethodInstance', 'setMethod', 'getBuildNotationCode']) + ->getMockForAbstractClass(); - $this->gatewayMock->expects($this->once()) - ->method('postRequest') - ->willThrowException(new \Exception()); + $paymentConfigInterfaceFactory->method('create')->willReturn($this->paymentConfig); - $this->object->authorize($this->paymentMock, 33); + return $paymentConfigInterfaceFactory; } /** - * @expectedException \Magento\Framework\Exception\LocalizedException - * @expectedExceptionMessage The payment couldn't be processed at this time. Please try again later. + * @return PaymentExtensionInterfaceFactory|MockObject */ - public function testAuthorizeValidationException() + private function getPaymentExtensionInterfaceFactory() { - $this->initializationAuthorizeMock(); - $this->buildRequestData(); - $voidResponseMock = $this->crateVoidResponseMock(); + $paymentExtensionInterfaceFactory = $this->getMockBuilder(PaymentExtensionInterfaceFactory::class) + ->setMethods(['create']) + ->disableOriginalConstructor() + ->getMock(); + $orderPaymentExtension = $this->getMockBuilder(OrderPaymentExtensionInterface::class) + ->setMethods(['setVaultPaymentToken']) + ->disableOriginalConstructor() + ->getMock(); - $this->gatewayMock->expects($this->at(0)) - ->method('postRequest') - ->willReturn($this->responseMock); + $paymentExtensionInterfaceFactory->method('create')->willReturn($orderPaymentExtension); - $this->responseValidator->expects($this->once()) - ->method('validate') - ->with($this->responseMock) - ->willThrowException(new \Magento\Framework\Exception\LocalizedException(__('Error'))); + return $paymentExtensionInterfaceFactory; + } - $this->gatewayMock->expects($this->at(1)) - ->method('postRequest') - ->willReturn($voidResponseMock); + /** + * @return StoreManagerInterface|MockObject + */ + private function getStoreManager() + { + $storeManager = $this->getMockBuilder(StoreManagerInterface::class) + ->getMockForAbstractClass(); + $store = $this->getMockBuilder(StoreInterface::class) + ->getMockForAbstractClass(); + + $storeManager->method('getStore')->willReturn($store); - $this->paymentMock->expects($this->once()) - ->method('getAdditionalInformation') - ->with(Payflowpro::PNREF) - ->willReturn('test-pnref'); + return $storeManager; + } - $this->responseMock->setData('result_code', Payflowpro::RESPONSE_CODE_FRAUDSERVICE_FILTER); + /** + * @return PayPalPayflowGateway|MockObject + */ + private function getPayPalPayflowGateway() + { + $this->payPalPayflowGateway = $this->getMockBuilder(PayPalPayflowGateway::class) + ->disableOriginalConstructor() + ->getMock(); + $this->payPalPayflowGateway->method('postRequest') + ->willReturn(new DataObject()); - $this->object->authorize($this->paymentMock, 33); + return $this->payPalPayflowGateway; } /** - * @param int $resultCode - * @param int $origResult - * - * @expectedException \Magento\Framework\Exception\LocalizedException - * @dataProvider authorizeLocalizedExceptionDataProvider + * @return PaymentTokenInterfaceFactory|MockObject */ - public function testAuthorizeLocalizedException( - $resultCode, - $origResult - ) { - $this->initializationAuthorizeMock(); - $this->buildRequestData(); + private function getPaymentTokenFactory() + { + $paymentTokenInterfaceFactory = $this->getMockBuilder(PaymentTokenInterfaceFactory::class) + ->disableOriginalConstructor() + ->getMock(); + $this->paymentToken = $this->getMockBuilder(PaymentTokenInterface::class) + ->getMockForAbstractClass(); - $this->responseMock->setData('result_code', $resultCode); - $this->responseMock->setData('origresult', $origResult); + $paymentTokenInterfaceFactory->method('create')->willReturn($this->paymentToken); - $this->gatewayMock->expects($this->exactly(1)) - ->method('postRequest') - ->willReturn($this->responseMock); - $this->object->authorize($this->paymentMock, 33); + return $paymentTokenInterfaceFactory; } /** - * @return array + * @return PayPalCartFactory|MockObject */ - public function authorizeLocalizedExceptionDataProvider() + private function getPayPalCartFactory() { - return [ - [ - 'origResult' => Payflowpro::RESPONSE_CODE_APPROVED, - 'resultCode' => Payflowpro::RESPONSE_CODE_DECLINED_BY_FILTER - ], - [ - 'origResult' => Payflowpro::RESPONSE_CODE_DECLINED_BY_FILTER, - 'resultCode' => Payflowpro::RESPONSE_CODE_FRAUDSERVICE_FILTER - ], - [ - 'origResult' => Payflowpro::RESPONSE_CODE_DECLINED, - 'resultCode' => 1111111111 - ], - [ - 'origResult' => 3432432423, - 'resultCode' => 23233432423 - ], - ]; + $payPalCartFactory = $this->getMockBuilder(PayPalCartFactory::class) + ->setMethods(['create']) + ->disableOriginalConstructor() + ->getMock(); + $this->payPalCart = $this->getMockBuilder(PayPalCart::class) + ->disableOriginalConstructor() + ->getMock(); + + $payPalCartFactory->method('create')->willReturn($this->payPalCart); + + return $payPalCartFactory; } /** - * Test method - * with resultCode = RESPONSE_CODE_APPROVED and Origresult != RESPONSE_CODE_FRAUDSERVICE_FILTER + * @return ScopeConfigInterface|MockObject */ - public function testAuthorize() + private function getScopeConfig() { - $this->initializationAuthorizeMock(); - $this->buildRequestData(); - - $paymentTokenMock = $this->createMock(PaymentTokenInterface::class); - $extensionAttributes = $this->getMockBuilder(\Magento\Sales\Api\Data\OrderPaymentExtensionInterface::class) - ->disableOriginalConstructor() - ->setMethods(['setVaultPaymentToken']) + $this->scopeConfig = $this->getMockBuilder(ScopeConfigInterface::class) ->getMockForAbstractClass(); - $ccDetails = [ - 'cc_type' => 'VI', - 'cc_number' => '1111' - ]; - - $this->responseMock->setData('result_code', Payflowpro::RESPONSE_CODE_APPROVED); - $this->responseMock->setData('origresult', 0); - $this->responseMock->setData('pnref', 'test-pnref'); - - $this->gatewayMock->expects($this->once())->method('postRequest')->willReturn($this->responseMock); - - $this->responseValidator->expects($this->once()) - ->method('validate') - ->with($this->responseMock); - - $this->paymentMock->expects($this->once()) - ->method('setTransactionId') - ->with('test-pnref') - ->willReturnSelf(); - $this->paymentMock->expects($this->once()) - ->method('setIsTransactionClosed') - ->with(0); - $this->paymentMock->expects($this->once()) - ->method('getCcExpYear') - ->willReturn('2017'); - $this->paymentMock->expects($this->once()) - ->method('getCcExpMonth') - ->willReturn('12'); - $this->paymentMock->expects(static::any()) - ->method('getAdditionalInformation') - ->willReturnMap( - [ - [Transparent::CC_DETAILS, $ccDetails], - [Transparent::PNREF, 'test-pnref'] - ] - ); - $this->paymentTokenFactory->expects(static::once()) - ->method('create') - ->willReturn($paymentTokenMock); - $paymentTokenMock->expects(static::once()) - ->method('setGatewayToken') - ->with('test-pnref'); - $paymentTokenMock->expects(static::once()) - ->method('setTokenDetails') - ->with(json_encode($ccDetails)); - $paymentTokenMock->expects(static::once()) - ->method('setExpiresAt') - ->with('2018-01-01 00:00:00'); - - $this->paymentMock->expects(static::once()) - ->method('getExtensionAttributes') - ->willReturn($extensionAttributes); - $extensionAttributes->expects(static::once()) - ->method('setVaultPaymentToken') - ->with($paymentTokenMock); - - $this->paymentMock->expects($this->at(8)) - ->method('unsAdditionalInformation') - ->with(Transparent::CC_DETAILS); - $this->paymentMock->expects($this->at(9)) - ->method('unsAdditionalInformation') - ->with(Transparent::PNREF); - - $this->assertSame($this->object, $this->object->authorize($this->paymentMock, 33)); + return $this->scopeConfig; } /** - * @covers \Magento\Paypal\Model\Payflow\Transparent::getInfoBlockType() + * @return Payment|MockObject */ - public function testGetInfoBlockType() + private function initPayment() { - static::assertEquals(Info::class, $this->object->getInfoBlockType()); + $this->payment = $this->getMockBuilder(Payment::class) + ->disableOriginalConstructor() + ->getMock(); + $this->order = $this->getMockBuilder(Order::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->payment->method('getOrder')->willReturn($this->order); + $this->payment->method('setTransactionId')->willReturnSelf(); + $this->payment->method('setIsTransactionClosed')->willReturnSelf(); + $this->payment->method('getCcExpYear')->willReturn('2019'); + $this->payment->method('getCcExpMonth')->willReturn('05'); + + return $this->payment; } } diff --git a/app/code/Magento/Paypal/view/frontend/layout/customer_account.xml b/app/code/Magento/Paypal/view/frontend/layout/customer_account.xml index 5eef537198139..712cccc3c1295 100644 --- a/app/code/Magento/Paypal/view/frontend/layout/customer_account.xml +++ b/app/code/Magento/Paypal/view/frontend/layout/customer_account.xml @@ -5,10 +5,14 @@ * See COPYING.txt for license details. */ --> -<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> +<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> <body> <referenceBlock name="customer_account_navigation"> - <block class="Magento\Customer\Block\Account\SortLinkInterface" name="customer-account-navigation-billing-agreements-link"> + <block class="Magento\Customer\Block\Account\SortLinkInterface" + name="customer-account-navigation-billing-agreements-link" + ifconfig="payment/paypal_express/active" + > <arguments> <argument name="path" xsi:type="string">paypal/billing_agreement</argument> <argument name="label" xsi:type="string" translate="true">Billing Agreements</argument> diff --git a/app/code/Magento/Paypal/view/frontend/templates/express/shortcut_button.phtml b/app/code/Magento/Paypal/view/frontend/templates/express/shortcut_button.phtml index ac0eda99ee939..ef24c5376aac2 100644 --- a/app/code/Magento/Paypal/view/frontend/templates/express/shortcut_button.phtml +++ b/app/code/Magento/Paypal/view/frontend/templates/express/shortcut_button.phtml @@ -10,4 +10,4 @@ * @var \Magento\Paypal\Block\Express\Shortcut $block */ ?> -<div data-mage-init='<?= /* @noEscape */ $block->getJsInitParams() ?>'></div> \ No newline at end of file +<div data-mage-init='<?= /* @noEscape */ $block->getJsInitParams() ?>'></div> diff --git a/app/code/Magento/Paypal/view/frontend/web/js/view/payment/method-renderer/payflowpro-method.js b/app/code/Magento/Paypal/view/frontend/web/js/view/payment/method-renderer/payflowpro-method.js index 786f1a5aa85fd..24d06b7d0f8f2 100644 --- a/app/code/Magento/Paypal/view/frontend/web/js/view/payment/method-renderer/payflowpro-method.js +++ b/app/code/Magento/Paypal/view/frontend/web/js/view/payment/method-renderer/payflowpro-method.js @@ -79,7 +79,10 @@ define([ placeOrder: function () { var self = this; - if (this.validateHandler() && additionalValidators.validate()) { + if (this.validateHandler() && + additionalValidators.validate() && + this.isPlaceOrderActionAllowed() === true + ) { this.isPlaceOrderActionAllowed(false); fullScreenLoader.startLoader(); $.when( diff --git a/app/code/Magento/PaypalGraphQl/Model/PaypalExpressAdditionalDataProvider.php b/app/code/Magento/PaypalGraphQl/Model/PaypalExpressAdditionalDataProvider.php new file mode 100644 index 0000000000000..bdc0bd3ffcb23 --- /dev/null +++ b/app/code/Magento/PaypalGraphQl/Model/PaypalExpressAdditionalDataProvider.php @@ -0,0 +1,47 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\PaypalGraphQl\Model; + +use Magento\Framework\Stdlib\ArrayManager; +use Magento\QuoteGraphQl\Model\Cart\Payment\AdditionalDataProviderInterface; + +/** + * Get payment additional data for Paypal Express payment + */ +class PaypalExpressAdditionalDataProvider implements AdditionalDataProviderInterface +{ + + private const PATH_ADDITIONAL_DATA = 'input/payment_method/additional_data'; + + /** + * @var ArrayManager + */ + private $arrayManager; + + /** + * @param ArrayManager $arrayManager + */ + public function __construct( + ArrayManager $arrayManager + ) { + $this->arrayManager = $arrayManager; + } + + /** + * Returns additional data + * + * @param array $args + * @return array + */ + public function getData(array $args): array + { + $additionalData = $this->arrayManager->get(self::PATH_ADDITIONAL_DATA, $args) ?? []; + + return $additionalData; + } +} diff --git a/app/code/Magento/PaypalGraphQl/Model/Plugin/Resolver/SetPaymentMethodOnCart.php b/app/code/Magento/PaypalGraphQl/Model/Plugin/Resolver/SetPaymentMethodOnCart.php new file mode 100644 index 0000000000000..d42715ab010d9 --- /dev/null +++ b/app/code/Magento/PaypalGraphQl/Model/Plugin/Resolver/SetPaymentMethodOnCart.php @@ -0,0 +1,137 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\PaypalGraphQl\Model\Plugin\Resolver; + +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\GraphQl\Exception\GraphQlInputException; +use Magento\Framework\GraphQl\Query\Resolver\ContextInterface; +use Magento\Framework\GraphQl\Query\ResolverInterface; +use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +use Magento\Paypal\Model\Express\Checkout\Factory as CheckoutFactory; +use Magento\PaypalGraphQl\Model\PaypalExpressAdditionalDataProvider; +use Magento\Framework\Stdlib\ArrayManager; +use Magento\PaypalGraphQl\Model\Provider\Checkout as CheckoutProvider; +use Magento\PaypalGraphQl\Model\Provider\Config as ConfigProvider; + +/** + * Plugin to perform Paypal-specific logic when setting payment method on cart + */ +class SetPaymentMethodOnCart +{ + private const PATH_CODE = 'input/payment_method/code'; + + private $allowedPaymentMethodCodes = []; + + /** + * @var CheckoutFactory + */ + private $checkoutFactory; + + /** + * @var PaypalExpressAdditionalDataProvider + */ + private $paypalExpressAdditionalDataProvider; + + /** + * @var ArrayManager + */ + private $arrayManager; + + /** + * @var CheckoutProvider + */ + private $checkoutProvider; + + /** + * @var ConfigProvider + */ + private $configProvider; + + /** + * @param CheckoutFactory $checkoutFactory + * @param PaypalExpressAdditionalDataProvider $paypalExpressAdditionalDataProvider + * @param ArrayManager $arrayManager + * @param CheckoutProvider $checkoutProvider + * @param ConfigProvider $configProvider + * @param array $allowedPaymentMethodCodes + */ + public function __construct( + CheckoutFactory $checkoutFactory, + PaypalExpressAdditionalDataProvider $paypalExpressAdditionalDataProvider, + ArrayManager $arrayManager, + CheckoutProvider $checkoutProvider, + ConfigProvider $configProvider, + array $allowedPaymentMethodCodes = [] + ) { + $this->checkoutFactory = $checkoutFactory; + $this->paypalExpressAdditionalDataProvider = $paypalExpressAdditionalDataProvider; + $this->arrayManager = $arrayManager; + $this->checkoutProvider = $checkoutProvider; + $this->configProvider = $configProvider; + $this->allowedPaymentMethodCodes = $allowedPaymentMethodCodes; + } + + /** + * Update Paypal payment information on cart + * + * @param ResolverInterface $subject + * @param array $resolvedValue + * @param Field $field + * @param ContextInterface $context + * @param ResolveInfo $info + * @param array|null $value + * @param array|null $args + * @return mixed + * @throws GraphQlInputException + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function afterResolve( + ResolverInterface $subject, + $resolvedValue, + Field $field, + $context, + ResolveInfo $info, + array $value = null, + array $args = null + ) { + $paymentCode = $this->arrayManager->get(self::PATH_CODE, $args) ?? ''; + if (!$this->isAllowedPaymentMethod($paymentCode)) { + return $resolvedValue; + } + + $paypalAdditionalData = $this->paypalExpressAdditionalDataProvider->getData($args); + $payerId = $paypalAdditionalData[$paymentCode]['payer_id'] ?? null; + $token = $paypalAdditionalData[$paymentCode]['token'] ?? null; + $cart = $resolvedValue['cart']['model']; + + if ($payerId && $token) { + $config = $this->configProvider->getConfig($paymentCode); + $checkout = $this->checkoutProvider->getCheckout($config, $cart); + + try { + $checkout->returnFromPaypal($token, $payerId); + } catch (LocalizedException $e) { + throw new GraphQlInputException(__($e->getMessage())); + } + } + + return $resolvedValue; + } + + /** + * Check if payment method code is one that should be handled by this plugin + * + * @param string $paymentCode + * @return bool + */ + private function isAllowedPaymentMethod(string $paymentCode): bool + { + return !empty($paymentCode) && in_array($paymentCode, $this->allowedPaymentMethodCodes); + } +} diff --git a/app/code/Magento/PaypalGraphQl/Model/Provider/Checkout.php b/app/code/Magento/PaypalGraphQl/Model/Provider/Checkout.php new file mode 100644 index 0000000000000..d01d6117319b1 --- /dev/null +++ b/app/code/Magento/PaypalGraphQl/Model/Provider/Checkout.php @@ -0,0 +1,69 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\PaypalGraphQl\Model\Provider; + +use Magento\Paypal\Model\AbstractConfig; +use Magento\Paypal\Model\Express\Checkout as ExpressCheckout; +use Magento\Paypal\Model\Express\Checkout\Factory as CheckoutFactory; +use Magento\Quote\Api\Data\CartInterface; +use Magento\Framework\GraphQl\Exception\GraphQlInputException; + +/** + * Provides correct Checkout instance for payment method + */ +class Checkout +{ + /** + * @var array + */ + private $checkoutTypes; + + /** + * @var CheckoutFactory + */ + private $checkoutFactory; + + /** + * @param CheckoutFactory $checkoutFactory + * @param array $checkoutTypes + */ + public function __construct( + CheckoutFactory $checkoutFactory, + array $checkoutTypes + ) { + $this->checkoutFactory = $checkoutFactory; + $this->checkoutTypes = $checkoutTypes; + } + + /** + * Get Checkout model by payment method code + * + * @param AbstractConfig $config + * @param CartInterface $cart + * @return ExpressCheckout + * @throws GraphQlInputException + */ + public function getCheckout(AbstractConfig $config, CartInterface $cart): ExpressCheckout + { + try { + $checkout = $this->checkoutFactory->create( + $this->checkoutTypes[$config->getMethodCode()], + [ + 'params' => [ + 'quote' => $cart, + 'config' => $config, + ], + ] + ); + } catch (\Exception $e) { + throw new GraphQlInputException(__('The requested Payment Method is not available.')); + } + + return $checkout; + } +} diff --git a/app/code/Magento/PaypalGraphQl/Model/Provider/Config.php b/app/code/Magento/PaypalGraphQl/Model/Provider/Config.php new file mode 100644 index 0000000000000..db31e903e9556 --- /dev/null +++ b/app/code/Magento/PaypalGraphQl/Model/Provider/Config.php @@ -0,0 +1,65 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\PaypalGraphQl\Model\Provider; + +use Magento\Framework\ObjectManagerInterface; +use Magento\Paypal\Model\AbstractConfig; +use Magento\Framework\GraphQl\Exception\GraphQlInputException; + +/** + * Provides correct Config instance for payment method + */ +class Config +{ + /** + * @var array + */ + private $configTypes; + + /** + * @var ObjectManagerInterface + */ + private $objectManager; + + /** + * @param ObjectManagerInterface $objectManager + * @param array $configTypes + */ + public function __construct( + ObjectManagerInterface $objectManager, + array $configTypes + ) { + $this->objectManager = $objectManager; + $this->configTypes = $configTypes; + } + + /** + * Get Config model by payment method code + * + * @param string $paymentMethod + * @return AbstractConfig + * @throws GraphQlInputException + */ + public function getConfig(string $paymentMethod): AbstractConfig + { + //validate code string + if (empty($this->configTypes[$paymentMethod]) || !class_exists($this->configTypes[$paymentMethod])) { + throw new GraphQlInputException(__('The requested Payment Method is not available.')); + } + + /** @var AbstractConfig $config */ + $config = $this->objectManager->get($this->configTypes[$paymentMethod]); + $config->setMethod($paymentMethod); + + if (!$config->isMethodAvailable($paymentMethod)) { + throw new GraphQlInputException(__('The requested Payment Method is not available.')); + } + + return $config; + } +} diff --git a/app/code/Magento/PaypalGraphQl/Model/Resolver/PaypalExpressToken.php b/app/code/Magento/PaypalGraphQl/Model/Resolver/PaypalExpressToken.php new file mode 100644 index 0000000000000..0c6449fc9d230 --- /dev/null +++ b/app/code/Magento/PaypalGraphQl/Model/Resolver/PaypalExpressToken.php @@ -0,0 +1,155 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\PaypalGraphQl\Model\Resolver; + +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Exception\GraphQlInputException; +use Magento\Framework\GraphQl\Query\ResolverInterface; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; +use Magento\Framework\Url\Validator as UrlValidator; +use Magento\Checkout\Helper\Data as CheckoutHelper; +use Magento\PaypalGraphQl\Model\Provider\Checkout as CheckoutProvider; +use Magento\PaypalGraphQl\Model\Provider\Config as ConfigProvider; +use Magento\QuoteGraphQl\Model\Cart\GetCartForUser; + +/** + * Resolver for generating Paypal token + */ +class PaypalExpressToken implements ResolverInterface +{ + /** + * @var GetCartForUser + */ + private $getCartForUser; + + /** + * @var ConfigProvider + */ + private $configProvider; + + /** + * @var CheckoutProvider + */ + private $checkoutProvider; + + /** + * @var UrlValidator + */ + private $urlValidator; + + /** + * @var CheckoutHelper + */ + private $checkoutHelper; + + /** + * @param GetCartForUser $getCartForUser + * @param CheckoutProvider $checkoutProvider + * @param ConfigProvider $configProvider + * @param UrlValidator $urlValidator + * @param CheckoutHelper $checkoutHelper + */ + public function __construct( + GetCartForUser $getCartForUser, + CheckoutProvider $checkoutProvider, + ConfigProvider $configProvider, + UrlValidator $urlValidator, + CheckoutHelper $checkoutHelper + ) { + $this->getCartForUser = $getCartForUser; + $this->checkoutProvider = $checkoutProvider; + $this->configProvider = $configProvider; + $this->urlValidator = $urlValidator; + $this->checkoutHelper = $checkoutHelper; + } + + /** + * @inheritdoc + */ + public function resolve( + Field $field, + $context, + ResolveInfo $info, + array $value = null, + array $args = null + ) { + $cartId = $args['input']['cart_id'] ?? ''; + $paymentCode = $args['input']['code'] ?? ''; + $usePaypalCredit = isset($args['input']['paypal_credit']) ? $args['input']['paypal_credit'] : false; + $usedExpressButton = isset($args['input']['express_button']) ? $args['input']['express_button'] : false; + $customerId = $context->getUserId(); + + $cart = $this->getCartForUser->execute($cartId, $customerId); + $config = $this->configProvider->getConfig($paymentCode); + $checkout = $this->checkoutProvider->getCheckout($config, $cart); + + if ($cart->getIsMultiShipping()) { + $cart->setIsMultiShipping(0); + $cart->removeAllAddresses(); + } + $checkout->setIsBml($usePaypalCredit); + + if ($customerId) { + $checkout->setCustomerWithAddressChange( + $cart->getCustomer(), + $cart->getBillingAddress(), + $cart->getShippingAddress() + ); + } else { + if (!$this->checkoutHelper->isAllowedGuestCheckout($cart)) { + throw new GraphQlInputException(__("Guest checkout is disabled.")); + } + } + + if (!empty($args['input']['urls'])) { + $this->validateUrls($args['input']['urls']); + } + $checkout->prepareGiropayUrls( + $args['input']['urls']['success_url'] ?? '', + $args['input']['urls']['cancel_url'] ?? '', + $args['input']['urls']['pending_url'] ?? '' + ); + + try { + $token = $checkout->start( + $args['input']['urls']['return_url'] ?? '', + $args['input']['urls']['cancel_url'] ?? '', + $usedExpressButton + ); + } catch (LocalizedException $e) { + throw new GraphQlInputException(__($e->getMessage())); + } + + return [ + 'token' => $token, + 'paypal_urls' => [ + 'start' => $checkout->getRedirectUrl(), + 'edit' => $config->getExpressCheckoutEditUrl($token) + ] + ]; + } + + /** + * Validate redirect Urls + * + * @param array $urls + * @return boolean + * @throws GraphQlInputException + */ + private function validateUrls(array $urls): bool + { + foreach ($urls as $url) { + if (!$this->urlValidator->isValid($url)) { + $errorMessage = $this->urlValidator->getMessages()['invalidUrl'] ?? "Invalid Url."; + throw new GraphQlInputException(__($errorMessage)); + } + } + return true; + } +} diff --git a/app/code/Magento/PaypalGraphQl/README.md b/app/code/Magento/PaypalGraphQl/README.md new file mode 100644 index 0000000000000..ad583328284ef --- /dev/null +++ b/app/code/Magento/PaypalGraphQl/README.md @@ -0,0 +1,3 @@ +# PaypalGraphQl + +**PaypalGraphQl** provides resolver information for using Paypal payment methods via GraphQl. diff --git a/app/code/Magento/PaypalGraphQl/composer.json b/app/code/Magento/PaypalGraphQl/composer.json new file mode 100644 index 0000000000000..68d5101e72550 --- /dev/null +++ b/app/code/Magento/PaypalGraphQl/composer.json @@ -0,0 +1,31 @@ +{ + "name": "magento/module-paypal-graph-ql", + "description": "GraphQl support for Paypal", + "config": { + "sort-packages": true + }, + "require": { + "php": "~7.1.3||~7.2.0", + "magento/framework": "*", + "magento/module-quote": "*", + "magento/module-checkout": "*", + "magento/module-paypal": "*", + "magento/module-quote-graph-ql": "*" + }, + "suggest": { + "magento/module-graph-ql": "*" + }, + "type": "magento2-module", + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "autoload": { + "files": [ + "registration.php" + ], + "psr-4": { + "Magento\\PaypalGraphQl\\": "" + } + } +} diff --git a/app/code/Magento/PaypalGraphQl/etc/graphql/di.xml b/app/code/Magento/PaypalGraphQl/etc/graphql/di.xml new file mode 100644 index 0000000000000..fe760ada08bd0 --- /dev/null +++ b/app/code/Magento/PaypalGraphQl/etc/graphql/di.xml @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> + <type name="Magento\QuoteGraphQl\Model\Resolver\SetPaymentMethodOnCart"> + <plugin name="paypal_express_payment_method" type="Magento\PaypalGraphQl\Model\Plugin\Resolver\SetPaymentMethodOnCart"/> + </type> + + <type name="Magento\PaypalGraphQl\Model\Plugin\Resolver\SetPaymentMethodOnCart"> + <arguments> + <argument name="allowedPaymentMethodCodes" xsi:type="array"> + <item name="paypal_express" xsi:type="const">\Magento\Paypal\Model\Config::METHOD_WPP_EXPRESS</item> + <item name="payflow_express" xsi:type="const">\Magento\Paypal\Model\Config::METHOD_WPP_PE_EXPRESS</item> + </argument> + </arguments> + </type> + + <type name="Magento\PaypalGraphQl\Model\Provider\Checkout"> + <arguments> + <argument name="checkoutTypes" xsi:type="array"> + <item name="paypal_express" xsi:type="string">\Magento\Paypal\Model\Express\Checkout</item> + <item name="payflow_express" xsi:type="string">\Magento\Paypal\Model\PayflowExpress\Checkout</item> + </argument> + </arguments> + </type> + + <type name="Magento\PaypalGraphQl\Model\Provider\Config"> + <arguments> + <argument name="configTypes" xsi:type="array"> + <item name="paypal_express" xsi:type="string">\Magento\Paypal\Model\Config</item> + <item name="payflow_express" xsi:type="string">\Magento\Paypal\Model\Config</item> + </argument> + </arguments> + </type> +</config> diff --git a/app/code/Magento/PaypalGraphQl/etc/module.xml b/app/code/Magento/PaypalGraphQl/etc/module.xml new file mode 100644 index 0000000000000..ae89ef2676a49 --- /dev/null +++ b/app/code/Magento/PaypalGraphQl/etc/module.xml @@ -0,0 +1,16 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> + <module name="Magento_PaypalGraphQl"> + <sequence> + <module name="Magento_Quote"/> + <module name="Magento_Paypal"/> + <module name="Magento_QuoteGraphQl"/> + </sequence> + </module> +</config> diff --git a/app/code/Magento/PaypalGraphQl/etc/schema.graphqls b/app/code/Magento/PaypalGraphQl/etc/schema.graphqls new file mode 100644 index 0000000000000..425f7af12ca22 --- /dev/null +++ b/app/code/Magento/PaypalGraphQl/etc/schema.graphqls @@ -0,0 +1,46 @@ +# Copyright © Magento, Inc. All rights reserved. +# See COPYING.txt for license details. + +type Mutation { + createPaypalExpressToken(input: PaypalExpressTokenInput): PaypalExpressToken @resolver(class: "\\Magento\\PaypalGraphQl\\Model\\Resolver\\PaypalExpressToken") @doc(description:"Initiates a PayPal checkout transaction and receives a token.") +} + +input PaypalExpressTokenInput @doc(description:"Defines the attributes required to receive a payment token from PayPal") { + cart_id: String! @doc(description:"The unique ID that identifies the customer's cart") + code: String! @doc(description:"Payment method code") + urls: PaypalExpressUrlsInput! @doc(description:"A set of URLs that PayPal uses to respond to a token request") + use_paypal_credit: Boolean @doc(description: "Indicates whether the buyer clicked the Paypal credit button. The default value is false") + express_button: Boolean @doc(description: "Indicates whether the buyer selected the quick checkout button. The default value is false") +} + +type PaypalExpressToken @doc(description: "Contains the token returned by PayPal and a set of URLs that allow the buyer to authorize payment and adjust checkout details") { + token: String @doc(description:"The token returned by PayPal") + paypal_urls: PaypalExpressUrlList @doc(description:"A set of URLs that allow the buyer to authorize payment and adjust checkout details") +} + +input PaymentMethodAdditionalDataInput { + paypal_express: PaypalExpressInput @doc(description:"Required input for PayPal Express Checkout payments") + payflow_express: PayflowExpressInput @doc(description:"Required input for PayPal Payflow Express Checkout payments") +} + +input PaypalExpressInput @doc(description:"Required input for PayPal Express Checkout payments") { + payer_id: String! @doc(description:"The unique ID of the PayPal user") + token: String! @doc(description:"The token returned by the createPaypalExpressToken mutation") +} + +input PayflowExpressInput @doc(description:"Required input for PayPal Payflow Express Checkout payments") { + payer_id: String! @doc(description:"The unique ID of the PayPal user") + token: String! @doc(description:"The token returned by the createPaypalExpressToken mutation") +} + +input PaypalExpressUrlsInput @doc(description:"A set of URLs that PayPal uses to respond to a token request") { + return_url: String! @doc(description:"The URL of the final review page on your website where the buyer confirms the order and payment") + cancel_url: String! @doc(description:"The URL of the original page on your website where the buyer initially chose PayPal as a payment type") + success_url: String @doc(description:"The URL to redirect upon success. Not applicable to most PayPal solutions") + pending_url: String @doc(description:"The URL to redirect for a pending transactions. Not applicable to most PayPal solutions") +} + +type PaypalExpressUrlList @doc(description:"A set of URLs that allow the buyer to authorize payment and adjust checkout details") { + start: String @doc(description:"The URL to the PayPal login page") + edit: String @doc(description:"The PayPal URL that allows the buyer to edit their checkout details") +} diff --git a/app/code/Magento/PaypalGraphQl/registration.php b/app/code/Magento/PaypalGraphQl/registration.php new file mode 100644 index 0000000000000..2e1676bc087cc --- /dev/null +++ b/app/code/Magento/PaypalGraphQl/registration.php @@ -0,0 +1,9 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use \Magento\Framework\Component\ComponentRegistrar; + +ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_PaypalGraphQl', __DIR__); diff --git a/app/code/Magento/Persistent/Test/Mftf/ActionGroup/StorefrontAssertPersistentRegistrationPageFieldsActionGroup.xml b/app/code/Magento/Persistent/Test/Mftf/ActionGroup/StorefrontAssertPersistentRegistrationPageFieldsActionGroup.xml new file mode 100644 index 0000000000000..34409480c9ecf --- /dev/null +++ b/app/code/Magento/Persistent/Test/Mftf/ActionGroup/StorefrontAssertPersistentRegistrationPageFieldsActionGroup.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontAssertPersistentRegistrationPageFields" extends="StorefrontAssertRegistrationPageFields"> + <seeCheckboxIsChecked selector="{{StorefrontCustomerSignInFormSection.rememberMe}}" after="seeConfirmPasswordField" stepKey="seeRememberMeChecked"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Persistent/Test/Mftf/ActionGroup/StorefrontCustomerActionGroup.xml b/app/code/Magento/Persistent/Test/Mftf/ActionGroup/StorefrontCustomerActionGroup.xml index 293fa04d80462..8c0f26dd11d90 100644 --- a/app/code/Magento/Persistent/Test/Mftf/ActionGroup/StorefrontCustomerActionGroup.xml +++ b/app/code/Magento/Persistent/Test/Mftf/ActionGroup/StorefrontCustomerActionGroup.xml @@ -19,4 +19,18 @@ before="clickSignInAccountButton" stepKey="unCheckRememberMe"/> </actionGroup> + + <actionGroup name="StorefrontRegisterCustomerRememberMe" extends="SignUpNewUserFromStorefrontActionGroup"> + <!--- Assume we are on customer registration page. --> + <remove keyForRemoval="amOnStorefrontPage"/> + <remove keyForRemoval="clickOnCreateAccountLink"/> + <checkOption selector="{{StorefrontCustomerSignInFormSection.rememberMe}}" before="clickCreateAccountButton" stepKey="checkRememberMe"/> + </actionGroup> + + <actionGroup name="StorefrontCreateCustomerOnRegisterPageDoNotRememberMe" extends="SignUpNewUserFromStorefrontActionGroup"> + <!--- Assume we are on customer registration page. --> + <remove keyForRemoval="amOnStorefrontPage"/> + <remove keyForRemoval="clickOnCreateAccountLink"/> + <uncheckOption selector="{{StorefrontCustomerSignInFormSection.rememberMe}}" before="clickCreateAccountButton" stepKey="unCheckRememberMe"/> + </actionGroup> </actionGroups> diff --git a/app/code/Magento/Persistent/Test/Mftf/ActionGroup/StorefrontPersistentAssertCustomerWelcomeMessageActionGroup.xml b/app/code/Magento/Persistent/Test/Mftf/ActionGroup/StorefrontPersistentAssertCustomerWelcomeMessageActionGroup.xml new file mode 100644 index 0000000000000..2e57c8f8e1ee8 --- /dev/null +++ b/app/code/Magento/Persistent/Test/Mftf/ActionGroup/StorefrontPersistentAssertCustomerWelcomeMessageActionGroup.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="StorefrontAssertPersistentCustomerWelcomeMessageActionGroup"> + <arguments> + <argument name="customerFullName" type="string" /> + </arguments> + <waitForPageLoad stepKey="waitForPageLoad"/> + <see selector="{{StorefrontPanelHeaderSection.welcomeMessage}}" userInput="Welcome, {{customerFullName}}! Not you?" stepKey="verifyMessage" /> + </actionGroup> + + <actionGroup name="StorefrontAssertPersistentCustomerWelcomeMessageNotPresentActionGroup" extends="StorefrontAssertPersistentCustomerWelcomeMessageActionGroup"> + <remove keyForRemoval="verifyMessage"/> + <dontSee selector="{{StorefrontPanelHeaderSection.welcomeMessage}}" userInput="Welcome, {{customerFullName}}! Not you?" stepKey="dontSeeWelcomeMessageNotYou"/> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Persistent/Test/Mftf/Data/PersistentData.xml b/app/code/Magento/Persistent/Test/Mftf/Data/PersistentData.xml index 39e55693811e9..9ae0ac6ef2437 100644 --- a/app/code/Magento/Persistent/Test/Mftf/Data/PersistentData.xml +++ b/app/code/Magento/Persistent/Test/Mftf/Data/PersistentData.xml @@ -34,4 +34,61 @@ <entity name="PersistentDisableLogoutClear" type="logout_clear"> <data key="value">0</data> </entity> + + + <!-- Persistent Configurations settings --> + <entity name="PersistentConfigSettings" type="persistent_config_state"> + <requiredEntity type="persistent_options_enabled">persistentEnabledState</requiredEntity> + <requiredEntity type="persistent_options_lifetime">SecondsOfPersistentLifetime</requiredEntity> + <requiredEntity type="persistent_options_remember_enabled">EnablePersistentRememberMe</requiredEntity> + <requiredEntity type="persistent_options_remember_default">EnablePersistentRememberMeDefaultValue</requiredEntity> + <requiredEntity type="persistent_options_logout_clear">PersistentDisableLogoutClear</requiredEntity> + <requiredEntity type="persistent_options_shopping_cart">EnablePersistentShoppingCart</requiredEntity> + </entity> + + <entity name="SecondsOfPersistentLifetime" type="lifetime"> + <data key="value">31536000</data> + </entity> + <entity name="EnablePersistentRememberMe" type="persistent_options_remember_enabled"> + <data key="value">1</data> + </entity> + <entity name="EnablePersistentRememberMeDefaultValue" type="persistent_options_remember_default"> + <data key="value">1</data> + </entity> + <entity name="EnablePersistentShoppingCart" type="persistent_options_shopping_cart"> + <data key="value">1</data> + </entity> + + <!-- Use System Value settings --> + <entity name="PersistentConfigUseSystemValue" type="persistent_config_state"> + <requiredEntity type="persistent_options_enabled">RestorePersistentOptionsEnabled</requiredEntity> + <requiredEntity type="persistent_options_lifetime">RestorePersistentOptionsLifetime</requiredEntity> + <requiredEntity type="persistent_options_remember_enabled">RestorePersistentOptionsRememberEnabled</requiredEntity> + <requiredEntity type="persistent_options_remember_default">RestorePersistentOptionsRememberDefault</requiredEntity> + <requiredEntity type="persistent_options_logout_clear">RestorePersistentOptionsLogout</requiredEntity> + <requiredEntity type="persistent_options_shopping_cart">RestorePersistentOptionsShoppingCart</requiredEntity> + </entity> + + <entity name="RestorePersistentOptionsEnabled" type="persistent_options_enabled"> + <requiredEntity type="persistent_options_use_system_value">PersistentOptionsUseInherit</requiredEntity> + </entity> + <entity name="RestorePersistentOptionsLogout" type="persistent_options_logout_clear"> + <requiredEntity type="persistent_options_use_system_value">PersistentOptionsUseInherit</requiredEntity> + </entity> + <entity name="RestorePersistentOptionsLifetime" type="persistent_options_lifetime"> + <requiredEntity type="persistent_options_use_system_value">PersistentOptionsUseInherit</requiredEntity> + </entity> + <entity name="RestorePersistentOptionsRememberEnabled" type="persistent_options_remember_enabled"> + <requiredEntity type="persistent_options_use_system_value">PersistentOptionsUseInherit</requiredEntity> + </entity> + <entity name="RestorePersistentOptionsRememberDefault" type="persistent_options_remember_default"> + <requiredEntity type="persistent_options_use_system_value">PersistentOptionsUseInherit</requiredEntity> + </entity> + <entity name="RestorePersistentOptionsShoppingCart" type="persistent_options_shopping_cart"> + <requiredEntity type="persistent_options_use_system_value">PersistentOptionsUseInherit</requiredEntity> + </entity> + + <entity name="PersistentOptionsUseInherit" type="persistent_options_use_system_value"> + <data key="value">1</data> + </entity> </entities> diff --git a/app/code/Magento/Persistent/Test/Mftf/Metadata/persistent_config-meta.xml b/app/code/Magento/Persistent/Test/Mftf/Metadata/persistent_config-meta.xml index 7f0e12f8bef93..5abcfa8a00045 100644 --- a/app/code/Magento/Persistent/Test/Mftf/Metadata/persistent_config-meta.xml +++ b/app/code/Magento/Persistent/Test/Mftf/Metadata/persistent_config-meta.xml @@ -7,15 +7,45 @@ --> <operations xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataOperation.xsd"> - <operation name="CreatePersistentConfigState" dataType="persistent_config_state" type="create" auth="adminFormKey" url="/admin/system_config/save/section/persistent/" method="POST"> + <operation name="CreatePersistentConfigState" dataType="persistent_config_state" type="create" auth="adminFormKey" url="/admin/system_config/save/section/persistent/" method="POST" successRegex="/messages-message-success/"> <object key="groups" dataType="persistent_config_state"> <object key="options" dataType="persistent_config_state"> <object key="fields" dataType="persistent_config_state"> <object key="enabled" dataType="persistent_options_enabled"> <field key="value">string</field> + <object key="inherit" dataType="persistent_options_use_system_value"> + <field key="value">integer</field> + </object> </object> <object key="logout_clear" dataType="persistent_options_logout_clear"> <field key="value">string</field> + <object key="inherit" dataType="persistent_options_use_system_value"> + <field key="value">integer</field> + </object> + </object> + <object key="lifetime" dataType="persistent_options_lifetime"> + <field key="value">string</field> + <object key="inherit" dataType="persistent_options_use_system_value"> + <field key="value">integer</field> + </object> + </object> + <object key="remember_enabled" dataType="persistent_options_remember_enabled"> + <field key="value">string</field> + <object key="inherit" dataType="persistent_options_use_system_value"> + <field key="value">integer</field> + </object> + </object> + <object key="remember_default" dataType="persistent_options_remember_default"> + <field key="value">string</field> + <object key="inherit" dataType="persistent_options_use_system_value"> + <field key="value">integer</field> + </object> + </object> + <object key="shopping_cart" dataType="persistent_options_shopping_cart"> + <field key="value">string</field> + <object key="inherit" dataType="persistent_options_use_system_value"> + <field key="value">integer</field> + </object> </object> </object> </object> diff --git a/app/code/Magento/Persistent/Test/Mftf/Test/StorefrontCorrectWelcomeMessageAfterCustomerIsLoggedOutTest.xml b/app/code/Magento/Persistent/Test/Mftf/Test/StorefrontCorrectWelcomeMessageAfterCustomerIsLoggedOutTest.xml index 2b58e5c7bf62b..61710cbc98082 100644 --- a/app/code/Magento/Persistent/Test/Mftf/Test/StorefrontCorrectWelcomeMessageAfterCustomerIsLoggedOutTest.xml +++ b/app/code/Magento/Persistent/Test/Mftf/Test/StorefrontCorrectWelcomeMessageAfterCustomerIsLoggedOutTest.xml @@ -54,7 +54,7 @@ stepKey="seeLoggedInCustomerWelcomeMessage"/> <!--Logout and check default welcome message--> <actionGroup ref="CustomerLogoutStorefrontByMenuItemsActionGroup" stepKey="storefrontCustomerLogout"/> - <seeInCurrentUrl url="{{StorefrontCustomerLogoutSuccessPage.url}}" wait="5" stepKey="seeCustomerSignOutPageUrl"/> + <seeInCurrentUrl url="{{StorefrontCustomerLogoutSuccessPage.url}}" stepKey="seeCustomerSignOutPageUrl"/> <see userInput="Default welcome msg!" selector="{{StorefrontHeaderSection.welcomeMessage}}" stepKey="seeDefaultWelcomeMessage"/> @@ -71,7 +71,7 @@ <!--Logout and check persistent customer welcome message--> <actionGroup ref="CustomerLogoutStorefrontByMenuItemsActionGroup" stepKey="storefrontCustomerLogout1"/> - <seeInCurrentUrl url="{{StorefrontCustomerLogoutSuccessPage.url}}" wait="5" stepKey="seeCustomerSignOutPageUrl1"/> + <seeInCurrentUrl url="{{StorefrontCustomerLogoutSuccessPage.url}}" stepKey="seeCustomerSignOutPageUrl1"/> <see userInput="Welcome, $$createCustomerForPersistent.firstname$$ $$createCustomerForPersistent.lastname$$! Not you?" selector="{{StorefrontHeaderSection.welcomeMessage}}" stepKey="seePersistentWelcomeMessage"/> diff --git a/app/code/Magento/Persistent/Test/Mftf/Test/StorefrontVerifyShoppingCartPersistenceUnderLongTermCookieTest.xml b/app/code/Magento/Persistent/Test/Mftf/Test/StorefrontVerifyShoppingCartPersistenceUnderLongTermCookieTest.xml new file mode 100644 index 0000000000000..e50fc4af83107 --- /dev/null +++ b/app/code/Magento/Persistent/Test/Mftf/Test/StorefrontVerifyShoppingCartPersistenceUnderLongTermCookieTest.xml @@ -0,0 +1,159 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontVerifyShoppingCartPersistenceUnderLongTermCookieTest"> + <annotations> + <features value="Persistent"/> + <stories value="Shopping Cart Persistence"/> + <title value="Verify Shopping Cart Persistence under long-term cookie"/> + <description value="Verify Shopping Cart Persistence under long-term cookie"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-12666"/> + <group value="persistent"/> + <group value="customer"/> + </annotations> + <before> + <!--Enable Persistence--> + <createData entity="PersistentConfigSettings" stepKey="persistentConfigSetting"/> + <!--Create Simple Product 1 and Product 2 --> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="_defaultProduct" stepKey="createSimple1"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="_defaultProduct" stepKey="createSimple2"> + <requiredEntity createDataKey="createCategory"/> + </createData> + </before> + <after> + <!-- Set Defaults Persistence configuration--> + <createData entity="PersistentConfigUseSystemValue" stepKey="persistentDefaultsConfiguration"/> + <!--Delete Simple Product 1, Product 2 and Category--> + <deleteData createDataKey="createSimple1" stepKey="deleteSimple1"/> + <deleteData createDataKey="createSimple2" stepKey="deleteSimple2"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <actionGroup ref="AdminDeleteCustomerActionGroup" stepKey="deleteJohnSmithCustomer"> + <argument name="customerEmail" value="John_Smith_Customer.email"/> + </actionGroup> + <actionGroup ref="AdminDeleteCustomerActionGroup" stepKey="deleteJohnDoeCustomer"> + <argument name="customerEmail" value="Simple_Customer_Without_Address.email"/> + </actionGroup> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- 1. Go to storefront and click the Create an Account link--> + <amOnPage url="{{StorefrontHomePage.url}}" stepKey="amOnHomePage"/> + <click selector="{{StorefrontPanelHeaderSection.createAnAccountLink}}" stepKey="clickCreateAnAccountLink" /> + <actionGroup ref="StorefrontAssertPersistentRegistrationPageFields" stepKey="assertPersistentRegistrationPageFields"/> + + <!-- 2. Fill fields for registration, set password and unselect the Remember Me checkbox--> + <actionGroup ref="StorefrontCreateCustomerOnRegisterPageDoNotRememberMe" stepKey="registrationJohnSmithCustomer"> + <argument name="Customer" value="John_Smith_Customer"/> + </actionGroup> + <!--Check customer name and last name in welcome message--> + <actionGroup ref="AssertMessageCustomerCreateAccountActionGroup" stepKey="customerCreatedSuccessMessageForJohnSmith"/> + <actionGroup ref="AssertCustomerWelcomeMessageActionGroup" stepKey="seeWelcomeMessageForJohnSmithCustomer"> + <argument name="customerFullName" value="{{John_Smith_Customer.fullname}}"/> + </actionGroup> + + <!-- 3. Put Simple Product 1 into Shopping Cart --> + <actionGroup ref="AddSimpleProductToCart" stepKey="addSimple1ProductToCartForJohnSmithCustomer"> + <argument name="product" value="$$createSimple1$$"/> + </actionGroup> + <actionGroup ref="assertOneProductNameInMiniCart" stepKey="checkSimple1InMiniCartForJohnSmithCustomer"> + <argument name="productName" value="$$createSimple1.name$$"/> + </actionGroup> + + <!-- 4. Click Sign Out --> + <actionGroup ref="CustomerLogoutStorefrontByMenuItemsActionGroup" stepKey="logoutJohnSmithCustomer"/> + <seeInCurrentUrl url="{{StorefrontCustomerLogoutSuccessPage.url}}" stepKey="seeLogoutSuccessPageUrlAfterLogOutJohnSmithCustomer"/> + <waitForPageLoad stepKey="waitForRedirectToHomePage"/> + <waitForText selector="{{StorefrontCMSPageSection.mainContent}}" userInput="CMS homepage content goes here." stepKey="waitForLoadContentMessage"/> + <actionGroup ref="StorefrontAssertPersistentCustomerWelcomeMessageNotPresentActionGroup" stepKey="dontSeeWelcomeJohnSmithCustomerNotYouMessage"> + <argument name="customerFullName" value="{{John_Smith_Customer.fullname}}"/> + </actionGroup> + <actionGroup ref="assertMiniCartEmpty" stepKey="assertMiniCartEmptyAfterJohnSmithSignOut" /> + + <!-- 5. Click the Create an Account link again and fill fields for registration of another customer, set password and check the Remember Me checkbox --> + <amOnPage url="{{StorefrontCustomerCreatePage.url}}" stepKey="amOnCustomerAccountCreatePage"/> + <actionGroup ref="StorefrontRegisterCustomerRememberMe" stepKey="registrationJohnDoeCustomer"> + <argument name="Customer" value="Simple_Customer_Without_Address"/> + </actionGroup> + <!--Check customer name and last name in welcome message--> + <actionGroup ref="AssertMessageCustomerCreateAccountActionGroup" stepKey="customerCreatedSuccessMessageForJohnDoe"/> + <actionGroup ref="AssertCustomerWelcomeMessageActionGroup" stepKey="seeWelcomeMessageForJohnDoeCustomer"> + <argument name="customerFullName" value="{{Simple_Customer_Without_Address.fullname}}"/> + </actionGroup> + <!-- 6. Add Simple Product 1 to Shopping Cart --> + <actionGroup ref="AddSimpleProductToCart" stepKey="addSimple1ProductToCartForJohnDoeCustomer"> + <argument name="product" value="$$createSimple1$$"/> + </actionGroup> + <see selector="{{StorefrontMinicartSection.productCount}}" userInput="1" stepKey="miniCartContainsOneProductForJohnDoeCustomer"/> + <actionGroup ref="assertOneProductNameInMiniCart" stepKey="checkSimple1InMiniCartForJohnDoeCustomer"> + <argument name="productName" value="$$createSimple1.name$$"/> + </actionGroup> + + <!-- 7. Click Log Out --> + <actionGroup ref="CustomerLogoutStorefrontByMenuItemsActionGroup" stepKey="logoutJohnDoeCustomer"/> + <seeInCurrentUrl url="{{StorefrontCustomerLogoutSuccessPage.url}}" stepKey="seeLogoutSuccessPageUrlAfterLogOutJohnDoeCustomer"/> + <actionGroup ref="StorefrontAssertPersistentCustomerWelcomeMessageActionGroup" stepKey="seeWelcomeForJohnDoeCustomer"> + <argument name="customerFullName" value="{{Simple_Customer_Without_Address.fullname}}"/> + </actionGroup> + <waitForPageLoad stepKey="waitForHomePageLoad"/> + <waitForText selector="{{StorefrontCMSPageSection.mainContent}}" userInput="CMS homepage content goes here." stepKey="waitForLoadContentMessageOnHomePage"/> + <waitForElementVisible selector="{{StorefrontMinicartSection.productCount}}" stepKey="waitForCartCounterVisible"/> + <see selector="{{StorefrontMinicartSection.productCount}}" userInput="1" stepKey="miniCartContainsOneProductForGuest"/> + <actionGroup ref="assertOneProductNameInMiniCart" stepKey="checkSimple1InMiniCartForGuestCustomer"> + <argument name="productName" value="$$createSimple1.name$$"/> + </actionGroup> + + <!-- 8. Go to Shopping Cart and verify Simple Product 1 is present there --> + <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="goToShoppingCart" /> + <see selector="{{CheckoutCartProductSection.productName}}" userInput="$$createSimple1.name$$" stepKey="checkSimple1InShoppingCart"/> + + <!-- 9. Add Simple Product 2 to Shopping Cart --> + <actionGroup ref="AddSimpleProductToCart" stepKey="addSimple2ProductToCartForGuest"> + <argument name="product" value="$$createSimple2$$"/> + </actionGroup> + <actionGroup ref="assertOneProductNameInMiniCart" stepKey="checkSimple1InMiniCartForGuestCustomerSecondTime"> + <argument name="productName" value="$$createSimple1.name$$"/> + </actionGroup> + <actionGroup ref="assertOneProductNameInMiniCart" stepKey="checkSimple2InMiniCartForGuestCustomer"> + <argument name="productName" value="$$createSimple2.name$$"/> + </actionGroup> + <see selector="{{StorefrontMinicartSection.productCount}}" userInput="2" stepKey="miniCartContainsTwoProductForGuest"/> + + <!-- 10. Go to My Account section --> + <amOnPage url="{{StorefrontCustomerDashboardPage.url}}" stepKey="amOnCustomerAccountPage"/> + <seeInCurrentUrl url="{{StorefrontCustomerSignInPage.url}}" stepKey="redirectToCustomerAccountLoginPage"/> + <seeElement selector="{{StorefrontCustomerSignInFormSection.customerLoginBlock}}" stepKey="checkSystemRequiresToLogIn"/> + + <!-- 11. Log in as John Doe --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="logInAsJohnDoeCustomer"> + <argument name="Customer" value="Simple_Customer_Without_Address"/> + </actionGroup> + <see selector="{{StorefrontMinicartSection.productCount}}" userInput="2" stepKey="miniCartContainsTwoProductForJohnDoeCustomer"/> + <actionGroup ref="assertOneProductNameInMiniCart" stepKey="checkSimple1InMiniCartForJohnDoeCustomerSecondTime"> + <argument name="productName" value="$$createSimple1.name$$"/> + </actionGroup> + <actionGroup ref="assertOneProductNameInMiniCart" stepKey="checkSimple2InMiniCartForJohnDoeCustomer"> + <argument name="productName" value="$$createSimple2.name$$"/> + </actionGroup> + + <!-- 12. Sign out and click the Not you? link --> + <actionGroup ref="CustomerLogoutStorefrontByMenuItemsActionGroup" stepKey="logoutJohnDoeCustomerSecondTime"/> + <seeInCurrentUrl url="{{StorefrontCustomerLogoutSuccessPage.url}}" stepKey="seeLogoutSuccessPageUrlAfterLogOutJohnSmithCustomerSecondTime"/> + <waitForPageLoad stepKey="waitForHomePageLoadAfter5Seconds"/> + <waitForText selector="{{StorefrontCMSPageSection.mainContent}}" userInput="CMS homepage content goes here." stepKey="waitForLoadMainContentMessageOnHomePage"/> + <click selector="{{StorefrontPanelHeaderSection.notYouLink}}" stepKey="clickOnNotYouLink" /> + <waitForPageLoad stepKey="waitForCustomerLoginPageLoad"/> + <actionGroup ref="assertMiniCartEmpty" stepKey="assertMiniCartEmptyAfterJohnDoeSignOut" /> + </test> +</tests> diff --git a/app/code/Magento/ProductAlert/etc/db_schema.xml b/app/code/Magento/ProductAlert/etc/db_schema.xml index 820a8029e2d95..cb91560f8daa6 100644 --- a/app/code/Magento/ProductAlert/etc/db_schema.xml +++ b/app/code/Magento/ProductAlert/etc/db_schema.xml @@ -14,7 +14,7 @@ default="0" comment="Customer id"/> <column xsi:type="int" name="product_id" padding="10" unsigned="true" nullable="false" identity="false" default="0" comment="Product id"/> - <column xsi:type="decimal" name="price" scale="4" precision="12" unsigned="false" nullable="false" default="0" + <column xsi:type="decimal" name="price" scale="6" precision="20" unsigned="false" nullable="false" default="0" comment="Price amount"/> <column xsi:type="smallint" name="website_id" padding="5" unsigned="true" nullable="false" identity="false" default="0" comment="Website id"/> diff --git a/app/code/Magento/ProductVideo/Test/Mftf/Section/StorefrontProductInfoMainSection.xml b/app/code/Magento/ProductVideo/Test/Mftf/Section/StorefrontProductInfoMainSection.xml index c8e1ebcf12d94..e94d426ba7638 100644 --- a/app/code/Magento/ProductVideo/Test/Mftf/Section/StorefrontProductInfoMainSection.xml +++ b/app/code/Magento/ProductVideo/Test/Mftf/Section/StorefrontProductInfoMainSection.xml @@ -10,9 +10,9 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontProductInfoMainSection"> <element name="productVideo" type="text" selector="//*[@class='product-video' and @data-type='{{videoType}}']" parameterized="true"/> - <element name="clickInVideo" type="video" selector="//*[@class='fotorama__stage__shaft']"/> - <element name="videoPausedMode" type="video" selector="//*[contains(@class, 'paused-mode')]"/> - <element name="videoPlayedMode" type="video" selector="//*[contains(@class,'playing-mode')]"/> - <element name="frameVideo" type="video" selector="widget2"/> + <element name="clickInVideo" type="button" selector="//*[@class='fotorama__stage__shaft']"/> + <element name="videoPausedMode" type="button" selector="//*[contains(@class, 'paused-mode')]"/> + <element name="videoPlayedMode" type="button" selector="//*[contains(@class,'playing-mode')]"/> + <element name="frameVideo" type="button" selector="widget2"/> </section> </sections> diff --git a/app/code/Magento/ProductVideo/view/adminhtml/templates/helper/gallery.phtml b/app/code/Magento/ProductVideo/view/adminhtml/templates/helper/gallery.phtml index 6dff53211892f..1548770d4032f 100755 --- a/app/code/Magento/ProductVideo/view/adminhtml/templates/helper/gallery.phtml +++ b/app/code/Magento/ProductVideo/view/adminhtml/templates/helper/gallery.phtml @@ -4,17 +4,18 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile +// phpcs:disable Magento2.Files.LineLength +// phpcs:disable Magento2.Templates.ThisInTemplate /** @var $block \Magento\Catalog\Block\Adminhtml\Product\Helper\Form\Gallery\Content */ -$elementName = $block->getElement()->getName() . '[images]'; -$formName = $block->getFormName(); +$elementNameEscaped = $block->escapeHtmlAttr($block->getElement()->getName()) . '[images]'; +$formNameEscaped = $block->escapeHtmlAttr($block->getFormName()); ?> <div class="row"> <div class="add-video-button-container"> <button id="add_video_button" - title="<?= $block->escapeHtml(__('Add Video')) ?>" + title="<?= $block->escapeHtmlAttr(__('Add Video')) ?>" data-role="add-video-button" type="button" class="action-secondary" @@ -29,109 +30,94 @@ $formName = $block->getFormName(); $element = $block->getElement(); $elementToggleCode = $element->getToggleCode() ? $element->getToggleCode() : 'toggleValueElements(this, this.parentNode.parentNode.parentNode)'; ?> - -<div id="<?= $block->getHtmlId() ?>" +<div id="<?= $block->escapeHtmlAttr($block->getHtmlId()) ?>" class="gallery" data-mage-init='{"openVideoModal":{}}' data-parent-component="<?= $block->escapeHtml($block->getData('config/parentComponent')) ?>" data-images="<?= $block->escapeHtmlAttr($block->getImagesJson()) ?>" - data-types="<?= $block->escapeHtml( - $this->helper('Magento\Framework\Json\Helper\Data')->jsonEncode($block->getImageTypes()) - ) ?>" + data-types='<?= /* @noEscape */ $this->helper(Magento\Framework\Json\Helper\Data::class)->jsonEncode($block->getImageTypes()) ?>' > - - <?php - if (!$block->getElement()->getReadonly()): - ?> + <?php if (!$block->getElement()->getReadonly()) : ?> <div class="image image-placeholder"> - <?php /* @escapeNotVerified */ echo $block->getUploaderHtml(); - ?> + <?= $block->getUploaderHtml(); ?> <div class="product-image-wrapper"> <p class="image-placeholder-text"> - <?= $block->escapeHtml( - __('Browse to find or drag image here') - ); ?> + <?= $block->escapeHtml(__('Browse to find or drag image here')); ?> </p> </div> </div> - <?= /* @escapeNotVerified */ $block->getChildHtml('additional_buttons') ?> - <?php - endif; - ?> - <?php - foreach ($block->getImageTypes() as $typeData): - ?> - <input name="<?= $block->escapeHtml($typeData['name']) ?>" - data-form-part="<?= /* @escapeNotVerified */ $formName ?>" - class="image-<?= $block->escapeHtml($typeData['code']) ?>" + <?= $block->getChildHtml('additional_buttons') ?> + <?php endif; ?> + <?php foreach ($block->getImageTypes() as $typeData) : ?> + <input name="<?= $block->escapeHtmlAttr($typeData['name']) ?>" + data-form-part="<?= /* @noEscape */ $formNameEscaped ?>" + class="image-<?= $block->escapeHtmlAttr($typeData['code']) ?>" type="hidden" - value="<?= $block->escapeHtml($typeData['value']) ?>"/> - <?php - endforeach; - ?> - <script id="<?= /* @escapeNotVerified */ $block->getHtmlId() ?>-template" data-template="image" type="text/x-magento-template"> + value="<?= $block->escapeHtmlAttr($typeData['value']) ?>"/> + <?php endforeach; ?> + <script id="<?= $block->escapeHtmlAttr($block->getHtmlId()) ?>-template" data-template="image" type="text/x-magento-template"> <div class="image item <% if (data.disabled == 1) { %>hidden-for-front<% } %> <% if (data.video_url) { %>video-item<% } %>" data-role="image"> <input type="hidden" - name="<?= /* @escapeNotVerified */ $elementName ?>[<%- data.file_id %>][position]" + name="<?= /* @noEscape */ $elementNameEscaped ?>[<%- data.file_id %>][position]" value="<%- data.position %>" - data-form-part="<?= /* @escapeNotVerified */ $formName ?>" + data-form-part="<?= /* @noEscape */ $formNameEscaped ?>" class="position"/> <% if (data.media_type !== 'external-video') {%> <input type="hidden" - name="<?= /* @escapeNotVerified */ $elementName ?>[<%- data.file_id %>][media_type]" - data-form-part="<?= /* @escapeNotVerified */ $formName ?>" + name="<?= /* @noEscape */ $elementNameEscaped ?>[<%- data.file_id %>][media_type]" + data-form-part="<?= /* @noEscape */ $formNameEscaped ?>" value="image"/> <% } else { %> <input type="hidden" - name="<?= /* @escapeNotVerified */ $elementName ?>[<%- data.file_id %>][media_type]" + name="<?= /* @noEscape */ $elementNameEscaped ?>[<%- data.file_id %>][media_type]" value="<%- data.media_type %>" - data-form-part="<?= /* @escapeNotVerified */ $formName ?>"/> + data-form-part="<?= /* @noEscape */ $formNameEscaped ?>"/> <% } %> <input type="hidden" - name="<?= /* @escapeNotVerified */ $elementName ?>[<%- data.file_id %>][video_provider]" + name="<?= /* @noEscape */ $elementNameEscaped ?>[<%- data.file_id %>][video_provider]" value="<%- data.video_provider %>" - data-form-part="<?= /* @escapeNotVerified */ $formName ?>"/> + data-form-part="<?= /* @noEscape */ $formNameEscaped ?>"/> <input type="hidden" - name="<?= /* @escapeNotVerified */ $elementName ?>[<%- data.file_id %>][file]" + name="<?= /* @noEscape */ $elementNameEscaped ?>[<%- data.file_id %>][file]" value="<%- data.file %>" - data-form-part="<?= /* @escapeNotVerified */ $formName ?>"/> + data-form-part="<?= /* @noEscape */ $formNameEscaped ?>"/> <input type="hidden" - name="<?= /* @escapeNotVerified */ $elementName ?>[<%- data.file_id %>][value_id]" + name="<?= /* @noEscape */ $elementNameEscaped ?>[<%- data.file_id %>][value_id]" value="<%- data.value_id %>" - data-form-part="<?= /* @escapeNotVerified */ $formName ?>"/> + data-form-part="<?= /* @noEscape */ $formNameEscaped ?>"/> <input type="hidden" - name="<?= /* @escapeNotVerified */ $elementName ?>[<%- data.file_id %>][label]" + name="<?= /* @noEscape */ $elementNameEscaped ?>[<%- data.file_id %>][label]" value="<%- data.label %>" - data-form-part="<?= /* @escapeNotVerified */ $formName ?>"/> + data-form-part="<?= /* @noEscape */ $formNameEscaped ?>"/> <input type="hidden" - name="<?= /* @escapeNotVerified */ $elementName ?>[<%- data.file_id %>][disabled]" + name="<?= /* @noEscape */ $elementNameEscaped ?>[<%- data.file_id %>][disabled]" value="<%- data.disabled %>" - data-form-part="<?= /* @escapeNotVerified */ $formName ?>"/> + data-form-part="<?= /* @noEscape */ $formNameEscaped ?>"/> <input type="hidden" - name="<?= /* @escapeNotVerified */ $elementName ?>[<%- data.file_id %>][removed]" + name="<?= /* @noEscape */ $elementNameEscaped ?>[<%- data.file_id %>][removed]" value="" class="is-removed" - data-form-part="<?= /* @escapeNotVerified */ $formName ?>"/> + data-form-part="<?= /* @noEscape */ $formNameEscaped ?>"/> <input type="hidden" - name="<?= /* @escapeNotVerified */ $elementName ?>[<%- data.file_id %>][video_url]" + name="<?= /* @noEscape */ $elementNameEscaped ?>[<%- data.file_id %>][video_url]" value="<%- data.video_url %>" - data-form-part="<?= /* @escapeNotVerified */ $formName ?>"/> + data-form-part="<?= /* @noEscape */ $formNameEscaped ?>"/> <input type="hidden" - name="<?= /* @escapeNotVerified */ $elementName ?>[<%- data.file_id %>][video_title]" + name="<?= /* @noEscape */ $elementNameEscaped ?>[<%- data.file_id %>][video_title]" value="<%- data.video_title %>" - data-form-part="<?= /* @escapeNotVerified */ $formName ?>"/> + data-form-part="<?= /* @noEscape */ $formNameEscaped ?>"/> <input type="hidden" - name="<?= /* @escapeNotVerified */ $elementName ?>[<%- data.file_id %>][video_description]" + name="<?= /* @noEscape */ $elementNameEscaped ?>[<%- data.file_id %>][video_description]" value="<%- data.video_description %>" - data-form-part="<?= /* @escapeNotVerified */ $formName ?>"/> + data-form-part="<?= /* @noEscape */ $formNameEscaped ?>"/> <input type="hidden" - name="<?= /* @escapeNotVerified */ $elementName ?>[<%- data.file_id %>][video_metadata]" + name="<?= /* @noEscape */ $elementNameEscaped ?>[<%- data.file_id %>][video_metadata]" value="<%- data.video_metadata %>" - data-form-part="<?= /* @escapeNotVerified */ $formName ?>"/> + data-form-part="<?= /* @noEscape */ $formNameEscaped ?>"/> <input type="hidden" - name="<?= /* @escapeNotVerified */ $elementName ?>[<%- data.file_id %>][role]" + name="<?= /* @noEscape */ $elementNameEscaped ?>[<%- data.file_id %>][role]" value="<%- data.video_description %>" - data-form-part="<?= /* @escapeNotVerified */ $formName ?>"/> + data-form-part="<?= /* @noEscape */ $formNameEscaped ?>"/> <div class="product-image-wrapper"> <img class="product-image" @@ -142,40 +128,28 @@ $elementToggleCode = $element->getToggleCode() ? $element->getToggleCode() : 'to <div class="actions"> <div class="tooltip"> <span class="delete-tooltiptext"> - <?= $block->escapeHtml( - __('Delete image in all store views') - ); ?> + <?= $block->escapeHtml(__('Delete image in all store views')); ?> </span> <button type="button" class="action-remove" data-role="delete-button" title="<% if (data.media_type == 'external-video') {%> - <?= $block->escapeHtml( - __('Delete video') - ); ?> + <?= $block->escapeHtmlAttr(__('Delete video')); ?> <%} else {%> - <?= $block->escapeHtml( - __('Delete image') - ); ?> + <?= $block->escapeHtmlAttr(__('Delete image')); ?> <%}%>"> <span> <% if (data.media_type == 'external-video') { %> - <?= $block->escapeHtml( - __('Delete video') - ); ?> + <?= $block->escapeHtml(__('Delete video')); ?> <% } else {%> - <?= $block->escapeHtml( - __('Delete image') - ); ?> + <?= $block->escapeHtml(__('Delete image')); ?> <%} %> </span> </button> </div> <div class="draggable-handle"></div> </div> - <div class="image-fade"><span><?= $block->escapeHtml( - __('Hidden') - ); ?></span></div> + <div class="image-fade"><span><?= $block->escapeHtml(__('Hidden')); ?></span></div> </div> <div class="item-description"> @@ -190,19 +164,11 @@ $elementToggleCode = $element->getToggleCode() ? $element->getToggleCode() : 'to </div> <ul class="item-roles" data-role="roles-labels"> - <?php - foreach ($block->getImageTypes() as $typeData): - ?> - <li data-role-code="<?= $block->escapeHtml( - $typeData['code'] - ) ?>" class="item-role item-role-<?= $block->escapeHtml( - $typeData['code'] - ) ?>"> + <?php foreach ($block->getImageTypes() as $typeData) : ?> + <li data-role-code="<?= $block->escapeHtmlAttr($typeData['code']) ?>" class="item-role item-role-<?= $block->escapeHtmlAttr($typeData['code']) ?>"> <?= $block->escapeHtml($typeData['label']) ?> </li> - <?php - endforeach; - ?> + <?php endforeach; ?> </ul> </div> </script> @@ -222,24 +188,20 @@ $elementToggleCode = $element->getToggleCode() ? $element->getToggleCode() : 'to <fieldset class="admin__fieldset fieldset-image-panel"> <div class="admin__field field-image-description"> <label class="admin__field-label" for="image-description"> - <span><?= /* @escapeNotVerified */ __('Alt Text') ?></span> + <span><?= $block->escapeHtml(__('Alt Text')) ?></span> </label> <div class="admin__field-control"> <textarea data-role="image-description" rows="3" class="admin__control-textarea" - name="<?php /* @escapeNotVerified */ - echo $elementName - ?>[<%- data.file_id %>][label]"><%- data.label %></textarea> + name="<?= /* @noEscape */ $elementNameEscaped ?>[<%- data.file_id %>][label]"><%- data.label %></textarea> </div> </div> <div class="admin__field field-image-role"> <label class="admin__field-label"> - <span><?= $block->escapeHtml( - __('Role') - ); ?></span> + <span><?= $block->escapeHtml(__('Role')); ?></span> </label> <div class="admin__field-control"> <ul class="multiselect-alt"> @@ -250,36 +212,30 @@ $elementToggleCode = $element->getToggleCode() ? $element->getToggleCode() : 'to <label> <input class="image-type" data-role="type-selector" - data-form-part="<?= /* @escapeNotVerified */ $formName ?>" + data-form-part="<?= /* @noEscape */ $formNameEscaped ?>" type="checkbox" - value="<?= $block->escapeHtml( - $attribute->getAttributeCode() - ) ?>" + value="<?= $block->escapeHtmlAttr($attribute->getAttributeCode()) ?>" /> - <?php /* @escapeNotVerified */ echo $block->escapeHtml( - $attribute->getFrontendLabel() - ) ?> + <?= $block->escapeHtml($attribute->getFrontendLabel()) ?> </label> </li> - <?php - endforeach; - ?> + <?php endforeach; ?> </ul> </div> </div> <div class="admin__field admin__field-inline field-image-size" data-role="size"> <label class="admin__field-label"> - <span><?= /* @escapeNotVerified */ __('Image Size') ?></span> + <span><?= $block->escapeHtml(__('Image Size')) ?></span> </label> - <div class="admin__field-value" data-message="<?= /* @escapeNotVerified */ __('{size}') ?>"></div> + <div class="admin__field-value" data-message="<?= $block->escapeHtmlAttr(__('{size}')) ?>"></div> </div> <div class="admin__field admin__field-inline field-image-resolution" data-role="resolution"> <label class="admin__field-label"> - <span><?= /* @escapeNotVerified */ __('Image Resolution') ?></span> + <span><?= $block->escapeHtml(__('Image Resolution')) ?></span> </label> - <div class="admin__field-value" data-message="<?= /* @escapeNotVerified */ __('{width}^{height} px') ?>"></div> + <div class="admin__field-value" data-message="<?= $block->escapeHtmlAttr(__('{width}^{height} px')) ?>"></div> </div> <div class="admin__field field-image-hide"> @@ -288,16 +244,14 @@ $elementToggleCode = $element->getToggleCode() ? $element->getToggleCode() : 'to <input type="checkbox" id="hide-from-product-page" data-role="visibility-trigger" - data-form-part="<?= /* @escapeNotVerified */ $formName ?>" + data-form-part="<?= /* @noEscape */ $formNameEscaped ?>" value="1" class="admin__control-checkbox" - name="<?= /* @escapeNotVerified */ $elementName ?>[<%- data.file_id %>][disabled]" + name="<?= /* @noEscape */ $elementNameEscaped ?>[<%- data.file_id %>][disabled]" <% if (data.disabled == 1) { %>checked="checked"<% } %> /> <label for="hide-from-product-page" class="admin__field-label"> - <?= $block->escapeHtml( - __('Hide from Product Page') - ); ?> + <?= $block->escapeHtml(__('Hide from Product Page')); ?> </label> </div> </div> @@ -306,28 +260,20 @@ $elementToggleCode = $element->getToggleCode() ? $element->getToggleCode() : 'to </div> </script> <div id="<?= /* @noEscape */ $block->getNewVideoBlockName() ?>" style="display:none"> - <?= /* @escapeNotVerified */ $block->getFormHtml() ?> + <?= $block->getFormHtml() ?> <div id="video-player-preview-location" class="video-player-sidebar"> <div class="video-player-container"></div> <div class="video-information title"> - <label><?= $block->escapeHtml( - __('Title:') - ); ?> </label><span></span> + <label><?= $block->escapeHtml(__('Title:')); ?> </label><span></span> </div> <div class="video-information uploaded"> - <label><?= $block->escapeHtml( - __('Uploaded:') - ); ?> </label><span></span> + <label><?= $block->escapeHtml(__('Uploaded:')); ?> </label><span></span> </div> <div class="video-information uploader"> - <label><?= $block->escapeHtml( - __('Uploader:') - ); ?> </label><span></span> + <label><?= $block->escapeHtml(__('Uploader:')); ?> </label><span></span> </div> <div class="video-information duration"> - <label><?= $block->escapeHtml( - __('Duration:') - ); ?> </label><span></span> + <label><?= $block->escapeHtml(__('Duration:')); ?> </label><span></span> </div> </div> </div> @@ -336,4 +282,4 @@ $elementToggleCode = $element->getToggleCode() ? $element->getToggleCode() : 'to </div> <script> jQuery('body').trigger('contentUpdated'); -</script> \ No newline at end of file +</script> diff --git a/app/code/Magento/ProductVideo/view/adminhtml/templates/product/edit/base_image.phtml b/app/code/Magento/ProductVideo/view/adminhtml/templates/product/edit/base_image.phtml index dc0d8206571dd..e1dcab9e8b2d4 100644 --- a/app/code/Magento/ProductVideo/view/adminhtml/templates/product/edit/base_image.phtml +++ b/app/code/Magento/ProductVideo/view/adminhtml/templates/product/edit/base_image.phtml @@ -8,30 +8,30 @@ <div class="add-video-button-container"> <button id="add_video_button" - title="<?= /* @escapeNotVerified */ $addVideoTitle ?>" + title="<?= $block->escapeHtmlAttr($addVideoTitle) ?>" type="button" class="action-secondary" onclick="jQuery('#new-video').modal('openModal'); jQuery('#new_video_form')[0].reset();" data-ui-id="widget-button-1"> - <span><?= /* @escapeNotVerified */ __('Add Video') ?></span> + <span><?= $block->escapeHtml(__('Add Video')) ?></span> </button> </div> </div> -<div id="<?= /* @escapeNotVerified */ $htmlId ?>-container" +<div id="<?= $block->escapeHtmlAttr($htmlId) ?>-container" class="images" data-mage-init='{"baseImage":{}}' - data-max-file-size="<?= /* @escapeNotVerified */ $fileMaxSize ?>" + data-max-file-size="<?= $block->escapeHtmlAttr($fileMaxSize) ?>" > <div class="image image-placeholder"> - <input type="file" name="image" data-url="<?= /* @escapeNotVerified */ $uploadUrl ?>" multiple="multiple" /> - <img class="spacer" src="<?= /* @escapeNotVerified */ $spacerImage ?>"/> - <p class="image-placeholder-text"><?= /* @escapeNotVerified */ $imagePlaceholderText ?></p> + <input type="file" name="image" data-url="<?= $block->escapeUrl($uploadUrl) ?>" multiple="multiple" /> + <img class="spacer" src="<?= $block->escapeUrl($spacerImage) ?>"/> + <p class="image-placeholder-text"><?= $block->escapeHtml($imagePlaceholderText) ?></p> </div> - <script id="<?= /* @escapeNotVerified */ $htmlId ?>-template" + <script id="<?= $block->escapeHtmlAttr($htmlId) ?>-template" data-template="image" type="text/x-magento-template"> <div class="image"> - <img class="spacer" src="<?= /* @escapeNotVerified */ $spacerImage ?>"/> + <img class="spacer" src="<?= $block->escapeUrl($spacerImage) ?>"/> <img class="product-image" src="<%- data.url %>" @@ -42,25 +42,25 @@ type="button" class="action-delete" data-role="delete-button" - title="<?= /* @escapeNotVerified */ $deleteImageText ?>"> - <span><?= /* @escapeNotVerified */ $deleteImageText ?></span> + title="<?= $block->escapeHtmlAttr($deleteImageText) ?>"> + <span><?= $block->escapeHtml($deleteImageText) ?></span> </button> <button type="button" class="action-make-base" data-role="make-base-button" - title="<?= /* @escapeNotVerified */ $makeBaseText ?>"> - <span><?= /* @escapeNotVerified */ $makeBaseText ?></span> + title="<?= $block->escapeHtmlAttr($makeBaseText) ?>"> + <span><?= $block->escapeHtml($makeBaseText) ?></span> </button> <div class="draggable-handle"></div> </div> <div class="image-label"></div> - <div class="image-fade"><span><?= /* @escapeNotVerified */ $hiddenText ?></span></div> + <div class="image-fade"><span><?= $block->escapeHtml($hiddenText) ?></span></div> </div> </script> </div> <span class="action-manage-images" data-activate-tab="image-management"> - <span><?= /* @escapeNotVerified */ $imageManagementText ?></span> + <span><?= $block->escapeHtml($imageManagementText) ?></span> </span> <script> require([ diff --git a/app/code/Magento/ProductVideo/view/adminhtml/templates/product/edit/slideout/form.phtml b/app/code/Magento/ProductVideo/view/adminhtml/templates/product/edit/slideout/form.phtml index e9421f8e74a87..7de3042b56ab5 100644 --- a/app/code/Magento/ProductVideo/view/adminhtml/templates/product/edit/slideout/form.phtml +++ b/app/code/Magento/ProductVideo/view/adminhtml/templates/product/edit/slideout/form.phtml @@ -5,23 +5,23 @@ */ /* @var Magento\ProductVideo\Block\Adminhtml\Product\Edit\NewVideo $block */ ?> -<div id="<?= /* @escapeNotVerified */ $block->getNameInLayout() ?>" style="display:none" - data-modal-info='<?= /* @escapeNotVerified */ $block->getWidgetOptions() ?>' +<div id="<?= $block->escapeHtmlAttr($block->getNameInLayout()) ?>" style="display:none" + data-modal-info='<?= /* @noEscape */ $block->getWidgetOptions() ?>' > - <?= /* @escapeNotVerified */ $block->getFormHtml() ?> + <?= $block->getFormHtml() ?> <div id="video-player-preview-location" class="video-player-sidebar"> <div class="video-player-container"></div> <div class="video-information title"> - <label><?= /* @escapeNotVerified */ __('Title:') ?> </label><span></span> + <label><?= $block->escapeHtml(__('Title:')) ?> </label><span></span> </div> <div class="video-information uploaded"> - <label><?= /* @escapeNotVerified */ __('Uploaded:') ?> </label><span></span> + <label><?= $block->escapeHtml(__('Uploaded:')) ?> </label><span></span> </div> <div class="video-information uploader"> - <label><?= /* @escapeNotVerified */ __('Uploader:') ?> </label><span></span> + <label><?= $block->escapeHtml(__('Uploader:')) ?> </label><span></span> </div> <div class="video-information duration"> - <label><?= /* @escapeNotVerified */ __('Duration:') ?> </label><span></span> + <label><?= $block->escapeHtml(__('Duration:')) ?> </label><span></span> </div> </div> </div> diff --git a/app/code/Magento/ProductVideo/view/frontend/templates/product/view/gallery.phtml b/app/code/Magento/ProductVideo/view/frontend/templates/product/view/gallery.phtml index 55486dbbb0cfe..bfa394b5e7007 100644 --- a/app/code/Magento/ProductVideo/view/frontend/templates/product/view/gallery.phtml +++ b/app/code/Magento/ProductVideo/view/frontend/templates/product/view/gallery.phtml @@ -14,8 +14,8 @@ { "[data-gallery-role=gallery-placeholder]": { "Magento_ProductVideo/js/fotorama-add-video-events": { - "videoData": <?= /* @escapeNotVerified */ $block->getMediaGalleryDataJson() ?>, - "videoSettings": <?= /* @escapeNotVerified */ $block->getVideoSettingsJson() ?>, + "videoData": <?= /* @noEscape */ $block->getMediaGalleryDataJson() ?>, + "videoSettings": <?= /* @noEscape */ $block->getVideoSettingsJson() ?>, "optionsVideoData": <?= /* @noEscape */ $block->getOptionsMediaGalleryDataJson() ?> } } diff --git a/app/code/Magento/Quote/Model/Quote.php b/app/code/Magento/Quote/Model/Quote.php index b1f68d0411cf0..82b7913446fd0 100644 --- a/app/code/Magento/Quote/Model/Quote.php +++ b/app/code/Magento/Quote/Model/Quote.php @@ -7,6 +7,7 @@ use Magento\Customer\Api\Data\CustomerInterface; use Magento\Customer\Api\Data\GroupInterface; +use Magento\Directory\Model\AllowedCountries; use Magento\Framework\Api\AttributeValueFactory; use Magento\Framework\Api\ExtensionAttribute\JoinProcessorInterface; use Magento\Framework\Model\AbstractExtensibleModel; @@ -14,6 +15,7 @@ use Magento\Quote\Model\Quote\Address; use Magento\Quote\Model\Quote\Address\Total as AddressTotal; use Magento\Sales\Model\Status; +use Magento\Store\Model\ScopeInterface; use Magento\Framework\App\ObjectManager; /** @@ -359,6 +361,11 @@ class Quote extends AbstractExtensibleModel implements \Magento\Quote\Api\Data\C */ private $orderIncrementIdChecker; + /** + * @var AllowedCountries + */ + private $allowedCountriesReader; + /** * @param \Magento\Framework\Model\Context $context * @param \Magento\Framework\Registry $registry @@ -401,6 +408,7 @@ class Quote extends AbstractExtensibleModel implements \Magento\Quote\Api\Data\C * @param \Magento\Framework\Data\Collection\AbstractDb|null $resourceCollection * @param array $data * @param \Magento\Sales\Model\OrderIncrementIdChecker|null $orderIncrementIdChecker + * @param AllowedCountries|null $allowedCountriesReader * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -444,7 +452,8 @@ public function __construct( \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null, \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null, array $data = [], - \Magento\Sales\Model\OrderIncrementIdChecker $orderIncrementIdChecker = null + \Magento\Sales\Model\OrderIncrementIdChecker $orderIncrementIdChecker = null, + AllowedCountries $allowedCountriesReader = null ) { $this->quoteValidator = $quoteValidator; $this->_catalogProduct = $catalogProduct; @@ -481,6 +490,8 @@ public function __construct( $this->shippingAssignmentFactory = $shippingAssignmentFactory; $this->orderIncrementIdChecker = $orderIncrementIdChecker ?: ObjectManager::getInstance() ->get(\Magento\Sales\Model\OrderIncrementIdChecker::class); + $this->allowedCountriesReader = $allowedCountriesReader + ?: ObjectManager::getInstance()->get(AllowedCountries::class); parent::__construct( $context, $registry, @@ -941,7 +952,7 @@ public function assignCustomerWithAddressChange( /** @var \Magento\Quote\Model\Quote\Address $billingAddress */ $billingAddress = $this->_quoteAddressFactory->create(); $billingAddress->importCustomerAddressData($defaultBillingAddress); - $this->setBillingAddress($billingAddress); + $this->assignAddress($billingAddress); } } @@ -959,7 +970,8 @@ public function assignCustomerWithAddressChange( $shippingAddress = $this->_quoteAddressFactory->create(); } } - $this->setShippingAddress($shippingAddress); + + $this->assignAddress($shippingAddress, false); } return $this; @@ -1374,7 +1386,7 @@ public function addShippingAddress(\Magento\Quote\Api\Data\AddressInterface $add * Retrieve quote items collection * * @param bool $useCache - * @return \Magento\Eav\Model\Entity\Collection\AbstractCollection + * @return \Magento\Eav\Model\Entity\Collection\AbstractCollection */ public function getItemsCollection($useCache = true) { @@ -1429,7 +1441,7 @@ public function getAllVisibleItems() */ public function hasItems() { - return sizeof($this->getAllItems()) > 0; + return count($this->getAllItems()) > 0; } /** @@ -1488,7 +1500,7 @@ public function getItemById($itemId) /** * Delete quote item. If it does not have identifier then it will be only removed from collection * - * @param \Magento\Quote\Model\Quote\Item $item + * @param \Magento\Quote\Model\Quote\Item $item * @return $this */ public function deleteItem(\Magento\Quote\Model\Quote\Item $item) @@ -1518,7 +1530,7 @@ public function deleteItem(\Magento\Quote\Model\Quote\Item $item) /** * Remove quote item by item identifier * - * @param int $itemId + * @param int $itemId * @return $this */ public function removeItem($itemId) @@ -1569,7 +1581,7 @@ public function removeAllItems() /** * Adding new item to quote * - * @param \Magento\Quote\Model\Quote\Item $item + * @param \Magento\Quote\Model\Quote\Item $item * @return $this * @throws \Magento\Framework\Exception\LocalizedException */ @@ -2363,7 +2375,7 @@ public function hasVirtualItems() /** * Merge quotes * - * @param Quote $quote + * @param Quote $quote * @return $this */ public function merge(Quote $quote) @@ -2596,4 +2608,34 @@ public function setExtensionAttributes(\Magento\Quote\Api\Data\CartExtensionInte { return $this->_setExtensionAttributes($extensionAttributes); } + + /** + * Check is address allowed for store + * + * @param Address $address + * @param int|null $storeId + * @return bool + */ + private function isAddressAllowedForWebsite(Address $address, $storeId): bool + { + $allowedCountries = $this->allowedCountriesReader->getAllowedCountries(ScopeInterface::SCOPE_STORE, $storeId); + + return in_array($address->getCountryId(), $allowedCountries); + } + + /** + * Assign address to quote + * + * @param Address $address + * @param bool $isBillingAddress + * @return void + */ + private function assignAddress(Address $address, bool $isBillingAddress = true): void + { + if ($this->isAddressAllowedForWebsite($address, $this->getStoreId())) { + $isBillingAddress + ? $this->setBillingAddress($address) + : $this->setShippingAddress($address); + } + } } diff --git a/app/code/Magento/Quote/Model/QuoteManagement.php b/app/code/Magento/Quote/Model/QuoteManagement.php index 0ad99ffe759f6..5bfbc80452bf9 100644 --- a/app/code/Magento/Quote/Model/QuoteManagement.php +++ b/app/code/Magento/Quote/Model/QuoteManagement.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Quote\Model; @@ -37,9 +38,9 @@ class QuoteManagement implements \Magento\Quote\Api\CartManagementInterface protected $eventManager; /** - * @var QuoteValidator + * @var SubmitQuoteValidator */ - protected $quoteValidator; + private $submitQuoteValidator; /** * @var OrderFactory @@ -158,7 +159,7 @@ class QuoteManagement implements \Magento\Quote\Api\CartManagementInterface /** * @param EventManager $eventManager - * @param QuoteValidator $quoteValidator + * @param SubmitQuoteValidator $submitQuoteValidator * @param OrderFactory $orderFactory * @param OrderManagement $orderManagement * @param CustomerManagement $customerManagement @@ -185,7 +186,7 @@ class QuoteManagement implements \Magento\Quote\Api\CartManagementInterface */ public function __construct( EventManager $eventManager, - QuoteValidator $quoteValidator, + SubmitQuoteValidator $submitQuoteValidator, OrderFactory $orderFactory, OrderManagement $orderManagement, CustomerManagement $customerManagement, @@ -210,7 +211,7 @@ public function __construct( \Magento\Framework\HTTP\PhpEnvironment\RemoteAddress $remoteAddress = null ) { $this->eventManager = $eventManager; - $this->quoteValidator = $quoteValidator; + $this->submitQuoteValidator = $submitQuoteValidator; $this->orderFactory = $orderFactory; $this->orderManagement = $orderManagement; $this->customerManagement = $customerManagement; @@ -267,6 +268,8 @@ public function createEmptyCartForCustomer($customerId) $storeId = $this->storeManager->getStore()->getStoreId(); $quote = $this->createCustomerCart($customerId, $storeId); + $this->_prepareCustomerQuote($quote); + try { $this->quoteRepository->save($quote); } catch (\Exception $e) { @@ -299,7 +302,7 @@ public function assignCustomer($cartId, $customerId, $storeId) throw new StateException( __("The customer can't be assigned to the cart because the customer already has an active cart.") ); - // phpcs:ignore Magento2.CodeAnalysis.EmptyBlock + // phpcs:ignore Magento2.CodeAnalysis.EmptyBlock } catch (\Magento\Framework\Exception\NoSuchEntityException $e) { } @@ -358,13 +361,15 @@ public function placeOrder($cartId, PaymentInterface $paymentMethod = null) { $quote = $this->quoteRepository->getActive($cartId); if ($paymentMethod) { - $paymentMethod->setChecks([ - \Magento\Payment\Model\Method\AbstractMethod::CHECK_USE_CHECKOUT, - \Magento\Payment\Model\Method\AbstractMethod::CHECK_USE_FOR_COUNTRY, - \Magento\Payment\Model\Method\AbstractMethod::CHECK_USE_FOR_CURRENCY, - \Magento\Payment\Model\Method\AbstractMethod::CHECK_ORDER_TOTAL_MIN_MAX, - \Magento\Payment\Model\Method\AbstractMethod::CHECK_ZERO_TOTAL, - ]); + $paymentMethod->setChecks( + [ + \Magento\Payment\Model\Method\AbstractMethod::CHECK_USE_CHECKOUT, + \Magento\Payment\Model\Method\AbstractMethod::CHECK_USE_FOR_COUNTRY, + \Magento\Payment\Model\Method\AbstractMethod::CHECK_USE_FOR_CURRENCY, + \Magento\Payment\Model\Method\AbstractMethod::CHECK_ORDER_TOTAL_MIN_MAX, + \Magento\Payment\Model\Method\AbstractMethod::CHECK_ZERO_TOTAL + ] + ); $quote->getPayment()->setQuote($quote); $data = $paymentMethod->getData(); @@ -484,7 +489,7 @@ protected function resolveItems(QuoteEntity $quote) protected function submitQuote(QuoteEntity $quote, $orderData = []) { $order = $this->orderFactory->create(); - $this->quoteValidator->validateBeforeSubmit($quote); + $this->submitQuoteValidator->validateQuote($quote); if (!$quote->getCustomerIsGuest()) { if ($quote->getCustomerId()) { $this->_prepareCustomerQuote($quote); @@ -539,6 +544,7 @@ protected function submitQuote(QuoteEntity $quote, $orderData = []) $order->setCustomerFirstname($quote->getCustomerFirstname()); $order->setCustomerMiddlename($quote->getCustomerMiddlename()); $order->setCustomerLastname($quote->getCustomerLastname()); + $this->submitQuoteValidator->validateOrder($order); $this->eventManager->dispatch( 'sales_model_service_quote_submit_before', @@ -566,7 +572,7 @@ protected function submitQuote(QuoteEntity $quote, $orderData = []) } /** - * Prepare quote for customer order submit + * Prepare address for customer quote. * * @param Quote $quote * @return void @@ -586,41 +592,69 @@ protected function _prepareCustomerQuote($quote) if ($shipping && !$shipping->getSameAsBilling() && (!$shipping->getCustomerId() || $shipping->getSaveInAddressBook()) ) { - $shippingAddress = $shipping->exportCustomerAddress(); - if (!$hasDefaultShipping) { - //Make provided address as default shipping address - $shippingAddress->setIsDefaultShipping(true); - $hasDefaultShipping = true; - if (!$hasDefaultBilling && !$billing->getSaveInAddressBook()) { - $shippingAddress->setIsDefaultBilling(true); - $hasDefaultBilling = true; + if ($shipping->getQuoteId()) { + $shippingAddress = $shipping->exportCustomerAddress(); + } else { + $defaultShipping = $this->customerRepository->getById($customer->getId())->getDefaultShipping(); + if ($defaultShipping) { + try { + $shippingAddress = $this->addressRepository->getById($defaultShipping); + // phpcs:ignore Magento2.CodeAnalysis.EmptyBlock + } catch (LocalizedException $e) { + // no address + } + } + } + if (isset($shippingAddress)) { + if (!$hasDefaultShipping) { + //Make provided address as default shipping address + $shippingAddress->setIsDefaultShipping(true); + $hasDefaultShipping = true; + if (!$hasDefaultBilling && !$billing->getSaveInAddressBook()) { + $shippingAddress->setIsDefaultBilling(true); + $hasDefaultBilling = true; + } } + //save here new customer address + $shippingAddress->setCustomerId($quote->getCustomerId()); + $this->addressRepository->save($shippingAddress); + $quote->addCustomerAddress($shippingAddress); + $shipping->setCustomerAddressData($shippingAddress); + $this->addressesToSync[] = $shippingAddress->getId(); + $shipping->setCustomerAddressId($shippingAddress->getId()); } - //save here new customer address - $shippingAddress->setCustomerId($quote->getCustomerId()); - $this->addressRepository->save($shippingAddress); - $quote->addCustomerAddress($shippingAddress); - $shipping->setCustomerAddressData($shippingAddress); - $this->addressesToSync[] = $shippingAddress->getId(); - $shipping->setCustomerAddressId($shippingAddress->getId()); } if (!$billing->getCustomerId() || $billing->getSaveInAddressBook()) { - $billingAddress = $billing->exportCustomerAddress(); - if (!$hasDefaultBilling) { - //Make provided address as default shipping address - if (!$hasDefaultShipping) { + if ($billing->getQuoteId()) { + $billingAddress = $billing->exportCustomerAddress(); + } else { + $defaultBilling = $this->customerRepository->getById($customer->getId())->getDefaultBilling(); + if ($defaultBilling) { + try { + $billingAddress = $this->addressRepository->getById($defaultBilling); + // phpcs:ignore Magento2.CodeAnalysis.EmptyBlock + } catch (LocalizedException $e) { + // no address + } + } + } + if (isset($billingAddress)) { + if (!$hasDefaultBilling) { //Make provided address as default shipping address - $billingAddress->setIsDefaultShipping(true); + if (!$hasDefaultShipping) { + //Make provided address as default shipping address + $billingAddress->setIsDefaultShipping(true); + } + $billingAddress->setIsDefaultBilling(true); } - $billingAddress->setIsDefaultBilling(true); + $billingAddress->setCustomerId($quote->getCustomerId()); + $this->addressRepository->save($billingAddress); + $quote->addCustomerAddress($billingAddress); + $billing->setCustomerAddressData($billingAddress); + $this->addressesToSync[] = $billingAddress->getId(); + $billing->setCustomerAddressId($billingAddress->getId()); } - $billingAddress->setCustomerId($quote->getCustomerId()); - $this->addressRepository->save($billingAddress); - $quote->addCustomerAddress($billingAddress); - $billing->setCustomerAddressData($billingAddress); - $this->addressesToSync[] = $billingAddress->getId(); - $billing->setCustomerAddressId($billingAddress->getId()); } if ($shipping && !$shipping->getCustomerId() && !$hasDefaultBilling) { $shipping->setIsDefaultBilling(true); @@ -654,13 +688,12 @@ private function rollbackAddresses( 'exception' => $e, ] ); - // phpcs:ignore Magento2.Exceptions.ThrowCatch + // phpcs:ignore Magento2.Exceptions.ThrowCatch } catch (\Exception $consecutiveException) { $message = sprintf( "An exception occurred on 'sales_model_service_quote_submit_failure' event: %s", $consecutiveException->getMessage() ); - // phpcs:ignore Magento2.Exceptions.DirectThrow throw new \Exception($message, 0, $e); } diff --git a/app/code/Magento/Quote/Model/QuoteRepository/SaveHandler.php b/app/code/Magento/Quote/Model/QuoteRepository/SaveHandler.php index 67310a1cc17cf..12a71648690d4 100644 --- a/app/code/Magento/Quote/Model/QuoteRepository/SaveHandler.php +++ b/app/code/Magento/Quote/Model/QuoteRepository/SaveHandler.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Quote\Model\QuoteRepository; use Magento\Quote\Api\Data\CartInterface; @@ -10,7 +12,11 @@ use Magento\Framework\App\ObjectManager; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\Exception\InputException; +use Magento\Quote\Api\Data\AddressInterfaceFactory; +/** + * Handler for saving quote. + */ class SaveHandler { /** @@ -38,19 +44,26 @@ class SaveHandler */ private $addressRepository; + /** + * @var AddressInterfaceFactory + */ + private $quoteAddressFactory; + /** * @param \Magento\Quote\Model\ResourceModel\Quote $quoteResource * @param \Magento\Quote\Model\Quote\Item\CartItemPersister $cartItemPersister * @param \Magento\Quote\Model\Quote\Address\BillingAddressPersister $billingAddressPersister * @param \Magento\Quote\Model\Quote\ShippingAssignment\ShippingAssignmentPersister $shippingAssignmentPersister * @param AddressRepositoryInterface $addressRepository + * @param AddressInterfaceFactory|null $addressFactory */ public function __construct( \Magento\Quote\Model\ResourceModel\Quote $quoteResource, \Magento\Quote\Model\Quote\Item\CartItemPersister $cartItemPersister, \Magento\Quote\Model\Quote\Address\BillingAddressPersister $billingAddressPersister, \Magento\Quote\Model\Quote\ShippingAssignment\ShippingAssignmentPersister $shippingAssignmentPersister, - AddressRepositoryInterface $addressRepository = null + AddressRepositoryInterface $addressRepository = null, + AddressInterfaceFactory $addressFactory = null ) { $this->quoteResourceModel = $quoteResource; $this->cartItemPersister = $cartItemPersister; @@ -58,6 +71,8 @@ public function __construct( $this->shippingAssignmentPersister = $shippingAssignmentPersister; $this->addressRepository = $addressRepository ?: ObjectManager::getInstance()->get(AddressRepositoryInterface::class); + $this->quoteAddressFactory = $addressFactory ?:ObjectManager::getInstance() + ->get(AddressInterfaceFactory::class); } /** @@ -80,6 +95,9 @@ public function save(CartInterface $quote) /** @var \Magento\Quote\Model\Quote\Item $item */ if (!$item->isDeleted()) { $quote->setLastAddedItem($this->cartItemPersister->save($quote, $item)); + } elseif (count($items) === 1) { + $quote->setBillingAddress($this->quoteAddressFactory->create()); + $quote->setShippingAddress($this->quoteAddressFactory->create()); } } } diff --git a/app/code/Magento/Quote/Model/SubmitQuoteValidator.php b/app/code/Magento/Quote/Model/SubmitQuoteValidator.php new file mode 100644 index 0000000000000..b97eeb9a522d3 --- /dev/null +++ b/app/code/Magento/Quote/Model/SubmitQuoteValidator.php @@ -0,0 +1,71 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Quote\Model; + +use Magento\Framework\Exception\LocalizedException; +use Magento\Sales\Model\Order; +use Magento\Sales\Model\Order\Address\Validator as OrderAddressValidator; + +/** + * Validates quote and order before quote submit. + */ +class SubmitQuoteValidator +{ + /** + * @var QuoteValidator + */ + private $quoteValidator; + + /** + * @var OrderAddressValidator + */ + private $orderAddressValidator; + + /** + * @param QuoteValidator $quoteValidator + * @param OrderAddressValidator $orderAddressValidator + */ + public function __construct( + QuoteValidator $quoteValidator, + OrderAddressValidator $orderAddressValidator + ) { + $this->quoteValidator = $quoteValidator; + $this->orderAddressValidator = $orderAddressValidator; + } + + /** + * Validates quote. + * + * @param Quote $quote + * @return void + * @throws LocalizedException + */ + public function validateQuote(Quote $quote): void + { + $this->quoteValidator->validateBeforeSubmit($quote); + } + + /** + * Validates order. + * + * @param Order $order + * @return void + * @throws LocalizedException + */ + public function validateOrder(Order $order): void + { + foreach ($order->getAddresses() as $address) { + $errors = $this->orderAddressValidator->validate($address); + if (!empty($errors)) { + throw new LocalizedException( + __("Failed address validation:\n%1", implode("\n", $errors)) + ); + } + } + } +} diff --git a/app/code/Magento/Quote/Test/Unit/Model/QuoteManagementTest.php b/app/code/Magento/Quote/Test/Unit/Model/QuoteManagementTest.php index 8d8200cd6ef62..cd2afc39733f2 100644 --- a/app/code/Magento/Quote/Test/Unit/Model/QuoteManagementTest.php +++ b/app/code/Magento/Quote/Test/Unit/Model/QuoteManagementTest.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Quote\Test\Unit\Model; @@ -17,6 +18,8 @@ /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @SuppressWarnings(PHPMD.TooManyFields) + * @SuppressWarnings(PHPMD.TooManyPublicMethods) + * @SuppressWarnings(PHPMD.ExcessiveClassLength) */ class QuoteManagementTest extends \PHPUnit\Framework\TestCase { @@ -26,9 +29,9 @@ class QuoteManagementTest extends \PHPUnit\Framework\TestCase protected $model; /** - * @var \Magento\Quote\Model\QuoteValidator|\PHPUnit_Framework_MockObject_MockObject + * @var \Magento\Quote\Model\SubmitQuoteValidator|\PHPUnit_Framework_MockObject_MockObject */ - protected $quoteValidator; + protected $submitQuoteValidator; /** * @var \Magento\Framework\Event\ManagerInterface|\PHPUnit_Framework_MockObject_MockObject @@ -162,7 +165,7 @@ protected function setUp() { $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $this->quoteValidator = $this->createMock(\Magento\Quote\Model\QuoteValidator::class); + $this->submitQuoteValidator = $this->createMock(\Magento\Quote\Model\SubmitQuoteValidator::class); $this->eventManager = $this->getMockForAbstractClass(\Magento\Framework\Event\ManagerInterface::class); $this->orderFactory = $this->createPartialMock( \Magento\Sales\Api\Data\OrderInterfaceFactory::class, @@ -195,21 +198,24 @@ protected function setUp() ['getStore', 'getStoreId'] ); - $this->quoteMock = $this->createPartialMock(\Magento\Quote\Model\Quote::class, [ - 'assignCustomer', - 'collectTotals', - 'getBillingAddress', - 'getCheckoutMethod', - 'getPayment', - 'setCheckoutMethod', - 'setCustomerEmail', - 'setCustomerGroupId', - 'setCustomerId', - 'setCustomerIsGuest', - 'setRemoteIp', - 'setXForwardedFor', - 'getId', - ]); + $this->quoteMock = $this->createPartialMock( + \Magento\Quote\Model\Quote::class, + [ + 'assignCustomer', + 'collectTotals', + 'getBillingAddress', + 'getCheckoutMethod', + 'getPayment', + 'setCheckoutMethod', + 'setCustomerEmail', + 'setCustomerGroupId', + 'setCustomerId', + 'setCustomerIsGuest', + 'setRemoteIp', + 'setXForwardedFor', + 'getId' + ] + ); $this->quoteAddressFactory = $this->createPartialMock( \Magento\Quote\Model\Quote\AddressFactory::class, @@ -232,7 +238,7 @@ protected function setUp() \Magento\Quote\Model\QuoteManagement::class, [ 'eventManager' => $this->eventManager, - 'quoteValidator' => $this->quoteValidator, + 'submitQuoteValidator' => $this->submitQuoteValidator, 'orderFactory' => $this->orderFactory, 'orderManagement' => $this->orderManagement, 'customerManagement' => $this->customerManagement, @@ -307,6 +313,14 @@ public function testCreateEmptyCartForCustomer() ->method('getActiveForCustomer') ->with($userId) ->willThrowException(new NoSuchEntityException()); + $customer = $this->getMockBuilder(\Magento\Customer\Api\Data\CustomerInterface::class) + ->setMethods(['getDefaultBilling'])->disableOriginalConstructor()->getMockForAbstractClass(); + $quoteAddress = $this->createPartialMock( + \Magento\Quote\Model\Quote\Address::class, + ['getCustomerId'] + ); + $quoteAddress->expects($this->atLeastOnce())->method('getCustomerId')->willReturn(567); + $quoteMock->expects($this->atLeastOnce())->method('getBillingAddress')->willReturn($quoteAddress); $this->quoteFactoryMock->expects($this->once())->method('create')->willReturn($quoteMock); $quoteMock->expects($this->any())->method('setStoreId')->with($storeId); @@ -314,6 +328,8 @@ public function testCreateEmptyCartForCustomer() $this->quoteRepositoryMock->expects($this->once())->method('save')->with($quoteMock); $quoteMock->expects($this->once())->method('getId')->willReturn($quoteId); + $this->customerRepositoryMock->expects($this->atLeastOnce())->method('getById')->willReturn($customer); + $customer->expects($this->atLeastOnce())->method('getDefaultBilling')->willReturn(0); $this->storeManagerMock->expects($this->once())->method('getStore')->willReturnSelf(); $this->storeManagerMock->expects($this->once())->method('getStoreId')->willReturn($storeId); @@ -333,6 +349,17 @@ public function testCreateEmptyCartForCustomerReturnExistsQuote() ->method('getActiveForCustomer') ->with($userId)->willReturn($quoteMock); + $customer = $this->getMockBuilder(\Magento\Customer\Api\Data\CustomerInterface::class) + ->setMethods(['getDefaultBilling'])->disableOriginalConstructor()->getMockForAbstractClass(); + $quoteAddress = $this->createPartialMock( + \Magento\Quote\Model\Quote\Address::class, + ['getCustomerId'] + ); + $quoteAddress->expects($this->atLeastOnce())->method('getCustomerId')->willReturn(567); + $quoteMock->expects($this->atLeastOnce())->method('getBillingAddress')->willReturn($quoteAddress); + $this->customerRepositoryMock->expects($this->atLeastOnce())->method('getById')->willReturn($customer); + $customer->expects($this->atLeastOnce())->method('getDefaultBilling')->willReturn(0); + $this->quoteFactoryMock->expects($this->never())->method('create')->willReturn($quoteMock); $this->quoteRepositoryMock->expects($this->once())->method('save')->with($quoteMock); @@ -564,7 +591,10 @@ public function testSubmit() $quoteId = 1; $quoteItem = $this->createMock(\Magento\Quote\Model\Quote\Item::class); $billingAddress = $this->createMock(\Magento\Quote\Model\Quote\Address::class); - $shippingAddress = $this->createMock(\Magento\Quote\Model\Quote\Address::class); + $shippingAddress = $this->createPartialMock( + \Magento\Quote\Model\Quote\Address::class, + ['getQuoteId', 'getShippingMethod', 'getId'] + ); $payment = $this->createMock(\Magento\Quote\Model\Quote\Payment::class); $baseOrder = $this->createMock(\Magento\Sales\Api\Data\OrderInterface::class); $convertedBilling = $this->createPartialMockForAbstractClass(OrderAddressInterface::class, ['setData']); @@ -587,7 +617,9 @@ public function testSubmit() $shippingAddress ); - $this->quoteValidator->expects($this->once())->method('validateBeforeSubmit')->with($quote); + $this->submitQuoteValidator->expects($this->once()) + ->method('validateQuote') + ->with($quote); $this->quoteAddressToOrder->expects($this->once()) ->method('convert') ->with($shippingAddress, $orderData) @@ -681,7 +713,7 @@ public function testPlaceOrderIfCustomerIsGuest() ->setConstructorArgs( [ 'eventManager' => $this->eventManager, - 'quoteValidator' => $this->quoteValidator, + 'quoteValidator' => $this->submitQuoteValidator, 'orderFactory' => $this->orderFactory, 'orderManagement' => $this->orderManagement, 'customerManagement' => $this->customerManagement, @@ -744,8 +776,8 @@ public function testPlaceOrder() ->setMethods(['submit']) ->setConstructorArgs( [ - 'eventManager' => $this->eventManager, - 'quoteValidator' => $this->quoteValidator, + 'eventManager' => $this->eventManager, + 'quoteValidator' => $this->submitQuoteValidator, 'orderFactory' => $this->orderFactory, 'orderManagement' => $this->orderManagement, 'customerManagement' => $this->customerManagement, @@ -886,6 +918,7 @@ protected function getQuote( $quote->expects($this->any()) ->method('getShippingAddress') ->willReturn($shippingAddress); + $shippingAddress->expects($this->any())->method('getQuoteId')->willReturn($id); } $quote->expects($this->any()) ->method('getBillingAddress') @@ -982,6 +1015,9 @@ protected function prepareOrderFactory( return $order; } + /** + * @throws NoSuchEntityException + */ public function testGetCartForCustomer() { $customerId = 100; @@ -1026,6 +1062,11 @@ protected function setPropertyValue(&$object, $property, $value) return $object; } + /** + * Test submit for customer + * + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ public function testSubmitForCustomer() { $orderData = []; @@ -1035,7 +1076,10 @@ public function testSubmitForCustomer() $quoteId = 1; $quoteItem = $this->createMock(\Magento\Quote\Model\Quote\Item::class); $billingAddress = $this->createMock(\Magento\Quote\Model\Quote\Address::class); - $shippingAddress = $this->createMock(\Magento\Quote\Model\Quote\Address::class); + $shippingAddress = $this->createPartialMock( + \Magento\Quote\Model\Quote\Address::class, + ['getQuoteId', 'getShippingMethod', 'getId', 'exportCustomerAddress'] + ); $payment = $this->createMock(\Magento\Quote\Model\Quote\Payment::class); $baseOrder = $this->createMock(\Magento\Sales\Api\Data\OrderInterface::class); $convertedBilling = $this->createPartialMockForAbstractClass(OrderAddressInterface::class, ['setData']); @@ -1058,7 +1102,8 @@ public function testSubmitForCustomer() $shippingAddress ); - $this->quoteValidator->expects($this->once())->method('validateBeforeSubmit')->with($quote); + $this->submitQuoteValidator->method('validateQuote') + ->with($quote); $this->quoteAddressToOrder->expects($this->once()) ->method('convert') ->with($shippingAddress, $orderData) diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/Payment/AdditionalDataProviderInterface.php b/app/code/Magento/QuoteGraphQl/Model/Cart/Payment/AdditionalDataProviderInterface.php new file mode 100644 index 0000000000000..414a73508c94f --- /dev/null +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/Payment/AdditionalDataProviderInterface.php @@ -0,0 +1,22 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\QuoteGraphQl\Model\Cart\Payment; + +/** + * Interface for payment method additional data provider + */ +interface AdditionalDataProviderInterface +{ + /** + * Return Additional Data + * + * @param array $args + * @return array + */ + public function getData(array $args): array; +} diff --git a/app/code/Magento/QuoteGraphQl/Model/Cart/Payment/AdditionalDataProviderPool.php b/app/code/Magento/QuoteGraphQl/Model/Cart/Payment/AdditionalDataProviderPool.php new file mode 100644 index 0000000000000..87c6456011502 --- /dev/null +++ b/app/code/Magento/QuoteGraphQl/Model/Cart/Payment/AdditionalDataProviderPool.php @@ -0,0 +1,44 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\QuoteGraphQl\Model\Cart\Payment; + +/** + * Pool model for AdditionalDataProvider + */ +class AdditionalDataProviderPool +{ + /** + * @var AdditionalDataProviderInterface[] + */ + private $dataProviders; + + /** + * @param array $dataProviders + */ + public function __construct(array $dataProviders = []) + { + $this->dataProviders = $dataProviders; + } + + /** + * Return additional data for the payment method + * + * @param string $methodCode + * @param array $args + * @return array + */ + public function getData(string $methodCode, array $args): array + { + $additionalData = []; + if (isset($this->dataProviders[$methodCode])) { + $additionalData = $this->dataProviders[$methodCode]->getData($args); + } + + return $additionalData; + } +} diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/SelectedPaymentMethod.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/SelectedPaymentMethod.php index 8cda06eba3c91..44912d249cc24 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/SelectedPaymentMethod.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/SelectedPaymentMethod.php @@ -34,9 +34,15 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value return []; } + try { + $methodTitle = $payment->getMethodInstance()->getTitle(); + } catch (LocalizedException $e) { + $methodTitle = ''; + } + return [ - 'code' => $payment->getMethod(), - 'title' => $payment->getMethodInstance()->getTitle(), + 'code' => $payment->getMethod() ?? '', + 'title' => $methodTitle, 'purchase_order_number' => $payment->getPoNumber(), ]; } diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/SetPaymentMethodOnCart.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/SetPaymentMethodOnCart.php index 7b81964f111c6..5d8f2ba62c778 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/SetPaymentMethodOnCart.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/SetPaymentMethodOnCart.php @@ -18,6 +18,8 @@ use Magento\QuoteGraphQl\Model\Cart\GetCartForUser; use Magento\Quote\Api\Data\PaymentInterfaceFactory; use Magento\Quote\Api\PaymentMethodManagementInterface; +use Magento\QuoteGraphQl\Model\Cart\Payment\AdditionalDataProviderPool; +use Magento\Framework\App\ObjectManager; /** * Mutation resolver for setting payment method for shopping cart @@ -39,19 +41,28 @@ class SetPaymentMethodOnCart implements ResolverInterface */ private $paymentFactory; + /** + * @var AdditionalDataProviderPool + */ + private $additionalDataProviderPool; + /** * @param GetCartForUser $getCartForUser * @param PaymentMethodManagementInterface $paymentMethodManagement * @param PaymentInterfaceFactory $paymentFactory + * @param AdditionalDataProviderPool $additionalDataProviderPool */ public function __construct( GetCartForUser $getCartForUser, PaymentMethodManagementInterface $paymentMethodManagement, - PaymentInterfaceFactory $paymentFactory + PaymentInterfaceFactory $paymentFactory, + AdditionalDataProviderPool $additionalDataProviderPool = null ) { $this->getCartForUser = $getCartForUser; $this->paymentMethodManagement = $paymentMethodManagement; $this->paymentFactory = $paymentFactory; + $this->additionalDataProviderPool = $additionalDataProviderPool + ?: ObjectManager::getInstance()->get(AdditionalDataProviderPool::class); } /** @@ -70,16 +81,17 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value $paymentMethodCode = $args['input']['payment_method']['code']; $poNumber = $args['input']['payment_method']['purchase_order_number'] ?? null; - $additionalData = $args['input']['payment_method']['additional_data'] ?? []; + $additionalData = $this->additionalDataProviderPool->getData($paymentMethodCode, $args) ?? []; $cart = $this->getCartForUser->execute($maskedCartId, $context->getUserId()); - $payment = $this->paymentFactory->create([ + $payment = $this->paymentFactory->create( + [ 'data' => [ PaymentInterface::KEY_METHOD => $paymentMethodCode, PaymentInterface::KEY_PO_NUMBER => $poNumber, PaymentInterface::KEY_ADDITIONAL_DATA => $additionalData, - ] - ]); + ]] + ); try { $this->paymentMethodManagement->set($cart->getId(), $payment); diff --git a/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddress/SelectedShippingMethod.php b/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddress/SelectedShippingMethod.php index 05bc196acfc22..b359971880036 100644 --- a/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddress/SelectedShippingMethod.php +++ b/app/code/Magento/QuoteGraphQl/Model/Resolver/ShippingAddress/SelectedShippingMethod.php @@ -49,7 +49,7 @@ public function resolve(Field $field, $context, ResolveInfo $info, array $value $carrierTitle = null; $methodTitle = null; - if (count($rates) > 0) { + if (count($rates) > 0 && !empty($address->getShippingMethod())) { list($carrierCode, $methodCode) = explode('_', $address->getShippingMethod(), 2); /** @var Rate $rate */ diff --git a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls index 9e9c83b358206..02d94231b7570 100644 --- a/app/code/Magento/QuoteGraphQl/etc/schema.graphqls +++ b/app/code/Magento/QuoteGraphQl/etc/schema.graphqls @@ -131,6 +131,10 @@ input SetPaymentMethodOnCartInput { input PaymentMethodInput { code: String! @doc(description:"Payment method code") purchase_order_number: String @doc(description:"Purchase order number") + additional_data: PaymentMethodAdditionalDataInput @doc(description: "Additional payment data") +} + +input PaymentMethodAdditionalDataInput { } input SetGuestEmailOnCartInput { diff --git a/app/code/Magento/RelatedProductGraphQl/Model/DataProvider/RelatedProductDataProvider.php b/app/code/Magento/RelatedProductGraphQl/Model/DataProvider/RelatedProductDataProvider.php new file mode 100644 index 0000000000000..173c0a94312ee --- /dev/null +++ b/app/code/Magento/RelatedProductGraphQl/Model/DataProvider/RelatedProductDataProvider.php @@ -0,0 +1,78 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\RelatedProductGraphQl\Model\DataProvider; + +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Link; +use Magento\Catalog\Model\Product\LinkFactory; + +/** + * Related Products Data Provider + */ +class RelatedProductDataProvider +{ + /** + * @var LinkFactory + */ + private $linkFactory; + + /** + * @param LinkFactory $linkFactory + */ + public function __construct( + LinkFactory $linkFactory + ) { + $this->linkFactory = $linkFactory; + } + + /** + * Related Products Data + * + * @param Product $product + * @param array $fields + * @param int $linkType + * @return array + */ + public function getData(Product $product, array $fields, int $linkType): array + { + $relatedProducts = $this->getRelatedProducts($product, $fields, $linkType); + + $productsData = []; + foreach ($relatedProducts as $relatedProduct) { + $productData = $relatedProduct->getData(); + $productData['model'] = $relatedProduct; + $productsData[] = $productData; + } + return $productsData; + } + + /** + * Get Related Products + * + * @param Product $product + * @param array $fields + * @param int $linkType + * @return Product[] + */ + private function getRelatedProducts(Product $product, array $fields, int $linkType): array + { + /** @var Link $link */ + $link = $this->linkFactory->create([ 'data' => [ + 'link_type_id' => $linkType, + ]]); + + $collection = $link->getProductCollection(); + $collection->setIsStrongMode(); + foreach ($fields as $field) { + $collection->addAttributeToSelect($field); + } + $collection->setProduct($product); + + return $collection->getItems(); + } +} diff --git a/app/code/Magento/RelatedProductGraphQl/Model/Resolver/CrossSellProducts.php b/app/code/Magento/RelatedProductGraphQl/Model/Resolver/CrossSellProducts.php new file mode 100644 index 0000000000000..dcaa75b85f599 --- /dev/null +++ b/app/code/Magento/RelatedProductGraphQl/Model/Resolver/CrossSellProducts.php @@ -0,0 +1,59 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\RelatedProductGraphQl\Model\Resolver; + +use Magento\Catalog\Model\Product\Link; +use Magento\CatalogGraphQl\Model\Resolver\Product\ProductFieldsSelector; +use Magento\Framework\Exception\LocalizedException; +use Magento\RelatedProductGraphQl\Model\DataProvider\RelatedProductDataProvider; +use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Query\ResolverInterface; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; + +/** + * CrossSell Products Resolver + */ +class CrossSellProducts implements ResolverInterface +{ + /** + * @var ProductFieldsSelector + */ + private $productFieldsSelector; + + /** + * @var RelatedProductDataProvider + */ + private $relatedProductDataProvider; + + /** + * @param ProductFieldsSelector $productFieldsSelector + * @param RelatedProductDataProvider $relatedProductDataProvider + */ + public function __construct( + ProductFieldsSelector $productFieldsSelector, + RelatedProductDataProvider $relatedProductDataProvider + ) { + $this->productFieldsSelector = $productFieldsSelector; + $this->relatedProductDataProvider = $relatedProductDataProvider; + } + + /** + * @inheritdoc + */ + public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) + { + if (!isset($value['model'])) { + throw new LocalizedException(__('"model" value should be specified')); + } + $product = $value['model']; + $fields = $this->productFieldsSelector->getProductFieldsFromInfo($info, 'crosssell_products'); + + $data = $this->relatedProductDataProvider->getData($product, $fields, Link::LINK_TYPE_CROSSSELL); + return $data; + } +} diff --git a/app/code/Magento/RelatedProductGraphQl/Model/Resolver/RelatedProducts.php b/app/code/Magento/RelatedProductGraphQl/Model/Resolver/RelatedProducts.php new file mode 100644 index 0000000000000..568c3282a6ee8 --- /dev/null +++ b/app/code/Magento/RelatedProductGraphQl/Model/Resolver/RelatedProducts.php @@ -0,0 +1,59 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\RelatedProductGraphQl\Model\Resolver; + +use Magento\Catalog\Model\Product\Link; +use Magento\CatalogGraphQl\Model\Resolver\Product\ProductFieldsSelector; +use Magento\Framework\Exception\LocalizedException; +use Magento\RelatedProductGraphQl\Model\DataProvider\RelatedProductDataProvider; +use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Query\ResolverInterface; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; + +/** + * Related Products Resolver + */ +class RelatedProducts implements ResolverInterface +{ + /** + * @var ProductFieldsSelector + */ + private $productFieldsSelector; + + /** + * @var RelatedProductDataProvider + */ + private $relatedProductDataProvider; + + /** + * @param ProductFieldsSelector $productFieldsSelector + * @param RelatedProductDataProvider $relatedProductDataProvider + */ + public function __construct( + ProductFieldsSelector $productFieldsSelector, + RelatedProductDataProvider $relatedProductDataProvider + ) { + $this->productFieldsSelector = $productFieldsSelector; + $this->relatedProductDataProvider = $relatedProductDataProvider; + } + + /** + * @inheritdoc + */ + public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) + { + if (!isset($value['model'])) { + throw new LocalizedException(__('"model" value should be specified')); + } + $product = $value['model']; + $fields = $this->productFieldsSelector->getProductFieldsFromInfo($info, 'related_products'); + + $data = $this->relatedProductDataProvider->getData($product, $fields, Link::LINK_TYPE_RELATED); + return $data; + } +} diff --git a/app/code/Magento/RelatedProductGraphQl/Model/Resolver/UpSellProducts.php b/app/code/Magento/RelatedProductGraphQl/Model/Resolver/UpSellProducts.php new file mode 100644 index 0000000000000..df3a62408c53f --- /dev/null +++ b/app/code/Magento/RelatedProductGraphQl/Model/Resolver/UpSellProducts.php @@ -0,0 +1,59 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\RelatedProductGraphQl\Model\Resolver; + +use Magento\Catalog\Model\Product\Link; +use Magento\CatalogGraphQl\Model\Resolver\Product\ProductFieldsSelector; +use Magento\Framework\Exception\LocalizedException; +use Magento\RelatedProductGraphQl\Model\DataProvider\RelatedProductDataProvider; +use Magento\Framework\GraphQl\Config\Element\Field; +use Magento\Framework\GraphQl\Query\ResolverInterface; +use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; + +/** + * UpSell Products Resolver + */ +class UpSellProducts implements ResolverInterface +{ + /** + * @var ProductFieldsSelector + */ + private $productFieldsSelector; + + /** + * @var RelatedProductDataProvider + */ + private $relatedProductDataProvider; + + /** + * @param ProductFieldsSelector $productFieldsSelector + * @param RelatedProductDataProvider $relatedProductDataProvider + */ + public function __construct( + ProductFieldsSelector $productFieldsSelector, + RelatedProductDataProvider $relatedProductDataProvider + ) { + $this->productFieldsSelector = $productFieldsSelector; + $this->relatedProductDataProvider = $relatedProductDataProvider; + } + + /** + * @inheritdoc + */ + public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args = null) + { + if (!isset($value['model'])) { + throw new LocalizedException(__('"model" value should be specified')); + } + $product = $value['model']; + $fields = $this->productFieldsSelector->getProductFieldsFromInfo($info, 'upsell_products'); + + $data = $this->relatedProductDataProvider->getData($product, $fields, Link::LINK_TYPE_UPSELL); + return $data; + } +} diff --git a/app/code/Magento/RelatedProductGraphQl/README.md b/app/code/Magento/RelatedProductGraphQl/README.md new file mode 100644 index 0000000000000..5b57af5af9884 --- /dev/null +++ b/app/code/Magento/RelatedProductGraphQl/README.md @@ -0,0 +1,3 @@ +# RelatedProductGraphQl + +**RelatedProductGraphQl** provides endpoints for getting Cross Sell / Related/ Up Sell products data. \ No newline at end of file diff --git a/app/code/Magento/RelatedProductGraphQl/composer.json b/app/code/Magento/RelatedProductGraphQl/composer.json new file mode 100644 index 0000000000000..0d314d433f177 --- /dev/null +++ b/app/code/Magento/RelatedProductGraphQl/composer.json @@ -0,0 +1,26 @@ +{ + "name": "magento/module-related-product-graph-ql", + "description": "N/A", + "type": "magento2-module", + "require": { + "php": "~7.1.3||~7.2.0", + "magento/module-catalog": "*", + "magento/module-catalog-graph-ql": "*", + "magento/framework": "*" + }, + "suggest": { + "magento/module-graph-ql": "*" + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "autoload": { + "files": [ + "registration.php" + ], + "psr-4": { + "Magento\\RelatedProductGraphQl\\": "" + } + } +} diff --git a/app/code/Magento/RelatedProductGraphQl/etc/module.xml b/app/code/Magento/RelatedProductGraphQl/etc/module.xml new file mode 100644 index 0000000000000..e30775c253a45 --- /dev/null +++ b/app/code/Magento/RelatedProductGraphQl/etc/module.xml @@ -0,0 +1,10 @@ +<?xml version="1.0"?> +<!-- +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> + <module name="Magento_RelatedProductGraphQl"/> +</config> diff --git a/app/code/Magento/RelatedProductGraphQl/etc/schema.graphqls b/app/code/Magento/RelatedProductGraphQl/etc/schema.graphqls new file mode 100644 index 0000000000000..240fc6c4496f0 --- /dev/null +++ b/app/code/Magento/RelatedProductGraphQl/etc/schema.graphqls @@ -0,0 +1,8 @@ +# Copyright © Magento, Inc. All rights reserved. +# See COPYING.txt for license details. + +interface ProductInterface { + related_products: [ProductInterface] @doc(description: "RelatedProduct") @resolver(class: "Magento\\RelatedProductGraphQl\\Model\\Resolver\\RelatedProducts") + upsell_products: [ProductInterface] @doc(description: "RelatedProduct") @resolver(class: "Magento\\RelatedProductGraphQl\\Model\\Resolver\\UpSellProducts") + crosssell_products: [ProductInterface] @doc(description: "RelatedProduct") @resolver(class: "Magento\\RelatedProductGraphQl\\Model\\Resolver\\CrossSellProducts") +} \ No newline at end of file diff --git a/app/code/Magento/RelatedProductGraphQl/registration.php b/app/code/Magento/RelatedProductGraphQl/registration.php new file mode 100644 index 0000000000000..d18dfc27cc7c6 --- /dev/null +++ b/app/code/Magento/RelatedProductGraphQl/registration.php @@ -0,0 +1,9 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\Framework\Component\ComponentRegistrar; + +ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magento_RelatedProductGraphQl', __DIR__); diff --git a/app/code/Magento/Reports/Helper/Data.php b/app/code/Magento/Reports/Helper/Data.php index 411c7dde14c9d..cd105eb5f4bfd 100644 --- a/app/code/Magento/Reports/Helper/Data.php +++ b/app/code/Magento/Reports/Helper/Data.php @@ -4,15 +4,13 @@ * See COPYING.txt for license details. */ -/** - * Reports data helper - */ namespace Magento\Reports\Helper; use Magento\Framework\Data\Collection; -use Magento\Framework\Stdlib\DateTime; /** + * Reports data helper. + * * @api * @since 100.0.2 */ @@ -63,22 +61,27 @@ public function getIntervals($from, $to, $period = self::REPORT_PERIOD_TYPE_DAY) $dateStart = new \DateTime($from); $dateEnd = new \DateTime($to); + $dateFormat = 'Y-m-d'; + $dateInterval = new \DateInterval('P1D'); + switch ($period) { + case self::REPORT_PERIOD_TYPE_MONTH: + $dateFormat = 'Y-m'; + $dateInterval = new \DateInterval('P1M'); + break; + case self::REPORT_PERIOD_TYPE_YEAR: + $dateFormat = 'Y'; + $dateInterval = new \DateInterval('P1Y'); + break; + } while ($dateStart->diff($dateEnd)->invert == 0) { - switch ($period) { - case self::REPORT_PERIOD_TYPE_DAY: - $intervals[] = $dateStart->format('Y-m-d'); - $dateStart->add(new \DateInterval('P1D')); - break; - case self::REPORT_PERIOD_TYPE_MONTH: - $intervals[] = $dateStart->format('Y-m'); - $dateStart->add(new \DateInterval('P1M')); - break; - case self::REPORT_PERIOD_TYPE_YEAR: - $intervals[] = $dateStart->format('Y'); - $dateStart->add(new \DateInterval('P1Y')); - break; - } + $intervals[] = $dateStart->format($dateFormat); + $dateStart->add($dateInterval); } + + if (!in_array($dateEnd->format($dateFormat), $intervals)) { + $intervals[] = $dateEnd->format($dateFormat); + } + return $intervals; } diff --git a/app/code/Magento/Reports/Model/Product/Index/AbstractIndex.php b/app/code/Magento/Reports/Model/Product/Index/AbstractIndex.php index 7337286149cc3..78b6b7d6c9ab7 100644 --- a/app/code/Magento/Reports/Model/Product/Index/AbstractIndex.php +++ b/app/code/Magento/Reports/Model/Product/Index/AbstractIndex.php @@ -7,8 +7,11 @@ /** * Reports Product Index Abstract Model + * + * phpcs:disable Magento2.Classes.AbstractApi * @api * @since 100.0.2 + * @SuppressWarnings(PHPMD.CookieAndSessionMisuse) */ abstract class AbstractIndex extends \Magento\Framework\Model\AbstractModel { @@ -156,7 +159,7 @@ public function getStoreId() } /** - * On customer loggin merge visitor/customer index + * On customer login merge visitor/customer index * * @return $this */ diff --git a/app/code/Magento/Reports/Model/ResourceModel/Product/Sold/Collection.php b/app/code/Magento/Reports/Model/ResourceModel/Product/Sold/Collection.php index 61dc77d188438..35a14e09e314f 100644 --- a/app/code/Magento/Reports/Model/ResourceModel/Product/Sold/Collection.php +++ b/app/code/Magento/Reports/Model/ResourceModel/Product/Sold/Collection.php @@ -14,6 +14,8 @@ use Magento\Framework\DB\Select; /** + * Data collection. + * * @SuppressWarnings(PHPMD.DepthOfInheritance) * @api * @since 100.0.2 @@ -21,7 +23,7 @@ class Collection extends \Magento\Reports\Model\ResourceModel\Order\Collection { /** - * Set Date range to collection + * Set Date range to collection. * * @param int $from * @param int $to @@ -79,6 +81,10 @@ public function addOrderedQty($from = '', $to = '') )->having( 'order_items.qty_ordered > ?', 0 + )->columns( + 'SUM(order_items.qty_ordered) as ordered_qty' + )->group( + 'order_items.product_id' ); return $this; } @@ -116,6 +122,8 @@ public function setOrder($attribute, $dir = self::SORT_ORDER_DESC) } /** + * @inheritdoc + * * @return Select * @since 100.2.0 */ diff --git a/app/code/Magento/Reports/Model/ResourceModel/Report/Collection.php b/app/code/Magento/Reports/Model/ResourceModel/Report/Collection.php index 522bfa7bcf0bf..f2a761dbd5080 100644 --- a/app/code/Magento/Reports/Model/ResourceModel/Report/Collection.php +++ b/app/code/Magento/Reports/Model/ResourceModel/Report/Collection.php @@ -12,6 +12,8 @@ namespace Magento\Reports\Model\ResourceModel\Report; /** + * Class Collection + * * @api * @since 100.0.2 */ @@ -100,6 +102,7 @@ public function __construct( /** * Set period + * * @codeCoverageIgnore * * @param int $period @@ -113,6 +116,7 @@ public function setPeriod($period) /** * Set interval + * * @codeCoverageIgnore * * @param \DateTimeInterface $fromDate @@ -248,7 +252,7 @@ protected function _getYearInterval(\DateTime $dateStart, \DateTime $dateEnd, $f ? $this->_localeDate->convertConfigTimeToUtc($dateStart->format('Y-m-d 00:00:00')) : $this->_localeDate->convertConfigTimeToUtc($dateStart->format('Y-01-01 00:00:00')); - $interval['end'] = $dateStart->diff($dateEnd)->y == 0 + $interval['end'] = $dateStart->format('Y') === $dateEnd->format('Y') ? $this->_localeDate->convertConfigTimeToUtc( $dateStart->setDate($dateStart->format('Y'), $dateEnd->format('m'), $dateEnd->format('d')) ->format('Y-m-d 23:59:59') @@ -275,6 +279,7 @@ public function getPeriods() /** * Set store ids + * * @codeCoverageIgnore * * @param array $storeIds @@ -288,6 +293,7 @@ public function setStoreIds($storeIds) /** * Get store ids + * * @codeCoverageIgnore * * @return array @@ -309,6 +315,7 @@ public function getSize() /** * Set page size + * * @codeCoverageIgnore * * @param int $size @@ -322,6 +329,7 @@ public function setPageSize($size) /** * Get page size + * * @codeCoverageIgnore * * @return int diff --git a/app/code/Magento/Reports/Test/Mftf/Section/OrderReportMainSection.xml b/app/code/Magento/Reports/Test/Mftf/Section/OrderReportMainSection.xml index 7ad9bdfa8c12c..6b1dcbb110583 100644 --- a/app/code/Magento/Reports/Test/Mftf/Section/OrderReportMainSection.xml +++ b/app/code/Magento/Reports/Test/Mftf/Section/OrderReportMainSection.xml @@ -17,8 +17,8 @@ <element name="dateFrom" type="input" selector="#sales_report_from"/> <element name="dateTo" type="input" selector="#sales_report_to"/> <element name="orderStatus" type="select" selector="#sales_report_show_order_statuses"/> - <element name="optionAny" type="option" selector="//select[@id='sales_report_show_order_statuses']/option[contains(text(), 'Any')]"/> - <element name="optionSpecified" type="option" selector="//select[@id='sales_report_show_order_statuses']/option[contains(text(), 'Specified')]"/> + <element name="optionAny" type="select" selector="//select[@id='sales_report_show_order_statuses']/option[contains(text(), 'Any')]"/> + <element name="optionSpecified" type="select" selector="//select[@id='sales_report_show_order_statuses']/option[contains(text(), 'Specified')]"/> <element name="orderStatusSpecified" type="select" selector="#sales_report_order_statuses"/> </section> diff --git a/app/code/Magento/Reports/Test/Mftf/Test/CancelOrdersInOrderSalesReportTest.xml b/app/code/Magento/Reports/Test/Mftf/Test/CancelOrdersInOrderSalesReportTest.xml index 7cb23e54aa1b7..ee39eac1e3719 100644 --- a/app/code/Magento/Reports/Test/Mftf/Test/CancelOrdersInOrderSalesReportTest.xml +++ b/app/code/Magento/Reports/Test/Mftf/Test/CancelOrdersInOrderSalesReportTest.xml @@ -17,6 +17,9 @@ <severity value="MAJOR"/> <testCaseId value="MAGETWO-95960"/> <useCaseId value="MAGETWO-95823"/> + <skip> + <issueId value="MC-17274"/> + </skip> </annotations> <before> diff --git a/app/code/Magento/Reports/Test/Unit/Helper/DataTest.php b/app/code/Magento/Reports/Test/Unit/Helper/DataTest.php index 25981c9d3cfcd..d92b51e8b9b78 100644 --- a/app/code/Magento/Reports/Test/Unit/Helper/DataTest.php +++ b/app/code/Magento/Reports/Test/Unit/Helper/DataTest.php @@ -6,34 +6,41 @@ namespace Magento\Reports\Test\Unit\Helper; +use Magento\Framework\App\Helper\Context; +use Magento\Framework\Data\Collection; use Magento\Reports\Helper\Data; +use Magento\Reports\Model\Item; +use Magento\Reports\Model\ItemFactory; +/** + * Unit tests for \Magento\Reports\Helper\Data class. + */ class DataTest extends \PHPUnit\Framework\TestCase { /** - * @var \Magento\Reports\Helper\Data + * @var Data */ protected $data; /** - * @var \Magento\Framework\App\Helper\Context|\PHPUnit_Framework_MockObject_MockObject + * @var Context|\PHPUnit_Framework_MockObject_MockObject */ protected $contextMock; /** - * @var \Magento\Reports\Model\ItemFactory|\PHPUnit_Framework_MockObject_MockObject + * @var ItemFactory|\PHPUnit_Framework_MockObject_MockObject */ protected $itemFactoryMock; /** - * {@inheritDoc} + * @inheritdoc */ protected function setUp() { - $this->contextMock = $this->getMockBuilder(\Magento\Framework\App\Helper\Context::class) + $this->contextMock = $this->getMockBuilder(Context::class) ->disableOriginalConstructor() ->getMock(); - $this->itemFactoryMock = $this->getMockBuilder(\Magento\Reports\Model\ItemFactory::class) + $this->itemFactoryMock = $this->getMockBuilder(ItemFactory::class) ->setMethods(['create']) ->disableOriginalConstructor() ->getMock(); @@ -67,12 +74,12 @@ public function testGetIntervals($from, $to, $period, $results) */ public function testPrepareIntervalsCollection($from, $to, $period, $results) { - $collection = $this->getMockBuilder(\Magento\Framework\Data\Collection::class) + $collection = $this->getMockBuilder(Collection::class) ->disableOriginalConstructor() ->setMethods(['addItem']) ->getMock(); - $item = $this->getMockBuilder(\Magento\Reports\Model\Item::class) + $item = $this->getMockBuilder(Item::class) ->disableOriginalConstructor() ->setMethods(['setPeriod', 'setIsEmpty']) ->getMock(); @@ -103,44 +110,50 @@ public function intervalsDataProvider() [ 'from' => '2000-01-15 10:00:00', 'to' => '2000-01-15 11:00:00', - 'period' => \Magento\Reports\Helper\Data::REPORT_PERIOD_TYPE_DAY, - 'results' => ['2000-01-15'] + 'period' => Data::REPORT_PERIOD_TYPE_DAY, + 'results' => ['2000-01-15'], ], [ 'from' => '2000-01-15 10:00:00', 'to' => '2000-01-17 10:00:00', - 'period' => \Magento\Reports\Helper\Data::REPORT_PERIOD_TYPE_MONTH, - 'results' => ['2000-01'] + 'period' => Data::REPORT_PERIOD_TYPE_MONTH, + 'results' => ['2000-01'], ], [ 'from' => '2000-01-15 10:00:00', 'to' => '2000-02-15 10:00:00', - 'period' => \Magento\Reports\Helper\Data::REPORT_PERIOD_TYPE_YEAR, + 'period' => Data::REPORT_PERIOD_TYPE_YEAR, 'results' => ['2000'] ], [ 'from' => '2000-01-15 10:00:00', 'to' => '2000-01-16 11:00:00', - 'period' => \Magento\Reports\Helper\Data::REPORT_PERIOD_TYPE_DAY, - 'results' => ['2000-01-15', '2000-01-16'] + 'period' => Data::REPORT_PERIOD_TYPE_DAY, + 'results' => ['2000-01-15', '2000-01-16'], ], [ 'from' => '2000-01-15 10:00:00', 'to' => '2000-02-17 10:00:00', - 'period' => \Magento\Reports\Helper\Data::REPORT_PERIOD_TYPE_MONTH, - 'results' => ['2000-01', '2000-02'] + 'period' => Data::REPORT_PERIOD_TYPE_MONTH, + 'results' => ['2000-01', '2000-02'], ], [ 'from' => '2000-01-15 10:00:00', 'to' => '2003-02-15 10:00:00', - 'period' => \Magento\Reports\Helper\Data::REPORT_PERIOD_TYPE_YEAR, - 'results' => ['2000', '2001', '2002', '2003'] + 'period' => Data::REPORT_PERIOD_TYPE_YEAR, + 'results' => ['2000', '2001', '2002', '2003'], + ], + [ + 'from' => '2000-12-31 10:00:00', + 'to' => '2001-01-01 10:00:00', + 'period' => Data::REPORT_PERIOD_TYPE_YEAR, + 'results' => ['2000', '2001'], ], [ 'from' => '', 'to' => '', - 'period' => \Magento\Reports\Helper\Data::REPORT_PERIOD_TYPE_YEAR, - 'results' => [] + 'period' => Data::REPORT_PERIOD_TYPE_YEAR, + 'results' => [], ] ]; } diff --git a/app/code/Magento/Review/Block/Adminhtml/Edit/Form.php b/app/code/Magento/Review/Block/Adminhtml/Edit/Form.php index 4f7237a0b44be..346d5b60aad1b 100644 --- a/app/code/Magento/Review/Block/Adminhtml/Edit/Form.php +++ b/app/code/Magento/Review/Block/Adminhtml/Edit/Form.php @@ -75,6 +75,17 @@ protected function _prepareForm() $review = $this->_coreRegistry->registry('review_data'); $product = $this->_productFactory->create()->load($review->getEntityPkValue()); + $formActionParams = [ + 'id' => $this->getRequest()->getParam('id'), + 'ret' => $this->_coreRegistry->registry('ret') + ]; + if ($this->getRequest()->getParam('productId')) { + $formActionParams['productId'] = $this->getRequest()->getParam('productId'); + } + if ($this->getRequest()->getParam('customerId')) { + $formActionParams['customerId'] = $this->getRequest()->getParam('customerId'); + } + /** @var \Magento\Framework\Data\Form $form */ $form = $this->_formFactory->create( [ @@ -82,11 +93,7 @@ protected function _prepareForm() 'id' => 'edit_form', 'action' => $this->getUrl( 'review/*/save', - [ - 'id' => $this->getRequest()->getParam('id'), - 'ret' => $this->_coreRegistry->registry('ret'), - 'productId' => $this->getRequest()->getParam('productId') - ] + $formActionParams ), 'method' => 'post', ], diff --git a/app/code/Magento/Review/Block/Customer/View.php b/app/code/Magento/Review/Block/Customer/View.php index 237b972f16573..da5aff1f4d2f8 100644 --- a/app/code/Magento/Review/Block/Customer/View.php +++ b/app/code/Magento/Review/Block/Customer/View.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Review\Block\Customer; use Magento\Catalog\Model\Product; @@ -160,6 +161,7 @@ public function getRating() /** * Get rating summary * + * @deprecated * @return array */ public function getRatingSummary() @@ -201,26 +203,7 @@ public function dateFormat($date) } /** - * Get product reviews summary - * - * @param \Magento\Catalog\Model\Product $product - * @param bool $templateType - * @param bool $displayIfNoReviews - * @return string - */ - public function getReviewsSummaryHtml( - \Magento\Catalog\Model\Product $product, - $templateType = false, - $displayIfNoReviews = false - ) { - if (!$product->getRatingSummary()) { - $this->_reviewFactory->create()->getEntitySummary($product, $this->_storeManager->getStore()->getId()); - } - return parent::getReviewsSummaryHtml($product, $templateType, $displayIfNoReviews); - } - - /** - * @return string + * @inheritDoc */ protected function _toHtml() { diff --git a/app/code/Magento/Review/Block/Product/Compare/ListCompare/Plugin/Review.php b/app/code/Magento/Review/Block/Product/Compare/ListCompare/Plugin/Review.php deleted file mode 100644 index 8393f87f8d45d..0000000000000 --- a/app/code/Magento/Review/Block/Product/Compare/ListCompare/Plugin/Review.php +++ /dev/null @@ -1,58 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -namespace Magento\Review\Block\Product\Compare\ListCompare\Plugin; - -class Review -{ - /** - * Review model - * - * @var \Magento\Review\Model\ReviewFactory - */ - protected $reviewFactory; - - /** - * Store manager - * - * @var \Magento\Store\Model\StoreManagerInterface - */ - protected $storeManager; - - /** - * @param \Magento\Store\Model\StoreManagerInterface $storeManager - * @param \Magento\Review\Model\ReviewFactory $reviewFactory - */ - public function __construct( - \Magento\Store\Model\StoreManagerInterface $storeManager, - \Magento\Review\Model\ReviewFactory $reviewFactory - ) { - $this->storeManager = $storeManager; - $this->reviewFactory = $reviewFactory; - } - - /** - * Initialize product review - * - * @param \Magento\Catalog\Block\Product\Compare\ListCompare $subject - * @param \Magento\Catalog\Model\Product $product - * @param bool $templateType - * @param bool $displayIfNoReviews - * - * @return void - * @SuppressWarnings(PHPMD.UnusedFormalParameter) - */ - public function beforeGetReviewsSummaryHtml( - \Magento\Catalog\Block\Product\Compare\ListCompare $subject, - \Magento\Catalog\Model\Product $product, - $templateType = false, - $displayIfNoReviews = false - ) { - if (!$product->getRatingSummary()) { - $this->reviewFactory->create()->getEntitySummary($product, $this->storeManager->getStore()->getId()); - } - } -} diff --git a/app/code/Magento/Review/Block/Product/ReviewRenderer.php b/app/code/Magento/Review/Block/Product/ReviewRenderer.php index 3183196ebf30c..0fd6327e1f777 100644 --- a/app/code/Magento/Review/Block/Product/ReviewRenderer.php +++ b/app/code/Magento/Review/Block/Product/ReviewRenderer.php @@ -5,10 +5,13 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Review\Block\Product; use Magento\Catalog\Block\Product\ReviewRendererInterface; use Magento\Catalog\Model\Product; +use Magento\Framework\App\ObjectManager; +use Magento\Review\Model\ReviewSummaryFactory; use Magento\Review\Observer\PredispatchReviewObserver; /** @@ -33,17 +36,26 @@ class ReviewRenderer extends \Magento\Framework\View\Element\Template implements */ protected $_reviewFactory; + /** + * @var ReviewSummaryFactory + */ + private $reviewSummaryFactory; + /** * @param \Magento\Framework\View\Element\Template\Context $context * @param \Magento\Review\Model\ReviewFactory $reviewFactory * @param array $data + * @param ReviewSummaryFactory $reviewSummaryFactory */ public function __construct( \Magento\Framework\View\Element\Template\Context $context, \Magento\Review\Model\ReviewFactory $reviewFactory, - array $data = [] + array $data = [], + ReviewSummaryFactory $reviewSummaryFactory = null ) { $this->_reviewFactory = $reviewFactory; + $this->reviewSummaryFactory = $reviewSummaryFactory ?? + ObjectManager::getInstance()->get(ReviewSummaryFactory::class); parent::__construct($context, $data); } @@ -52,7 +64,7 @@ public function __construct( * * @return string */ - public function isReviewEnabled() : string + public function isReviewEnabled(): string { return $this->_scopeConfig->getValue( PredispatchReviewObserver::XML_PATH_REVIEW_ACTIVE, @@ -68,17 +80,22 @@ public function isReviewEnabled() : string * @param bool $displayIfNoReviews * * @return string + * @throws \Magento\Framework\Exception\LocalizedException + * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function getReviewsSummaryHtml( \Magento\Catalog\Model\Product $product, $templateType = self::DEFAULT_VIEW, $displayIfNoReviews = false ) { - if (!$product->getRatingSummary()) { - $this->_reviewFactory->create()->getEntitySummary($product, $this->_storeManager->getStore()->getId()); + if ($product->getRatingSummary() === null) { + $this->reviewSummaryFactory->create()->appendSummaryDataToObject( + $product, + $this->_storeManager->getStore()->getId() + ); } - if (!$product->getRatingSummary() && !$displayIfNoReviews) { + if (null === $product->getRatingSummary() && !$displayIfNoReviews) { return ''; } // pick template among available @@ -101,7 +118,7 @@ public function getReviewsSummaryHtml( */ public function getRatingSummary() { - return $this->getProduct()->getRatingSummary()->getRatingSummary(); + return $this->getProduct()->getRatingSummary(); } /** @@ -111,7 +128,7 @@ public function getRatingSummary() */ public function getReviewsCount() { - return $this->getProduct()->getRatingSummary()->getReviewsCount(); + return $this->getProduct()->getReviewsCount(); } /** diff --git a/app/code/Magento/Review/Block/View.php b/app/code/Magento/Review/Block/View.php index 95b7176b48c44..82a5f37f9b6bf 100644 --- a/app/code/Magento/Review/Block/View.php +++ b/app/code/Magento/Review/Block/View.php @@ -119,6 +119,7 @@ public function getRating() /** * Retrieve rating summary for current product * + * @deprecated * @return string */ public function getRatingSummary() @@ -160,23 +161,4 @@ public function dateFormat($date) { return $this->formatDate($date, \IntlDateFormatter::LONG); } - - /** - * Get product reviews summary - * - * @param \Magento\Catalog\Model\Product $product - * @param bool $templateType - * @param bool $displayIfNoReviews - * @return string - */ - public function getReviewsSummaryHtml( - \Magento\Catalog\Model\Product $product, - $templateType = false, - $displayIfNoReviews = false - ) { - if (!$product->getRatingSummary()) { - $this->_reviewFactory->create()->getEntitySummary($product, $this->_storeManager->getStore()->getId()); - } - return parent::getReviewsSummaryHtml($product, $templateType, $displayIfNoReviews); - } } diff --git a/app/code/Magento/Review/Controller/Adminhtml/Product/Save.php b/app/code/Magento/Review/Controller/Adminhtml/Product/Save.php index 57b1e538ddb6b..d9498580c39ba 100644 --- a/app/code/Magento/Review/Controller/Adminhtml/Product/Save.php +++ b/app/code/Magento/Review/Controller/Adminhtml/Product/Save.php @@ -77,6 +77,10 @@ public function execute() if ($productId) { $resultRedirect->setPath("catalog/product/edit/id/$productId"); } + $customerId = (int)$this->getRequest()->getParam('customerId'); + if ($customerId) { + $resultRedirect->setPath("customer/index/edit/id/$customerId"); + } return $resultRedirect; } $resultRedirect->setPath('review/*/'); diff --git a/app/code/Magento/Review/Model/ResourceModel/Review/Summary.php b/app/code/Magento/Review/Model/ResourceModel/Review/Summary.php index b69065fbaf6cd..f18bc2094930a 100644 --- a/app/code/Magento/Review/Model/ResourceModel/Review/Summary.php +++ b/app/code/Magento/Review/Model/ResourceModel/Review/Summary.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Review\Model\ResourceModel\Review; use Magento\Framework\Model\AbstractModel; @@ -73,4 +74,46 @@ public function reAggregate($summary) } return $this; } + + /** + * Append review summary fields to product collection + * + * @param \Magento\Catalog\Model\ResourceModel\Product\Collection $productCollection + * @param string $storeId + * @param string $entityCode + * @return Summary + * @throws \Magento\Framework\Exception\LocalizedException + */ + public function appendSummaryFieldsToCollection( + \Magento\Catalog\Model\ResourceModel\Product\Collection $productCollection, + string $storeId, + string $entityCode + ) { + if (!$productCollection->isLoaded()) { + $summaryEntitySubSelect = $this->getConnection()->select(); + $summaryEntitySubSelect + ->from( + ['review_entity' => $this->getTable('review_entity')], + ['entity_id'] + )->where( + 'entity_code = ?', + $entityCode + ); + $joinCond = new \Zend_Db_Expr( + "e.entity_id = review_summary.entity_pk_value AND review_summary.store_id = {$storeId}" + . " AND review_summary.entity_type = ({$summaryEntitySubSelect})" + ); + $productCollection->getSelect() + ->joinLeft( + ['review_summary' => $this->getMainTable()], + $joinCond, + [ + 'reviews_count' => new \Zend_Db_Expr("IFNULL(review_summary.reviews_count, 0)"), + 'rating_summary' => new \Zend_Db_Expr("IFNULL(review_summary.rating_summary, 0)") + ] + ); + } + + return $this; + } } diff --git a/app/code/Magento/Review/Model/Review.php b/app/code/Magento/Review/Model/Review.php index e689d4ed460ac..0c581f570ef0c 100644 --- a/app/code/Magento/Review/Model/Review.php +++ b/app/code/Magento/Review/Model/Review.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Review\Model; use Magento\Framework\DataObject; @@ -100,6 +101,7 @@ class Review extends \Magento\Framework\Model\AbstractModel implements IdentityI /** * Review model summary * + * @deprecated Summary factory injected as separate property * @var \Magento\Review\Model\Review\Summary */ protected $_reviewSummary; @@ -214,6 +216,7 @@ public function aggregate() /** * Get entity summary * + * @deprecated * @param Product $product * @param int $storeId * @return void @@ -301,10 +304,12 @@ public function afterDeleteCommit() } /** - * Append review summary to product collection + * Append review summary data object to product collection * + * @deprecated * @param ProductCollection $collection * @return $this + * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function appendSummary($collection) { @@ -313,7 +318,7 @@ public function appendSummary($collection) $entityIds[] = $item->getEntityId(); } - if (sizeof($entityIds) == 0) { + if (count($entityIds) === 0) { return $this; } @@ -356,7 +361,7 @@ public function isAvailableOnStore($store = null) { $store = $this->_storeManager->getStore($store); if ($store) { - return in_array($store->getId(), (array) $this->getStores()); + return in_array($store->getId(), (array)$this->getStores()); } return false; } diff --git a/app/code/Magento/Review/Model/ReviewSummary.php b/app/code/Magento/Review/Model/ReviewSummary.php new file mode 100644 index 0000000000000..46851339ae6d7 --- /dev/null +++ b/app/code/Magento/Review/Model/ReviewSummary.php @@ -0,0 +1,52 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Review\Model; + +use Magento\Framework\Model\AbstractModel; +use Magento\Review\Model\ResourceModel\Review\Summary\CollectionFactory as SummaryCollectionFactory; + +/** + * ReviewSummary model. + */ +class ReviewSummary +{ + /** + * @var SummaryCollectionFactory + */ + private $summaryCollectionFactory; + + /** + * @param SummaryCollectionFactory $sumColFactory + */ + public function __construct( + SummaryCollectionFactory $sumColFactory + ) { + $this->summaryCollectionFactory = $sumColFactory; + } + + /** + * Append review summary data to product + * + * @param AbstractModel $object + * @param int $storeId + * @param int $entityType + */ + public function appendSummaryDataToObject(AbstractModel $object, int $storeId, int $entityType = 1): void + { + $summary = $this->summaryCollectionFactory->create() + ->addEntityFilter($object->getId(), $entityType) + ->addStoreFilter($storeId) + ->getFirstItem(); + $object->addData( + [ + 'reviews_count' => $summary->getData('reviews_count'), + 'rating_summary' => $summary->getData('rating_summary') + ] + ); + } +} diff --git a/app/code/Magento/Review/Observer/CatalogBlockProductCollectionBeforeToHtmlObserver.php b/app/code/Magento/Review/Observer/CatalogBlockProductCollectionBeforeToHtmlObserver.php deleted file mode 100644 index f35d6eac27ea8..0000000000000 --- a/app/code/Magento/Review/Observer/CatalogBlockProductCollectionBeforeToHtmlObserver.php +++ /dev/null @@ -1,49 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Review\Observer; - -use Magento\Framework\Event\ObserverInterface; - -/** - * Review block observer. - */ -class CatalogBlockProductCollectionBeforeToHtmlObserver implements ObserverInterface -{ - /** - * Review model - * - * @var \Magento\Review\Model\ReviewFactory - */ - protected $_reviewFactory; - - /** - * @param \Magento\Review\Model\ReviewFactory $reviewFactory - */ - public function __construct( - \Magento\Review\Model\ReviewFactory $reviewFactory - ) { - $this->_reviewFactory = $reviewFactory; - } - - /** - * Append review summary before rendering html - * - * @param \Magento\Framework\Event\Observer $observer - * @return $this - */ - public function execute(\Magento\Framework\Event\Observer $observer) - { - $productCollection = $observer->getEvent()->getCollection(); - if ($productCollection instanceof \Magento\Framework\Data\Collection) { - if (!$productCollection->isLoaded()) { - $productCollection->load(); - } - $this->_reviewFactory->create()->appendSummary($productCollection); - } - - return $this; - } -} diff --git a/app/code/Magento/Review/Observer/CatalogProductListCollectionAppendSummaryFieldsObserver.php b/app/code/Magento/Review/Observer/CatalogProductListCollectionAppendSummaryFieldsObserver.php new file mode 100644 index 0000000000000..bb69284b5f0b8 --- /dev/null +++ b/app/code/Magento/Review/Observer/CatalogProductListCollectionAppendSummaryFieldsObserver.php @@ -0,0 +1,62 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Review\Observer; + +use Magento\Framework\Event\Observer as EventObserver; +use Magento\Framework\Event\ObserverInterface; +use Magento\Review\Model\ResourceModel\Review\SummaryFactory; +use Magento\Store\Model\StoreManagerInterface; + +/** + * Append review summary to product list collection. + */ +class CatalogProductListCollectionAppendSummaryFieldsObserver implements ObserverInterface +{ + /** + * Review model + * + * @var Summary + */ + private $sumResourceFactory; + + /** + * @var StoreManagerInterface + */ + private $storeManager; + + /** + * @param SummaryFactory $sumResourceFactory + * @param StoreManagerInterface $storeManager + */ + public function __construct( + SummaryFactory $sumResourceFactory, + StoreManagerInterface $storeManager + ) { + $this->sumResourceFactory = $sumResourceFactory; + $this->storeManager = $storeManager; + } + + /** + * Append review summary to collection + * + * @param EventObserver $observer + * @return $this + * @throws \Magento\Framework\Exception\LocalizedException + */ + public function execute(EventObserver $observer) + { + $productCollection = $observer->getEvent()->getCollection(); + $this->sumResourceFactory->create()->appendSummaryFieldsToCollection( + $productCollection, + $this->storeManager->getStore()->getId(), + \Magento\Review\Model\Review::ENTITY_PRODUCT_CODE + ); + + return $this; + } +} diff --git a/app/code/Magento/Review/Observer/TagProductCollectionLoadAfterObserver.php b/app/code/Magento/Review/Observer/TagProductCollectionLoadAfterObserver.php deleted file mode 100644 index 52d6f09a08557..0000000000000 --- a/app/code/Magento/Review/Observer/TagProductCollectionLoadAfterObserver.php +++ /dev/null @@ -1,41 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Review\Observer; - -use Magento\Framework\Event\ObserverInterface; - -class TagProductCollectionLoadAfterObserver implements ObserverInterface -{ - /** - * Review model - * - * @var \Magento\Review\Model\ReviewFactory - */ - protected $_reviewFactory; - - /** - * @param \Magento\Review\Model\ReviewFactory $reviewFactory - */ - public function __construct( - \Magento\Review\Model\ReviewFactory $reviewFactory - ) { - $this->_reviewFactory = $reviewFactory; - } - - /** - * Add review summary info for tagged product collection - * - * @param \Magento\Framework\Event\Observer $observer - * @return $this - */ - public function execute(\Magento\Framework\Event\Observer $observer) - { - $collection = $observer->getEvent()->getCollection(); - $this->_reviewFactory->create()->appendSummary($collection); - - return $this; - } -} diff --git a/app/code/Magento/Review/Test/Unit/Model/ReviewSummaryTest.php b/app/code/Magento/Review/Test/Unit/Model/ReviewSummaryTest.php new file mode 100644 index 0000000000000..9723ece0c4904 --- /dev/null +++ b/app/code/Magento/Review/Test/Unit/Model/ReviewSummaryTest.php @@ -0,0 +1,94 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Review\Test\Unit\Model; + +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager as ObjectManagerHelper; +use PHPUnit\Framework\MockObject\MockObject; + +/** + * Test for Magento\Review\Model\ReviewSummary class. + */ +class ReviewSummaryTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var MockObject + */ + private $reviewSummaryCollectionFactoryMock; + + /** + * @var \Magento\Review\Model\ReviewSummary | MockObject + */ + private $reviewSummary; + + /** + * @var ObjectManagerHelper + */ + private $objectManagerHelper; + + protected function setUp() + { + $this->reviewSummaryCollectionFactoryMock = $this->createPartialMock( + \Magento\Review\Model\ResourceModel\Review\Summary\CollectionFactory::class, + ['create'] + ); + + $this->objectManagerHelper = new ObjectManagerHelper($this); + $this->reviewSummary = $this->objectManagerHelper->getObject( + \Magento\Review\Model\ReviewSummary::class, + [ + 'sumColFactory' => $this->reviewSummaryCollectionFactoryMock + ] + ); + } + + public function testAppendSummaryDataToObject() + { + $productId = 6; + $storeId = 4; + $testSummaryData = [ + 'reviews_count' => 2, + 'rating_summary' => 80 + ]; + $product = $this->createPartialMock( + \Magento\Catalog\Model\Product::class, + ['getId', 'addData', '__wakeup'] + ); + $product->expects($this->once())->method('getId')->will($this->returnValue($productId)); + $product->expects($this->once())->method('addData') + ->with($testSummaryData) + ->will($this->returnSelf()); + + $summaryData = $this->createPartialMock( + \Magento\Review\Model\Review\Summary::class, + ['getData', '__wakeup'] + ); + $summaryData->expects($this->atLeastOnce())->method('getData')->will( + $this->returnValueMap( + [ + ['reviews_count', null, $testSummaryData['reviews_count']], + ['rating_summary', null, $testSummaryData['rating_summary']] + ] + ) + ); + $summaryCollection = $this->createPartialMock( + \Magento\Review\Model\ResourceModel\Review\Summary\Collection::class, + ['addEntityFilter', 'addStoreFilter', 'getFirstItem', '__wakeup'] + ); + $summaryCollection->expects($this->once())->method('addEntityFilter') + ->will($this->returnSelf()); + $summaryCollection->expects($this->once())->method('addStoreFilter') + ->will($this->returnSelf()); + $summaryCollection->expects($this->once())->method('getFirstItem') + ->will($this->returnValue($summaryData)); + + $this->reviewSummaryCollectionFactoryMock->expects($this->once())->method('create') + ->will($this->returnValue($summaryCollection)); + + $this->assertNull($this->reviewSummary->appendSummaryDataToObject($product, $storeId)); + } +} diff --git a/app/code/Magento/Review/Test/Unit/Model/ReviewTest.php b/app/code/Magento/Review/Test/Unit/Model/ReviewTest.php index 9f57b289fa749..3302ba7e6a036 100644 --- a/app/code/Magento/Review/Test/Unit/Model/ReviewTest.php +++ b/app/code/Magento/Review/Test/Unit/Model/ReviewTest.php @@ -51,7 +51,7 @@ class ReviewTest extends \PHPUnit\Framework\TestCase /** @var \Magento\Review\Model\ResourceModel\Review|\PHPUnit_Framework_MockObject_MockObject */ protected $resource; - /** @var int */ + /** @var int */ protected $reviewId = 8; protected function setUp() @@ -135,6 +135,9 @@ public function testAggregate() $this->assertSame($this->review, $this->review->aggregate()); } + /** + * @deprecated + */ public function testGetEntitySummary() { $productId = 6; diff --git a/app/code/Magento/Review/etc/frontend/di.xml b/app/code/Magento/Review/etc/frontend/di.xml index e6efb36e88d56..4ea0f4449cdd8 100644 --- a/app/code/Magento/Review/etc/frontend/di.xml +++ b/app/code/Magento/Review/etc/frontend/di.xml @@ -40,7 +40,4 @@ </argument> </arguments> </type> - <type name="Magento\Catalog\Block\Product\Compare\ListCompare"> - <plugin name="reviewInitializer" type="Magento\Review\Block\Product\Compare\ListCompare\Plugin\Review" /> - </type> </config> diff --git a/app/code/Magento/Review/etc/frontend/events.xml b/app/code/Magento/Review/etc/frontend/events.xml index 8e883ce328a2c..44cc888fb323f 100644 --- a/app/code/Magento/Review/etc/frontend/events.xml +++ b/app/code/Magento/Review/etc/frontend/events.xml @@ -6,11 +6,8 @@ */ --> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd"> - <event name="tag_tag_product_collection_load_after"> - <observer name="review" instance="Magento\Review\Observer\TagProductCollectionLoadAfterObserver" shared="false" /> - </event> <event name="catalog_block_product_list_collection"> - <observer name="review" instance="Magento\Review\Observer\CatalogBlockProductCollectionBeforeToHtmlObserver" shared="false" /> + <observer name="review" instance="Magento\Review\Observer\CatalogProductListCollectionAppendSummaryFieldsObserver" shared="false" /> </event> <event name="controller_action_predispatch_review"> <observer name="catalog_review_enabled" instance="Magento\Review\Observer\PredispatchReviewObserver" /> diff --git a/app/code/Magento/Review/view/frontend/web/js/process-reviews.js b/app/code/Magento/Review/view/frontend/web/js/process-reviews.js index 88c61fa38af34..9d1083d662d5a 100644 --- a/app/code/Magento/Review/view/frontend/web/js/process-reviews.js +++ b/app/code/Magento/Review/view/frontend/web/js/process-reviews.js @@ -41,7 +41,7 @@ define([ requiredReviewTabRole = 'tab'; if (reviewTab.attr('role') === requiredReviewTabRole && reviewTab.hasClass('active')) { - processReviews(config.productReviewUrl); + processReviews(config.productReviewUrl, location.hash === '#reviews'); } else { reviewTab.one('beforeOpen', function () { processReviews(config.productReviewUrl); @@ -50,18 +50,23 @@ define([ $(function () { $('.product-info-main .reviews-actions a').click(function (event) { - var anchor; + var anchor, addReviewBlock; event.preventDefault(); anchor = $(this).attr('href').replace(/^.*?(#|$)/, ''); - $('.product.data.items [data-role="content"]').each(function (index) { //eslint-disable-line - if (this.id == 'reviews') { //eslint-disable-line eqeqeq - $('.product.data.items').tabs('activate', index); - $('html, body').animate({ - scrollTop: $('#' + anchor).offset().top - 50 - }, 300); - } - }); + addReviewBlock = $('.block.review-add .block-content #' + anchor); + + if (addReviewBlock.length) { + $('.product.data.items [data-role="content"]').each(function (index) { //eslint-disable-line + if (this.id == 'reviews') { //eslint-disable-line eqeqeq + $('.product.data.items').tabs('activate', index); + } + }); + $('html, body').animate({ + scrollTop: addReviewBlock.offset().top - 50 + }, 300); + } + }); }); }; diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Address.php b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Address.php index 0e3d308df912e..e4b9dd4c63b93 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Address.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Form/Address.php @@ -9,6 +9,8 @@ use Magento\Framework\App\ObjectManager; use Magento\Framework\Data\Form\Element\AbstractElement; use Magento\Framework\Pricing\PriceCurrencyInterface; +use Magento\Customer\Api\Data\AddressInterface; +use Magento\Eav\Model\AttributeDataFactory; /** * Order create address form @@ -191,17 +193,19 @@ public function getAddressCollectionJson() $emptyAddressForm = $this->_customerFormFactory->create( 'customer_address', 'adminhtml_customer_address', - [\Magento\Customer\Api\Data\AddressInterface::COUNTRY_ID => $defaultCountryId] + [AddressInterface::COUNTRY_ID => $defaultCountryId] ); - $data = [0 => $emptyAddressForm->outputData(\Magento\Eav\Model\AttributeDataFactory::OUTPUT_FORMAT_JSON)]; + $data = [0 => $emptyAddressForm->outputData(AttributeDataFactory::OUTPUT_FORMAT_JSON)]; foreach ($this->getAddressCollection() as $address) { $addressForm = $this->_customerFormFactory->create( 'customer_address', 'adminhtml_customer_address', - $this->addressMapper->toFlatArray($address) + $this->addressMapper->toFlatArray($address), + false, + false ); $data[$address->getId()] = $addressForm->outputData( - \Magento\Eav\Model\AttributeDataFactory::OUTPUT_FORMAT_JSON + AttributeDataFactory::OUTPUT_FORMAT_JSON ); } diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Shipping/Address.php b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Shipping/Address.php index 7a8f3effef07e..c4e8db28b48cb 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Shipping/Address.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Order/Create/Shipping/Address.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Sales\Block\Adminhtml\Order\Create\Shipping; /** @@ -81,6 +83,15 @@ public function getIsAsBilling() */ public function getDontSaveInAddressBook() { + $shippingIsTheSameAsBilling = $this->getIsAsBilling() && $this->getIsShipping(); + $params = $this->getRequest()->getParams(); + if ($shippingIsTheSameAsBilling && $params) { + $save = $params['order']['billing_address']['save_in_address_book'] ?? false; + return !$save; + } + if ($shippingIsTheSameAsBilling) { + return !$shippingIsTheSameAsBilling; + } return $this->getIsAsBilling(); } @@ -121,6 +132,7 @@ public function getAddress() /** * Return is address disabled flag + * * Return true is the quote is virtual * * @return bool diff --git a/app/code/Magento/Sales/Block/Adminhtml/Order/Creditmemo/Create/Adjustments.php b/app/code/Magento/Sales/Block/Adminhtml/Order/Creditmemo/Create/Adjustments.php index 9e13e9424d1fd..50d29c195968c 100644 --- a/app/code/Magento/Sales/Block/Adminhtml/Order/Creditmemo/Create/Adjustments.php +++ b/app/code/Magento/Sales/Block/Adminhtml/Order/Creditmemo/Create/Adjustments.php @@ -8,7 +8,7 @@ use Magento\Framework\Pricing\PriceCurrencyInterface; /** - * Credit memo adjustmets block + * Credit memo adjustments block * * @api * @since 100.0.2 diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order/AddressSave.php b/app/code/Magento/Sales/Controller/Adminhtml/Order/AddressSave.php index c71de8cb0252d..5633e16d7d3d0 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Order/AddressSave.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Order/AddressSave.php @@ -10,6 +10,7 @@ use Magento\Backend\App\Action\Context; use Magento\Backend\Model\View\Result\Redirect; use Magento\Directory\Model\RegionFactory; +use Magento\Sales\Api\OrderAddressRepositoryInterface; use Magento\Sales\Api\OrderManagementInterface; use Magento\Sales\Api\OrderRepositoryInterface; use Magento\Sales\Api\Data\OrderAddressInterface; @@ -25,11 +26,14 @@ use Magento\Framework\Controller\Result\RawFactory; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\App\ObjectManager; +use Magento\Framework\App\Action\HttpPostActionInterface; /** + * Sales address save + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class AddressSave extends Order +class AddressSave extends Order implements HttpPostActionInterface { /** * Authorization level of a basic admin session @@ -43,6 +47,11 @@ class AddressSave extends Order */ private $regionFactory; + /** + * @var OrderAddressRepositoryInterface + */ + private $orderAddressRepository; + /** * @param Context $context * @param Registry $coreRegistry @@ -56,6 +65,7 @@ class AddressSave extends Order * @param OrderRepositoryInterface $orderRepository * @param LoggerInterface $logger * @param RegionFactory|null $regionFactory + * @param OrderAddressRepositoryInterface|null $orderAddressRepository * * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ @@ -71,9 +81,12 @@ public function __construct( OrderManagementInterface $orderManagement, OrderRepositoryInterface $orderRepository, LoggerInterface $logger, - RegionFactory $regionFactory = null + RegionFactory $regionFactory = null, + OrderAddressRepositoryInterface $orderAddressRepository = null ) { $this->regionFactory = $regionFactory ?: ObjectManager::getInstance()->get(RegionFactory::class); + $this->orderAddressRepository = $orderAddressRepository ?: ObjectManager::getInstance() + ->get(OrderAddressRepositoryInterface::class); parent::__construct( $context, $coreRegistry, @@ -107,7 +120,7 @@ public function execute() if ($data && $address->getId()) { $address->addData($data); try { - $address->save(); + $this->orderAddressRepository->save($address); $this->_eventManager->dispatch( 'admin_sales_order_address_update', [ diff --git a/app/code/Magento/Sales/Controller/Adminhtml/Order/Create/Index.php b/app/code/Magento/Sales/Controller/Adminhtml/Order/Create/Index.php index 603aa2586b051..634ecd50c6f9a 100644 --- a/app/code/Magento/Sales/Controller/Adminhtml/Order/Create/Index.php +++ b/app/code/Magento/Sales/Controller/Adminhtml/Order/Create/Index.php @@ -20,7 +20,7 @@ class Index extends \Magento\Sales\Controller\Adminhtml\Order\Create implements public function execute() { $this->_initSession(); - + $this->_getOrderCreateModel()->initRuleData(); /** @var \Magento\Backend\Model\View\Result\Page $resultPage */ $resultPage = $this->resultPageFactory->create(); $resultPage->setActiveMenu('Magento_Sales::sales_order'); diff --git a/app/code/Magento/Sales/Model/AdminOrder/Create.php b/app/code/Magento/Sales/Model/AdminOrder/Create.php index 063433140566a..44fd72c33dbc6 100644 --- a/app/code/Magento/Sales/Model/AdminOrder/Create.php +++ b/app/code/Magento/Sales/Model/AdminOrder/Create.php @@ -398,6 +398,7 @@ protected function _getQuoteItem($item) */ public function initRuleData() { + $this->_coreRegistry->unregister('rule_data'); $this->_coreRegistry->register( 'rule_data', new \Magento\Framework\DataObject( @@ -415,7 +416,8 @@ public function initRuleData() /** * Set collect totals flag for quote * - * @param bool $flag + * @param bool $flag + * * @return $this */ public function setRecollect($flag) @@ -1129,6 +1131,7 @@ public function updateQuoteItems($items) } catch (\Magento\Framework\Exception\LocalizedException $e) { $this->recollectCart(); throw $e; + // phpcs:ignore Magento2.Exceptions.ThrowCatch } catch (\Exception $e) { $this->_logger->critical($e); } @@ -1790,7 +1793,7 @@ public function _prepareCustomer() ->setWebsiteId($store->getWebsiteId()) ->setCreatedAt(null); $customer = $this->_validateCustomerData($customer); - } else if (!$customer->getId()) { + } elseif (!$customer->getId()) { /** Create new customer */ $customerBillingAddressDataObject = $this->getBillingAddress()->exportCustomerAddress(); $customer->setSuffix($customerBillingAddressDataObject->getSuffix()) @@ -1862,6 +1865,7 @@ protected function _prepareCustomerAddress($customer, $quoteCustomerAddress) } elseif ($addressType == \Magento\Quote\Model\Quote\Address::ADDRESS_TYPE_SHIPPING) { try { $billingAddressDataObject = $this->accountManagement->getDefaultBillingAddress($customer->getId()); + // phpcs:ignore Magento2.CodeAnalysis.EmptyBlock } catch (\Exception $e) { /** Billing address does not exist. */ } @@ -2003,6 +2007,7 @@ protected function _validate() } else { try { $method->validate(); + // phpcs:ignore Magento2.Exceptions.ThrowCatch } catch (\Magento\Framework\Exception\LocalizedException $e) { $this->_errors[] = $e->getMessage(); } diff --git a/app/code/Magento/Sales/Model/Order/AddressRepository.php b/app/code/Magento/Sales/Model/Order/AddressRepository.php index af83dde99c6f2..deeeb16b7714c 100644 --- a/app/code/Magento/Sales/Model/Order/AddressRepository.php +++ b/app/code/Magento/Sales/Model/Order/AddressRepository.php @@ -6,7 +6,11 @@ namespace Magento\Sales\Model\Order; +use Magento\Customer\Model\AttributeMetadataDataProvider; +use Magento\Customer\Model\ResourceModel\Form\Attribute\Collection as AttributeCollection; use Magento\Framework\Api\SearchCriteria\CollectionProcessorInterface; +use Magento\Framework\App\ObjectManager; +use Magento\Sales\Api\Data\OrderAddressInterface; use Magento\Sales\Model\ResourceModel\Metadata; use Magento\Sales\Api\Data\OrderAddressSearchResultInterfaceFactory as SearchResultFactory; use Magento\Framework\Exception\CouldNotDeleteException; @@ -41,20 +45,88 @@ class AddressRepository implements \Magento\Sales\Api\OrderAddressRepositoryInte */ private $collectionProcessor; + /** + * @var AttributeMetadataDataProvider + */ + private $attributeMetadataDataProvider; + + /** + * @var AttributeCollection|null + */ + private $attributesList = null; + /** * AddressRepository constructor. * @param Metadata $metadata * @param SearchResultFactory $searchResultFactory * @param CollectionProcessorInterface|null $collectionProcessor + * @param AttributeMetadataDataProvider $attributeMetadataDataProvider */ public function __construct( Metadata $metadata, SearchResultFactory $searchResultFactory, - CollectionProcessorInterface $collectionProcessor = null + CollectionProcessorInterface $collectionProcessor = null, + AttributeMetadataDataProvider $attributeMetadataDataProvider = null ) { $this->metadata = $metadata; $this->searchResultFactory = $searchResultFactory; $this->collectionProcessor = $collectionProcessor ?: $this->getCollectionProcessor(); + $this->attributeMetadataDataProvider = $attributeMetadataDataProvider ?: ObjectManager::getInstance() + ->get(AttributeMetadataDataProvider::class); + } + + /** + * Format multiline and multiselect attributes + * + * @param OrderAddressInterface $orderAddress + * + * @return void + */ + private function formatCustomAddressAttributes(OrderAddressInterface $orderAddress) + { + $attributesList = $this->getAttributesList(); + + foreach ($attributesList as $attribute) { + $attributeCode = $attribute->getAttributeCode(); + if (!$orderAddress->hasData($attributeCode)) { + continue; + } + $attributeValue = $orderAddress->getData($attributeCode); + if (is_array($attributeValue)) { + $glue = $attribute->getFrontendInput() === 'multiline' ? PHP_EOL : ','; + $attributeValue = trim(implode($glue, $attributeValue)); + } + $orderAddress->setData($attributeCode, $attributeValue); + } + } + + /** + * Get list of custom attributes. + * + * @return AttributeCollection|null + */ + private function getAttributesList() + { + if (!$this->attributesList) { + $attributesList = $this->attributeMetadataDataProvider->loadAttributesCollection( + 'customer_address', + 'customer_register_address' + ); + $attributesList->addFieldToFilter('is_user_defined', 1); + $attributesList->addFieldToFilter( + 'frontend_input', + [ + 'in' => [ + 'multiline', + 'multiselect', + ], + ] + ); + + $this->attributesList = $attributesList; + } + + return $this->attributesList; } /** @@ -98,7 +170,7 @@ public function getList(\Magento\Framework\Api\SearchCriteriaInterface $searchCr $searchResult = $this->searchResultFactory->create(); $this->collectionProcessor->process($searchCriteria, $searchResult); $searchResult->setSearchCriteria($searchCriteria); - + return $searchResult; } @@ -144,6 +216,7 @@ public function deleteById($id) */ public function save(\Magento\Sales\Api\Data\OrderAddressInterface $entity) { + $this->formatCustomAddressAttributes($entity); try { $this->metadata->getMapper()->save($entity); $this->registry[$entity->getEntityId()] = $entity; diff --git a/app/code/Magento/Sales/Model/Order/Pdf/AbstractPdf.php b/app/code/Magento/Sales/Model/Order/Pdf/AbstractPdf.php index 85e34f560bb7b..723940a5f67c0 100644 --- a/app/code/Magento/Sales/Model/Order/Pdf/AbstractPdf.php +++ b/app/code/Magento/Sales/Model/Order/Pdf/AbstractPdf.php @@ -11,6 +11,7 @@ /** * Sales Order PDF abstract model * + * phpcs:disable Magento2.Classes.AbstractApi * @api * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -116,6 +117,11 @@ abstract public function getPdf(); */ protected $addressRenderer; + /** + * @var array $pageSettings + */ + private $pageSettings; + /** * @param \Magento\Payment\Helper\Data $paymentData * @param \Magento\Framework\Stdlib\StringUtils $string @@ -172,10 +178,12 @@ public function __construct( */ public function widthForStringUsingFontSize($string, $font, $fontSize) { + // phpcs:ignore Generic.PHP.NoSilencedErrors $drawingString = '"libiconv"' == ICONV_IMPL ? iconv( 'UTF-8', 'UTF-16BE//IGNORE', $string + // phpcs:ignore Generic.PHP.NoSilencedErrors ) : @iconv( 'UTF-8', 'UTF-16BE', @@ -183,7 +191,10 @@ public function widthForStringUsingFontSize($string, $font, $fontSize) ); $characters = []; - for ($i = 0; $i < strlen($drawingString); $i++) { + + $drawingStringLength = strlen($drawingString); + + for ($i = 0; $i < $drawingStringLength; $i++) { $characters[] = ord($drawingString[$i++]) << 8 | ord($drawingString[$i]); } $glyphs = $font->glyphNumbersForCharacters($characters); @@ -224,14 +235,14 @@ public function getAlignCenter($string, $x, $columnWidth, \Zend_Pdf_Resource_Fon $width = $this->widthForStringUsingFontSize($string, $font, $fontSize); return $x + round(($columnWidth - $width) / 2); } - /** * Insert logo to pdf page * - * @param \Zend_Pdf_Page &$page + * @param \Zend_Pdf_Page $page * @param string|null $store * @return void * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * @throws \Zend_Pdf_Exception */ protected function insertLogo(&$page, $store = null) { @@ -283,7 +294,7 @@ protected function insertLogo(&$page, $store = null) /** * Insert address to pdf page * - * @param \Zend_Pdf_Page &$page + * @param \Zend_Pdf_Page $page * @param string|null $store * @return void */ @@ -364,9 +375,9 @@ protected function _calcAddressHeight($address) } /** - * Insert order to pdf page + * Insert order to pdf page. * - * @param \Zend_Pdf_Page &$page + * @param \Zend_Pdf_Page $page * @param \Magento\Sales\Model\Order $obj * @param bool $putOrderId * @return void @@ -511,7 +522,7 @@ protected function insertOrder(&$page, $obj, $putOrderId = true) $this->y -= 15; $this->_setFontBold($page, 12); $page->setFillColor(new \Zend_Pdf_Color_GrayScale(0)); - $page->drawText(__('Payment Method'), 35, $this->y, 'UTF-8'); + $page->drawText(__('Payment Method:'), 35, $this->y, 'UTF-8'); $page->drawText(__('Shipping Method:'), 285, $this->y, 'UTF-8'); $this->y -= 10; @@ -964,9 +975,11 @@ public function newPage(array $settings = []) * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + * @SuppressWarnings(PHPMD.UnusedLocalVariable) */ public function drawLineBlocks(\Zend_Pdf_Page $page, array $draw, array $pageSettings = []) { + $this->pageSettings = $pageSettings; foreach ($draw as $itemsProp) { if (!isset($itemsProp['lines']) || !is_array($itemsProp['lines'])) { throw new \Magento\Framework\Exception\LocalizedException( @@ -975,7 +988,6 @@ public function drawLineBlocks(\Zend_Pdf_Page $page, array $draw, array $pageSet } $lines = $itemsProp['lines']; $height = isset($itemsProp['height']) ? $itemsProp['height'] : 10; - if (empty($itemsProp['shift'])) { $shift = 0; foreach ($lines as $line) { @@ -986,6 +998,7 @@ public function drawLineBlocks(\Zend_Pdf_Page $page, array $draw, array $pageSet $column['text'] = [$column['text']]; } $top = 0; + // foreach ($column['text'] as $part) { $top += $lineSpacing; } @@ -1000,69 +1013,99 @@ public function drawLineBlocks(\Zend_Pdf_Page $page, array $draw, array $pageSet if ($this->y - $itemsProp['shift'] < 15) { $page = $this->newPage($pageSettings); } + $this->correctLines($lines, $page, $height); + } - foreach ($lines as $line) { - $maxHeight = 0; - foreach ($line as $column) { - $fontSize = empty($column['font_size']) ? 10 : $column['font_size']; - if (!empty($column['font_file'])) { - $font = \Zend_Pdf_Font::fontWithPath($column['font_file']); - $page->setFont($font, $fontSize); - } else { - $fontStyle = empty($column['font']) ? 'regular' : $column['font']; - switch ($fontStyle) { - case 'bold': - $font = $this->_setFontBold($page, $fontSize); - break; - case 'italic': - $font = $this->_setFontItalic($page, $fontSize); - break; - default: - $font = $this->_setFontRegular($page, $fontSize); - break; - } - } - - if (!is_array($column['text'])) { - $column['text'] = [$column['text']]; - } - - $lineSpacing = !empty($column['height']) ? $column['height'] : $height; - $top = 0; - foreach ($column['text'] as $part) { - if ($this->y - $lineSpacing < 15) { - $page = $this->newPage($pageSettings); - } + return $page; + } - $feed = $column['feed']; - $textAlign = empty($column['align']) ? 'left' : $column['align']; - $width = empty($column['width']) ? 0 : $column['width']; - switch ($textAlign) { - case 'right': - if ($width) { - $feed = $this->getAlignRight($part, $feed, $width, $font, $fontSize); - } else { - $feed = $feed - $this->widthForStringUsingFontSize($part, $font, $fontSize); - } - break; - case 'center': - if ($width) { - $feed = $this->getAlignCenter($part, $feed, $width, $font, $fontSize); - } - break; - default: - break; - } - $page->drawText($part, $feed, $this->y - $top, 'UTF-8'); - $top += $lineSpacing; + /** + * Correct lines. + * + * @param array $lines + * @param \Zend_Pdf_Page $page + * @param int $height + * @throws \Zend_Pdf_Exception + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + */ + private function correctLines($lines, $page, $height) :void + { + foreach ($lines as $line) { + $maxHeight = 0; + foreach ($line as $column) { + $fontSize = empty($column['font_size']) ? 10 : $column['font_size']; + if (!empty($column['font_file'])) { + $font = \Zend_Pdf_Font::fontWithPath($column['font_file']); + $page->setFont($font, $fontSize); + } else { + $fontStyle = empty($column['font']) ? 'regular' : $column['font']; + switch ($fontStyle) { + case 'bold': + $font = $this->_setFontBold($page, $fontSize); + break; + case 'italic': + $font = $this->_setFontItalic($page, $fontSize); + break; + default: + $font = $this->_setFontRegular($page, $fontSize); + break; } + } - $maxHeight = $top > $maxHeight ? $top : $maxHeight; + if (!is_array($column['text'])) { + $column['text'] = [$column['text']]; } - $this->y -= $maxHeight; + $top = $this->correctText($column, $height, $font, $page); + + $maxHeight = $top > $maxHeight ? $top : $maxHeight; } + $this->y -= $maxHeight; } + } - return $page; + /** + * Correct text. + * + * @param array $column + * @param int $height + * @param \Zend_Pdf_Resource_Font $font + * @param \Zend_Pdf_Page $page + * @throws \Zend_Pdf_Exception + * @return int + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + */ + private function correctText($column, $height, $font, $page) :int + { + $top = 0; + $lineSpacing = !empty($column['height']) ? $column['height'] : $height; + $fontSize = empty($column['font_size']) ? 10 : $column['font_size']; + foreach ($column['text'] as $part) { + if ($this->y - $lineSpacing < 15) { + $page = $this->newPage($this->pageSettings); + } + + $feed = $column['feed']; + $textAlign = empty($column['align']) ? 'left' : $column['align']; + $width = empty($column['width']) ? 0 : $column['width']; + switch ($textAlign) { + case 'right': + if ($width) { + $feed = $this->getAlignRight($part, $feed, $width, $font, $fontSize); + } else { + $feed = $feed - $this->widthForStringUsingFontSize($part, $font, $fontSize); + } + break; + case 'center': + if ($width) { + $feed = $this->getAlignCenter($part, $feed, $width, $font, $fontSize); + } + break; + default: + break; + } + $page->drawText($part, $feed, $this->y - $top, 'UTF-8'); + $top += $lineSpacing; + } + return $top; } } diff --git a/app/code/Magento/Sales/Model/Service/InvoiceService.php b/app/code/Magento/Sales/Model/Service/InvoiceService.php index 18efeba726c1b..ceef0f054015b 100644 --- a/app/code/Magento/Sales/Model/Service/InvoiceService.php +++ b/app/code/Magento/Sales/Model/Service/InvoiceService.php @@ -3,13 +3,18 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Sales\Model\Service; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Serialize\Serializer\Json as JsonSerializer; +use Magento\Sales\Api\Data\InvoiceInterface; +use Magento\Sales\Api\Data\InvoiceItemInterface; +use Magento\Sales\Api\Data\OrderItemInterface; use Magento\Sales\Api\InvoiceManagementInterface; use Magento\Sales\Model\Order; -use Magento\Framework\App\ObjectManager; -use Magento\Framework\Serialize\Serializer\Json; -use Magento\Catalog\Model\Product\Type; +use Magento\Sales\Model\Order\Invoice; /** * Class InvoiceService @@ -19,36 +24,26 @@ class InvoiceService implements InvoiceManagementInterface { /** - * Repository - * * @var \Magento\Sales\Api\InvoiceRepositoryInterface */ protected $repository; /** - * Repository - * * @var \Magento\Sales\Api\InvoiceCommentRepositoryInterface */ protected $commentRepository; /** - * Search Criteria Builder - * * @var \Magento\Framework\Api\SearchCriteriaBuilder */ protected $criteriaBuilder; /** - * Filter Builder - * * @var \Magento\Framework\Api\FilterBuilder */ protected $filterBuilder; /** - * Invoice Notifier - * * @var \Magento\Sales\Model\Order\InvoiceNotifier */ protected $invoiceNotifier; @@ -64,15 +59,11 @@ class InvoiceService implements InvoiceManagementInterface protected $orderConverter; /** - * Serializer interface instance. - * - * @var Json + * @var JsonSerializer */ private $serializer; /** - * Constructor - * * @param \Magento\Sales\Api\InvoiceRepositoryInterface $repository * @param \Magento\Sales\Api\InvoiceCommentRepositoryInterface $commentRepository * @param \Magento\Framework\Api\SearchCriteriaBuilder $criteriaBuilder @@ -80,7 +71,7 @@ class InvoiceService implements InvoiceManagementInterface * @param \Magento\Sales\Model\Order\InvoiceNotifier $notifier * @param \Magento\Sales\Api\OrderRepositoryInterface $orderRepository * @param \Magento\Sales\Model\Convert\Order $orderConverter - * @param Json|null $serializer + * @param JsonSerializer $serializer */ public function __construct( \Magento\Sales\Api\InvoiceRepositoryInterface $repository, @@ -90,7 +81,7 @@ public function __construct( \Magento\Sales\Model\Order\InvoiceNotifier $notifier, \Magento\Sales\Api\OrderRepositoryInterface $orderRepository, \Magento\Sales\Model\Convert\Order $orderConverter, - Json $serializer = null + JsonSerializer $serializer ) { $this->repository = $repository; $this->commentRepository = $commentRepository; @@ -99,7 +90,7 @@ public function __construct( $this->invoiceNotifier = $notifier; $this->orderRepository = $orderRepository; $this->orderConverter = $orderConverter; - $this->serializer = $serializer ?: ObjectManager::getInstance()->get(Json::class); + $this->serializer = $serializer; } /** @@ -140,40 +131,53 @@ public function setVoid($id) } /** - * Creates an invoice based on the order and quantities provided + * Creates an invoice based on the order and quantities provided. + * + * Explanation for `if` statements: + * - using qty defined in `$preparedItemsQty` is prioritized + * - if qty is not defined and item is dummy, get ordered qty + * - if qty is not defined, get qty to invoice + * - else qty is 0 * * @param Order $order - * @param array $qtys - * @return \Magento\Sales\Model\Order\Invoice - * @throws \Magento\Framework\Exception\LocalizedException + * @param array $orderItemsQtyToInvoice + * @return Invoice + * @throws LocalizedException + * @throws \Exception */ - public function prepareInvoice(Order $order, array $qtys = []) - { - $isQtysEmpty = empty($qtys); - $invoice = $this->orderConverter->toInvoice($order); + public function prepareInvoice( + Order $order, + array $orderItemsQtyToInvoice = [] + ): InvoiceInterface { $totalQty = 0; - $qtys = $this->prepareItemsQty($order, $qtys); + $invoice = $this->orderConverter->toInvoice($order); + $preparedItemsQty = $this->prepareItemsQty($order, $orderItemsQtyToInvoice); + foreach ($order->getAllItems() as $orderItem) { - if (!$this->_canInvoiceItem($orderItem, $qtys)) { + if (!$this->canInvoiceItem($orderItem, $preparedItemsQty)) { continue; } - $item = $this->orderConverter->itemToInvoiceItem($orderItem); - if (isset($qtys[$orderItem->getId()])) { - $qty = (double) $qtys[$orderItem->getId()]; + + if (isset($preparedItemsQty[$orderItem->getId()])) { + $qty = $preparedItemsQty[$orderItem->getId()]; } elseif ($orderItem->isDummy()) { $qty = $orderItem->getQtyOrdered() ? $orderItem->getQtyOrdered() : 1; - } elseif ($isQtysEmpty) { + } elseif (empty($orderItemsQtyToInvoice)) { $qty = $orderItem->getQtyToInvoice(); } else { $qty = 0; } + + $invoiceItem = $this->orderConverter->itemToInvoiceItem($orderItem); + $this->setInvoiceItemQuantity($invoiceItem, (float) $qty); + $invoice->addItem($invoiceItem); $totalQty += $qty; - $this->setInvoiceItemQuantity($item, $qty); - $invoice->addItem($item); } + $invoice->setTotalQty($totalQty); $invoice->collectTotals(); $order->getInvoiceCollection()->addItem($invoice); + return $invoice; } @@ -181,92 +185,66 @@ public function prepareInvoice(Order $order, array $qtys = []) * Prepare qty to invoice for parent and child products if theirs qty is not specified in initial request. * * @param Order $order - * @param array $qtys + * @param array $orderItemsQtyToInvoice * @return array */ - private function prepareItemsQty(Order $order, array $qtys = []) - { + private function prepareItemsQty( + Order $order, + array $orderItemsQtyToInvoice + ): array { foreach ($order->getAllItems() as $orderItem) { - if (empty($qtys[$orderItem->getId()])) { - if ($orderItem->getProductType() == Type::TYPE_BUNDLE && !$orderItem->isShipSeparately()) { - $qtys[$orderItem->getId()] = $orderItem->getQtyOrdered() - $orderItem->getQtyInvoiced(); - } else { - $parentItem = $orderItem->getParentItem(); - $parentItemId = $parentItem ? $parentItem->getId() : null; - if ($parentItemId && isset($qtys[$parentItemId])) { - $qtys[$orderItem->getId()] = $qtys[$parentItemId]; - } - continue; + if (isset($orderItemsQtyToInvoice[$orderItem->getId()])) { + if ($orderItem->isDummy() && $orderItem->getHasChildren()) { + $orderItemsQtyToInvoice = $this->setChildItemsQtyToInvoice($orderItem, $orderItemsQtyToInvoice); + } + } else { + if (isset($orderItemsQtyToInvoice[$orderItem->getParentItemId()])) { + $orderItemsQtyToInvoice[$orderItem->getId()] = + $orderItemsQtyToInvoice[$orderItem->getParentItemId()]; } } - - $this->prepareItemQty($orderItem, $qtys); } - return $qtys; + return $orderItemsQtyToInvoice; } /** - * Prepare qty_invoiced for order item + * Sets qty to invoice for children order items, if not set. * - * @param \Magento\Sales\Api\Data\OrderItemInterface $orderItem - * @param array $qtys + * @param OrderItemInterface $parentOrderItem + * @param array $orderItemsQtyToInvoice + * @return array */ - private function prepareItemQty(\Magento\Sales\Api\Data\OrderItemInterface $orderItem, &$qtys) - { - $this->prepareBundleQty($orderItem, $qtys); + private function setChildItemsQtyToInvoice( + OrderItemInterface $parentOrderItem, + array $orderItemsQtyToInvoice + ): array { + /** @var OrderItemInterface $childOrderItem */ + foreach ($parentOrderItem->getChildrenItems() as $childOrderItem) { + if (!isset($orderItemsQtyToInvoice[$childOrderItem->getItemId()])) { + $productOptions = $childOrderItem->getProductOptions(); - if ($orderItem->isDummy()) { - if ($orderItem->getHasChildren()) { - foreach ($orderItem->getChildrenItems() as $child) { - if (!isset($qtys[$child->getId()])) { - $qtys[$child->getId()] = $child->getQtyToInvoice(); - } - $parentId = $orderItem->getParentItemId(); - if ($parentId && array_key_exists($parentId, $qtys)) { - $qtys[$orderItem->getId()] = $qtys[$parentId]; - } else { - continue; - } - } - } elseif ($orderItem->getParentItem()) { - $parent = $orderItem->getParentItem(); - if (!isset($qtys[$parent->getId()])) { - $qtys[$parent->getId()] = $parent->getQtyToInvoice(); + if (isset($productOptions['bundle_selection_attributes'])) { + $bundleSelectionAttributes = $this->serializer + ->unserialize($productOptions['bundle_selection_attributes']); + $orderItemsQtyToInvoice[$childOrderItem->getItemId()] = + $bundleSelectionAttributes['qty'] * $orderItemsQtyToInvoice[$parentOrderItem->getItemId()]; } } } - } - - /** - * Prepare qty to invoice for bundle products - * - * @param \Magento\Sales\Api\Data\OrderItemInterface $orderItem - * @param array $qtys - */ - private function prepareBundleQty(\Magento\Sales\Api\Data\OrderItemInterface $orderItem, &$qtys) - { - if ($orderItem->getProductType() == Type::TYPE_BUNDLE && !$orderItem->isShipSeparately()) { - foreach ($orderItem->getChildrenItems() as $childItem) { - $bundleSelectionAttributes = $childItem->getProductOptionByCode('bundle_selection_attributes'); - if (is_string($bundleSelectionAttributes)) { - $bundleSelectionAttributes = $this->serializer->unserialize($bundleSelectionAttributes); - } - $qtys[$childItem->getId()] = $qtys[$orderItem->getId()] * $bundleSelectionAttributes['qty']; - } - } + return $orderItemsQtyToInvoice; } /** * Check if order item can be invoiced. * - * @param \Magento\Sales\Api\Data\OrderItemInterface $item + * @param OrderItemInterface $item * @param array $qtys * @return bool * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ - protected function _canInvoiceItem(\Magento\Sales\Api\Data\OrderItemInterface $item, array $qtys = []) + private function canInvoiceItem(OrderItemInterface $item, array $qtys): bool { if ($item->getLockedDoInvoice()) { return false; @@ -299,14 +277,14 @@ protected function _canInvoiceItem(\Magento\Sales\Api\Data\OrderItemInterface $i } /** - * Set quantity to invoice item + * Set quantity to invoice item. * - * @param \Magento\Sales\Api\Data\InvoiceItemInterface $item + * @param InvoiceItemInterface $item * @param float $qty - * @return $this - * @throws \Magento\Framework\Exception\LocalizedException + * @return InvoiceManagementInterface + * @throws LocalizedException */ - protected function setInvoiceItemQuantity(\Magento\Sales\Api\Data\InvoiceItemInterface $item, $qty) + private function setInvoiceItemQuantity(InvoiceItemInterface $item, float $qty): InvoiceManagementInterface { $qty = ($item->getOrderItem()->getIsQtyDecimal()) ? (double) $qty : (int) $qty; $qty = $qty > 0 ? $qty : 0; @@ -317,7 +295,7 @@ protected function setInvoiceItemQuantity(\Magento\Sales\Api\Data\InvoiceItemInt $qtyToInvoice = sprintf("%F", $item->getOrderItem()->getQtyToInvoice()); $qty = sprintf("%F", $qty); if ($qty > $qtyToInvoice && !$item->getOrderItem()->isDummy()) { - throw new \Magento\Framework\Exception\LocalizedException( + throw new LocalizedException( __('We found an invalid quantity to invoice item "%1".', $item->getName()) ); } diff --git a/app/code/Magento/Sales/Model/Service/OrderService.php b/app/code/Magento/Sales/Model/Service/OrderService.php index e4a71f028cc82..2e062caca9a24 100644 --- a/app/code/Magento/Sales/Model/Service/OrderService.php +++ b/app/code/Magento/Sales/Model/Service/OrderService.php @@ -7,6 +7,7 @@ use Magento\Sales\Api\OrderManagementInterface; use Magento\Payment\Gateway\Command\CommandException; +use Psr\Log\LoggerInterface; /** * Class OrderService @@ -55,6 +56,11 @@ class OrderService implements OrderManagementInterface */ private $paymentFailures; + /** + * @var LoggerInterface + */ + private $logger; + /** * Constructor * @@ -65,7 +71,8 @@ class OrderService implements OrderManagementInterface * @param \Magento\Sales\Model\OrderNotifier $notifier * @param \Magento\Framework\Event\ManagerInterface $eventManager * @param \Magento\Sales\Model\Order\Email\Sender\OrderCommentSender $orderCommentSender - * @param \Magento\Sales\Api\PaymentFailuresInterface|null $paymentFailures + * @param \Magento\Sales\Api\PaymentFailuresInterface $paymentFailures + * @param LoggerInterface $logger */ public function __construct( \Magento\Sales\Api\OrderRepositoryInterface $orderRepository, @@ -75,7 +82,8 @@ public function __construct( \Magento\Sales\Model\OrderNotifier $notifier, \Magento\Framework\Event\ManagerInterface $eventManager, \Magento\Sales\Model\Order\Email\Sender\OrderCommentSender $orderCommentSender, - \Magento\Sales\Api\PaymentFailuresInterface $paymentFailures = null + \Magento\Sales\Api\PaymentFailuresInterface $paymentFailures, + LoggerInterface $logger ) { $this->orderRepository = $orderRepository; $this->historyRepository = $historyRepository; @@ -84,8 +92,8 @@ public function __construct( $this->notifier = $notifier; $this->eventManager = $eventManager; $this->orderCommentSender = $orderCommentSender; - $this->paymentFailures = $paymentFailures ? : \Magento\Framework\App\ObjectManager::getInstance() - ->get(\Magento\Sales\Api\PaymentFailuresInterface::class); + $this->paymentFailures = $paymentFailures; + $this->logger = $logger; } /** @@ -189,25 +197,31 @@ public function unHold($id) } /** + * Perform place order. + * * @param \Magento\Sales\Api\Data\OrderInterface $order * @return \Magento\Sales\Api\Data\OrderInterface * @throws \Exception */ public function place(\Magento\Sales\Api\Data\OrderInterface $order) { - // transaction will be here - //begin transaction try { $order->place(); - return $this->orderRepository->save($order); - //commit + } catch (CommandException $e) { + $this->paymentFailures->handle((int)$order->getQuoteId(), __($e->getMessage())); + throw $e; + } + + try { + $order = $this->orderRepository->save($order); } catch (\Exception $e) { - if ($e instanceof CommandException) { - $this->paymentFailures->handle((int)$order->getQuoteId(), __($e->getMessage())); - } + $this->logger->critical( + 'Saving order ' . $order->getIncrementId() . ' failed: ' . $e->getMessage() + ); throw $e; - //rollback; } + + return $order; } /** diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminCreateInvoiceActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminCreateInvoiceActionGroup.xml new file mode 100644 index 0000000000000..416d3f488dd1f --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminCreateInvoiceActionGroup.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminCreateInvoiceActionGroup"> + <click selector="{{AdminOrderDetailsMainActionsSection.invoice}}" stepKey="clickInvoice"/> + <waitForPageLoad stepKey="waitForInvoicePage"/> + <click selector="{{AdminInvoiceMainActionsSection.submitInvoice}}" stepKey="submitInvoice"/> + <waitForPageLoad stepKey="waitForLoadPage"/> + <see userInput="The invoice has been created." stepKey="seeMessage"/> + </actionGroup> + <actionGroup name="AdminCreateInvoiceAndShipmentActionGroup" extends="AdminCreateInvoiceActionGroup"> + <checkOption selector="{{AdminInvoicePaymentShippingSection.CreateShipment}}" stepKey="checkCreateShipment" after="waitForInvoicePage"/> + <see userInput="You created the invoice and shipment." stepKey="seeMessage"/> + </actionGroup> + <actionGroup name="AdminCreateInvoiceAndCreditMemoActionGroup" extends="AdminCreateInvoiceActionGroup"> + <click selector="{{AdminOrderDetailsMainActionsSection.creditMemo}}" stepKey="pushButtonCreditMemo" after="seeMessage"/> + <waitForPageLoad stepKey="waitForLoadingCreditMemoPage" after="pushButtonCreditMemo"/> + <scrollTo selector="{{AdminCreditMemoTotalSection.submitRefundOffline}}" stepKey="scrollToBottom" after="waitForLoadingCreditMemoPage"/> + <click selector="{{AdminCreditMemoTotalSection.submitRefundOffline}}" stepKey="clickSubmitRefund" after="scrollToBottom"/> + <waitForPageLoad stepKey="waitForMainOrderPageLoad" after="clickSubmitRefund"/> + <see userInput="You created the credit memo." stepKey="seeCreditMemoMessage" after="waitForMainOrderPageLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminInvoiceActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminInvoiceActionGroup.xml index 71a979d085dc5..8e6cae2583f51 100644 --- a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminInvoiceActionGroup.xml +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminInvoiceActionGroup.xml @@ -43,6 +43,7 @@ <see selector="{{AdminOrderDetailsMessagesSection.successMessage}}" userInput="The invoice has been created." stepKey="seeSuccessMessage"/> <click selector="{{AdminOrderDetailsOrderViewSection.invoices}}" stepKey="clickInvoices"/> <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask5" /> + <conditionalClick selector="{{AdminOrderInvoicesTabSection.clearFilters}}" dependentSelector="{{AdminOrderInvoicesTabSection.clearFilters}}" visible="true" stepKey="clearExistingOrderFilters"/> <click selector="{{AdminOrderInvoicesTabSection.viewInvoice}}" stepKey="openInvoicePage"/> <waitForPageLoad stepKey="waitForInvoicePageLoad"/> </actionGroup> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml index 093dc8365bad9..f7d24cda34142 100644 --- a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionGroup.xml @@ -141,6 +141,12 @@ <click selector="{{AdminOrderFormItemsSection.addSelected}}" stepKey="clickAddSelectedProducts"/> </actionGroup> + <actionGroup name="newAddConfigurableProductToOrder" extends="addConfigurableProductToOrder"> + <remove keyForRemoval="waitForConfigurablePopover"/> + <remove keyForRemoval="selectionConfigurableOption"/> + <selectOption selector="{{AdminOrderFormConfigureProductSection.selectOption}}" userInput="{{option.value}}" stepKey="selectOption" after="waitForOptionsToLoad"/> + </actionGroup> + <!--Add configurable product to order --> <actionGroup name="addConfigurableProductToOrderFromAdmin" extends="addConfigurableProductToOrder"> <waitForElementVisible selector="{{AdminOrderFormConfigureProductSection.optionSelect(attribute.default_frontend_label)}}" stepKey="waitForConfigurablePopover"/> @@ -406,6 +412,7 @@ <argument name="customer"/> </arguments> <amOnPage stepKey="navigateToNewOrderPage" url="{{AdminOrderCreatePage.url}}"/> + <waitForPageLoad stepKey="waitForNewOrderPageOpened"/> <click stepKey="chooseCustomer" selector="{{AdminOrdersGridSection.customerInOrdersSection(customer.firstname)}}"/> <waitForPageLoad stepKey="waitForStoresPageOpened"/> <click selector="{{OrdersGridSection.addProducts}}" stepKey="clickOnAddProducts"/> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionOnGridActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionOnGridActionGroup.xml new file mode 100644 index 0000000000000..a9091deb039fc --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderActionOnGridActionGroup.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminOrderActionOnGridActionGroup"> + <arguments> + <argument name="action" type="string"/> + <argument name="orderId" type="string"/> + </arguments> + <checkOption selector="{{AdminOrdersGridSection.selectOrderID(orderId)}}" stepKey="selectOrder"/> + <waitForLoadingMaskToDisappear stepKey="waitForCheck"/> + <click selector="{{AdminOrdersGridSection.selectActions}}" stepKey="openActions"/> + <click selector="{{AdminOrdersGridSection.dropdownActionItem(action)}}" stepKey="selectAction"/> + <waitForPageLoad stepKey="waitForResults"/> + </actionGroup> + <actionGroup name="AdminTwoOrderActionOnGridActionGroup" extends="AdminOrderActionOnGridActionGroup"> + <arguments> + <argument name="secondOrderId" type="string"/> + </arguments> + <checkOption selector="{{AdminOrdersGridSection.selectOrderID(secondOrderId)}}" stepKey="selectSecondOrder" after="waitForCheck"/> + <waitForLoadingMaskToDisappear stepKey="waitForSecondCheck" after="selectSecondOrder"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderFilterByOrderIdAndStatusActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderFilterByOrderIdAndStatusActionGroup.xml new file mode 100644 index 0000000000000..352155c190c64 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminOrderFilterByOrderIdAndStatusActionGroup.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminOrderFilterByOrderIdAndStatusActionGroup"> + <arguments> + <argument name="orderId" type="string"/> + <argument name="orderStatus" type="string"/> + </arguments> + <amOnPage url="{{AdminOrdersPage.url}}" stepKey="navigateToOrderGridPage"/> + <waitForPageLoad stepKey="waitForLoadingPage"/> + <conditionalClick selector="{{AdminOrdersGridSection.clearFilters}}" dependentSelector="{{AdminOrdersGridSection.clearFilters}}" visible="true" stepKey="clearExistingOrderFilters"/> + <click selector="{{AdminOrdersGridSection.filters}}" stepKey="openOrderGridFilters"/> + <fillField selector="{{AdminOrdersGridSection.idFilter}}" userInput="{{orderId}}" stepKey="fillOrderIdFilter"/> + <selectOption selector="{{AdminOrdersGridSection.selectStatus}}" userInput="{{orderStatus}}" stepKey="selectOrderStatus"/> + <click selector="{{AdminOrdersGridSection.applyFilters}}" stepKey="clickOrderApplyFilters"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoadingMask"/> + </actionGroup> +</actionGroups> + diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminSelectFlatRateShippingMethodActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminSelectFlatRateShippingMethodActionGroup.xml new file mode 100644 index 0000000000000..cbf92422fd0ee --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/AdminSelectFlatRateShippingMethodActionGroup.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminSelectFlatRateShippingMethodActionGroup"> + <waitForPageLoad stepKey="waitForOrderPageToLoad"/> + <click selector="{{AdminInvoicePaymentShippingSection.getShippingMethodAndRates}}" stepKey="openShippingMethod"/> + <waitForPageLoad stepKey="waitForShippingMethods"/> + <click selector="{{AdminInvoicePaymentShippingSection.shippingMethod}}" stepKey="chooseShippingMethod"/> + <waitForPageLoad stepKey="waitForPageToLoad"/> + </actionGroup> +</actionGroups> \ No newline at end of file diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/FilterOrderStatusByLabelAndCodeActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/FilterOrderStatusByLabelAndCodeActionGroup.xml new file mode 100644 index 0000000000000..96e562cb95c6f --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/FilterOrderStatusByLabelAndCodeActionGroup.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + <actionGroup name="FilterOrderStatusByLabelAndCodeActionGroup"> + <arguments> + <argument name="statusLabel" type="string"/> + <argument name="statusCode" type="string"/> + </arguments> + <conditionalClick selector="{{AdminOrderStatusGridSection.resetFilter}}" dependentSelector="{{AdminOrderStatusGridSection.resetFilter}}" visible="true" stepKey="clearOrderStatusFilters" /> + <fillField selector="{{AdminOrderStatusGridSection.statusLabel}}" userInput="{{statusLabel}}" stepKey="fillStatusLabel"/> + <fillField selector="{{AdminOrderStatusGridSection.statusCode}}" userInput="{{statusCode}}" stepKey="fillStatusCode"/> + <click selector="{{AdminOrderStatusGridSection.search}}" stepKey="clickSearch"/> + <waitForPageLoad stepKey="waitForSearch"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/ActionGroup/SelectActionForOrdersActionGroup.xml b/app/code/Magento/Sales/Test/Mftf/ActionGroup/SelectActionForOrdersActionGroup.xml new file mode 100644 index 0000000000000..073eb03b11bfa --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/ActionGroup/SelectActionForOrdersActionGroup.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/magento2-functional-testing-framework/src/Magento/FunctionalTestingFramework/Test/etc/actionGroupSchema.xsd"> + <actionGroup name="SelectActionForOrdersActionGroup"> + <arguments> + <argument name="action" type="string"/> + </arguments> + <checkOption selector="{{AdminOrdersGridSection.checkOrder}}" stepKey="checkOrder"/> + <click selector="{{AdminOrdersGridSection.orderActions}}" stepKey="clickOrderActions"/> + <click selector="{{AdminOrdersGridSection.changeOrderStatus(action)}}" stepKey="changeOrdersAction"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Sales/Test/Mftf/Data/OrderActionsData.xml b/app/code/Magento/Sales/Test/Mftf/Data/OrderActionsData.xml new file mode 100644 index 0000000000000..64502f5632fc2 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Data/OrderActionsData.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="OrderActions" type="orderActions"> + <data key="cancel">Cancel</data> + <data key="hold">Hold</data> + <data key="unhold">Unhold</data> + <data key="printInvoices">Print Invoices</data> + <data key="printPackingSlips">Print Packing Slips</data> + <data key="printCreditMemos">Print Credit Memos</data> + <data key="printAll">Print All</data> + <data key="printShippingLabels">Print Shipping Labels</data> + </entity> +</entities> diff --git a/app/code/Magento/Sales/Test/Mftf/Data/OrderData.xml b/app/code/Magento/Sales/Test/Mftf/Data/OrderData.xml index eb5600f112ea1..9f14b56ffbe1d 100644 --- a/app/code/Magento/Sales/Test/Mftf/Data/OrderData.xml +++ b/app/code/Magento/Sales/Test/Mftf/Data/OrderData.xml @@ -14,6 +14,11 @@ <data key="shipping">10.00</data> <data key="grandTotal">256.00</data> </entity> + <entity name="AdminOrderSimpleProductWithCatalogRule" type="order"> + <data key="subtotal">110.70</data> + <data key="shipping">5.00</data> + <data key="grandTotal">115.70</data> + </entity> <entity name="AdminOrderSimpleProduct" type="order"> <data key="subtotal">123.00</data> <data key="shipping">5.00</data> @@ -23,7 +28,11 @@ <data key="from">200</data> <data key="to">400</data> </entity> - + <entity name="AdminOrderMultipleProducts" type="order"> + <data key="subtotal">60.00</data> + <data key="shipping">0.00</data> + <data key="grandTotal">60.00</data> + </entity> <entity name="OrderStatus" type="status"> <data key="canceled">Canceled</data> <data key="closed">Closed</data> @@ -34,5 +43,6 @@ <data key="pending">Pending</data> <data key="pendingPayment">Pending Payment</data> <data key="processing">Processing</data> + <data key="ordered">Ordered</data> </entity> </entities> diff --git a/app/code/Magento/Sales/Test/Mftf/Data/OrderStateData.xml b/app/code/Magento/Sales/Test/Mftf/Data/OrderStateData.xml new file mode 100644 index 0000000000000..9493124fd6e40 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Data/OrderStateData.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="OrderState" type="state"> + <data key="canceled">Canceled</data> + <data key="closed">Closed</data> + <data key="complete">Complete</data> + <data key="payment_review">Payment Review</data> + <data key="processing">Processing</data> + <data key="holded">On Hold</data> + <data key="new">Pending</data> + <data key="pending_payment">Pending Payment</data> + </entity> +</entities> diff --git a/app/code/Magento/Sales/Test/Mftf/Data/OrderStatusConfigData.xml b/app/code/Magento/Sales/Test/Mftf/Data/OrderStatusConfigData.xml new file mode 100644 index 0000000000000..4bb8256b68be1 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Data/OrderStatusConfigData.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="EnableCheckmoOrderStatusPending"> + <data key="path">payment/checkmo/order_status</data> + <data key="scope">payment</data> + <data key="scope_id">1</data> + <data key="label">Pending</data> + <data key="value">pending</data> + </entity> +</entities> diff --git a/app/code/Magento/Sales/Test/Mftf/Data/PurchaseOrderNumberData.xml b/app/code/Magento/Sales/Test/Mftf/Data/PurchaseOrderNumberData.xml new file mode 100644 index 0000000000000..e3b3582ba839b --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Data/PurchaseOrderNumberData.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd"> + <entity name="PurchaseOrderNumber"> + <data key="number" unique="suffix">PONumber</data> + </entity> +</entities> \ No newline at end of file diff --git a/app/code/Magento/Sales/Test/Mftf/Page/StorefrontCustomerOrdersHistoryPage.xml b/app/code/Magento/Sales/Test/Mftf/Page/StorefrontCustomerOrdersHistoryPage.xml new file mode 100644 index 0000000000000..ccbb95a7f97e7 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Page/StorefrontCustomerOrdersHistoryPage.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> + <page name="StorefrontCustomerOrdersHistoryPage" url="sales/order/history/" module="Magento_Sales" area="storefront"> + <section name="StorefrontCustomerOrdersGridSection"/> + </page> +</pages> diff --git a/app/code/Magento/Sales/Test/Mftf/Page/StorefrontOrderInformationPage.xml b/app/code/Magento/Sales/Test/Mftf/Page/StorefrontOrderInformationPage.xml index 4159f9435c866..a9281be1bed08 100644 --- a/app/code/Magento/Sales/Test/Mftf/Page/StorefrontOrderInformationPage.xml +++ b/app/code/Magento/Sales/Test/Mftf/Page/StorefrontOrderInformationPage.xml @@ -8,7 +8,7 @@ <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> - <page name="StorefrontOrderInformationPage" url="sales/guest/view" area="guest" module="Magento_Sales"> + <page name="StorefrontOrderInformationPage" url="sales/guest/view" area="storefront" module="Magento_Sales"> <section name="StorefrontOrderInformationMainSection"/> </page> </pages> diff --git a/app/code/Magento/Sales/Test/Mftf/Page/StorefrontOrdersAndReturnsPage.xml b/app/code/Magento/Sales/Test/Mftf/Page/StorefrontOrdersAndReturnsPage.xml index ee546174d9680..cd15a27fa8c3d 100644 --- a/app/code/Magento/Sales/Test/Mftf/Page/StorefrontOrdersAndReturnsPage.xml +++ b/app/code/Magento/Sales/Test/Mftf/Page/StorefrontOrdersAndReturnsPage.xml @@ -8,7 +8,7 @@ <pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/PageObject.xsd"> - <page name="StorefrontOrdersAndReturnsPage" url="sales/guest/form" area="guest" module="Magento_Sales"> + <page name="StorefrontOrdersAndReturnsPage" url="sales/guest/form" area="storefront" module="Magento_Sales"> <section name="StorefrontOrderAndReturnInformationSection"/> </page> </pages> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminAssignOrderStatusToStateSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminAssignOrderStatusToStateSection.xml index 42a870e41f453..53e5e14497c63 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminAssignOrderStatusToStateSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminAssignOrderStatusToStateSection.xml @@ -5,11 +5,14 @@ * See COPYING.txt for license details. */ --> + <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="AdminAssignOrderStatusToStateSection"> - <element name="orderStatus" type="select" selector="#container [name=status]"/> - <element name="orderState" type="select" selector="#container [name=state]"/> + <element name="orderStatus" type="select" selector="#status"/> + <element name="orderState" type="select" selector="#state"/> + <element name="orderStatusAsDefault" type="checkbox" selector="#is_default"/> + <element name="visibleOnStorefront" type="checkbox" selector="#visible_on_front"/> <element name="saveStatusAssignment" type="button" selector="#save" timeout="30"/> </section> </sections> \ No newline at end of file diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminCreateOrderShoppingCartSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminCreateOrderShoppingCartSection.xml new file mode 100644 index 0000000000000..fdd80a1813aa6 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminCreateOrderShoppingCartSection.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminCreateOrderShoppingCartSection"> + <element name="shoppingCartBlock" type="text" selector="#sidebar_data_cart"/> + <element name="addToOrderCheckBox" type="checkbox" selector="//div[@id='order-sidebar_cart']//tr[td[.='{{productName}}']]//input[contains(@name,'sidebar[add_cart_item]')]" parameterized="true" timeout="30"/> + </section> +</sections> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminCreateOrderWishListSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminCreateOrderWishListSection.xml new file mode 100644 index 0000000000000..d6c0c994dda36 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminCreateOrderWishListSection.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminCreateOrderWishListSection"> + <element name="wishListBlock" type="text" selector="#sidebar_data_wishlist"/> + <element name="addProductToOrderCheckBox" type="checkbox" selector="//div[@id='order-sidebar_wishlist']//tr[td[.='{{productName}}']]//input[contains(@name,'sidebar[add_wishlist_item]')]" parameterized="true" timeout="30"/> + <element name="addConfigProductToOrder" type="text" selector="//div[@id='order-sidebar_wishlist']//tr[td[contains(.,'{{configProductName}}')]]//a[contains(@class, 'icon-configure')]" parameterized="true" timeout="30"/> + </section> +</sections> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderDetailsMainActionsSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderDetailsMainActionsSection.xml index 2c100c31c4b83..45fdfebfa8145 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderDetailsMainActionsSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderDetailsMainActionsSection.xml @@ -20,6 +20,7 @@ <element name="reorder" type="button" selector="#order_reorder" timeout="30"/> <element name="edit" type="button" selector="#order_edit" timeout="30"/> <element name="modalOk" type="button" selector=".action-accept"/> + <element name="unhold" type="button" selector="#order-view-unhold-button" timeout="30"/> <element name="invoiceBtn" type="button" selector="//button[@title='Invoice']"/> <element name="shipBtn" type="button" selector="//button[@title='Ship']"/> <element name="shipmentsTab" type="button" selector="#sales_order_view_tabs_order_shipments"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormAccountSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormAccountSection.xml index 11d973d1e19de..71050f487ef75 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormAccountSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormAccountSection.xml @@ -13,6 +13,6 @@ <element name="email" type="input" selector="#email"/> <element name="requiredGroup" type="text" selector=".admin__field.required[data-ui-id='billing-address-fieldset-element-form-field-group-id']"/> <element name="requiredEmail" type="text" selector=".admin__field.required[data-ui-id='billing-address-fieldset-element-form-field-email']"/> - <element name="defaultGeneral" type="text" selector="//*[contains(text(),'General')]" time="15"/> + <element name="defaultGeneral" type="text" selector="//*[contains(text(),'General')]" timeout="15"/> </section> </sections> \ No newline at end of file diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormActionSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormActionSection.xml index 027962282b2c3..d19137c5ad6a4 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormActionSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormActionSection.xml @@ -15,5 +15,6 @@ <element name="submitOrder" type="button" selector="#submit_order_top_button" timeout="30"/> <element name="cancel" type="button" selector="#reset_order_top_button" timeout="30"/> <element name="createNewCustomer" type="button" selector="#order-customer-selector .actions button.primary" timeout="30"/> + <element name="pageHeader" type="text" selector=".page-header.row"/> </section> </sections> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormConfigureProductSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormConfigureProductSection.xml index cd72da7445642..d97717dd65bec 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormConfigureProductSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormConfigureProductSection.xml @@ -12,5 +12,6 @@ <element name="optionSelect" type="select" selector="//div[contains(@class,'product-options')]/div/div/select[../../label[text() = '{{option}}']]" parameterized="true"/> <element name="quantity" type="input" selector="#product_composite_configure_input_qty"/> <element name="ok" type="button" selector=".modal-header .page-actions button[data-role='action']" timeout="30"/> + <element name="selectOption" type="select" selector="//form[@id='product_composite_configure_form']//select"/> </section> </sections> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormItemsOrderedSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormItemsOrderedSection.xml index beb566b20806c..e3417e7c662b9 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormItemsOrderedSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormItemsOrderedSection.xml @@ -16,5 +16,7 @@ <element name="addProductToOrder" type="input" selector="//*[@title='Add Products to Order']"/> <element name="itemsOrderedSummaryText" type="textarea" selector="//table[@class='data-table admin__table-primary order-tables']/tfoot/tr"/> <element name="configureSelectAttribute" type="select" selector="select[id*=attribute]"/> + <element name="itemsSKU" type="text" selector="(//div[contains(@class, 'product-sku-block')])[{{productNumber}}]" parameterized="true"/> + <element name="moveProduct" type="select" selector="//td[contains(.,'{{productName}}')]/../..//td//select" parameterized="true"/> </section> </sections> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormPaymentSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormPaymentSection.xml index 326a27ff0d417..b31582552cccc 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormPaymentSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderFormPaymentSection.xml @@ -23,5 +23,9 @@ <element name="fieldPurchaseOrderNumber" type="input" selector="#po_number"/> <element name="paymentBlock" type="text" selector="#order-billing_method" /> <element name="paymentError" type="text" selector="#payment[method]-error"/> + <element name="bankTransferOption" type="radio" selector="#p_method_banktransfer" timeout="30"/> + <element name="cashOnDeliveryOption" type="radio" selector="#p_method_cashondelivery" timeout="30"/> + <element name="purchaseOrderOption" type="radio" selector="#p_method_purchaseorder" timeout="30"/> + <element name="purchaseOrderNumber" type="input" selector="#po_number"/> </section> </sections> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderStatusGridSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderStatusGridSection.xml index 050035840bfcb..815016ab1beb6 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderStatusGridSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrderStatusGridSection.xml @@ -12,8 +12,13 @@ <element name="statusCodeFilterField" type="input" selector="[data-role=filter-form] [name=status]"/> <element name="statusCodeDataColumn" type="input" selector="[data-role=row] [data-column=status]"/> <element name="statusLabelDataColumn" type="input" selector="[data-role=row] [data-column=label]"/> + <element name="assignStatusToStateBtn" type="button" selector="#assign" timeout="30"/> + <element name="statusLabel" type="input" selector="#sales_order_status_grid_filter_label"/> + <element name="statusCode" type="input" selector="#sales_order_status_grid_filter_status"/> + <element name="resetFilter" type="button" selector="//div[contains(concat(' ',normalize-space(@class),' '),' action-reset ')]" timeout="30"/> + <element name="search" type="button" selector="[data-action='grid-filter-apply']" timeout="30"/> + <element name="gridCell" type="text" selector="//tr['{{row}}']//td[count(//div[contains(concat(' ',normalize-space(@class),' '),' admin__data-grid-wrap ')]//tr//th[contains(., '{{cellName}}')]/preceding-sibling::th) +1 ]" parameterized="true" timeout="30"/> <element name="stateCodeAndTitleDataColumn" type="input" selector="[data-role=row] [data-column=state]"/> - <element name="assignStatusToStateButton" type="button" selector="#assign" timeout="30"/> <element name="unassign" type="text" selector="[data-role=row] [data-column=unassign]"/> </section> </sections> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrdersGridSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrdersGridSection.xml index c87a1b70cb8fb..7373ff0715336 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/AdminOrdersGridSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/AdminOrdersGridSection.xml @@ -16,6 +16,7 @@ <element name="submitSearch22" type="button" selector=".//*[@class="admin__data-grid-filters-wrap"]/parent::*/div[@class="data-grid-search-control-wrap"]/button"/> <element name="filters" type="button" selector="button[data-action='grid-filter-expand']" timeout="30"/> <element name="idFilter" type="input" selector=".admin__data-grid-filters input[name='increment_id']"/> + <element name="selectStatus" type="select" selector="select[name='status']"/> <element name="billToNameFilter" type="input" selector=".admin__data-grid-filters input[name='billing_name']"/> <element name="enabledFilters" type="block" selector=".admin__data-grid-header .admin__data-grid-filters-current._show"/> <element name="clearFilters" type="button" selector=".admin__data-grid-header [data-action='grid-filter-reset']" timeout="30"/> @@ -31,6 +32,12 @@ <element name="viewColumnCheckbox" type="checkbox" selector="//div[contains(@class,'admin__data-grid-action-columns')]//div[contains(@class, 'admin__field-option')]//label[text() = '{{column}}']/preceding-sibling::input" parameterized="true"/> <element name="customerInOrdersSection" type="button" selector="(//td[contains(text(),'{{customer}}')])[1]" parameterized="true"/> <element name="productForOrder" type="button" selector="//td[contains(text(),'{{var}}')]" parameterized="true"/> + <element name="selectActions" type="button" selector=".action-select-wrap > .action-select" timeout="30"/> + <element name="dropdownActionItem" type="button" selector="(//div[contains(@class, 'action-menu-items')]//span[text()='{{action}}'])[1]" timeout="30" parameterized="true"/> + <element name="checkOrder" type="input" selector="//td[count(//div[@data-role='grid-wrapper'])]//input"/> + <element name="orderActions" type="button" selector="//div[contains(concat(' ',normalize-space(@class),' '),' row-gutter ')]//button[@title='Select Items']"/> + <element name="changeOrderStatus" type="button" selector="//div[contains(concat(' ',normalize-space(@class),' '),' row-gutter ')]//span[text()='{{status}}']" parameterized="true" timeout="30"/> <element name="viewLink" type="text" selector="//td/div[contains(.,'{{orderID}}')]/../..//a[@class='action-menu-item']" parameterized="true"/> + <element name="selectOrderID" type="checkbox" selector="//td/div[text()='{{orderId}}']/../preceding-sibling::td//input" parameterized="true"/> </section> </sections> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/StorefrontCustomerOrdersGridSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/StorefrontCustomerOrdersGridSection.xml new file mode 100644 index 0000000000000..415bac7fd051d --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Section/StorefrontCustomerOrdersGridSection.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="StorefrontCustomerOrdersGridSection"> + <element name="orderView" type="button" selector="//td[text()='{{orderNumber}}']/following-sibling::td[@class='col actions']/a[contains(@class, 'view')]" parameterized="true" /> + </section> +</sections> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/StorefrontOrderAndReturnInformationSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/StorefrontOrderAndReturnInformationSection.xml index aa57dd9bc17ba..55396ce4a0f8d 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/StorefrontOrderAndReturnInformationSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/StorefrontOrderAndReturnInformationSection.xml @@ -14,7 +14,7 @@ <element name="findOrderBy" type="select" selector="#quick-search-type-id"/> <element name="email" type="input" selector="#oar_email"/> <element name="bilingZipCode" type="input" selector="//input[@id='oar_zip']"/> - <element name="continueButton" type="submit" selector="//button[@title='Continue']"/> - <element name="ordersAndReturnsTitle" type="span" selector="//span[@id='page-title-wrapper']"/> + <element name="continueButton" type="button" selector="//button[@title='Continue']"/> + <element name="ordersAndReturnsTitle" type="text" selector="//span[@id='page-title-wrapper']"/> </section> </sections> diff --git a/app/code/Magento/Sales/Test/Mftf/Section/StorefrontOrderInformationMainSection.xml b/app/code/Magento/Sales/Test/Mftf/Section/StorefrontOrderInformationMainSection.xml index e42c301206152..3912e5d5877cb 100644 --- a/app/code/Magento/Sales/Test/Mftf/Section/StorefrontOrderInformationMainSection.xml +++ b/app/code/Magento/Sales/Test/Mftf/Section/StorefrontOrderInformationMainSection.xml @@ -9,7 +9,8 @@ <sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> <section name="StorefrontOrderInformationMainSection"> - <element name="orderTitle" type="span" selector="#page-title-wrapper"/> - <element name="return" type="span" selector="//span[contains(text(), 'Return')]"/> + <element name="orderTitle" type="text" selector="#page-title-wrapper"/> + <element name="return" type="text" selector="//span[contains(text(), 'Return')]"/> + <element name="emptyMessage" type="text" selector="//div[contains(concat(' ',normalize-space(@class),' '),' message info empty ')]/span"/> </section> </sections> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithBankTransferPaymentMethodTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithBankTransferPaymentMethodTest.xml new file mode 100644 index 0000000000000..9457e4400947e --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithBankTransferPaymentMethodTest.xml @@ -0,0 +1,83 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCancelTheCreatedOrderWithBankTransferPaymentMethodTest"> + <annotations> + <group value="Sales"/> + <stories value="Cancel Created Order"/> + <title value="Cancel the created order with bank transfer payment method"/> + <description value="Created an order with bank transfer payment method and cancel the order"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-16068"/> + <group value="mtf_migrated"/> + </annotations> + + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + + <!-- Enable Bank Transfer payment --> + <magentoCLI command="config:set {{EnablePaymentBankTransferConfigData.path}} {{EnablePaymentBankTransferConfigData.value}}" stepKey="enableBankTransferPayment"/> + + <!--Set default flat rate shipping method settings--> + <createData entity="FlatRateShippingMethodDefault" stepKey="setDefaultFlatRateShippingMethod"/> + + <!--Create simple customer--> + <createData entity="Simple_US_Customer_CA" stepKey="simpleCustomer"/> + + <!-- Create Simple Product --> + <createData entity="SimpleProduct2" stepKey="simpleProduct"> + <field key="price">10.00</field> + </createData> + </before> + <after> + <magentoCLI command="config:set {{DisablePaymentBankTransferConfigData.path}} {{DisablePaymentBankTransferConfigData.value}}" stepKey="disableBankTransferPayment"/> + <deleteData createDataKey="simpleCustomer" stepKey="deleteSimpleCustomer"/> + <deleteData createDataKey="simpleProduct" stepKey="deleteSimpleProduct"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!--Create new customer order--> + <actionGroup ref="navigateToNewOrderPageExistingCustomer" stepKey="navigateToNewOrderWithExistingCustomer"> + <argument name="customer" value="$$simpleCustomer$$"/> + </actionGroup> + + <!--Add Simple product to order--> + <actionGroup ref="addSimpleProductToOrder" stepKey="addSimpleProductToTheOrder"> + <argument name="product" value="$$simpleProduct$$"/> + </actionGroup> + + <!--Select FlatRate shipping method--> + <actionGroup ref="AdminSelectFlatRateShippingMethodActionGroup" stepKey="selectFlatRateShippingMethod"/> + + <!-- Select bank Transfer payment method --> + <waitForElementVisible selector="{{AdminOrderFormPaymentSection.paymentBlock}}" stepKey="waitForPaymentOptions"/> + <conditionalClick selector="{{AdminOrderFormPaymentSection.bankTransferOption}}" dependentSelector="{{AdminOrderFormPaymentSection.bankTransferOption}}" visible="true" stepKey="checkBankTransferOption"/> + + <!-- Submit order --> + <click selector="{{AdminOrderFormActionSection.SubmitOrder}}" stepKey="submitOrder"/> + + <!-- Verify order information --> + <actionGroup ref="verifyCreatedOrderInformation" stepKey="verifyCreatedOrderInformation"/> + <grabTextFrom selector="|Order # (\d+)|" stepKey="orderId"/> + + <!-- Cancel the Order --> + <actionGroup ref="cancelPendingOrder" stepKey="cancelPendingOrder"/> + + <!--Log in to Storefront as Customer --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="signUp"> + <argument name="Customer" value="$$simpleCustomer$$"/> + </actionGroup> + + <!-- Assert OrderId and status in frontend order grid --> + <click selector="{{StorefrontCustomerSidebarSection.sidebarCurrentTab('My Orders')}}" stepKey="clickOnMyOrders"/> + <waitForPageLoad stepKey="waitForOrderDetailsToLoad"/> + <seeElement selector="{{StorefrontCustomerOrderViewSection.orderStatusInGrid('$orderId', 'Canceled')}}" stepKey="seeOrderStatusInGrid"/> + </test> +</tests> \ No newline at end of file diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithCashOnDeliveryPaymentMethodTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithCashOnDeliveryPaymentMethodTest.xml new file mode 100644 index 0000000000000..eeb6affec4fd5 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithCashOnDeliveryPaymentMethodTest.xml @@ -0,0 +1,83 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCancelTheCreatedOrderWithCashOnDeliveryPaymentMethodTest"> + <annotations> + <group value="Sales"/> + <stories value="Cancel Created Order"/> + <title value="Cancel the created order with cash on delivery payment method"/> + <description value="Create an order with cash on delivery payment method and cancel the order"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-16069"/> + <group value="mtf_migrated"/> + </annotations> + + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + + <!-- Enable Cash On Delivery payment method --> + <magentoCLI command="config:set {{EnableCashOnDeliveryConfigData.path}} {{EnableCashOnDeliveryConfigData.value}}" stepKey="enableCashOnDeliveryPayment"/> + + <!--Set default flat rate shipping method settings--> + <createData entity="FlatRateShippingMethodDefault" stepKey="setDefaultFlatRateShippingMethod"/> + + <!--Create simple customer--> + <createData entity="Simple_US_Customer_CA" stepKey="simpleCustomer"/> + + <!-- Create Simple Product --> + <createData entity="SimpleProduct2" stepKey="simpleProduct"> + <field key="price">10.00</field> + </createData> + </before> + <after> + <magentoCLI command="config:set {{DisableCashOnDeliveryConfigData.path}} {{DisableCashOnDeliveryConfigData.value}}" stepKey="disableCashOnDeliveryPayment"/> + <deleteData createDataKey="simpleCustomer" stepKey="deleteSimpleCustomer"/> + <deleteData createDataKey="simpleProduct" stepKey="deleteSimpleProduct"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- Create new customer order --> + <actionGroup ref="navigateToNewOrderPageExistingCustomer" stepKey="navigateToNewOrderWithExistingCustomer"> + <argument name="customer" value="$$simpleCustomer$$"/> + </actionGroup> + + <!-- Add Simple product to order --> + <actionGroup ref="addSimpleProductToOrder" stepKey="addSimpleProductToTheOrder"> + <argument name="product" value="$$simpleProduct$$"/> + </actionGroup> + + <!-- Select FlatRate shipping method --> + <actionGroup ref="AdminSelectFlatRateShippingMethodActionGroup" stepKey="selectFlatRateShippingMethod"/> + + <!-- Select Cash On Delivery payment method --> + <waitForElementVisible selector="{{AdminOrderFormPaymentSection.paymentBlock}}" stepKey="waitForPaymentOptions"/> + <checkOption selector="{{AdminOrderFormPaymentSection.cashOnDeliveryOption}}" stepKey="selectCashOnDeliveryPaymentOption"/> + + <!-- Submit order --> + <click selector="{{AdminOrderFormActionSection.SubmitOrder}}" stepKey="submitOrder"/> + + <!--Verify order information--> + <actionGroup ref="verifyCreatedOrderInformation" stepKey="verifyCreatedOrderInformation"/> + <grabTextFrom selector="|Order # (\d+)|" stepKey="orderId"/> + + <!-- Cancel the Order --> + <actionGroup ref="cancelPendingOrder" stepKey="cancelPendingOrder"/> + + <!--Log in to Storefront as Customer --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="signUp"> + <argument name="Customer" value="$$simpleCustomer$$"/> + </actionGroup> + + <!-- Assert Order status in frontend grid --> + <click selector="{{StorefrontCustomerSidebarSection.sidebarCurrentTab('My Orders')}}" stepKey="clickOnMyOrders"/> + <waitForPageLoad stepKey="waitForOrderDetailsToLoad"/> + <seeElement selector="{{StorefrontCustomerOrderViewSection.orderStatusInGrid('$orderId', 'Canceled')}}" stepKey="seeOrderStatusInGrid"/> + </test> +</tests> \ No newline at end of file diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithCheckMoneyOrderPaymentMethodTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithCheckMoneyOrderPaymentMethodTest.xml new file mode 100644 index 0000000000000..614946d7f8a8a --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithCheckMoneyOrderPaymentMethodTest.xml @@ -0,0 +1,215 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCancelTheCreatedOrderWithCheckMoneyOrderPaymentMethodTest"> + <annotations> + <group value="Sales"/> + <stories value="Cancel Created Order"/> + <title value="Cancel the created order with check/money order payment method"/> + <description value=" Create an order with check/money order payment method and cancel the order"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-16066"/> + <group value="mtf_migrated"/> + </annotations> + + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + + <!--Set default flat rate shipping method settings--> + <createData entity="FlatRateShippingMethodDefault" stepKey="setDefaultFlatRateShippingMethod"/> + + <!--Create simple customer--> + <createData entity="Simple_US_Customer_CA" stepKey="simpleCustomer"/> + + <!-- Create the category --> + <createData entity="ApiCategory" stepKey="createCategory"/> + + <!-- Create Virtual Product --> + <createData entity="VirtualProduct" stepKey="virtualProduct"> + <field key="price">10.00</field> + </createData> + + <!-- Create Simple Product --> + <createData entity="ApiSimplePrice10Qty10" stepKey="simpleProduct"> + <requiredEntity createDataKey="createCategory"/> + <field key="price">10.00</field> + </createData> + + <!--Create Bundle product--> + <createData entity="SimpleProduct2" stepKey="simpleProduct1"> + <field key="price">10.00</field> + </createData> + <createData entity="BundleProductPriceViewRange" stepKey="createBundleProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="DropDownBundleOption" stepKey="createBundleOption1_1"> + <requiredEntity createDataKey="createBundleProduct"/> + <field key="required">True</field> + </createData> + <createData entity="ApiBundleLink" stepKey="linkOptionToProduct"> + <requiredEntity createDataKey="createBundleProduct"/> + <requiredEntity createDataKey="createBundleOption1_1"/> + <requiredEntity createDataKey="simpleProduct1"/> + </createData> + + <!-- Create configurable product and add it to the category --> + <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + + <!-- Create an attribute with two options to be used in the first child product --> + <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> + <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + + <!-- Add the attribute we just created to default attribute set --> + <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + + <!-- Get the option of the attribute we created --> + <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + + <!-- Create a simple product and give it the attribute with option --> + <createData entity="ApiSimpleOne" stepKey="createConfigChildProduct1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + </createData> + + <!-- Create the configurable product --> + <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + </createData> + + <!-- Add simple product to the configurable product --> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild1"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct1"/> + </createData> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + <after> + <deleteData createDataKey="simpleCustomer" stepKey="deleteSimpleCustomer"/> + <deleteData createDataKey="simpleProduct" stepKey="deleteSimpleProduct"/> + <deleteData createDataKey="virtualProduct" stepKey="deleteVirtualProduct"/> + <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> + <deleteData createDataKey="createBundleProduct" stepKey="deleteBundleProduct"/> + <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct"/> + <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigurableProduct"/> + <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteProductAttribute"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!--Create new customer order--> + <actionGroup ref="navigateToNewOrderPageExistingCustomer" stepKey="navigateToNewOrderWithExistingCustomer"> + <argument name="customer" value="$$simpleCustomer$$"/> + </actionGroup> + + <!--Add bundle product to order and check product price in grid--> + <actionGroup ref="addBundleProductToOrder" stepKey="addBundleProductToOrder"> + <argument name="product" value="$$createBundleProduct$$"/> + <argument name="quantity" value="1"/> + </actionGroup> + + <!--Add configurable product to order--> + <actionGroup ref="newAddConfigurableProductToOrder" stepKey="addConfigurableProductToOrder"> + <argument name="product" value="$$createConfigProduct$$"/> + <argument name="attribute" value="$$createConfigProductAttribute$$"/> + <argument name="option" value="$$getConfigAttributeOption1$$"/> + </actionGroup> + + <!--Add Simple product to order--> + <actionGroup ref="addSimpleProductToOrder" stepKey="addSimpleProductToTheOrder"> + <argument name="product" value="$$simpleProduct$$"/> + </actionGroup> + + <!--Add Virtual product to order--> + <actionGroup ref="addSimpleProductToOrder" stepKey="addVirtualProductToTheOrder"> + <argument name="product" value="$$virtualProduct$$"/> + </actionGroup> + + <!--Select FlatRate shipping method--> + <actionGroup ref="AdminSelectFlatRateShippingMethodActionGroup" stepKey="selectFlatRateShippingMethod"/> + + <!-- Submit order --> + <click selector="{{AdminOrderFormActionSection.SubmitOrder}}" stepKey="submitOrder"/> + + <!--Verify order information--> + <actionGroup ref="verifyCreatedOrderInformation" stepKey="verifyCreatedOrderInformation"/> + <grabTextFrom selector="|Order # (\d+)|" stepKey="orderId"/> + + <!-- Cancel the Order --> + <actionGroup ref="cancelPendingOrder" stepKey="cancelPendingOrder"/> + + <!-- Assert Simple Product Quantity in backend after order Canceled --> + <actionGroup ref="filterAndSelectProduct" stepKey="filterAndSelectTheProduct"> + <argument name="productSku" value="$$simpleProduct.sku$$"/> + </actionGroup> + <waitForElementVisible selector="{{AdminProductFormSection.productName}}" stepKey="waitForProductDetailsToLoad"/> + <seeInField selector="{{AdminProductFormSection.productName}}" userInput="$$simpleProduct.name$$" stepKey="seeProductName"/> + <seeInField selector="{{AdminProductFormSection.productQuantity}}" userInput="10" stepKey="seeProductQuantity"/> + <seeInField selector="{{AdminProductFormSection.productStockStatus}}" userInput="In Stock" stepKey="seeProductStockStatus"/> + + <!-- Assert Virtual Product Quantity in backend after order canceled--> + <actionGroup ref="filterAndSelectProduct" stepKey="filterAndSelectTheVirtualProduct"> + <argument name="productSku" value="$$virtualProduct.sku$$"/> + </actionGroup> + <waitForElementVisible selector="{{AdminProductFormSection.productName}}" stepKey="waitForProductDetailsToLoad1"/> + <seeInField selector="{{AdminProductFormSection.productName}}" userInput="$$virtualProduct.name$$" stepKey="seeVirtualProductName"/> + <seeInField selector="{{AdminProductFormSection.productQuantity}}" userInput="1000" stepKey="seeVirtualProductQuantity"/> + <seeInField selector="{{AdminProductFormSection.productStockStatus}}" userInput="In Stock" stepKey="seeVirtualProductStockStatus"/> + + <!-- Assert Bundle Product quantity in backend after order canceled--> + <actionGroup ref="filterAndSelectProduct" stepKey="filterAndSelectTheBundleProduct"> + <argument name="productSku" value="$$simpleProduct1.sku$$"/> + </actionGroup> + <waitForElementVisible selector="{{AdminProductFormSection.productName}}" stepKey="waitForProductDetailsToLoad2"/> + <seeInField selector="{{AdminProductFormSection.productName}}" userInput="$$simpleProduct1.name$$" stepKey="seeBundleProductName"/> + <seeInField selector="{{AdminProductFormSection.productQuantity}}" userInput="1000" stepKey="seeBundleProductQuantity"/> + <seeInField selector="{{AdminProductFormSection.productStockStatus}}" userInput="In Stock" stepKey="seeBundleProductStockStatus"/> + + <!-- Assert Configurable Product quantity in backend after order canceled--> + <actionGroup ref="filterAndSelectProduct" stepKey="filterAndSelectTheConfigProduct"> + <argument name="productSku" value="$$createConfigChildProduct1.sku$$"/> + </actionGroup> + <waitForElementVisible selector="{{AdminProductFormSection.productName}}" stepKey="waitForProductDetailsToLoad3"/> + <seeInField selector="{{AdminProductFormSection.productName}}" userInput="$$createConfigChildProduct1.name$$" stepKey="seeConfigProductName"/> + <seeInField selector="{{AdminProductFormSection.productQuantity}}" userInput="1000" stepKey="seeConfigProductQuantity"/> + <seeInField selector="{{AdminProductFormSection.productStockStatus}}" userInput="In Stock" stepKey="seeConfigProductStockStatus"/> + + <!-- Open Order Index Page --> + <amOnPage url="{{AdminOrdersPage.url}}" stepKey="goToOrders"/> + <waitForPageLoad stepKey="waitForPageLoad5"/> + + <!-- Filter order using orderId --> + <actionGroup ref="filterOrderGridById" stepKey="filterOrderGridById"> + <argument name="orderId" value="$orderId"/> + </actionGroup> + <see selector="{{AdminOrdersGridSection.firstRow}}" userInput="$orderId" stepKey="seeOrderIdInGrid"/> + <see selector="{{AdminOrdersGridSection.firstRow}}" userInput="Canceled" stepKey="seeStatusInGrid"/> + + <!--Log in to Storefront as Customer --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="signUp"> + <argument name="Customer" value="$$simpleCustomer$$"/> + </actionGroup> + + <!-- Assert Order status in frontend order grid --> + <click selector="{{StorefrontCustomerSidebarSection.sidebarCurrentTab('My Orders')}}" stepKey="clickOnMyOrders"/> + <waitForPageLoad stepKey="waitForOrderDetailsToLoad"/> + <seeElement selector="{{StorefrontCustomerOrderViewSection.orderStatusInGrid('$orderId', 'Canceled')}}" stepKey="seeOrderStatusInGrid"/> + </test> +</tests> \ No newline at end of file diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithProductQtyWithoutStockDecreaseTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithProductQtyWithoutStockDecreaseTest.xml new file mode 100644 index 0000000000000..4300f22c3fb3a --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithProductQtyWithoutStockDecreaseTest.xml @@ -0,0 +1,125 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCancelTheCreatedOrderWithProductQtyWithoutStockDecreaseTest"> + <annotations> + <group value="Sales"/> + <stories value="Cancel Created Order"/> + <title value="Cancel the created order with product quantity without stock decrease"/> + <description value="Create an order with product quantity without stock decrease, cancel the order and verify product quantity in backend"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-16071"/> + <group value="mtf_migrated"/> + </annotations> + + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <magentoCLI command="config:set {{DisableCatalogInventoryConfigData.path}} {{DisableCatalogInventoryConfigData.value}}" stepKey="disableDecreaseInQuantityAfterOrder"/> + + <!--Set default flat rate shipping method settings--> + <createData entity="FlatRateShippingMethodDefault" stepKey="setDefaultFlatRateShippingMethod"/> + + <!--Create simple customer--> + <createData entity="Simple_US_Customer_CA" stepKey="simpleCustomer"/> + + <!-- Create the category --> + <createData entity="ApiCategory" stepKey="createCategory"/> + + <!-- Create Simple Product --> + <createData entity="ApiSimplePrice10Qty10" stepKey="simpleProduct"> + <requiredEntity createDataKey="createCategory"/> + <field key="price">10.00</field> + </createData> + </before> + <after> + <magentoCLI command="config:set {{EnableCatalogInventoryConfigData.path}} {{EnableCatalogInventoryConfigData.value}}" stepKey="enableDecreaseInQuantityAfterOrder"/> + <deleteData createDataKey="simpleCustomer" stepKey="deleteSimpleCustomer"/> + <deleteData createDataKey="simpleProduct" stepKey="deleteSimpleProduct"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!--Create new customer order--> + <actionGroup ref="navigateToNewOrderPageExistingCustomer" stepKey="navigateToNewOrderWithExistingCustomer"> + <argument name="customer" value="$$simpleCustomer$$"/> + </actionGroup> + + <!--Add Simple product to order--> + <actionGroup ref="addSimpleProductToOrder" stepKey="addSimpleProductToTheOrder"> + <argument name="product" value="$$simpleProduct$$"/> + </actionGroup> + + <!--Select FlatRate shipping method--> + <actionGroup ref="AdminSelectFlatRateShippingMethodActionGroup" stepKey="selectFlatRateShippingMethod"/> + + <!--Submit order--> + <click selector="{{AdminOrderFormActionSection.SubmitOrder}}" stepKey="submitOrder"/> + + <!--Verify order information--> + <actionGroup ref="verifyCreatedOrderInformation" stepKey="verifyCreatedOrderInformation"/> + <grabTextFrom selector="|Order # (\d+)|" stepKey="orderId"/> + + <!-- Assert Simple Product Quantity in backend after order --> + <actionGroup ref="filterAndSelectProduct" stepKey="filterAndSelectTheProduct"> + <argument name="productSku" value="$$simpleProduct.sku$$"/> + </actionGroup> + <waitForElementVisible selector="{{AdminProductFormSection.productName}}" stepKey="waitForProductDetailsToLoad"/> + <seeInField selector="{{AdminProductFormSection.productName}}" userInput="$$simpleProduct.name$$" stepKey="seeProductName"/> + <seeInField selector="{{AdminProductFormSection.productQuantity}}" userInput="10" stepKey="seeProductQuantity"/> + <seeInField selector="{{AdminProductFormSection.productStockStatus}}" userInput="In Stock" stepKey="seeProductStockStatus"/> + + <!-- Open Order Index Page --> + <amOnPage url="{{AdminOrdersPage.url}}" stepKey="goToOrders"/> + <waitForPageLoad stepKey="waitForPageLoad5"/> + + <!-- Filter Order using orderId --> + <actionGroup ref="filterOrderGridById" stepKey="filterOrderGridById"> + <argument name="orderId" value="$orderId"/> + </actionGroup> + <click selector="{{AdminOrdersGridSection.rowViewAction('1')}}" stepKey="clickOnViewAction"/> + + <!-- Cancel the Order --> + <actionGroup ref="cancelPendingOrder" stepKey="cancelPendingOrder"/> + + <!-- Assert Simple Product Quantity in backend after Cancelling the order --> + <actionGroup ref="filterAndSelectProduct" stepKey="filterAndSelectTheProduct1"> + <argument name="productSku" value="$$simpleProduct.sku$$"/> + </actionGroup> + <waitForElementVisible selector="{{AdminProductFormSection.productName}}" stepKey="waitForProductDetailsToLoad1"/> + <seeInField selector="{{AdminProductFormSection.productName}}" userInput="$$simpleProduct.name$$" stepKey="seeProductName1"/> + <seeInField selector="{{AdminProductFormSection.productQuantity}}" userInput="10" stepKey="seeProductQuantityAfterCancelOrder"/> + <seeInField selector="{{AdminProductFormSection.productStockStatus}}" userInput="In Stock" stepKey="seeProductStockStatusAfterCancelOrder"/> + + <!-- Open Order Index Page --> + <amOnPage url="{{AdminOrdersPage.url}}" stepKey="goToOrders1"/> + <waitForPageLoad stepKey="waitForPageLoad6"/> + + <!-- Filter Order using orderId --> + <actionGroup ref="filterOrderGridById" stepKey="filterOrderGridById1"> + <argument name="orderId" value="$orderId"/> + </actionGroup> + <click selector="{{AdminOrdersGridSection.rowViewAction('1')}}" stepKey="clickOnViewAction1"/> + <waitForPageLoad stepKey="waitForOrderPageToLoad"/> + + <!-- Reorder the product --> + <click selector="{{AdminOrderDetailsMainActionsSection.reorder}}" stepKey="clickOnReorderButton"/> + <waitForPageLoad stepKey="waitForReorderFormToLoad"/> + <click selector="{{AdminOrderFormActionSection.SubmitOrder}}" stepKey="submitOrder1"/> + + <!-- Assert Simple Product Quantity in backend after Reorder --> + <actionGroup ref="filterAndSelectProduct" stepKey="filterAndSelectTheProduct2"> + <argument name="productSku" value="$$simpleProduct.sku$$"/> + </actionGroup> + <waitForElementVisible selector="{{AdminProductFormSection.productName}}" stepKey="waitForProductDetailsToLoad2"/> + <seeInField selector="{{AdminProductFormSection.productName}}" userInput="$$simpleProduct.name$$" stepKey="seeProductName2"/> + <seeInField selector="{{AdminProductFormSection.productQuantity}}" userInput="10" stepKey="seeProductQuantityAfterReorder"/> + <seeInField selector="{{AdminProductFormSection.productStockStatus}}" userInput="In Stock" stepKey="seeProductStockStatusAfterReorder"/> + </test> +</tests> \ No newline at end of file diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithPurchaseOrderPaymentMethodTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithPurchaseOrderPaymentMethodTest.xml new file mode 100644 index 0000000000000..d14987dd2e87b --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithPurchaseOrderPaymentMethodTest.xml @@ -0,0 +1,89 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCancelTheCreatedOrderWithPurchaseOrderPaymentMethodTest"> + <annotations> + <group value="Sales"/> + <stories value="Cancel Created Order"/> + <title value="Cancel the created order with purchase order payment method"/> + <description value="Create an order using Purchase Order payment method and cancel the order"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-16070"/> + <group value="mtf_migrated"/> + </annotations> + + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + + <!-- Enable Purchase Order payment method --> + <magentoCLI command="config:set {{EnablePurchaseOrderConfigData.path}} {{EnablePurchaseOrderConfigData.value}}" stepKey="enableCPurchaseOrderPayment"/> + + <!--Set default flat rate shipping method settings--> + <createData entity="FlatRateShippingMethodDefault" stepKey="setDefaultFlatRateShippingMethod"/> + + <!--Create simple customer--> + <createData entity="Simple_US_Customer_CA" stepKey="simpleCustomer"/> + + <!-- Create Simple Product --> + <createData entity="SimpleProduct2" stepKey="simpleProduct"> + <field key="price">10.00</field> + </createData> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + <after> + <magentoCLI command="config:set {{DisablePurchaseOrderConfigData.path}} {{DisablePurchaseOrderConfigData.value}}" stepKey="disablePurchaseOrderPayment"/> + <deleteData createDataKey="simpleCustomer" stepKey="deleteSimpleCustomer"/> + <deleteData createDataKey="simpleProduct" stepKey="deleteSimpleProduct"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!--Create new customer order--> + <actionGroup ref="navigateToNewOrderPageExistingCustomer" stepKey="navigateToNewOrderWithExistingCustomer"> + <argument name="customer" value="$$simpleCustomer$$"/> + </actionGroup> + + <!--Add Simple product to order--> + <actionGroup ref="addSimpleProductToOrder" stepKey="addSimpleProductToTheOrder"> + <argument name="product" value="$$simpleProduct$$"/> + </actionGroup> + + <!--Select FlatRate shipping method--> + <actionGroup ref="AdminSelectFlatRateShippingMethodActionGroup" stepKey="selectFlatRateShippingMethod"/> + + <!-- Select purchase order payment method and enter purchase order number --> + <waitForElementVisible selector="{{AdminOrderFormPaymentSection.paymentBlock}}" stepKey="waitForPaymentOptions"/> + <checkOption selector="{{AdminOrderFormPaymentSection.purchaseOrderOption}}" stepKey="selectPurchaseOrderPaymentOption"/> + <waitForElementVisible selector="{{AdminOrderFormPaymentSection.purchaseOrderNumber}}" stepKey="waitForElementToBeVisible"/> + <fillField selector="{{AdminOrderFormPaymentSection.purchaseOrderNumber}}" userInput="{{PurchaseOrderNumber.number}}" stepKey="fillPurchaseOrderNumber"/> + <click selector="{{AdminOrderFormActionSection.pageHeader}}" stepKey="clickOnHeader"/> + <waitForPageLoad stepKey="waitForPageToLoad"/> + + <!--Submit order--> + <click selector="{{AdminOrderFormActionSection.SubmitOrder}}" stepKey="submitOrder"/> + + <!--Verify order information--> + <actionGroup ref="verifyCreatedOrderInformation" stepKey="verifyCreatedOrderInformation"/> + <grabTextFrom selector="|Order # (\d+)|" stepKey="orderId"/> + + <!-- Cancel the Order --> + <actionGroup ref="cancelPendingOrder" stepKey="cancelPendingOrder"/> + + <!--Log in to Storefront as Customer --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="signUp"> + <argument name="Customer" value="$$simpleCustomer$$"/> + </actionGroup> + + <!-- Assert Order status in frontend order grid --> + <click selector="{{StorefrontCustomerSidebarSection.sidebarCurrentTab('My Orders')}}" stepKey="clickOnMyOrders"/> + <waitForPageLoad stepKey="waitForOrderDetailsToLoad"/> + <seeElement selector="{{StorefrontCustomerOrderViewSection.orderStatusInGrid('$orderId', 'Canceled')}}" stepKey="seeOrderStatusInGrid"/> + </test> +</tests> \ No newline at end of file diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithZeroSubtotalCheckoutTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithZeroSubtotalCheckoutTest.xml new file mode 100644 index 0000000000000..f8c5b46dc6ff9 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCancelTheCreatedOrderWithZeroSubtotalCheckoutTest.xml @@ -0,0 +1,84 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminCancelTheCreatedOrderWithZeroSubtotalCheckoutTest"> + <annotations> + <group value="Sales"/> + <stories value="Cancel Created Order"/> + <title value="Cancel the created order with zero subtotal checkout"/> + <description value="Create an order with Zero Subtotal checkout and cancel the order"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-16067"/> + <group value="mtf_migrated"/> + </annotations> + + <before> + <!-- Enable Zero Subtotal Checkout --> + <magentoCLI command="config:set {{EnableZeroSubtotalCheckoutConfigData.path}} {{EnableZeroSubtotalCheckoutConfigData.value}}" stepKey="enableZeroSubtotalCheckout"/> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + + <!--Set Free shipping method settings--> + <createData entity="FreeShippingMethodsSettingConfig" stepKey="freeShippingMethodsSettingConfig"/> + + <!--Create simple customer--> + <createData entity="Simple_US_Customer_CA" stepKey="simpleCustomer"/> + + <!-- Create Simple Product --> + <createData entity="SimpleProduct2" stepKey="simpleProduct"> + <field key="price">10.00</field> + </createData> + + <!-- Create a slaes rule with fixed discount --> + <createData entity="SalesRuleNoCouponWithFixedDiscount" stepKey="createSalesRule"/> + </before> + <after> + <!-- Disable Free Shipping --> + <createData entity="DefaultShippingMethodsConfig" stepKey="defaultShippingMethodsConfig"/> + <createData entity="DisableFreeShippingConfig" stepKey="disableFreeShippingConfig"/> + <deleteData createDataKey="createSalesRule" stepKey="deleteSalesRule"/> + <deleteData createDataKey="simpleCustomer" stepKey="deleteSimpleCustomer"/> + <deleteData createDataKey="simpleProduct" stepKey="deleteSimpleProduct"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!--Create new customer order--> + <actionGroup ref="navigateToNewOrderPageExistingCustomer" stepKey="navigateToNewOrderWithExistingCustomer"> + <argument name="customer" value="$$simpleCustomer$$"/> + </actionGroup> + + <!--Add Simple product to order--> + <actionGroup ref="addSimpleProductToOrder" stepKey="addSimpleProductToTheOrder"> + <argument name="product" value="$$simpleProduct$$"/> + </actionGroup> + + <!--Select FlatRate shipping method--> + <actionGroup ref="AdminSelectFlatRateShippingMethodActionGroup" stepKey="selectFlatRateShippingMethod"/> + + <!--Submit order--> + <click selector="{{AdminOrderFormActionSection.SubmitOrder}}" stepKey="submitOrder"/> + + <!--Verify order information--> + <actionGroup ref="verifyCreatedOrderInformation" stepKey="verifyCreatedOrderInformation"/> + <grabTextFrom selector="|Order # (\d+)|" stepKey="orderId"/> + + <!-- Cancel the Order --> + <actionGroup ref="cancelPendingOrder" stepKey="cancelPendingOrder"/> + + <!--Log in to Storefront as Customer --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="signUp"> + <argument name="Customer" value="$$simpleCustomer$$"/> + </actionGroup> + + <!-- Assert Order status in frontend grid --> + <click selector="{{StorefrontCustomerSidebarSection.sidebarCurrentTab('My Orders')}}" stepKey="clickOnMyOrders"/> + <waitForPageLoad stepKey="waitForOrderDetailsToLoad"/> + <seeElement selector="{{StorefrontCustomerOrderViewSection.orderStatusInGrid('$orderId', 'Canceled')}}" stepKey="seeOrderStatusInGrid"/> + </test> +</tests> \ No newline at end of file diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoBankTransferPaymentTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoBankTransferPaymentTest.xml index 89003ed89fd13..e498279ee99ae 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoBankTransferPaymentTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoBankTransferPaymentTest.xml @@ -17,6 +17,9 @@ <testCaseId value="MC-15862"/> <group value="sales"/> <group value="mtf_migrated"/> + <skip> + <issueId value="MC-17140"/> + </skip> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoConfigurableProductTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoConfigurableProductTest.xml index 4da7ec3e22d42..06cffd27933d9 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoConfigurableProductTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoConfigurableProductTest.xml @@ -17,6 +17,9 @@ <testCaseId value="MC-15865"/> <group value="sales"/> <group value="mtf_migrated"/> + <skip> + <issueId value="MC-17140"/> + </skip> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoPartialRefundTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoPartialRefundTest.xml index 03bd436834c11..256be7cde7188 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoPartialRefundTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoPartialRefundTest.xml @@ -17,6 +17,9 @@ <testCaseId value="MC-15861"/> <group value="sales"/> <group value="mtf_migrated"/> + <skip> + <issueId value="MC-17140"/> + </skip> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoWithCashOnDeliveryTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoWithCashOnDeliveryTest.xml index 29df8d56bd5a6..d7d2abb5b2e8a 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoWithCashOnDeliveryTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoWithCashOnDeliveryTest.xml @@ -17,6 +17,9 @@ <testCaseId value="MC-15863"/> <group value="sales"/> <group value="mtf_migrated"/> + <skip> + <issueId value="MC-17140"/> + </skip> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoWithPurchaseOrderTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoWithPurchaseOrderTest.xml index 1c3aaf0d3b440..95b326a13fa5b 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoWithPurchaseOrderTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminCreateCreditMemoWithPurchaseOrderTest.xml @@ -17,6 +17,9 @@ <testCaseId value="MC-15864"/> <group value="sales"/> <group value="mtf_migrated"/> + <skip> + <issueId value="MC-17140"/> + </skip> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminHoldCreatedOrderTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminHoldCreatedOrderTest.xml new file mode 100644 index 0000000000000..9e8949c9ba600 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminHoldCreatedOrderTest.xml @@ -0,0 +1,96 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminHoldCreatedOrderTest"> + <annotations> + <group value="sales"/> + <stories value="Hold Created Order"/> + <title value="Hold the created order"/> + <description value="Create an order and hold the order"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-16160"/> + <group value="mtf_migrated"/> + </annotations> + + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + + <!--Set default flat rate shipping method settings--> + <createData entity="FlatRateShippingMethodDefault" stepKey="setDefaultFlatRateShippingMethod"/> + + <!--Create simple customer--> + <createData entity="Simple_US_Customer_CA" stepKey="simpleCustomer"/> + + <!-- Create Simple Products --> + <createData entity="SimpleProduct2" stepKey="simpleProduct"> + <field key="price">10.00</field> + </createData> + <createData entity="SimpleProduct2" stepKey="simpleProduct1"> + <field key="price">20.00</field> + </createData> + </before> + <after> + <deleteData createDataKey="simpleCustomer" stepKey="deleteSimpleCustomer"/> + <deleteData createDataKey="simpleProduct" stepKey="deleteSimpleProduct"/> + <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!--Create new customer order--> + <actionGroup ref="navigateToNewOrderPageExistingCustomer" stepKey="navigateToNewOrderWithExistingCustomer"> + <argument name="customer" value="$$simpleCustomer$$"/> + </actionGroup> + + <!--Add Simple product to order--> + <actionGroup ref="addSimpleProductToOrder" stepKey="addSimpleProductToTheOrder"> + <argument name="product" value="$$simpleProduct$$"/> + </actionGroup> + + <!--Add second product to order--> + <actionGroup ref="addSimpleProductToOrder" stepKey="addSecondProductToTheOrder"> + <argument name="product" value="$$simpleProduct1$$"/> + </actionGroup> + + <!--Select FlatRate shipping method--> + <actionGroup ref="AdminSelectFlatRateShippingMethodActionGroup" stepKey="selectFlatRateShippingMethod"/> + + <!-- Submit order --> + <click selector="{{AdminOrderFormActionSection.SubmitOrder}}" stepKey="submitOrder"/> + + <!-- Verify order information --> + <actionGroup ref="verifyCreatedOrderInformation" stepKey="verifyCreatedOrderInformation"/> + <grabTextFrom selector="|Order # (\d+)|" stepKey="orderId"/> + + <!-- Hold the Order --> + <click selector="{{AdminOrderDetailsMainActionsSection.hold}}" stepKey="clickOnHoldButton"/> + <see selector="{{AdminOrderDetailsMessagesSection.successMessage}}" userInput="You put the order on hold" stepKey="seeSuccessHoldMessage"/> + + <!--Assert Order Status and Unhold button--> + <see selector="{{AdminOrderDetailsInformationSection.orderStatus}}" userInput="On Hold" stepKey="seeOrderStatusOnHold"/> + <seeElement selector="{{AdminOrderDetailsMainActionsSection.unhold}}" stepKey="seeUnholdButton"/> + + <!--Assert invoice, cancel, reorder, ship, and edit buttons are unavailable--> + <dontSeeElement selector="{{AdminOrderDetailsMainActionsSection.invoice}}" stepKey="dontSeeInvoiceButton"/> + <dontSeeElement selector="{{AdminOrderDetailsMainActionsSection.cancel}}" stepKey="dontSeeCancelButton"/> + <dontSeeElement selector="{{AdminOrderDetailsMainActionsSection.reorder}}" stepKey="dontSeeReorderButton"/> + <dontSeeElement selector="{{AdminOrderDetailsMainActionsSection.ship}}" stepKey="dontSeeShipButton"/> + <dontSeeElement selector="{{AdminOrderDetailsMainActionsSection.edit}}" stepKey="dontSeeEditButton"/> + + <!--Log in to Storefront as Customer --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="signUp"> + <argument name="Customer" value="$$simpleCustomer$$"/> + </actionGroup> + + <!-- Assert OrderId and status in frontend order grid --> + <click selector="{{StorefrontCustomerSidebarSection.sidebarCurrentTab('My Orders')}}" stepKey="clickOnMyOrders"/> + <waitForPageLoad stepKey="waitForOrderDetailsToLoad"/> + <seeElement selector="{{StorefrontCustomerOrderViewSection.orderStatusInGrid('$orderId', 'On Hold')}}" stepKey="seeOrderStatusInGrid"/> + </test> +</tests> \ No newline at end of file diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersCancelCompleteAndClosedTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersCancelCompleteAndClosedTest.xml new file mode 100644 index 0000000000000..e5f43818c1524 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersCancelCompleteAndClosedTest.xml @@ -0,0 +1,94 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminMassOrdersCancelCompleteAndClosedTest"> + <annotations> + <stories value="Mass Update Orders"/> + <title value="Mass cancel orders in status Complete, Closed"/> + <description value="Try to cancel orders in status Complete, Closed"/> + <severity value="MAJOR"/> + <testCaseId value="MC-16183"/> + <group value="sales"/> + <group value="mtf_migrated"/> + <skip> + <issueId value="MC-17274"/> + </skip> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + + <!-- Create Data --> + <createData entity="Simple_US_Customer" stepKey="createCustomer"/> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="defaultSimpleProduct" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + </before> + <after> + <!-- Delete data --> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- Create first order --> + <actionGroup ref="CreateOrderActionGroup" stepKey="createFirstOrder"> + <argument name="product" value="$$createProduct$$"/> + <argument name="customer" value="$$createCustomer$$"/> + </actionGroup> + <grabTextFrom selector="|Order # (\d+)|" stepKey="getFirstOrderId"/> + <assertNotEmpty actual="$getFirstOrderId" stepKey="assertOrderIdIsNotEmpty" after="getFirstOrderId"/> + + <!-- Create Shipment for first Order --> + <actionGroup ref="AdminCreateInvoiceAndShipmentActionGroup" stepKey="createShipmentForFirstOrder"/> + + <!-- Create second order --> + <actionGroup ref="CreateOrderActionGroup" stepKey="createSecondOrder"> + <argument name="product" value="$$createProduct$$"/> + <argument name="customer" value="$$createCustomer$$"/> + </actionGroup> + <grabTextFrom selector="|Order # (\d+)|" stepKey="getSecondOrderId"/> + <assertNotEmpty actual="$getSecondOrderId" stepKey="assertSecondOrderIdIsNotEmpty" after="getSecondOrderId"/> + + <!-- Create CreditMemo for second Order --> + <actionGroup ref="AdminCreateInvoiceAndCreditMemoActionGroup" stepKey="createCreditMemo"/> + + <!-- Navigate to backend: Go to Sales > Orders --> + <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrderPage"/> + <waitForPageLoad stepKey="waitForOrderPageLoad"/> + <actionGroup ref="AdminOrdersGridClearFiltersActionGroup" stepKey="clearFilters"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoading"/> + + <!-- Select Mass Action according to dataset: Cancel --> + <actionGroup ref="AdminTwoOrderActionOnGridActionGroup" stepKey="massActionCancel"> + <argument name="action" value="Cancel"/> + <argument name="orderId" value="{$getFirstOrderId}"/> + <argument name="secondOrderId" value="{$getSecondOrderId}"/> + </actionGroup> + <see userInput="You cannot cancel the order(s)." stepKey="assertOrderCancelMassActionFailMessage"/> + + <!--Assert first order in orders grid --> + <actionGroup ref="AdminOrderFilterByOrderIdAndStatusActionGroup" stepKey="seeFirstOrder"> + <argument name="orderId" value="{$getFirstOrderId}"/> + <argument name="orderStatus" value="Complete"/> + </actionGroup> + <see userInput="{$getFirstOrderId}" selector="{{AdminOrdersGridSection.gridCell('1','ID')}}" stepKey="assertFirstOrderID"/> + <see userInput="Complete" selector="{{AdminOrdersGridSection.gridCell('1','Status')}}" stepKey="assertFirstOrderStatus"/> + + <!--Assert second order in orders grid --> + <actionGroup ref="AdminOrderFilterByOrderIdAndStatusActionGroup" stepKey="seeSecondOrder"> + <argument name="orderId" value="{$getSecondOrderId}"/> + <argument name="orderStatus" value="Closed"/> + </actionGroup> + <see userInput="{$getSecondOrderId}" selector="{{AdminOrdersGridSection.gridCell('1','ID')}}" stepKey="assertSecondOrderID"/> + <see userInput="Closed" selector="{{AdminOrdersGridSection.gridCell('1','Status')}}" stepKey="assertSecondStatus"/> + </test> +</tests> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersCancelProcessingAndClosedTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersCancelProcessingAndClosedTest.xml new file mode 100644 index 0000000000000..b9e7106676e2c --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersCancelProcessingAndClosedTest.xml @@ -0,0 +1,94 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminMassOrdersCancelProcessingAndClosedTest"> + <annotations> + <stories value="Mass Update Orders"/> + <title value="Mass cancel orders in status Processing, Closed"/> + <description value="Try to cancel orders in status Processing, Closed"/> + <severity value="MAJOR"/> + <testCaseId value="MC-16184"/> + <group value="sales"/> + <group value="mtf_migrated"/> + <skip> + <issueId value="MC-17274"/> + </skip> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + + <!-- Create Data --> + <createData entity="Simple_US_Customer" stepKey="createCustomer"/> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="defaultSimpleProduct" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + </before> + <after> + <!-- Delete data --> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- Create first order --> + <actionGroup ref="CreateOrderActionGroup" stepKey="createFirstOrder"> + <argument name="product" value="$$createProduct$$"/> + <argument name="customer" value="$$createCustomer$$"/> + </actionGroup> + <grabTextFrom selector="|Order # (\d+)|" stepKey="getFirstOrderId"/> + <assertNotEmpty actual="$getFirstOrderId" stepKey="assertOrderIdIsNotEmpty" after="getFirstOrderId"/> + + <!-- Create Invoice for first Order --> + <actionGroup ref="AdminCreateInvoiceActionGroup" stepKey="createInvoice"/> + + <!-- Create second order --> + <actionGroup ref="CreateOrderActionGroup" stepKey="createSecondOrder"> + <argument name="product" value="$$createProduct$$"/> + <argument name="customer" value="$$createCustomer$$"/> + </actionGroup> + <grabTextFrom selector="|Order # (\d+)|" stepKey="getSecondOrderId"/> + <assertNotEmpty actual="$getSecondOrderId" stepKey="assertSecondOrderIdIsNotEmpty" after="getSecondOrderId"/> + + <!-- Create CreditMemo for second Order --> + <actionGroup ref="AdminCreateInvoiceAndCreditMemoActionGroup" stepKey="createCreditMemo"/> + + <!-- Navigate to backend: Go to Sales > Orders --> + <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrderPage"/> + <waitForPageLoad stepKey="waitForOrderPageLoad"/> + <actionGroup ref="AdminOrdersGridClearFiltersActionGroup" stepKey="clearFilters"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoading"/> + + <!-- Select Mass Action according to dataset: Cancel --> + <actionGroup ref="AdminTwoOrderActionOnGridActionGroup" stepKey="massActionCancel"> + <argument name="action" value="Cancel"/> + <argument name="orderId" value="{$getFirstOrderId}"/> + <argument name="secondOrderId" value="{$getSecondOrderId}"/> + </actionGroup> + <see userInput="You cannot cancel the order(s)." stepKey="assertOrderCancelMassActionFailMessage"/> + + <!--Assert first order in orders grid --> + <actionGroup ref="AdminOrderFilterByOrderIdAndStatusActionGroup" stepKey="seeFirstOrder"> + <argument name="orderId" value="{$getFirstOrderId}"/> + <argument name="orderStatus" value="Processing"/> + </actionGroup> + <see userInput="{$getFirstOrderId}" selector="{{AdminOrdersGridSection.gridCell('1','ID')}}" stepKey="assertFirstOrderID"/> + <see userInput="Processing" selector="{{AdminOrdersGridSection.gridCell('1','Status')}}" stepKey="assertFirstOrderStatus"/> + + <!--Assert second order in orders grid --> + <actionGroup ref="AdminOrderFilterByOrderIdAndStatusActionGroup" stepKey="seeSecondOrder"> + <argument name="orderId" value="{$getSecondOrderId}"/> + <argument name="orderStatus" value="Closed"/> + </actionGroup> + <see userInput="{$getSecondOrderId}" selector="{{AdminOrdersGridSection.gridCell('1','ID')}}" stepKey="assertSecondOrderID"/> + <see userInput="Closed" selector="{{AdminOrdersGridSection.gridCell('1','Status')}}" stepKey="assertSecondStatus"/> + </test> +</tests> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersHoldOnCompleteTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersHoldOnCompleteTest.xml new file mode 100644 index 0000000000000..913361c77cdd2 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersHoldOnCompleteTest.xml @@ -0,0 +1,74 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminMassOrdersHoldOnCompleteTest"> + <annotations> + <stories value="Mass Update Orders"/> + <title value="Try to put order in status Complete on Hold"/> + <description value="Try to put order in status Complete on Hold"/> + <severity value="MAJOR"/> + <testCaseId value="MC-16186"/> + <group value="sales"/> + <group value="mtf_migrated"/> + <skip> + <issueId value="MC-17274"/> + </skip> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + + <!-- Create Data --> + <createData entity="Simple_US_Customer" stepKey="createCustomer"/> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="defaultSimpleProduct" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + </before> + <after> + <!-- Delete data --> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- Create order --> + <actionGroup ref="CreateOrderActionGroup" stepKey="createFirstOrder"> + <argument name="product" value="$$createProduct$$"/> + <argument name="customer" value="$$createCustomer$$"/> + </actionGroup> + <grabTextFrom selector="|Order # (\d+)|" stepKey="getOrderId"/> + <assertNotEmpty actual="$getOrderId" stepKey="assertOrderIdIsNotEmpty" after="getOrderId"/> + + <!-- Create Shipment for Order --> + <actionGroup ref="AdminCreateInvoiceAndShipmentActionGroup" stepKey="createShipment"/> + + <!-- Navigate to backend: Go to Sales > Orders --> + <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrderPage"/> + <waitForPageLoad stepKey="waitForOrderPageLoad"/> + <actionGroup ref="AdminOrdersGridClearFiltersActionGroup" stepKey="clearFilters"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoading"/> + + <!-- Select Mass Action according to dataset: Hold --> + <actionGroup ref="AdminOrderActionOnGridActionGroup" stepKey="actionHold"> + <argument name="action" value="Hold"/> + <argument name="orderId" value="$getOrderId"/> + </actionGroup> + <see userInput="No order(s) were put on hold." stepKey="assertOrderOnHoldFailMessage"/> + + <!--Assert order in orders grid --> + <actionGroup ref="AdminOrderFilterByOrderIdAndStatusActionGroup" stepKey="seeFirstOrder"> + <argument name="orderId" value="{$getOrderId}"/> + <argument name="orderStatus" value="Complete"/> + </actionGroup> + <see userInput="{$getOrderId}" selector="{{AdminOrdersGridSection.gridCell('1','ID')}}" stepKey="assertOrderID"/> + <see userInput="Complete" selector="{{AdminOrdersGridSection.gridCell('1','Status')}}" stepKey="assertOrderStatus"/> + </test> +</tests> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersHoldOnPendingAndProcessingTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersHoldOnPendingAndProcessingTest.xml new file mode 100644 index 0000000000000..8e3b3b5361437 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersHoldOnPendingAndProcessingTest.xml @@ -0,0 +1,91 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminMassOrdersHoldOnPendingAndProcessingTest"> + <annotations> + <stories value="Mass Update Orders"/> + <title value="Mass put orders in statuses Pending, Processing on Hold"/> + <description value="Put orders in statuses Pending, Processing on Hold"/> + <severity value="MAJOR"/> + <testCaseId value="MC-16185"/> + <group value="sales"/> + <group value="mtf_migrated"/> + <skip> + <issueId value="MC-17274"/> + </skip> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + + <!-- Create Data --> + <createData entity="Simple_US_Customer" stepKey="createCustomer"/> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="defaultSimpleProduct" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + </before> + <after> + <!-- Delete data --> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- Create first order --> + <actionGroup ref="CreateOrderActionGroup" stepKey="createFirstOrder"> + <argument name="product" value="$$createProduct$$"/> + <argument name="customer" value="$$createCustomer$$"/> + </actionGroup> + <grabTextFrom selector="|Order # (\d+)|" stepKey="getFirstOrderId"/> + <assertNotEmpty actual="$getFirstOrderId" stepKey="assertOrderIdIsNotEmpty" after="getFirstOrderId"/> + + <!-- Create second order --> + <actionGroup ref="CreateOrderActionGroup" stepKey="createSecondOrder"> + <argument name="product" value="$$createProduct$$"/> + <argument name="customer" value="$$createCustomer$$"/> + </actionGroup> + <grabTextFrom selector="|Order # (\d+)|" stepKey="getSecondOrderId"/> + <assertNotEmpty actual="$getSecondOrderId" stepKey="assertSecondOrderIdIsNotEmpty" after="getSecondOrderId"/> + + <!-- Create Invoice for second Order --> + <actionGroup ref="AdminCreateInvoiceActionGroup" stepKey="createInvoice"/> + + <!-- Navigate to backend: Go to Sales > Orders --> + <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrderPage"/> + <waitForPageLoad stepKey="waitForOrderPageLoad"/> + <actionGroup ref="AdminOrdersGridClearFiltersActionGroup" stepKey="clearFilters"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoading"/> + + <!-- Select Mass Action according to dataset: Hold --> + <actionGroup ref="AdminTwoOrderActionOnGridActionGroup" stepKey="massActionHold"> + <argument name="action" value="Hold"/> + <argument name="orderId" value="{$getFirstOrderId}"/> + <argument name="secondOrderId" value="{$getSecondOrderId}"/> + </actionGroup> + <see userInput="You have put 2 order(s) on hold." stepKey="assertOrderOnHoldSuccessMessage"/> + + <!--Assert first order in orders grid --> + <actionGroup ref="AdminOrderFilterByOrderIdAndStatusActionGroup" stepKey="seeFirstOrder"> + <argument name="orderId" value="{$getFirstOrderId}"/> + <argument name="orderStatus" value="On Hold"/> + </actionGroup> + <see userInput="{$getFirstOrderId}" selector="{{AdminOrdersGridSection.gridCell('1','ID')}}" stepKey="assertFirstOrderID"/> + <see userInput="On Hold" selector="{{AdminOrdersGridSection.gridCell('1','Status')}}" stepKey="assertFirstOrderStatus"/> + + <!--Assert second order in orders grid --> + <actionGroup ref="AdminOrderFilterByOrderIdAndStatusActionGroup" stepKey="seeSecondOrder"> + <argument name="orderId" value="{$getSecondOrderId}"/> + <argument name="orderStatus" value="On Hold"/> + </actionGroup> + <see userInput="{$getSecondOrderId}" selector="{{AdminOrdersGridSection.gridCell('1','ID')}}" stepKey="assertSecondOrderID"/> + <see userInput="On Hold" selector="{{AdminOrdersGridSection.gridCell('1','Status')}}" stepKey="assertSecondStatus"/> + </test> +</tests> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersReleasePendingOrderTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersReleasePendingOrderTest.xml new file mode 100644 index 0000000000000..e7a936b088f4f --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersReleasePendingOrderTest.xml @@ -0,0 +1,71 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminMassOrdersReleasePendingOrderTest"> + <annotations> + <stories value="Mass Update Orders"/> + <title value="Try to Release order in status Pending"/> + <description value="Try to Release order in status Pending"/> + <severity value="MAJOR"/> + <testCaseId value="MC-16188"/> + <group value="sales"/> + <group value="mtf_migrated"/> + <skip> + <issueId value="MC-17274"/> + </skip> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + + <!-- Create Data --> + <createData entity="Simple_US_Customer" stepKey="createCustomer"/> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="defaultSimpleProduct" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + </before> + <after> + <!-- Delete data --> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- Create order --> + <actionGroup ref="CreateOrderActionGroup" stepKey="createFirstOrder"> + <argument name="product" value="$$createProduct$$"/> + <argument name="customer" value="$$createCustomer$$"/> + </actionGroup> + <grabTextFrom selector="|Order # (\d+)|" stepKey="getOrderId"/> + <assertNotEmpty actual="$getOrderId" stepKey="assertOrderIdIsNotEmpty" after="getOrderId"/> + + <!-- Navigate to backend: Go to Sales > Orders --> + <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrderPage"/> + <waitForPageLoad stepKey="waitForOrderPageLoad"/> + <actionGroup ref="AdminOrdersGridClearFiltersActionGroup" stepKey="clearFilters"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoading"/> + + <!-- Select Mass Action according to dataset: Unhold --> + <actionGroup ref="AdminOrderActionOnGridActionGroup" stepKey="actionUnhold"> + <argument name="action" value="Unhold"/> + <argument name="orderId" value="$getOrderId"/> + </actionGroup> + <see userInput="No order(s) were released from on hold status." stepKey="assertOrderReleaseFailMessage"/> + + <!--Assert order in orders grid --> + <actionGroup ref="AdminOrderFilterByOrderIdAndStatusActionGroup" stepKey="seeFirstOrder"> + <argument name="orderId" value="{$getOrderId}"/> + <argument name="orderStatus" value="Pending"/> + </actionGroup> + <see userInput="{$getOrderId}" selector="{{AdminOrdersGridSection.gridCell('1','ID')}}" stepKey="assertOrderID"/> + <see userInput="Pending" selector="{{AdminOrdersGridSection.gridCell('1','Status')}}" stepKey="assertOrderStatus"/> + </test> +</tests> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersUpdateCancelPendingOrderTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersUpdateCancelPendingOrderTest.xml new file mode 100644 index 0000000000000..f2995f07d137d --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminMassOrdersUpdateCancelPendingOrderTest.xml @@ -0,0 +1,70 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminMassOrdersUpdateCancelPendingOrderTest"> + <annotations> + <stories value="Mass Update Orders"/> + <title value="Mass cancel orders in status Pending"/> + <description value="Mass cancel orders in status Pending"/> + <severity value="MAJOR"/> + <testCaseId value="MC-16182"/> + <group value="sales"/> + <group value="mtf_migrated"/> + <skip> + <issueId value="MC-17274"/> + </skip> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <!-- Create Data --> + <createData entity="Simple_US_Customer" stepKey="createCustomer"/> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="defaultSimpleProduct" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + </before> + <after> + <!-- Delete data --> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- Create order --> + <actionGroup ref="CreateOrderActionGroup" stepKey="createOrder"> + <argument name="product" value="$$createProduct$$"/> + <argument name="customer" value="$$createCustomer$$"/> + </actionGroup> + <grabTextFrom selector="|Order # (\d+)|" stepKey="getOrderId"/> + <assertNotEmpty actual="$getOrderId" stepKey="assertOrderIdIsNotEmpty" after="getOrderId"/> + + <!-- Navigate to backend: Go to Sales > Orders --> + <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrderPage"/> + <waitForPageLoad stepKey="waitForOrderPageLoad"/> + <actionGroup ref="AdminOrdersGridClearFiltersActionGroup" stepKey="clearFilters"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoading"/> + + <!-- Select Mass Action according to dataset: Cancel --> + <actionGroup ref="AdminOrderActionOnGridActionGroup" stepKey="ActionCancel"> + <argument name="action" value="Cancel"/> + <argument name="orderId" value="$getOrderId"/> + </actionGroup> + <see userInput="We canceled 1 order(s)." stepKey="assertOrderCancelMassActionSuccessMessage"/> + + <!--Assert orders in orders grid --> + <actionGroup ref="AdminOrderFilterByOrderIdAndStatusActionGroup" stepKey="filterOrder"> + <argument name="orderId" value="{$getOrderId}"/> + <argument name="orderStatus" value="Canceled"/> + </actionGroup> + <see userInput="{$getOrderId}" selector="{{AdminOrdersGridSection.gridCell('1','ID')}}" stepKey="assertOrderID"/> + <see userInput="Canceled" selector="{{AdminOrdersGridSection.gridCell('1','Status')}}" stepKey="assertOrderStatus"/> + </test> +</tests> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminOrdersReleaseInUnholdStatusTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminOrdersReleaseInUnholdStatusTest.xml new file mode 100644 index 0000000000000..30f66cb9fd312 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminOrdersReleaseInUnholdStatusTest.xml @@ -0,0 +1,76 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminOrdersReleaseInUnholdStatusTest"> + <annotations> + <stories value="Mass Update Orders"/> + <title value="Release order in status On Hold"/> + <description value="Release order in status On Hold"/> + <severity value="MAJOR"/> + <testCaseId value="MC-16187"/> + <group value="sales"/> + <group value="mtf_migrated"/> + <skip> + <issueId value="MC-17274"/> + </skip> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + + <!-- Create Data --> + <createData entity="Simple_US_Customer" stepKey="createCustomer"/> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="defaultSimpleProduct" stepKey="createProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + </before> + <after> + <!-- Delete data --> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- Create order --> + <actionGroup ref="CreateOrderActionGroup" stepKey="createFirstOrder"> + <argument name="product" value="$$createProduct$$"/> + <argument name="customer" value="$$createCustomer$$"/> + </actionGroup> + <grabTextFrom selector="|Order # (\d+)|" stepKey="getOrderId"/> + <assertNotEmpty actual="$getOrderId" stepKey="assertOrderIdIsNotEmpty" after="getOrderId"/> + + <!-- Hold Order --> + <click selector="{{AdminOrderDetailsMainActionsSection.hold}}" stepKey="pushButtonHold"/> + <waitForPageLoad stepKey="waitForHold"/> + <see userInput="You put the order on hold." stepKey="seeHoldMessage"/> + + <!-- Navigate to backend: Go to Sales > Orders --> + <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrderPage"/> + <waitForPageLoad stepKey="waitForOrderPageLoad"/> + <actionGroup ref="AdminOrdersGridClearFiltersActionGroup" stepKey="clearFilters"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoading"/> + + <!-- Select Mass Action according to dataset: Unhold --> + <actionGroup ref="AdminOrderActionOnGridActionGroup" stepKey="actionUnold"> + <argument name="action" value="Unhold"/> + <argument name="orderId" value="$getOrderId"/> + </actionGroup> + <see userInput="1 order(s) have been released from on hold status." stepKey="assertOrderReleaseSuccessMessage"/> + + <!--Assert order in orders grid --> + <actionGroup ref="AdminOrderFilterByOrderIdAndStatusActionGroup" stepKey="seeFirstOrder"> + <argument name="orderId" value="{$getOrderId}"/> + <argument name="orderStatus" value="Pending"/> + </actionGroup> + <see userInput="{$getOrderId}" selector="{{AdminOrdersGridSection.gridCell('1','ID')}}" stepKey="assertOrderID"/> + <see userInput="Pending" selector="{{AdminOrdersGridSection.gridCell('1','Status')}}" stepKey="assertOrderStatus"/> + </test> +</tests> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminReorderWithCatalogPriceTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminReorderWithCatalogPriceTest.xml new file mode 100644 index 0000000000000..d2c9bd0f24b77 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminReorderWithCatalogPriceTest.xml @@ -0,0 +1,73 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminReorderWithCatalogPriceTest"> + <annotations> + <features value="Sales"/> + <stories value="Admin create order"/> + <title value="Reorder doesn't show discount price in Order Totals block"/> + <description value="Reorder doesn't show discount price in Order Totals block"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-16695"/> + <useCaseId value="MAGETWO-99691"/> + <group value="sales"/> + <group value="catalogRule"/> + <skip> + <issueId value="MC-17140"/> + </skip> + </annotations> + <before> + <!--Create the catalog price rule --> + <createData entity="CatalogRuleToPercent" stepKey="createCatalogRule"/> + <!--Create product--> + <createData entity="SimpleProduct2" stepKey="createSimpleProductApi"/> + <!--Create order via API--> + <createData entity="GuestCart" stepKey="createGuestCart"/> + <createData entity="SimpleCartItem" stepKey="addCartItem"> + <requiredEntity createDataKey="createGuestCart"/> + <requiredEntity createDataKey="createSimpleProductApi"/> + </createData> + <createData entity="GuestAddressInformation" stepKey="addGuestOrderAddress"> + <requiredEntity createDataKey="createGuestCart"/> + </createData> + <updateData createDataKey="createGuestCart" entity="GuestOrderPaymentMethod" stepKey="sendGuestPaymentInformation"> + <requiredEntity createDataKey="createGuestCart"/> + </updateData> + <!--END Create order via API--> + </before> + <after> + <deleteData createDataKey="createSimpleProductApi" stepKey="deleteSimpleProductApi"/> + <!-- Delete the rule --> + <actionGroup ref="RemoveCatalogPriceRule" stepKey="deletePriceRule"> + <argument name="ruleName" value="{{CatalogRuleToPercent.name}}" /> + </actionGroup> + <!--Clear all filters in grid--> + <actionGroup ref="clearFiltersAdminDataGrid" stepKey="resetCatalogRuleGridFilters"/> + <amOnPage url="{{AdminOrdersPage.url}}" stepKey="onOrdersPage"/> + <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearGridFilter"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + <!-- Login as admin --> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <!--Open order by Id--> + <actionGroup ref="OpenOrderById" stepKey="openOrderById"> + <argument name="orderId" value="$createGuestCart.return$"/> + </actionGroup> + <!--Reorder--> + <click selector="{{AdminOrderDetailsMainActionsSection.reorder}}" stepKey="clickReorder"/> + <!--Verify totals on Order page--> + <scrollTo selector="{{AdminOrderFormTotalSection.grandTotal}}" stepKey="scrollToOrderGrandTotal"/> + <waitForElementVisible selector="{{AdminOrderFormTotalSection.total('Subtotal')}}" stepKey="waitOrderSubtotalToBeVisible"/> + <see selector="{{AdminOrderFormTotalSection.total('Subtotal')}}" userInput="${{AdminOrderSimpleProductWithCatalogRule.subtotal}}" stepKey="seeOrderSubTotal"/> + <waitForElementVisible selector="{{AdminOrderFormTotalSection.total('Shipping')}}" stepKey="waitOrderShippingToBeVisible"/> + <see selector="{{AdminOrderFormTotalSection.total('Shipping')}}" userInput="${{AdminOrderSimpleProductWithCatalogRule.shipping}}" stepKey="seeOrderShipping"/> + <waitForElementVisible selector="{{AdminOrderFormTotalSection.grandTotal}}" stepKey="waitOrderGrandTotalToBeVisible"/> + <see selector="{{AdminOrderFormTotalSection.grandTotal}}" userInput="${{AdminOrderSimpleProductWithCatalogRule.grandTotal}}" stepKey="seeCorrectGrandTotal"/> + </test> +</tests> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminSaveInAddressBookCheckboxStateTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminSaveInAddressBookCheckboxStateTest.xml new file mode 100644 index 0000000000000..650152a191d16 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminSaveInAddressBookCheckboxStateTest.xml @@ -0,0 +1,69 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminSaveInAddressBookCheckboxStateTest"> + <annotations> + <stories value="Create Order"/> + <title value="The state of 'Save in address book' check-box inside 'Shipping Address' section on 'create Order' Admin page"/> + <description value="The state of 'Save in address book' check-box inside 'Shipping Address' section on 'create Order' Admin page"/> + <features value="Sales"/> + <severity value="MAJOR"/> + <testCaseId value="MAGETWO-36337"/> + <useCaseId value="MAGETWO-99320"/> + <group value="sales"/> + </annotations> + <before> + <!-- Create customer, category, product and log in --> + <comment userInput="Create customer, category, product and log in" stepKey="createTestData"/> + <createData entity="Simple_US_CA_Customer" stepKey="createCustomer"/> + <createData entity="ApiCategory" stepKey="createCategory"/> + <createData entity="ApiSimpleProduct" stepKey="createSimpleProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <actionGroup ref="LoginActionGroup" stepKey="login"/> + </before> + <after> + <!-- Delete created data and log out --> + <comment userInput="Delete created data and log out" stepKey="deleteDataAndLogOut"/> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <actionGroup ref="logout" stepKey="logOut"/> + </after> + <!-- Create new order and choose an existing customer --> + <comment userInput="Create new order and choose an existing customer" stepKey="createOrderAndAddCustomer"/> + <actionGroup ref="navigateToNewOrderPageExistingCustomer" stepKey="navigateToNewOrderPage"> + <argument name="customer" value="$$createCustomer$$"/> + </actionGroup> + <!-- Add simple product to order --> + <comment userInput="Add simple product to order" stepKey="addSimpleProdToOrder"/> + <actionGroup ref="addSimpleProductToOrder" stepKey="addSimpleProductToOrder"> + <argument name="product" value="$$createSimpleProduct$$"/> + </actionGroup> + <!-- Just in case uncheck and check 'Same as Billing Address checkbox' --> + <comment userInput="Just in case uncheck and check 'Same as Billing Address checkbox'" stepKey="uncheckAndCheckAgain"/> + <uncheckOption selector="{{AdminOrderFormShippingAddressSection.SameAsBilling}}" stepKey="unCheckSameAsShippingAddressCheckbox"/> + <waitForPageLoad stepKey="waitPageToLoad1"/> + <checkOption selector="{{AdminOrderFormShippingAddressSection.SameAsBilling}}" stepKey="checkSameAsShippingAddressCheckbox"/> + <waitForPageLoad stepKey="waitPageToLoad2"/> + <!-- Check 'Save in address book' checkbox in 'Billing Address' section --> + <comment userInput="Check 'Save in address book' checkbox in Billing Address section" stepKey="checkSaveInAddressBookCheckbox"/> + <checkOption selector="{{AdminOrderFormBillingAddressSection.SaveAddress}}" stepKey="checkSaveBillingAddressCheckbox"/> + <!-- See if 'Save in Address Book' checkbox is selected in 'Shipping Address' section --> + <comment userInput="'Save in Address Book' checkbox is checked in 'Shipping Address' section" stepKey="checkIfCheckboxIsChecked"/> + <seeCheckboxIsChecked selector="{{AdminOrderFormShippingAddressSection.SaveAddress}}" stepKey="seeCheckBoxIsSelected"/> + <!-- Uncheck 'Save in Address Book' checkbox in 'Billing Address' section --> + <comment userInput="Uncheck 'Save in Address Book' checkbox in 'Billing Address' section" stepKey="uncheckCheckboxInBillingAddressSection"/> + <uncheckOption selector="{{AdminOrderFormBillingAddressSection.SaveAddress}}" stepKey="uncheckSaveBillingAddressCheckbox"/> + <!-- See if 'Save in Address Book' checkbox is deselected in 'Shipping Address' section --> + <comment userInput="See if 'Save in Address Book' checkbox is unchecked in 'Shipping Address' section" stepKey="seeIfCheckboxIsUnchecked"/> + <dontSeeCheckboxIsChecked selector="{{AdminOrderFormShippingAddressSection.SaveAddress}}" stepKey="seeCheckBoxIsUnchecked"/> + </test> +</tests> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AdminUnassignCustomOrderStatusTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AdminUnassignCustomOrderStatusTest.xml index 982f0b9251cfc..5f7a7c7606619 100644 --- a/app/code/Magento/Sales/Test/Mftf/Test/AdminUnassignCustomOrderStatusTest.xml +++ b/app/code/Magento/Sales/Test/Mftf/Test/AdminUnassignCustomOrderStatusTest.xml @@ -40,7 +40,7 @@ <argument name="status" value="{{defaultOrderStatus.status}}"/> <argument name="label" value="{{defaultOrderStatus.label}}"/> </actionGroup> - <click selector="{{AdminOrderStatusGridSection.assignStatusToStateButton}}" stepKey="clickAssignStatusToStateButton"/> + <click selector="{{AdminOrderStatusGridSection.assignStatusToStateBtn}}" stepKey="clickAssignStatusToStateButton"/> <waitForPageLoad stepKey="waitForAssignOrderStatusToStateLoad"/> <selectOption selector="{{AdminAssignOrderStatusToStateSection.orderStatus}}" userInput="{{defaultOrderStatus.label}}" stepKey="selectOrderStatus"/> <waitForPageLoad stepKey="waitForOrderStatusLoad"/> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AssignCustomOrderStatusNotVisibleOnStorefrontTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AssignCustomOrderStatusNotVisibleOnStorefrontTest.xml new file mode 100644 index 0000000000000..d66078e245aee --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Test/AssignCustomOrderStatusNotVisibleOnStorefrontTest.xml @@ -0,0 +1,137 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AssignCustomOrderStatusNotVisibleOnStorefrontTest"> + <annotations> + <features value="Sales"/> + <stories value="Assign Custom Order Status"/> + <title value="Assign custom order status not visible on storefront test"/> + <description value="Assign custom order status not visible on storefront"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-16053"/> + <group value="sales"/> + <group value="mtf_migrated"/> + <skip> + <issueId value="MC-17274"/> + </skip> + </annotations> + <before> + <!-- Create customer --> + <createData entity="Simple_US_Customer" stepKey="createCustomer"/> + + <!-- Create product --> + <createData entity="SimpleProduct2" stepKey="createSimpleProduct"/> + + <!-- Login as admin --> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <!-- Disable created order status --> + <magentoCLI command="config:set {{EnableCheckmoOrderStatusPending.path}} {{EnableCheckmoOrderStatusPending.value}}" stepKey="rollbackNewOrderStatus"/> + + <!-- Logout customer --> + <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="customerLogout"/> + + <!-- Delete product --> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteProduct"/> + + <!-- Delete customer --> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + + <!-- Log out --> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- Create order status --> + <amOnPage url="{{AdminOrderStatusPage.url}}" stepKey="goToOrderStatusPage"/> + <waitForPageLoad stepKey="waitForOrderStatusPageLoad"/> + <click selector="{{AdminMainActionsSection.add}}" stepKey="clickCreateNewStatus"/> + + <!-- Fill form and validate message --> + <actionGroup ref="AdminOrderStatusFormFillAndSave" stepKey="fillFormAndClickSave"> + <argument name="status" value="{{defaultOrderStatus.status}}"/> + <argument name="label" value="{{defaultOrderStatus.label}}"/> + </actionGroup> + <actionGroup ref="AssertOrderStatusFormSaveSuccess" stepKey="seeFormSaveSuccess"/> + + <!-- Assign status to state --> + <click selector="{{AdminOrderStatusGridSection.assignStatusToStateBtn}}" stepKey="clickAssignStatusBtn"/> + <selectOption selector="{{AdminAssignOrderStatusToStateSection.orderStatus}}" userInput="{{defaultOrderStatus.status}}" stepKey="selectOrderStatus"/> + <selectOption selector="{{AdminAssignOrderStatusToStateSection.orderState}}" userInput="{{OrderState.new}}" stepKey="selectOrderState"/> + <checkOption selector="{{AdminAssignOrderStatusToStateSection.orderStatusAsDefault}}" stepKey="orderStatusAsDefault"/> + <uncheckOption selector="{{AdminAssignOrderStatusToStateSection.visibleOnStorefront}}" stepKey="visibleOnStorefront"/> + <click selector="{{AdminAssignOrderStatusToStateSection.saveStatusAssignment}}" stepKey="clickSaveStatus"/> + <see selector="{{AdminMessagesSection.success}}" userInput="You assigned the order status." stepKey="seeSuccess"/> + + <!-- Prepare data for constraints --> + <magentoCLI command="config:set {{EnableCheckmoOrderStatusPending.path}} {{defaultOrderStatus.label}}" stepKey="enableNewOrderStatus"/> + + <!-- Assert order status in grid --> + <actionGroup ref="FilterOrderStatusByLabelAndCodeActionGroup" stepKey="filterOrderStatusGrid"> + <argument name="statusLabel" value="{{defaultOrderStatus.label}}"/> + <argument name="statusCode" value="{{defaultOrderStatus.status}}"/> + </actionGroup> + <see selector="{{AdminOrderStatusGridSection.gridCell('1', 'State Code and Title')}}" userInput="new[{{defaultOrderStatus.label}}]" stepKey="seeOrderStatusInOrderGrid"/> + + <!-- Create order and grab order id --> + <actionGroup ref="CreateOrderActionGroup" stepKey="createNewOrder"> + <argument name="product" value="$$createSimpleProduct$$"/> + <argument name="customer" value="$$createCustomer$$"/> + </actionGroup> + <grabTextFrom selector="|Order # (\d+)|" stepKey="getOrderId"/> + + <!-- Assert order status is correct --> + <amOnPage url="{{AdminOrdersPage.url}}" stepKey="goToOrdersPage"/> + <waitForPageLoad stepKey="waitForOrdersPageLoad"/> + <actionGroup ref="filterOrderGridById" stepKey="filterOrdersGridById"> + <argument name="orderId" value="$getOrderId"/> + </actionGroup> + <click selector="{{AdminDataGridTableSection.firstRow}}" stepKey="clickCreatedOrderInGrid"/> + <see selector="{{AdminOrderDetailsInformationSection.orderStatus}}" userInput="{{defaultOrderStatus.label}}" stepKey="seeOrderStatus"/> + + <!-- Login as customer --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginToStorefrontAccount"> + <argument name="Customer" value="$$createCustomer$$"/> + </actionGroup> + <waitForPageLoad stepKey="waitForCustomerLogin"/> + + <!-- Open My Orders --> + <amOnPage url="{{StorefrontCustomerDashboardPage.url}}" stepKey="goToCustomerDashboardPage"/> + <waitForPageLoad stepKey="waitForCustomerDashboardPageLoad"/> + <actionGroup ref="StorefrontCustomerGoToSidebarMenu" stepKey="goToMyOrdersPage"> + <argument name="menu" value="My Orders"/> + </actionGroup> + + <!-- Assert order not visible on My Orders --> + <see selector="{{StorefrontOrderInformationMainSection.emptyMessage}}" userInput="You have placed no orders." stepKey="seeEmptyMessage"/> + + <!-- Cancel order --> + <amOnPage url="{{AdminOrdersPage.url}}" stepKey="goToAdminOrdersPage"/> + <waitForPageLoad stepKey="waitForAdminOrdersPageLoad"/> + <actionGroup ref="filterOrderGridById" stepKey="filterOrdersGridByOrderId"> + <argument name="orderId" value="$getOrderId"/> + </actionGroup> + <checkOption selector="{{AdminOrdersGridSection.checkOrder}}" stepKey="selectOrder"/> + <actionGroup ref="SelectActionForOrdersActionGroup" stepKey="selectCancelOrderAction"> + <argument name="action" value="{{OrderActions.cancel}}"/> + </actionGroup> + <see selector="{{AdminMessagesSection.success}}" userInput="We canceled 1 order(s)." stepKey="seeSuccessMessage"/> + + <!-- Unassign order status --> + <amOnPage url="{{AdminOrderStatusPage.url}}" stepKey="goToOrderStatus"/> + <waitForPageLoad stepKey="waitForStatusPageLoad"/> + <actionGroup ref="FilterOrderStatusByLabelAndCodeActionGroup" stepKey="filterStatusGrid"> + <argument name="statusLabel" value="{{defaultOrderStatus.label}}"/> + <argument name="statusCode" value="{{defaultOrderStatus.status}}"/> + </actionGroup> + <click selector="{{AdminOrderStatusGridSection.unassign}}" stepKey="unassignOrderStatus"/> + <see selector="{{AdminMessagesSection.success}}" userInput="You have unassigned the order status." stepKey="seeMessage"/> + </test> +</tests> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/AssignCustomOrderStatusVisibleOnStorefrontTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/AssignCustomOrderStatusVisibleOnStorefrontTest.xml new file mode 100644 index 0000000000000..102fec494d125 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Test/AssignCustomOrderStatusVisibleOnStorefrontTest.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AssignCustomOrderStatusVisibleOnStorefrontTest" extends="AssignCustomOrderStatusNotVisibleOnStorefrontTest"> + <annotations> + <features value="Sales"/> + <stories value="Assign Custom Order Status"/> + <title value="Assign custom order status visible on storefront test"/> + <description value="Assign custom order status visible on storefront"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-16054"/> + <group value="sales"/> + <group value="mtf_migrated"/> + <skip> + <issueId value="MC-17274"/> + </skip> + </annotations> + <remove keyForRemoval="seeEmptyMessage"/> + + <!-- Assign status to state part --> + <checkOption selector="{{AdminAssignOrderStatusToStateSection.visibleOnStorefront}}" stepKey="visibleOnStorefront"/> + + <!-- Assert order in orders grid on frontend --> + <see selector="{{StorefrontCustomerOrderSection.status}}" userInput="{{defaultOrderStatus.label}}" stepKey="seeOrderStatusOnStorefront" after="goToMyOrdersPage"/> + </test> +</tests> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/CreateOrderFromEditCustomerPageTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/CreateOrderFromEditCustomerPageTest.xml new file mode 100644 index 0000000000000..7ead092a1aea9 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Test/CreateOrderFromEditCustomerPageTest.xml @@ -0,0 +1,212 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="CreateOrderFromEditCustomerPageTest"> + <annotations> + <group value="Sales"/> + <stories value="Create Order"/> + <title value="Create order from edit customer page and add products to wish list and shopping cart"/> + <description value="Create an order from edit customer page and add products to the wish list and shopping cart "/> + <severity value="CRITICAL"/> + <testCaseId value="MC-16161"/> + <group value="mtf_migrated"/> + <group value="banana"/> + </annotations> + + <before> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <createData entity="FreeShippingMethodsSettingConfig" stepKey="freeShippingMethodsSettingConfig"/> + + <!--Create simple customer--> + <createData entity="Simple_US_Customer_CA" stepKey="simpleCustomer"/> + + <!-- Create Simple Product --> + <createData entity="SimpleProduct2" stepKey="simpleProduct"> + <field key="price">10.00</field> + </createData> + <createData entity="SimpleProduct2" stepKey="simpleProduct1"> + <field key="price">20.00</field> + </createData> + + <!-- Create configurable product and add it to the category --> + <createData entity="ApiCategory" stepKey="createCategory"/> + <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + + <!-- Create an attribute with two options to be used in the first child product --> + <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> + <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + + <!-- Add the attribute we just created to default attribute set --> + <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + + <!-- Get the option of the attribute we created --> + <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeOption1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + + <!-- Create a simple product and give it the attribute with option --> + <createData entity="ApiSimpleOne" stepKey="createConfigChildProduct1"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + <field key="price">30.00</field> + </createData> + + <!-- Create the configurable product --> + <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption1"/> + </createData> + + <!-- Add simple product to the configurable product --> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild1"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct1"/> + </createData> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + <magentoCLI command="cache:flush" stepKey="flushCache"/> + </before> + <after> + <createData entity="DisableFreeShippingConfig" stepKey="disableFreeShippingConfig"/> + <deleteData createDataKey="simpleCustomer" stepKey="deleteSimpleCustomer"/> + <deleteData createDataKey="simpleProduct" stepKey="deleteSimpleProduct"/> + <deleteData createDataKey="simpleProduct1" stepKey="deleteSimpleProduct1"/> + <deleteData createDataKey="createConfigChildProduct1" stepKey="deleteConfigChildProduct"/> + <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigurableProduct"/> + <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteProductAttribute"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!--Filter and Open the customer edit page --> + <actionGroup ref="AdminFilterCustomerByEmail" stepKey="filterTheCustomer"> + <argument name="email" value="$$simpleCustomer.email$$"/> + </actionGroup> + <click selector="{{AdminCustomerGridSection.customerEditLinkByEmail($$simpleCustomer.email$$)}}" stepKey="clickOnEditButton"/> + <waitForPageLoad stepKey="waitForCustomerEditPageToLoad"/> + <click selector="{{AdminCustomerMainActionsSection.createOrderBtn}}" stepKey="clickOnCreateOrderButton"/> + <waitForPageLoad stepKey="waitForOrderPageToLoad"/> + + <!--Add configurable product to order--> + <actionGroup ref="addConfigurableProductToOrderFromAdmin" stepKey="addConfigurableProductToOrder"> + <argument name="product" value="$$createConfigProduct$$"/> + <argument name="attribute" value="$$createConfigProductAttribute$$"/> + <argument name="option" value="$$getConfigAttributeOption1$$"/> + </actionGroup> + + <!--Add Simple product to order--> + <actionGroup ref="addSimpleProductToOrder" stepKey="addSimpleProductToOrder"> + <argument name="product" value="$$simpleProduct$$"/> + </actionGroup> + + <!--Add Second Simple product to order--> + <actionGroup ref="addSimpleProductToOrder" stepKey="addSecondSimpleProductToOrder"> + <argument name="product" value="$$simpleProduct1$$"/> + </actionGroup> + + <!-- Move Products to the WishList --> + <selectOption selector="{{AdminOrderFormItemsOrderedSection.moveProduct($$simpleProduct.name$$)}}" userInput="Move to Wish List" stepKey="moveProductToWishList"/> + <selectOption selector="{{AdminOrderFormItemsOrderedSection.moveProduct($$createConfigProduct.name$$)}}" userInput="Move to Wish List" stepKey="moveConfigurableProductToWishList"/> + <click selector="{{OrdersGridSection.update}}" stepKey="clickOnUpdateItemsAndQuantity"/> + <waitForPageLoad stepKey="waitForAdminCreateOrderWishListSectionPageLoad"/> + + <!-- Assert products in Wish List section --> + <see selector="{{AdminCreateOrderWishListSection.wishListBlock}}" userInput="$$simpleProduct.name$$" stepKey="seeSimpleProductInWishList"/> + <see selector="{{AdminCreateOrderWishListSection.wishListBlock}}" userInput="$$createConfigProduct.name$$" stepKey="seeConfigurableProductInWishList"/> + + <!-- Add products to order from Wish List --> + <waitForElementVisible selector="{{AdminCreateOrderWishListSection.addProductToOrderCheckBox($$simpleProduct.name$$)}}" stepKey="waitForCheckBoxToVisible"/> + <click selector="{{AdminCreateOrderWishListSection.addProductToOrderCheckBox($$simpleProduct.name$$)}}" stepKey="selectProductToAddToOrder"/> + <click selector="{{AdminCreateOrderWishListSection.addConfigProductToOrder($$createConfigProduct.name$$)}}" stepKey="AddConfigurableProductToOrder"/> + <waitForElementVisible selector="{{AdminOrderFormConfigureProductSection.optionSelect($$createConfigProductAttribute.default_frontend_label$$)}}" stepKey="waitForConfigurablePopover"/> + <selectOption selector="{{AdminOrderFormConfigureProductSection.optionSelect($$createConfigProductAttribute.default_frontend_label$$)}}" userInput="$$getConfigAttributeOption1.label$$" stepKey="selectConfigurableOption"/> + <click selector="{{AdminOrderFormConfigureProductSection.ok}}" stepKey="clickOkButton"/> + <click selector="{{AdminCustomerCreateNewOrderSection.updateChangesBtn}}" stepKey="clickOnUpdateButton"/> + <waitForPageLoad stepKey="waitForAdminOrderItemsOrderedSectionPageLoad1"/> + + <!-- Assert Products in Order item section --> + <see selector="{{AdminOrderItemsOrderedSection.productName}}" userInput="$$simpleProduct.name$$" stepKey="seeSimpleProductInOrderItemGrid"/> + <see selector="{{AdminOrderItemsOrderedSection.productName}}" userInput="$$createConfigProduct.name$$" stepKey="seeConfigProductInOrderItemGrid"/> + + <!-- Move Products to the Shopping Cart --> + <selectOption selector="{{AdminOrderFormItemsOrderedSection.moveProduct($$simpleProduct.name$$)}}" userInput="Move to Shopping Cart" stepKey="moveFirstSimpleProductToShoppingCart"/> + <selectOption selector="{{AdminOrderFormItemsOrderedSection.moveProduct($$simpleProduct1.name$$)}}" userInput="Move to Shopping Cart" stepKey="moveSecondSimpleProductToShoppingCart"/> + <click selector="{{OrdersGridSection.update}}" stepKey="clickOnUpdateItems"/> + <waitForPageLoad stepKey="waitForAdminCreateOrderShoppingCartSectionPageLoad"/> + + <!-- Assert products in Shopping cart section --> + <see selector="{{AdminCreateOrderShoppingCartSection.shoppingCartBlock}}" userInput="$$simpleProduct.name$$" stepKey="seeProductInShoppingCart"/> + <see selector="{{AdminCreateOrderShoppingCartSection.shoppingCartBlock}}" userInput="$$simpleProduct1.name$$" stepKey="seeSecondProductInShoppingCart"/> + + <!-- Move products to the order from shopping cart --> + <waitForElementVisible selector="{{AdminCreateOrderShoppingCartSection.addToOrderCheckBox($$simpleProduct.name$$)}}" stepKey="waitForAddToOrderCheckBox"/> + <click selector="{{AdminCreateOrderShoppingCartSection.addToOrderCheckBox($$simpleProduct.name$$)}}" stepKey="selectFirstProduct"/> + <click selector="{{AdminCreateOrderShoppingCartSection.addToOrderCheckBox($$simpleProduct1.name$$)}}" stepKey="selectSecondProduct"/> + <click selector="{{AdminCustomerCreateNewOrderSection.updateChangesBtn}}" stepKey="clickOnUpdateButton1"/> + <waitForPageLoad stepKey="waitForAdminCreateOrderShoppingCartSectionPageLoad1"/> + + <!-- After move, assert products are not present in Shopping cart section --> + <dontSee selector="{{AdminCreateOrderShoppingCartSection.shoppingCartBlock}}" userInput="$$simpleProduct.name$$" stepKey="donSeeProductInShoppingCart"/> + <dontSee selector="{{AdminCreateOrderShoppingCartSection.shoppingCartBlock}}" userInput="$$simpleProduct1.name$$" stepKey="dontSeeSecondProductInShoppingCart"/> + + <!-- After move, assert products are present in Wish List section --> + <see selector="{{AdminCreateOrderWishListSection.wishListBlock}}" userInput="$$simpleProduct.name$$" stepKey="seeSimpleProductInWishList1"/> + <see selector="{{AdminCreateOrderWishListSection.wishListBlock}}" userInput="$$createConfigProduct.name$$" stepKey="seeConfigurableProductInWishList1"/> + + <!-- After move, assert products are present in order items section --> + <see selector="{{AdminOrderItemsOrderedSection.productName}}" userInput="$$simpleProduct.name$$" stepKey="seeSimpleProductInOrderItemGrid1"/> + <see selector="{{AdminOrderItemsOrderedSection.productName}}" userInput="$$createConfigProduct.name$$" stepKey="seeConfigProductInOrderItemGrid1"/> + <see selector="{{AdminOrderItemsOrderedSection.productName}}" userInput="$$simpleProduct1.name$$" stepKey="seeSecondProductInOrderItemGrid1"/> + + <!-- Select Free Shipping --> + <waitForElementVisible selector="{{AdminInvoicePaymentShippingSection.getShippingMethodAndRates}}" stepKey="waitForShippingSection"/> + <click selector="{{AdminInvoicePaymentShippingSection.getShippingMethodAndRates}}" stepKey="openShippingMethod"/> + <waitForPageLoad stepKey="waitForShippingMethods"/> + <click selector="{{AdminOrderFormPaymentSection.freeShippingOption}}" stepKey="chooseShippingMethod"/> + <waitForPageLoad stepKey="waitForPageToLoad"/> + + <!-- Submit order --> + <click selector="{{AdminOrderFormActionSection.SubmitOrder}}" stepKey="submitOrder"/> + <waitForPageLoad stepKey="waitForAdminOrderFormLoad"/> + + <!-- Verify order information --> + <actionGroup ref="verifyCreatedOrderInformation" stepKey="verifyCreatedOrderInformation"/> + <grabTextFrom selector="|Order # (\d+)|" stepKey="orderId"/> + + <!-- Filter and Open the customer edit page --> + <actionGroup ref="AdminFilterCustomerByEmail" stepKey="filterTheCustomer1"> + <argument name="email" value="$$simpleCustomer.email$$"/> + </actionGroup> + <click selector="{{AdminCustomerGridSection.customerEditLinkByEmail($$simpleCustomer.email$$)}}" stepKey="clickOnEditButton1"/> + <waitForPageLoad stepKey="waitForCustomerEditPageToLoad1"/> + <click selector="{{AdminEditCustomerInformationSection.orders}}" stepKey="clickOnOrdersButton"/> + <waitForPageLoad stepKey="waitForOrderPageToOpen"/> + <click selector="{{AdminEditCustomerOrdersSection.orderIdInGrid('$orderId')}}" stepKey="selectOnOrderID"/> + + <!-- Assert ordered product in customer order section--> + <waitForPageLoad stepKey="waitForOrderInformationToLoad"/> + <see selector="{{AdminOrderItemsOrderedSection.productNameColumn}}" userInput="$createConfigProduct.name$" stepKey="seeConfigurableProductInGrid"/> + <see selector="{{AdminOrderItemsOrderedSection.productNameColumn}}" userInput="$simpleProduct.name$" stepKey="seeFirstProductInGrid"/> + <see selector="{{AdminOrderItemsOrderedSection.productNameColumn}}" userInput="$simpleProduct1.name$" stepKey="seeSecondProductInGrid"/> + <see selector="{{AdminOrderItemsOrderedSection.statusColumn}}" userInput="{{OrderStatus.ordered}}" stepKey="seeProductStatus"/> + <see selector="{{AdminOrderItemsOrderedSection.subtotalColumn}}" userInput="$createConfigChildProduct1.price$" stepKey="seeConfigurableProductSubtotal"/> + <see selector="{{AdminOrderItemsOrderedSection.subtotalColumn}}" userInput="$simpleProduct.price$" stepKey="seeFirstProductSubtotal"/> + <see selector="{{AdminOrderItemsOrderedSection.subtotalColumn}}" userInput="$simpleProduct1.price$" stepKey="seeSecondProductSubtotal"/> + <see selector="{{AdminOrderTotalSection.subTotal}}" userInput="{{AdminOrderMultipleProducts.subtotal}}" stepKey="checkSubtotal" /> + <see selector="{{AdminOrderTotalSection.shippingAndHandling}}" userInput="{{AdminOrderMultipleProducts.shipping}}" stepKey="checkShippingAndHandling" /> + <see selector="{{AdminOrderTotalSection.grandTotal}}" userInput="{{AdminOrderMultipleProducts.grandTotal}}" stepKey="checkGrandTotal" /> + </test> +</tests> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/MoveLastOrderedConfigurableProductOnOrderPageTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/MoveLastOrderedConfigurableProductOnOrderPageTest.xml new file mode 100644 index 0000000000000..3cc14392c6183 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Test/MoveLastOrderedConfigurableProductOnOrderPageTest.xml @@ -0,0 +1,112 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="MoveLastOrderedConfigurableProductOnOrderPageTest"> + <annotations> + <features value="Sales"/> + <stories value="Add Products to Order from Last Ordered Products Section"/> + <title value="Move last ordered configurable product on order page test"/> + <description value="Move last ordered configurable product on order page"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-16155"/> + <group value="sales"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <!-- Login as admin --> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + + <!-- Create customer --> + <createData entity="Simple_US_CA_Customer" stepKey="createCustomer"/> + + <!-- Create category --> + <createData entity="_defaultCategory" stepKey="createCategory"/> + + <!-- Create configurable product --> + <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> + <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeOption"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + <createData entity="ApiSimpleOne" stepKey="createConfigChildProduct"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption"/> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct"/> + </createData> + </before> + <after> + <!-- Delete created data --> + <actionGroup ref="logout" stepKey="logout"/> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + <deleteData createDataKey="createConfigChildProduct" stepKey="deleteConfigChildProduct"/> + <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> + <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + </after> + + <!-- Create order --> + <actionGroup ref="navigateToNewOrderPageExistingCustomer" stepKey="goToCreateOrderPage"> + <argument name="customer" value="$$createCustomer$$"/> + </actionGroup> + + <!-- Add configurable product to order --> + <actionGroup ref="addConfigurableProductToOrderFromAdmin" stepKey="addConfigurableProductToOrder"> + <argument name="product" value="$$createConfigProduct$$"/> + <argument name="attribute" value="$$createConfigProductAttribute$$"/> + <argument name="option" value="$$getConfigAttributeOption$$"/> + </actionGroup> + + <!-- Select shipping method --> + <click selector="{{AdminInvoicePaymentShippingSection.getShippingMethodAndRates}}" stepKey="openShippingMethod"/> + <waitForPageLoad stepKey="waitForShippingMethods"/> + <click selector="{{AdminInvoicePaymentShippingSection.shippingMethod}}" stepKey="chooseShippingMethod"/> + <waitForPageLoad stepKey="waitForShippingMethodLoad"/> + + <!-- Submit order --> + <actionGroup ref="AdminSubmitOrderActionGroup" stepKey="submitOrder"/> + + <!-- Search and open customer --> + <actionGroup ref="AdminFilterCustomerByEmail" stepKey="filterCreatedCustomer"> + <argument name="email" value="$$createCustomer.email$$"/> + </actionGroup> + <click selector="{{AdminCustomerGridSection.firstRowEditLink}}" stepKey="clickEditButton"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + + <!-- Click create order --> + <click selector="{{AdminCustomerMainActionsSection.createOrderBtn}}" stepKey="clickCreateOrder"/> + + <!-- Select product in Last Ordered Items section --> + <click selector="{{AdminCustomerActivitiesLastOrderedSection.addProductToOrder($$createConfigProduct.name$$)}}" stepKey="addProductToOrder"/> + + <!-- Click Update Changes --> + <click selector="{{AdminCustomerCreateNewOrderSection.updateChangesBtn}}" stepKey="clickUpdateChangesBtn"/> + + <!-- Assert product in items ordered grid --> + <see selector="{{AdminCustomerCreateNewOrderSection.gridCell('1', 'Product')}}" userInput="$$createConfigProduct.name$$" stepKey="seeProductName"/> + <see selector="{{AdminCustomerCreateNewOrderSection.gridCell('1', 'Price')}}" userInput="$123.00" stepKey="seeProductPrice"/> + </test> +</tests> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/MoveLastOrderedSimpleProductOnOrderPageTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/MoveLastOrderedSimpleProductOnOrderPageTest.xml new file mode 100644 index 0000000000000..66f0a14ea2712 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Test/MoveLastOrderedSimpleProductOnOrderPageTest.xml @@ -0,0 +1,68 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="MoveLastOrderedSimpleProductOnOrderPageTest"> + <annotations> + <features value="Sales"/> + <stories value="Add Products to Order from Last Ordered Products Section"/> + <title value="Move last ordered simple product on order page test"/> + <description value="Move last ordered simple product on order page"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-16154"/> + <group value="sales"/> + <group value="mtf_migrated"/> + <skip> + <issueId value="MC-17274"/> + </skip> + </annotations> + <before> + <!-- Login as admin --> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + + <!-- Create customer --> + <createData entity="Simple_US_CA_Customer" stepKey="createCustomer"/> + + <!-- Create product --> + <createData entity="SimpleProduct2" stepKey="createProduct"/> + </before> + <after> + <!-- Delete created data --> + <deleteData createDataKey="createProduct" stepKey="deleteProduct"/> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + <actionGroup ref="logout" stepKey="logOut"/> + </after> + + <!-- Create order --> + <actionGroup ref="CreateOrderActionGroup" stepKey="createOrder"> + <argument name="product" value="$$createProduct$$"/> + <argument name="customer" value="$$createCustomer$$"/> + </actionGroup> + + <!-- Search and open customer --> + <actionGroup ref="AdminFilterCustomerByEmail" stepKey="filterCreatedCustomer"> + <argument name="email" value="$$createCustomer.email$$"/> + </actionGroup> + <click selector="{{AdminCustomerGridSection.firstRowEditLink}}" stepKey="clickEditButton"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + + <!-- Click create order --> + <click selector="{{AdminCustomerMainActionsSection.createOrderBtn}}" stepKey="clickCreateOrder"/> + + <!-- Select product in Last Ordered Items section --> + <click selector="{{AdminCustomerActivitiesLastOrderedSection.addProductToOrder($$createProduct.name$$)}}" stepKey="addProductToOrder"/> + + <!-- Click Update Changes --> + <click selector="{{AdminCustomerCreateNewOrderSection.updateChangesBtn}}" stepKey="clickUpdateChangesBtn"/> + + <!-- Assert product in items ordered grid --> + <see selector="{{AdminCustomerCreateNewOrderSection.gridCell('1', 'Product')}}" userInput="$$createProduct.name$$" stepKey="seeProductName"/> + <see selector="{{AdminCustomerCreateNewOrderSection.gridCell('1', 'Price')}}" userInput="$$createProduct.price$$" stepKey="seeProductPrice"/> + </test> +</tests> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/MoveRecentlyViewedBundleFixedProductOnOrderPageTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/MoveRecentlyViewedBundleFixedProductOnOrderPageTest.xml new file mode 100644 index 0000000000000..9e794ce079b6e --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Test/MoveRecentlyViewedBundleFixedProductOnOrderPageTest.xml @@ -0,0 +1,110 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="MoveRecentlyViewedBundleFixedProductOnOrderPageTest"> + <annotations> + <features value="Sales"/> + <stories value="Add Products to Order from Recently Viewed Products Section"/> + <title value="Move recently viewed bundle fixed product on order page test"/> + <description value="Move recently viewed bundle fixed product on order page"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-16164"/> + <group value="sales"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <!-- Login as admin --> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + + <!-- Create customer --> + <createData entity="Simple_US_CA_Customer" stepKey="createCustomer"/> + + <!-- Create category --> + <createData entity="SimpleSubCategory" stepKey="createCategory"/> + + <!-- Create simple products --> + <createData entity="SimpleProduct2" stepKey="createFirstProduct"> + <field key="price">755.00</field> + </createData> + <createData entity="SimpleProduct2" stepKey="createSecondProduct"> + <field key="price">756.00</field> + </createData> + + <!-- Create Bundle product --> + <createData entity="BundleProductPriceViewRange" stepKey="createBundleProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="DropDownBundleOption" stepKey="createBundleOption"> + <requiredEntity createDataKey="createBundleProduct"/> + </createData> + <createData entity="ApiBundleLink" stepKey="linkFirstOptionToProduct"> + <requiredEntity createDataKey="createBundleProduct"/> + <requiredEntity createDataKey="createBundleOption"/> + <requiredEntity createDataKey="createFirstProduct"/> + </createData> + <createData entity="ApiBundleLink" stepKey="linkSecondOptionToProduct"> + <requiredEntity createDataKey="createBundleProduct"/> + <requiredEntity createDataKey="createBundleOption"/> + <requiredEntity createDataKey="createSecondProduct"/> + </createData> + <magentoCLI command="indexer:reindex" stepKey="reindex"/> + </before> + <after> + <!-- Admin logout --> + <actionGroup ref="logout" stepKey="logout"/> + + <!-- Customer logout --> + <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="customerLogout"/> + + <!-- Delete customer --> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + + <!-- Delete created product data --> + <deleteData createDataKey="createBundleProduct" stepKey="deleteProduct"/> + <deleteData createDataKey="createFirstProduct" stepKey="deleteFirstProduct"/> + <deleteData createDataKey="createSecondProduct" stepKey="deleteSecondProduct"/> + + <!-- Delete category --> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + </after> + + <!-- Login as customer --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginAsCustomer"> + <argument name="Customer" value="$$createCustomer$$"/> + </actionGroup> + + <!-- Go to created product page --> + <amOnPage url="{{StorefrontProductPage.url($$createBundleProduct.custom_attributes[url_key]$$)}}" stepKey="goToProductPage"/> + <waitForPageLoad stepKey="waitForProductPageLoad"/> + + <!-- Search and open customer --> + <actionGroup ref="AdminFilterCustomerByEmail" stepKey="filterCreatedCustomer"> + <argument name="email" value="$$createCustomer.email$$"/> + </actionGroup> + <click selector="{{AdminCustomerGridSection.firstRowEditLink}}" stepKey="clickEditButton"/> + <waitForPageLoad stepKey="waitForCustomerPageLoad"/> + + <!-- Click create order --> + <click selector="{{AdminCustomerMainActionsSection.createOrderBtn}}" stepKey="clickCreateOrder"/> + + <!-- Add configure to bundle product --> + <click selector="{{AdminCustomerActivitiesRecentlyViewedSection.addToOrderConfigure($$createBundleProduct.name$$)}}" stepKey="configureProduct"/> + <click selector="{{AdminCustomerActivitiesConfigureSection.dropdownProductSelection($$createFirstProduct.name$$)}}" stepKey="selectProductOption"/> + <click selector="{{AdminCustomerActivitiesConfigureSection.okButton}}" stepKey="clickOkBtn"/> + <waitForPageLoad stepKey="waitForAddingConfigure"/> + + <!-- Click 'Update Changes' --> + <click selector="{{AdminCustomerCreateNewOrderSection.updateChangesBtn}}" stepKey="clickUpdateChangesBtn"/> + + <!-- Assert products in items ordered grid --> + <see selector="{{AdminCustomerCreateNewOrderSection.gridCell('1', 'Product')}}" userInput="$$createBundleProduct.name$$" stepKey="seeProductName"/> + <see selector="{{AdminCustomerCreateNewOrderSection.gridCell('1', 'Price')}}" userInput="$755.00" stepKey="seeProductPrice"/> + </test> +</tests> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/MoveRecentlyViewedConfigurableProductOnOrderPageTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/MoveRecentlyViewedConfigurableProductOnOrderPageTest.xml new file mode 100644 index 0000000000000..35dc49d2b8a43 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Test/MoveRecentlyViewedConfigurableProductOnOrderPageTest.xml @@ -0,0 +1,113 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="MoveRecentlyViewedConfigurableProductOnOrderPageTest"> + <annotations> + <features value="Sales"/> + <stories value="Add Products to Order from Recently Viewed Products Section"/> + <title value="Move recently viewed configurable product on order page test"/> + <description value="Move recently viewed configurable product on order page"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-16163"/> + <group value="sales"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <!-- Login as admin --> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + + <!-- Create customer --> + <createData entity="Simple_US_CA_Customer" stepKey="createCustomer"/> + + <!-- Create category --> + <createData entity="_defaultCategory" stepKey="createCategory"/> + + <!-- Create configurable product --> + <createData entity="ApiConfigurableProduct" stepKey="createConfigProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="productAttributeWithTwoOptions" stepKey="createConfigProductAttribute"/> + <createData entity="productAttributeOption1" stepKey="createConfigProductAttributeOption"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <createData entity="AddToDefaultSet" stepKey="createConfigAddToAttributeSet"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </createData> + <getData entity="ProductAttributeOptionGetter" index="1" stepKey="getConfigAttributeOption"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + </getData> + <createData entity="ApiSimpleOne" stepKey="createConfigChildProduct"> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption"/> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="ConfigurableProductTwoOptions" stepKey="createConfigProductOption"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigProductAttribute"/> + <requiredEntity createDataKey="getConfigAttributeOption"/> + </createData> + <createData entity="ConfigurableProductAddChild" stepKey="createConfigProductAddChild"> + <requiredEntity createDataKey="createConfigProduct"/> + <requiredEntity createDataKey="createConfigChildProduct"/> + </createData> + </before> + <after> + <!-- Admin logout --> + <actionGroup ref="logout" stepKey="logout"/> + + <!-- Customer logout --> + <actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="customerLogout"/> + + <!-- Delete customer --> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + + <!-- Delete created data --> + <deleteData createDataKey="createConfigChildProduct" stepKey="deleteConfigChildProduct"/> + <deleteData createDataKey="createConfigProduct" stepKey="deleteConfigProduct"/> + <deleteData createDataKey="createConfigProductAttribute" stepKey="deleteConfigProductAttribute"/> + + <!-- Delete category --> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + </after> + + <!-- Login as customer --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginAsCustomer"> + <argument name="Customer" value="$$createCustomer$$"/> + </actionGroup> + + <!-- Go to created product page --> + <amOnPage url="{{StorefrontProductPage.url($$createConfigProduct.custom_attributes[url_key]$$)}}" stepKey="goToProductPage"/> + <waitForPageLoad stepKey="waitForProductPageLoad"/> + <selectOption selector="{{StorefrontProductInfoMainSection.productAttributeOptionsSelectButton}}" userInput="$$getConfigAttributeOption.value$$" stepKey="selectOption"/> + + <!-- Search and open customer --> + <actionGroup ref="AdminFilterCustomerByEmail" stepKey="filterCreatedCustomer"> + <argument name="email" value="$$createCustomer.email$$"/> + </actionGroup> + <click selector="{{AdminCustomerGridSection.firstRowEditLink}}" stepKey="clickEditButton"/> + <waitForPageLoad stepKey="waitForCustomerPageLoad"/> + + <!-- Click create order --> + <click selector="{{AdminCustomerMainActionsSection.createOrderBtn}}" stepKey="clickCreateOrder"/> + + <!-- Add configure to product --> + <click selector="{{AdminCustomerActivitiesRecentlyViewedSection.addToOrderConfigure($$createConfigProduct.name$$)}}" stepKey="configureProduct"/> + <selectOption selector="{{AdminCustomerActivitiesConfigureSection.addAttribute}}" userInput="$$getConfigAttributeOption.value$$" stepKey="selectProductOption"/> + <click selector="{{AdminCustomerActivitiesConfigureSection.okButton}}" stepKey="clickOkBtn"/> + <waitForPageLoad stepKey="waitForProductConfigureLoad"/> + + <!-- Click 'Update Changes' --> + <click selector="{{AdminCustomerCreateNewOrderSection.updateChangesBtn}}" stepKey="clickUpdateChangesBtn"/> + + <!-- Assert products in items ordered grid --> + <see selector="{{AdminCustomerCreateNewOrderSection.gridCell('1', 'Product')}}" userInput="$$createConfigProduct.name$$" stepKey="seeProductName"/> + <see selector="{{AdminCustomerCreateNewOrderSection.gridCell('1', 'Price')}}" userInput="$123.00" stepKey="seeProductPrice"/> + </test> +</tests> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/StorefrontOrderPagerDisplayedTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/StorefrontOrderPagerDisplayedTest.xml new file mode 100644 index 0000000000000..b8772f24a2a42 --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Test/StorefrontOrderPagerDisplayedTest.xml @@ -0,0 +1,222 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontOrderPagerDisplayedTest"> + <annotations> + <stories value="Storefront order pager"/> + <title value="Pager is displayed for 21 order items"/> + <description value="Pager is displayed for 21 order items"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-16167"/> + <group value="sales"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <!-- 21 products created and category --> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="SimpleProduct" stepKey="createProduct01"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="SimpleProduct" stepKey="createProduct02"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="SimpleProduct" stepKey="createProduct03"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="SimpleProduct" stepKey="createProduct04"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="_defaultProduct" stepKey="createProduct05"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="_defaultProduct" stepKey="createProduct06"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="_defaultProduct" stepKey="createProduct07"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="_defaultProduct" stepKey="createProduct08"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="_defaultProduct" stepKey="createProduct09"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="SimpleProduct" stepKey="createProduct10"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="SimpleProduct" stepKey="createProduct11"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="SimpleProduct" stepKey="createProduct12"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="ApiSimpleProduct" stepKey="createProduct13"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="ApiSimpleProduct" stepKey="createProduct14"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="ApiSimpleProduct" stepKey="createProduct15"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="ApiSimpleProduct" stepKey="createProduct16"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="ApiSimpleProduct" stepKey="createProduct17"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="ApiSimpleProduct" stepKey="createProduct18"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="ApiSimpleProduct" stepKey="createProduct19"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="ApiSimpleProduct" stepKey="createProduct20"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="SimpleProduct" stepKey="createProduct21"> + <requiredEntity createDataKey="createCategory"/> + </createData> + + <!-- Customer is created --> + <createData entity="Simple_US_Customer" stepKey="createCustomer"/> + + </before> + <after> + <!-- Delete category and products --> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createProduct01" stepKey="deleteProduct1"/> + <deleteData createDataKey="createProduct02" stepKey="deleteProduct2"/> + <deleteData createDataKey="createProduct03" stepKey="deleteProduct3"/> + <deleteData createDataKey="createProduct04" stepKey="deleteProduct4"/> + <deleteData createDataKey="createProduct05" stepKey="deleteProduct5"/> + <deleteData createDataKey="createProduct06" stepKey="deleteProduct6"/> + <deleteData createDataKey="createProduct07" stepKey="deleteProduct7"/> + <deleteData createDataKey="createProduct08" stepKey="deleteProduct8"/> + <deleteData createDataKey="createProduct09" stepKey="deleteProduct9"/> + <deleteData createDataKey="createProduct10" stepKey="deleteProduct10"/> + <deleteData createDataKey="createProduct11" stepKey="deleteProduct11"/> + <deleteData createDataKey="createProduct12" stepKey="deleteProduct12"/> + <deleteData createDataKey="createProduct13" stepKey="deleteProduct13"/> + <deleteData createDataKey="createProduct14" stepKey="deleteProduct14"/> + <deleteData createDataKey="createProduct15" stepKey="deleteProduct15"/> + <deleteData createDataKey="createProduct16" stepKey="deleteProduct16"/> + <deleteData createDataKey="createProduct17" stepKey="deleteProduct17"/> + <deleteData createDataKey="createProduct18" stepKey="deleteProduct18"/> + <deleteData createDataKey="createProduct19" stepKey="deleteProduct19"/> + <deleteData createDataKey="createProduct20" stepKey="deleteProduct20"/> + <deleteData createDataKey="createProduct21" stepKey="deleteProduct21"/> + + <!-- Delete Customer --> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- Login to Storefront as Customer --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginAsCustomer"> + <argument name="Customer" value="$$createCustomer$$" /> + </actionGroup> + + <!-- Customer placed the order with 20 products --> + <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="onCategoryPage"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <scrollTo selector="{{StorefrontCategoryMainSection.perPage}}" stepKey="scrollToLimiter"/> + <selectOption userInput="30" selector="{{StorefrontCategoryMainSection.perPage}}" stepKey="selectLimitOnPage"/> + <waitForPageLoad stepKey="waitForLoadProducts"/> + <scrollToTopOfPage stepKey="scrollToTopOfPage"/> + + <actionGroup ref="StorefrontAddProductToCartFromCategoryActionGroup" stepKey="addProduct1"> + <argument name="productName" value="$$createProduct01.name$$"/> + </actionGroup> + <actionGroup ref="StorefrontAddProductToCartFromCategoryActionGroup" stepKey="addProduct2"> + <argument name="productName" value="$$createProduct02.name$$"/> + </actionGroup> + <actionGroup ref="StorefrontAddProductToCartFromCategoryActionGroup" stepKey="addProduct3"> + <argument name="productName" value="$$createProduct03.name$$"/> + </actionGroup> + <actionGroup ref="StorefrontAddProductToCartFromCategoryActionGroup" stepKey="addProduct4"> + <argument name="productName" value="$$createProduct04.name$$"/> + </actionGroup> + <actionGroup ref="StorefrontAddProductToCartFromCategoryActionGroup" stepKey="addProduct5"> + <argument name="productName" value="$$createProduct05.name$$"/> + </actionGroup> + <actionGroup ref="StorefrontAddProductToCartFromCategoryActionGroup" stepKey="addProduct6"> + <argument name="productName" value="$$createProduct06.name$$"/> + </actionGroup> + <actionGroup ref="StorefrontAddProductToCartFromCategoryActionGroup" stepKey="addProduct7"> + <argument name="productName" value="$$createProduct07.name$$"/> + </actionGroup> + <actionGroup ref="StorefrontAddProductToCartFromCategoryActionGroup" stepKey="addProduct8"> + <argument name="productName" value="$$createProduct08.name$$"/> + </actionGroup> + <actionGroup ref="StorefrontAddProductToCartFromCategoryActionGroup" stepKey="addProduct9"> + <argument name="productName" value="$$createProduct09.name$$"/> + </actionGroup> + <actionGroup ref="StorefrontAddProductToCartFromCategoryActionGroup" stepKey="addProduct10"> + <argument name="productName" value="$$createProduct10.name$$"/> + </actionGroup> + <actionGroup ref="StorefrontAddProductToCartFromCategoryActionGroup" stepKey="addProduct11"> + <argument name="productName" value="$$createProduct11.name$$"/> + </actionGroup> + <actionGroup ref="StorefrontAddProductToCartFromCategoryActionGroup" stepKey="addProduct12"> + <argument name="productName" value="$$createProduct12.name$$"/> + </actionGroup> + <actionGroup ref="StorefrontAddProductToCartFromCategoryActionGroup" stepKey="addProduct13"> + <argument name="productName" value="$$createProduct13.name$$"/> + </actionGroup> + <actionGroup ref="StorefrontAddProductToCartFromCategoryActionGroup" stepKey="addProduct14"> + <argument name="productName" value="$$createProduct14.name$$"/> + </actionGroup> + <actionGroup ref="StorefrontAddProductToCartFromCategoryActionGroup" stepKey="addProduct15"> + <argument name="productName" value="$$createProduct15.name$$"/> + </actionGroup> + <actionGroup ref="StorefrontAddProductToCartFromCategoryActionGroup" stepKey="addProduct16"> + <argument name="productName" value="$$createProduct16.name$$"/> + </actionGroup> + <actionGroup ref="StorefrontAddProductToCartFromCategoryActionGroup" stepKey="addProduct17"> + <argument name="productName" value="$$createProduct17.name$$"/> + </actionGroup> + <actionGroup ref="StorefrontAddProductToCartFromCategoryActionGroup" stepKey="addProduct18"> + <argument name="productName" value="$$createProduct18.name$$"/> + </actionGroup> + <actionGroup ref="StorefrontAddProductToCartFromCategoryActionGroup" stepKey="addProduct19"> + <argument name="productName" value="$$createProduct19.name$$"/> + </actionGroup> + <actionGroup ref="StorefrontAddProductToCartFromCategoryActionGroup" stepKey="addProduct20"> + <argument name="productName" value="$$createProduct20.name$$"/> + </actionGroup> + <actionGroup ref="StorefrontAddProductToCartFromCategoryActionGroup" stepKey="addProduct21"> + <argument name="productName" value="$$createProduct21.name$$"/> + </actionGroup> + + <!-- Place Order --> + <actionGroup ref="StorefrontOpenCheckoutPageActionGroup" stepKey="onCheckout"/> + <see userInput="21" selector="{{CheckoutOrderSummarySection.itemsQtyInCart}}" stepKey="see21Products"/> + <click selector="{{CheckoutShippingSection.next}}" stepKey="clickNextButton"/> + <waitForLoadingMaskToDisappear stepKey="waitForCheckoutLoad"/> + <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="placeOrder"/> + <waitForPageLoad stepKey="waitForSuccess"/> + <grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber22}}" stepKey="grabOrderNumber"/> + + <!-- Go to My Account > My Orders page --> + <amOnPage url="{{StorefrontCustomerDashboardPage.url}}" stepKey="onMyAccount"/> + <waitForPageLoad stepKey="waitForAccountPage"/> + <click selector="{{StorefrontCustomerSidebarSection.sidebarTab('My Orders')}}" stepKey="clickOnMyOrders"/> + <waitForPageLoad stepKey="waitForOrdersLoad"/> + + <!-- Click 'View Order' link on order from preconditions --> + <click selector="{{StorefrontCustomerOrdersGridSection.orderView({$grabOrderNumber})}}" stepKey="clickOrderView"/> + <waitForPageLoad stepKey="waitForOrderPageLoad"/> + + <!-- Assert: Pager is displayed for 21 order items --> + <seeElement selector="{{StorefrontCustomerOrderViewSection.pager}}" stepKey="assertPagerIsDisplayed"/> + </test> +</tests> diff --git a/app/code/Magento/Sales/Test/Mftf/Test/StorefrontOrderPagerIsAbsentTest.xml b/app/code/Magento/Sales/Test/Mftf/Test/StorefrontOrderPagerIsAbsentTest.xml new file mode 100644 index 0000000000000..9909fca44fe2c --- /dev/null +++ b/app/code/Magento/Sales/Test/Mftf/Test/StorefrontOrderPagerIsAbsentTest.xml @@ -0,0 +1,215 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="StorefrontOrderPagerIsAbsentTest"> + <annotations> + <stories value="Storefront order pager"/> + <title value="Pager is absent for 20 order items"/> + <description value="Pager is disabled for orders with less than 20 items"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-16166"/> + <group value="sales"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <actionGroup ref="LoginAsAdmin" stepKey="LoginAsAdmin"/> + <!-- 20 products created and category --> + <createData entity="_defaultCategory" stepKey="createCategory"/> + <createData entity="SimpleProduct" stepKey="createProduct01"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="SimpleProduct" stepKey="createProduct02"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="SimpleProduct" stepKey="createProduct03"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="SimpleProduct" stepKey="createProduct04"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="_defaultProduct" stepKey="createProduct05"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="_defaultProduct" stepKey="createProduct06"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="_defaultProduct" stepKey="createProduct07"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="_defaultProduct" stepKey="createProduct08"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="_defaultProduct" stepKey="createProduct09"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="SimpleProduct" stepKey="createProduct10"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="SimpleProduct" stepKey="createProduct11"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="SimpleProduct" stepKey="createProduct12"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="ApiSimpleProduct" stepKey="createProduct13"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="ApiSimpleProduct" stepKey="createProduct14"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="ApiSimpleProduct" stepKey="createProduct15"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="ApiSimpleProduct" stepKey="createProduct16"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="ApiSimpleProduct" stepKey="createProduct17"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="ApiSimpleProduct" stepKey="createProduct18"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="ApiSimpleProduct" stepKey="createProduct19"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <createData entity="ApiSimpleProduct" stepKey="createProduct20"> + <requiredEntity createDataKey="createCategory"/> + </createData> + + <!-- Customer is created --> + <createData entity="Simple_US_Customer" stepKey="createCustomer"/> + + </before> + <after> + <!-- Delete category and products --> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createProduct01" stepKey="deleteProduct1"/> + <deleteData createDataKey="createProduct02" stepKey="deleteProduct2"/> + <deleteData createDataKey="createProduct03" stepKey="deleteProduct3"/> + <deleteData createDataKey="createProduct04" stepKey="deleteProduct4"/> + <deleteData createDataKey="createProduct05" stepKey="deleteProduct5"/> + <deleteData createDataKey="createProduct06" stepKey="deleteProduct6"/> + <deleteData createDataKey="createProduct07" stepKey="deleteProduct7"/> + <deleteData createDataKey="createProduct08" stepKey="deleteProduct8"/> + <deleteData createDataKey="createProduct09" stepKey="deleteProduct9"/> + <deleteData createDataKey="createProduct10" stepKey="deleteProduct10"/> + <deleteData createDataKey="createProduct11" stepKey="deleteProduct11"/> + <deleteData createDataKey="createProduct12" stepKey="deleteProduct12"/> + <deleteData createDataKey="createProduct13" stepKey="deleteProduct13"/> + <deleteData createDataKey="createProduct14" stepKey="deleteProduct14"/> + <deleteData createDataKey="createProduct15" stepKey="deleteProduct15"/> + <deleteData createDataKey="createProduct16" stepKey="deleteProduct16"/> + <deleteData createDataKey="createProduct17" stepKey="deleteProduct17"/> + <deleteData createDataKey="createProduct18" stepKey="deleteProduct18"/> + <deleteData createDataKey="createProduct19" stepKey="deleteProduct19"/> + <deleteData createDataKey="createProduct20" stepKey="deleteProduct20"/> + + <!-- Delete Customer --> + <deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/> + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- Login to Storefront as Customer --> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginAsCustomer"> + <argument name="Customer" value="$$createCustomer$$" /> + </actionGroup> + + <!-- Customer placed the order with 20 products --> + <amOnPage url="{{StorefrontCategoryPage.url($$createCategory.name$$)}}" stepKey="onCategoryPage"/> + <waitForPageLoad stepKey="waitForPageLoad"/> + <scrollTo selector="{{StorefrontCategoryMainSection.perPage}}" stepKey="scrollToLimiter"/> + <selectOption userInput="30" selector="{{StorefrontCategoryMainSection.perPage}}" stepKey="selectLimitOnPage"/> + <waitForPageLoad stepKey="waitForLoadProducts"/> + <scrollToTopOfPage stepKey="scrollToTopOfPage"/> + + <actionGroup ref="StorefrontAddProductToCartFromCategoryActionGroup" stepKey="addProduct1"> + <argument name="productName" value="$$createProduct01.name$$"/> + </actionGroup> + <actionGroup ref="StorefrontAddProductToCartFromCategoryActionGroup" stepKey="addProduct2"> + <argument name="productName" value="$$createProduct02.name$$"/> + </actionGroup> + <actionGroup ref="StorefrontAddProductToCartFromCategoryActionGroup" stepKey="addProduct3"> + <argument name="productName" value="$$createProduct03.name$$"/> + </actionGroup> + <actionGroup ref="StorefrontAddProductToCartFromCategoryActionGroup" stepKey="addProduct4"> + <argument name="productName" value="$$createProduct04.name$$"/> + </actionGroup> + <actionGroup ref="StorefrontAddProductToCartFromCategoryActionGroup" stepKey="addProduct5"> + <argument name="productName" value="$$createProduct05.name$$"/> + </actionGroup> + <actionGroup ref="StorefrontAddProductToCartFromCategoryActionGroup" stepKey="addProduct6"> + <argument name="productName" value="$$createProduct06.name$$"/> + </actionGroup> + <actionGroup ref="StorefrontAddProductToCartFromCategoryActionGroup" stepKey="addProduct7"> + <argument name="productName" value="$$createProduct07.name$$"/> + </actionGroup> + <actionGroup ref="StorefrontAddProductToCartFromCategoryActionGroup" stepKey="addProduct8"> + <argument name="productName" value="$$createProduct08.name$$"/> + </actionGroup> + <actionGroup ref="StorefrontAddProductToCartFromCategoryActionGroup" stepKey="addProduct9"> + <argument name="productName" value="$$createProduct09.name$$"/> + </actionGroup> + <actionGroup ref="StorefrontAddProductToCartFromCategoryActionGroup" stepKey="addProduct10"> + <argument name="productName" value="$$createProduct10.name$$"/> + </actionGroup> + <actionGroup ref="StorefrontAddProductToCartFromCategoryActionGroup" stepKey="addProduct11"> + <argument name="productName" value="$$createProduct11.name$$"/> + </actionGroup> + <actionGroup ref="StorefrontAddProductToCartFromCategoryActionGroup" stepKey="addProduct12"> + <argument name="productName" value="$$createProduct12.name$$"/> + </actionGroup> + <actionGroup ref="StorefrontAddProductToCartFromCategoryActionGroup" stepKey="addProduct13"> + <argument name="productName" value="$$createProduct13.name$$"/> + </actionGroup> + <actionGroup ref="StorefrontAddProductToCartFromCategoryActionGroup" stepKey="addProduct14"> + <argument name="productName" value="$$createProduct14.name$$"/> + </actionGroup> + <actionGroup ref="StorefrontAddProductToCartFromCategoryActionGroup" stepKey="addProduct15"> + <argument name="productName" value="$$createProduct15.name$$"/> + </actionGroup> + <actionGroup ref="StorefrontAddProductToCartFromCategoryActionGroup" stepKey="addProduct16"> + <argument name="productName" value="$$createProduct16.name$$"/> + </actionGroup> + <actionGroup ref="StorefrontAddProductToCartFromCategoryActionGroup" stepKey="addProduct17"> + <argument name="productName" value="$$createProduct17.name$$"/> + </actionGroup> + <actionGroup ref="StorefrontAddProductToCartFromCategoryActionGroup" stepKey="addProduct18"> + <argument name="productName" value="$$createProduct18.name$$"/> + </actionGroup> + <actionGroup ref="StorefrontAddProductToCartFromCategoryActionGroup" stepKey="addProduct19"> + <argument name="productName" value="$$createProduct19.name$$"/> + </actionGroup> + <actionGroup ref="StorefrontAddProductToCartFromCategoryActionGroup" stepKey="addProduct20"> + <argument name="productName" value="$$createProduct20.name$$"/> + </actionGroup> + + <!-- Place Order --> + <actionGroup ref="StorefrontOpenCheckoutPageActionGroup" stepKey="onCheckout"/> + <see userInput="20" selector="{{CheckoutOrderSummarySection.itemsQtyInCart}}" stepKey="see20Products"/> + <click selector="{{CheckoutShippingSection.next}}" stepKey="clickNextButton"/> + <waitForLoadingMaskToDisappear stepKey="waitForCheckoutLoad"/> + <click selector="{{CheckoutPaymentSection.placeOrder}}" stepKey="placeOrder"/> + <waitForPageLoad stepKey="waitForSuccess"/> + <grabTextFrom selector="{{CheckoutSuccessMainSection.orderNumber22}}" stepKey="grabOrderNumber"/> + + <!-- Go to My Account > My Orders page --> + <amOnPage url="{{StorefrontCustomerDashboardPage.url}}" stepKey="onMyAccount"/> + <waitForPageLoad stepKey="waitForAccountPage"/> + <click selector="{{StorefrontCustomerSidebarSection.sidebarTab('My Orders')}}" stepKey="clickOnMyOrders"/> + <waitForPageLoad stepKey="waitForOrdersLoad"/> + + <!-- Click 'View Order' link on order from preconditions --> + <click selector="{{StorefrontCustomerOrdersGridSection.orderView({$grabOrderNumber})}}" stepKey="clickOrderView"/> + <waitForPageLoad stepKey="waitForOrderPageLoad"/> + + <!-- Assert: Order items pager hidden on frontend --> + <dontSeeElement selector="{{StorefrontCustomerOrderViewSection.pager}}" stepKey="assertPagerIsAbsent"/> + </test> +</tests> diff --git a/app/code/Magento/Sales/Test/Unit/Block/Adminhtml/Order/Create/Form/AddressTest.php b/app/code/Magento/Sales/Test/Unit/Block/Adminhtml/Order/Create/Form/AddressTest.php new file mode 100644 index 0000000000000..5b6d6ded1561a --- /dev/null +++ b/app/code/Magento/Sales/Test/Unit/Block/Adminhtml/Order/Create/Form/AddressTest.php @@ -0,0 +1,263 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Sales\Test\Unit\Block\Adminhtml\Order\Create\Form; + +use Magento\Backend\Model\Session\Quote as QuoteSession; +use Magento\Store\Model\Store; +use Magento\Directory\Helper\Data as DirectoryHelper; +use Magento\Eav\Model\AttributeDataFactory; +use Magento\Sales\Block\Adminhtml\Order\Create\Form\Address; +use Magento\Customer\Api\AddressRepositoryInterface; +use Magento\Customer\Api\Data\AddressSearchResultsInterface; +use Magento\Customer\Api\Data\AddressInterface; +use Magento\Customer\Model\Metadata\Form; +use Magento\Customer\Model\Metadata\FormFactory; +use Magento\Customer\Model\Address\Mapper; +use Magento\Framework\Api\FilterBuilder; +use Magento\Framework\Api\Filter; +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\Framework\Api\SearchCriteria; +use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use PHPUnit\Framework\TestCase; +use PHPUnit_Framework_MockObject_MockObject as MockObject; + +/** + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class AddressTest extends TestCase +{ + /** + * @var QuoteSession|MockObject + */ + private $quoteSession; + + /** + * @var Store|MockObject + */ + private $store; + + /** + * @var DirectoryHelper|MockObject + */ + private $directoryHelper; + + /** + * @var int + */ + private $defaultCountryId; + + /** + * @var int + */ + private $customerId; + + /** + * @var int + */ + private $addressId; + + /** + * @var FormFactory|MockObject + */ + private $formFactory; + + /** + * @var FilterBuilder|MockObject + */ + private $filterBuilder; + + /** + * @var SearchCriteriaBuilder|MockObject + */ + private $criteriaBuilder; + + /** + * @var AddressInterface|MockObject + */ + private $addressItem; + + /** + * @var AddressRepositoryInterface|MockObject + */ + private $addressService; + + /** + * @var Mapper|MockObject + */ + private $addressMapper; + + /** + * @var Address + */ + private $address; + + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @inheritdoc + */ + protected function setUp() + { + $this->objectManager = new ObjectManager($this); + + $this->defaultCountryId = 1; + $this->customerId = 10; + $this->addressId = 100; + + $this->quoteSession = $this->getMockBuilder(QuoteSession::class) + ->disableOriginalConstructor() + ->setMethods(['getStore', 'getCustomerId']) + ->getMock(); + $this->store = $this->getMockBuilder(Store::class) + ->disableOriginalConstructor() + ->getMock(); + $this->quoteSession->expects($this->any()) + ->method('getStore') + ->willReturn($this->store); + $this->quoteSession->expects($this->any()) + ->method('getCustomerId') + ->willReturn($this->customerId); + $this->directoryHelper = $this->getMockBuilder(DirectoryHelper::class) + ->disableOriginalConstructor() + ->setMethods(['getDefaultCountry']) + ->getMock(); + $this->directoryHelper->expects($this->any()) + ->method('getDefaultCountry') + ->willReturn($this->defaultCountryId); + $this->formFactory = $this->getMockBuilder(FormFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + $this->filterBuilder = $this->getMockBuilder(FilterBuilder::class) + ->disableOriginalConstructor() + ->setMethods(['setField', 'setValue', 'setConditionType', 'create']) + ->getMock(); + $this->criteriaBuilder = $this->getMockBuilder(SearchCriteriaBuilder::class) + ->disableOriginalConstructor() + ->setMethods(['create', 'addFilters']) + ->getMock(); + $this->addressService = $this->getMockBuilder(AddressRepositoryInterface::class) + ->setMethods(['getList']) + ->getMockForAbstractClass(); + $this->addressItem = $this->getMockBuilder(AddressInterface::class) + ->setMethods(['getId']) + ->getMockForAbstractClass(); + $this->addressItem->expects($this->any()) + ->method('getId') + ->willReturn($this->addressId); + $this->addressMapper = $this->getMockBuilder(Mapper::class) + ->disableOriginalConstructor() + ->setMethods(['toFlatArray']) + ->getMock(); + + $this->address = $this->objectManager->getObject( + Address::class, + [ + 'directoryHelper' => $this->directoryHelper, + 'sessionQuote' => $this->quoteSession, + 'customerFormFactory' => $this->formFactory, + 'filterBuilder' => $this->filterBuilder, + 'criteriaBuilder' => $this->criteriaBuilder, + 'addressService' => $this->addressService, + 'addressMapper' => $this->addressMapper + ] + ); + } + + public function testGetAddressCollectionJson() + { + /** @var Form|MockObject $emptyForm */ + $emptyForm = $this->getMockBuilder(Form::class) + ->disableOriginalConstructor() + ->setMethods(['outputData']) + ->getMock(); + $emptyForm->expects($this->once()) + ->method('outputData') + ->with(AttributeDataFactory::OUTPUT_FORMAT_JSON) + ->willReturn('emptyFormData'); + + /** @var Filter|MockObject $filter */ + $filter = $this->getMockBuilder(Filter::class) + ->disableOriginalConstructor() + ->getMock(); + $this->filterBuilder->expects($this->once()) + ->method('setField') + ->with('parent_id') + ->willReturnSelf(); + $this->filterBuilder->expects($this->once()) + ->method('setValue') + ->with($this->customerId) + ->willReturnSelf(); + $this->filterBuilder->expects($this->once()) + ->method('setConditionType') + ->with('eq') + ->willReturnSelf(); + $this->filterBuilder->expects($this->once()) + ->method('create') + ->willReturn($filter); + + /** @var SearchCriteria|MockObject $searchCriteria */ + $searchCriteria = $this->getMockBuilder(SearchCriteria::class) + ->disableOriginalConstructor() + ->getMock(); + $this->criteriaBuilder->expects($this->once()) + ->method('create') + ->willReturn($searchCriteria); + $this->criteriaBuilder->expects($this->once()) + ->method('addFilters') + ->with([$filter]); + + /** @var AddressSearchResultsInterface|MockObject $result */ + $result = $this->getMockBuilder(AddressSearchResultsInterface::class) + ->setMethods(['getList']) + ->getMockForAbstractClass(); + $result->expects($this->once()) + ->method('getItems') + ->willReturn([$this->addressItem]); + $this->addressService->expects($this->once()) + ->method('getList') + ->with($searchCriteria) + ->willReturn($result); + + /** @var Form|MockObject $emptyForm */ + $addressForm = $this->getMockBuilder(Form::class) + ->disableOriginalConstructor() + ->setMethods(['outputData']) + ->getMock(); + $addressForm->expects($this->once()) + ->method('outputData') + ->with(AttributeDataFactory::OUTPUT_FORMAT_JSON) + ->willReturn('addressFormData'); + $this->addressMapper->expects($this->once()) + ->method('toFlatArray') + ->with($this->addressItem) + ->willReturn([]); + + $this->directoryHelper->expects($this->once()) + ->method('getDefaultCountry') + ->with($this->store) + ->willReturn($this->defaultCountryId); + $this->formFactory->expects($this->at(0)) + ->method('create') + ->with( + 'customer_address', + 'adminhtml_customer_address', + [AddressInterface::COUNTRY_ID => $this->defaultCountryId] + ) + ->willReturn($emptyForm); + $this->formFactory->expects($this->at(1)) + ->method('create') + ->with('customer_address', 'adminhtml_customer_address', [], false, false) + ->willReturn($addressForm); + + $this->address->getAddressCollectionJson(); + } +} diff --git a/app/code/Magento/Sales/Test/Unit/Model/Order/AddressRepositoryTest.php b/app/code/Magento/Sales/Test/Unit/Model/Order/AddressRepositoryTest.php index 33f1c2f8923af..1e66d43874786 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Order/AddressRepositoryTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Order/AddressRepositoryTest.php @@ -3,128 +3,181 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Sales\Test\Unit\Model\Order; +use Magento\Customer\Model\AttributeMetadataDataProvider; +use Magento\Eav\Model\Entity\Attribute; use Magento\Framework\Api\SearchCriteria\CollectionProcessorInterface; use Magento\Framework\TestFramework\Unit\Helper\ObjectManager; +use Magento\Sales\Model\Order\Address as OrderAddress; +use Magento\Framework\Model\ResourceModel\Db\AbstractDb; +use Magento\Sales\Model\Order\AddressRepository; +use Magento\Sales\Model\ResourceModel\Order\Address\Collection as OrderAddressCollection; +use Magento\Customer\Model\ResourceModel\Form\Attribute\Collection as FormAttributeCollection; +use Magento\Framework\Api\SearchCriteria; +use Magento\Sales\Api\Data\OrderAddressSearchResultInterfaceFactory; +use Magento\Sales\Model\ResourceModel\Metadata; +use Magento\Sales\Model\Order\AddressRepository as OrderAddressRepository; +use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\MockObject\MockObject; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Exception\InputException; /** * Unit test for order address repository class. * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class AddressRepositoryTest extends \PHPUnit\Framework\TestCase +class AddressRepositoryTest extends TestCase { /** * Subject of testing. * - * @var \Magento\Sales\Model\Order\AddressRepository + * @var OrderAddressRepository */ protected $subject; /** * Sales resource metadata. * - * @var \Magento\Sales\Model\ResourceModel\Metadata|\PHPUnit_Framework_MockObject_MockObject + * @var Metadata|MockObject */ protected $metadata; /** - * @var \Magento\Sales\Api\Data\OrderAddressSearchResultInterfaceFactory|\PHPUnit_Framework_MockObject_MockObject + * @var OrderAddressSearchResultInterfaceFactory|MockObject */ protected $searchResultFactory; /** - * @var CollectionProcessorInterface |\PHPUnit_Framework_MockObject_MockObject + * @var CollectionProcessorInterface|MockObject */ private $collectionProcessorMock; + /** + * @var Attribute[] + */ + private $attributesList; + + /** + * @var AttributeMetadataDataProvider + */ + private $attributeMetadataDataProvider; + + /** + * @var OrderAddress|MockObject + */ + private $orderAddress; + + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @inheritdoc + */ protected function setUp() { - $objectManager = new ObjectManager($this); + $this->objectManager = new ObjectManager($this); + $this->orderAddress = $this->createPartialMock(OrderAddress::class, ['getEntityId', 'load']); $this->metadata = $this->createPartialMock( - \Magento\Sales\Model\ResourceModel\Metadata::class, + Metadata::class, ['getNewInstance', 'getMapper'] ); + $this->attributeMetadataDataProvider = $this->getMockBuilder(AttributeMetadataDataProvider::class) + ->disableOriginalConstructor() + ->setMethods(['loadAttributesCollection']) + ->getMock(); + $collectionAttribute = $this->getMockBuilder(FormAttributeCollection::class) + ->setMethods(['addFieldToFilter', 'getIterator']) + ->disableOriginalConstructor() + ->getMock(); + $collectionAttribute->method('getIterator') + ->willReturn(new \ArrayIterator([])); + $this->attributeMetadataDataProvider->method('loadAttributesCollection')->willReturn($collectionAttribute); + $this->searchResultFactory = $this->createPartialMock( - \Magento\Sales\Api\Data\OrderAddressSearchResultInterfaceFactory::class, + OrderAddressSearchResultInterfaceFactory::class, ['create'] ); $this->collectionProcessorMock = $this->getMockBuilder(CollectionProcessorInterface::class) ->getMock(); - $this->subject = $objectManager->getObject( - \Magento\Sales\Model\Order\AddressRepository::class, + $this->subject = $this->objectManager->getObject( + OrderAddressRepository::class, [ 'metadata' => $this->metadata, 'searchResultFactory' => $this->searchResultFactory, 'collectionProcessor' => $this->collectionProcessorMock, + 'attributeMetadataDataProvider' => $this->attributeMetadataDataProvider ] ); } /** + * Test for get order address + * * @param int|null $id * @param int|null $entityId + * + * @return void * @dataProvider getDataProvider */ - public function testGet($id, $entityId) + public function testGet(?int $id, ?int $entityId): void { if (!$id) { - $this->expectException( - \Magento\Framework\Exception\InputException::class - ); - + $this->expectException(InputException::class); $this->subject->get($id); } else { - $address = $this->createPartialMock(\Magento\Sales\Model\Order\Address::class, ['load', 'getEntityId']); - $address->expects($this->once()) + + $this->orderAddress->expects($this->once()) ->method('load') ->with($id) - ->willReturn($address); - $address->expects($this->once()) + ->willReturn($this->orderAddress); + $this->orderAddress->expects($this->once()) ->method('getEntityId') ->willReturn($entityId); $this->metadata->expects($this->once()) ->method('getNewInstance') - ->willReturn($address); + ->willReturn($this->orderAddress); if (!$entityId) { - $this->expectException( - \Magento\Framework\Exception\NoSuchEntityException::class - ); - + $this->expectException(NoSuchEntityException::class); $this->subject->get($id); } else { - $this->assertEquals($address, $this->subject->get($id)); + $this->assertEquals($this->orderAddress, $this->subject->get($id)); - $address->expects($this->never()) + $this->orderAddress->expects($this->never()) ->method('load') ->with($id) - ->willReturn($address); - $address->expects($this->never()) + ->willReturn($this->orderAddress); + $this->orderAddress->expects($this->never()) ->method('getEntityId') ->willReturn($entityId); $this->metadata->expects($this->never()) ->method('getNewInstance') - ->willReturn($address); + ->willReturn($this->orderAddress); // Retrieve Address from registry. - $this->assertEquals($address, $this->subject->get($id)); + $this->assertEquals($this->orderAddress, $this->subject->get($id)); } } } /** + * Data for testGet + * * @return array */ - public function getDataProvider() + public function getDataProvider(): array { return [ [null, null], @@ -133,10 +186,15 @@ public function getDataProvider() ]; } - public function testGetList() + /** + * Test for get list order address + * + * @return void + */ + public function testGetList(): void { - $searchCriteria = $this->createMock(\Magento\Framework\Api\SearchCriteria::class); - $collection = $this->createMock(\Magento\Sales\Model\ResourceModel\Order\Address\Collection::class); + $searchCriteria = $this->createMock(SearchCriteria::class); + $collection = $this->createMock(OrderAddressCollection::class); $this->collectionProcessorMock->expects($this->once()) ->method('process') @@ -148,15 +206,19 @@ public function testGetList() $this->assertEquals($collection, $this->subject->getList($searchCriteria)); } - public function testDelete() + /** + * Test for delete order address + * + * @return void + */ + public function testDelete(): void { - $address = $this->createPartialMock(\Magento\Sales\Model\Order\Address::class, ['getEntityId']); - $address->expects($this->once()) + $this->orderAddress->expects($this->once()) ->method('getEntityId') ->willReturn(1); $mapper = $this->getMockForAbstractClass( - \Magento\Framework\Model\ResourceModel\Db\AbstractDb::class, + AbstractDb::class, [], '', false, @@ -166,27 +228,29 @@ public function testDelete() ); $mapper->expects($this->once()) ->method('delete') - ->with($address); + ->with($this->orderAddress); $this->metadata->expects($this->any()) ->method('getMapper') ->willReturn($mapper); - $this->assertTrue($this->subject->delete($address)); + $this->assertTrue($this->subject->delete($this->orderAddress)); } /** + * Test for delete order address with exception + * + * @return void * @expectedException \Magento\Framework\Exception\CouldNotDeleteException * @expectedExceptionMessage The order address couldn't be deleted. */ - public function testDeleteWithException() + public function testDeleteWithException(): void { - $address = $this->createPartialMock(\Magento\Sales\Model\Order\Address::class, ['getEntityId']); - $address->expects($this->never()) + $this->orderAddress->expects($this->never()) ->method('getEntityId'); $mapper = $this->getMockForAbstractClass( - \Magento\Framework\Model\ResourceModel\Db\AbstractDb::class, + AbstractDb::class, [], '', false, @@ -202,18 +266,22 @@ public function testDeleteWithException() ->method('getMapper') ->willReturn($mapper); - $this->subject->delete($address); + $this->subject->delete($this->orderAddress); } - public function testSave() + /** + * Test for save order address + * + * @return void + */ + public function testSave(): void { - $address = $this->createPartialMock(\Magento\Sales\Model\Order\Address::class, ['getEntityId']); - $address->expects($this->any()) + $this->orderAddress->expects($this->any()) ->method('getEntityId') ->willReturn(1); $mapper = $this->getMockForAbstractClass( - \Magento\Framework\Model\ResourceModel\Db\AbstractDb::class, + AbstractDb::class, [], '', false, @@ -223,27 +291,29 @@ public function testSave() ); $mapper->expects($this->once()) ->method('save') - ->with($address); + ->with($this->orderAddress); $this->metadata->expects($this->any()) ->method('getMapper') ->willReturn($mapper); - $this->assertEquals($address, $this->subject->save($address)); + $this->assertEquals($this->orderAddress, $this->subject->save($this->orderAddress)); } /** + * Test for save order address with exception + * + * @return void * @expectedException \Magento\Framework\Exception\CouldNotSaveException * @expectedExceptionMessage The order address couldn't be saved. */ - public function testSaveWithException() + public function testSaveWithException(): void { - $address = $this->createPartialMock(\Magento\Sales\Model\Order\Address::class, ['getEntityId']); - $address->expects($this->never()) + $this->orderAddress->expects($this->never()) ->method('getEntityId'); $mapper = $this->getMockForAbstractClass( - \Magento\Framework\Model\ResourceModel\Db\AbstractDb::class, + AbstractDb::class, [], '', false, @@ -259,17 +329,117 @@ public function testSaveWithException() ->method('getMapper') ->willReturn($mapper); - $this->assertEquals($address, $this->subject->save($address)); + $this->assertEquals($this->orderAddress, $this->subject->save($this->orderAddress)); } - public function testCreate() + /** + * Tets for create order address + * + * @return void + */ + public function testCreate(): void { - $address = $this->createPartialMock(\Magento\Sales\Model\Order\Address::class, ['getEntityId']); - $this->metadata->expects($this->once()) ->method('getNewInstance') - ->willReturn($address); + ->willReturn($this->orderAddress); + + $this->assertEquals($this->orderAddress, $this->subject->create()); + } + + /** + * Test for save sales address with multi-attribute. + * + * @param string $attributeType + * @param string $attributeCode + * @param array $attributeValue + * @param string $expected + * + * @return void + * @dataProvider dataMultiAttribute + */ + public function testSaveWithMultiAttribute( + string $attributeType, + string $attributeCode, + array $attributeValue, + string $expected + ): void { + $orderAddress = $this->getMockBuilder(OrderAddress::class) + ->disableOriginalConstructor() + ->setMethods(['getEntityId', 'hasData', 'getData', 'setData']) + ->getMock(); + + $orderAddress->expects($this->any()) + ->method('getEntityId') + ->willReturn(1); + + $mapper = $this->getMockForAbstractClass( + AbstractDb::class, + [], + '', + false, + true, + true, + ['save'] + ); + $mapper->method('save') + ->with($orderAddress); + $this->metadata->method('getMapper') + ->willReturn($mapper); + + $attributeModel = $this->getMockBuilder(Attribute::class) + ->setMethods(['getFrontendInput', 'getAttributeCode']) + ->disableOriginalConstructor() + ->getMock(); + $attributeModel->method('getFrontendInput')->willReturn($attributeType); + $attributeModel->method('getAttributeCode')->willReturn($attributeCode); + $this->attributesList = [$attributeModel]; + + $this->subject = $this->objectManager->getObject( + AddressRepository::class, + [ + 'metadata' => $this->metadata, + 'searchResultFactory' => $this->searchResultFactory, + 'collectionProcessor' => $this->collectionProcessorMock, + 'attributeMetadataDataProvider' => $this->attributeMetadataDataProvider, + 'attributesList' => $this->attributesList, + ] + ); + + $orderAddress->method('hasData')->with($attributeCode)->willReturn(true); + $orderAddress->method('getData')->with($attributeCode)->willReturn($attributeValue); + $orderAddress->expects($this->once())->method('setData')->with($attributeCode, $expected); + + $this->assertEquals($orderAddress, $this->subject->save($orderAddress)); + } + + /** + * Data for testSaveWithMultiAttribute + * + * @return array + */ + public function dataMultiAttribute(): array + { + $data = [ + 'multiselect' => [ + 'multiselect', + 'attr_multiselect', + [ + 'opt1', + 'opt2', + ], + 'opt1,opt2', + ], + 'multiline' => [ + 'multiline', + 'attr_multiline', + [ + 'line1', + 'line2', + ], + 'line1'.PHP_EOL.'line2', + ], + ]; - $this->assertEquals($address, $this->subject->create()); + return $data; } } diff --git a/app/code/Magento/Sales/Test/Unit/Model/Service/OrderServiceTest.php b/app/code/Magento/Sales/Test/Unit/Model/Service/OrderServiceTest.php index 067f83d1e5b32..72fe7380ce8e4 100644 --- a/app/code/Magento/Sales/Test/Unit/Model/Service/OrderServiceTest.php +++ b/app/code/Magento/Sales/Test/Unit/Model/Service/OrderServiceTest.php @@ -5,6 +5,9 @@ */ namespace Magento\Sales\Test\Unit\Model\Service; +use Magento\Sales\Api\PaymentFailuresInterface; +use Psr\Log\LoggerInterface; + /** * Class OrderUnHoldTest * @@ -140,6 +143,12 @@ protected function setUp() ->disableOriginalConstructor() ->getMock(); + /** @var PaymentFailuresInterface|\PHPUnit_Framework_MockObject_MockObject $paymentFailures */ + $paymentFailures = $this->createMock(PaymentFailuresInterface::class); + + /** @var LoggerInterface|\PHPUnit_Framework_MockObject_MockObject $logger */ + $logger = $this->createMock(LoggerInterface::class); + $this->orderService = new \Magento\Sales\Model\Service\OrderService( $this->orderRepositoryMock, $this->orderStatusHistoryRepositoryMock, @@ -147,7 +156,9 @@ protected function setUp() $this->filterBuilderMock, $this->orderNotifierMock, $this->eventManagerMock, - $this->orderCommentSender + $this->orderCommentSender, + $paymentFailures, + $logger ); } diff --git a/app/code/Magento/Sales/etc/db_schema.xml b/app/code/Magento/Sales/etc/db_schema.xml index d6ea9b7d54861..82e6d5d10b53a 100644 --- a/app/code/Magento/Sales/etc/db_schema.xml +++ b/app/code/Magento/Sales/etc/db_schema.xml @@ -219,7 +219,7 @@ <column xsi:type="varchar" name="remote_ip" nullable="true" length="45" comment="Remote Ip"/> <column xsi:type="varchar" name="shipping_method" nullable="true" length="120"/> <column xsi:type="varchar" name="store_currency_code" nullable="true" length="3" comment="Store Currency Code"/> - <column xsi:type="varchar" name="store_name" nullable="true" length="32" comment="Store Name"/> + <column xsi:type="varchar" name="store_name" nullable="true" length="255" comment="Store Name"/> <column xsi:type="varchar" name="x_forwarded_for" nullable="true" length="32" comment="X Forwarded For"/> <column xsi:type="text" name="customer_note" nullable="true" comment="Customer Note"/> <column xsi:type="timestamp" name="created_at" on_update="false" nullable="false" default="CURRENT_TIMESTAMP" diff --git a/app/code/Magento/Sales/view/adminhtml/templates/order/create/form/account.phtml b/app/code/Magento/Sales/view/adminhtml/templates/order/create/form/account.phtml index 0d2ee1f24d5b3..f7d5f4aa8aa33 100644 --- a/app/code/Magento/Sales/view/adminhtml/templates/order/create/form/account.phtml +++ b/app/code/Magento/Sales/view/adminhtml/templates/order/create/form/account.phtml @@ -5,16 +5,16 @@ */ ?> -<div class="admin__page-section-title <?= /* @escapeNotVerified */ $block->getHeaderCssClass() ?>"> - <span class="title"><?= /* @escapeNotVerified */ $block->getHeaderText() ?></span> +<div class="admin__page-section-title <?= /* @noEscape */ $block->getHeaderCssClass() ?>"> + <span class="title"><?= /* @noEscape */ $block->getHeaderText() ?></span> <div class="actions"></div> </div> -<div id="customer_account_fieds" class="admin__page-section-content"> +<div id="customer_account_fields" class="admin__page-section-content"> <?= $block->getForm()->getHtml() ?> </div> <script> require(["prototype", "Magento_Sales/order/create/form"], function(){ - order.accountFieldsBind($('customer_account_fieds')); + order.accountFieldsBind($('customer_account_fields')); }); </script> diff --git a/app/code/Magento/Sales/view/adminhtml/templates/order/create/form/address.phtml b/app/code/Magento/Sales/view/adminhtml/templates/order/create/form/address.phtml index d1a90783c68c7..22db07d046fc9 100644 --- a/app/code/Magento/Sales/view/adminhtml/templates/order/create/form/address.phtml +++ b/app/code/Magento/Sales/view/adminhtml/templates/order/create/form/address.phtml @@ -32,7 +32,7 @@ if ($block->getIsShipping()): require(["Magento_Sales/order/create/form"], function(){ order.shippingAddressContainer = '<?= /* @escapeNotVerified */ $_fieldsContainerId ?>'; - order.setAddresses(<?= /* @escapeNotVerified */ $customerAddressFormatter->getAddressesJson($addressArray) ?>); + order.setAddresses(<?= /* @noEscape */ $block->getAddressCollectionJson() ?>); }); </script> diff --git a/app/code/Magento/Sales/view/adminhtml/web/order/create/scripts.js b/app/code/Magento/Sales/view/adminhtml/web/order/create/scripts.js index c508a5ecdfa58..5f20325eb686e 100644 --- a/app/code/Magento/Sales/view/adminhtml/web/order/create/scripts.js +++ b/app/code/Magento/Sales/view/adminhtml/web/order/create/scripts.js @@ -42,6 +42,7 @@ define([ this.isOnlyVirtualProduct = false; this.excludedPaymentMethods = []; this.summarizePrice = true; + this.selectAddressEvent = false; this.shippingTemplate = template(shippingTemplate, { data: { title: jQuery.mage.__('Shipping Method'), @@ -169,17 +170,19 @@ define([ }, selectAddress : function(el, container){ + id = el.value; if (id.length == 0) { id = '0'; } - if(this.addresses[id]){ - this.fillAddressFields(container, this.addresses[id]); - } - else{ + this.selectAddressEvent = true; + if (this.addresses[id]) { + this.fillAddressFields(container, this.addresses[id]); + } else { this.fillAddressFields(container, {}); } + this.selectAddressEvent = false; var data = this.serializeData(container); data[el.name] = id; @@ -190,6 +193,7 @@ define([ } else{ this.saveData(data); } + }, /** @@ -259,7 +263,7 @@ define([ data = data.toObject(); if (type === 'billing' && this.shippingAsBilling) { - this.syncAddressField(this.shippingAddressContainer, field.name, field.value); + this.syncAddressField(this.shippingAddressContainer, field.name, field); resetShipping = true; } @@ -279,6 +283,10 @@ define([ $('order-' + type + '_address_customer_address_id').value; } + if (name === 'country_id' && this.selectAddressEvent === false) { + $('order-' + type + '_address_customer_address_id').value = ''; + } + this.resetPaymentMethod(); if (data['reset_shipping']) { @@ -308,7 +316,11 @@ define([ $(container).select('[name="' + syncName + '"]').each(function (element) { if (~['input', 'textarea', 'select'].indexOf(element.tagName.toLowerCase())) { - element.value = fieldValue; + if (element.type === "checkbox") { + element.checked = fieldValue.checked; + } else { + element.value = fieldValue.value; + } } }); }, @@ -561,6 +573,9 @@ define([ applyCoupon : function(code){ this.loadArea(['items', 'shipping_method', 'totals', 'billing_method'], true, {'order[coupon][code]':code, reset_shipping: 0}); this.orderItemChanged = false; + jQuery('html, body').animate({ + scrollTop: 0 + }); }, addProduct : function(id){ diff --git a/app/code/Magento/Sales/view/frontend/templates/email/shipment/track.phtml b/app/code/Magento/Sales/view/frontend/templates/email/shipment/track.phtml index f1cd5f2b99865..6de8e42dea583 100644 --- a/app/code/Magento/Sales/view/frontend/templates/email/shipment/track.phtml +++ b/app/code/Magento/Sales/view/frontend/templates/email/shipment/track.phtml @@ -9,8 +9,9 @@ ?> <?php $_shipment = $block->getShipment() ?> <?php $_order = $block->getOrder() ?> +<?php if ($_shipment && $_order): ?> <?php $trackCollection = $_order->getTracksCollection($_shipment->getId()) ?> -<?php if ($_shipment && $_order && $trackCollection): ?> +<?php if ($trackCollection): ?> <br /> <table class="shipment-track"> <thead> @@ -29,3 +30,4 @@ </tbody> </table> <?php endif; ?> +<?php endif; ?> diff --git a/app/code/Magento/SalesRule/Model/Quote/Address/Total/ShippingDiscount.php b/app/code/Magento/SalesRule/Model/Quote/Address/Total/ShippingDiscount.php index c37ca276e0ee2..53adcd268f81d 100644 --- a/app/code/Magento/SalesRule/Model/Quote/Address/Total/ShippingDiscount.php +++ b/app/code/Magento/SalesRule/Model/Quote/Address/Total/ShippingDiscount.php @@ -89,7 +89,7 @@ public function fetch(Quote $quote, Total $total): array $amount = $total->getDiscountAmount(); if ($amount != 0) { - $description = $total->getDiscountDescription() ?: ''; + $description = (string)$total->getDiscountDescription() ?: ''; $result = [ 'code' => DiscountCollector::COLLECTOR_TYPE_CODE, 'title' => strlen($description) ? __('Discount (%1)', $description) : __('Discount'), diff --git a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminAssertCustomerGroupOnCartPriceRuleFormActionGroup.xml b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminAssertCustomerGroupOnCartPriceRuleFormActionGroup.xml new file mode 100644 index 0000000000000..083e220d0e937 --- /dev/null +++ b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/AdminAssertCustomerGroupOnCartPriceRuleFormActionGroup.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminAssertCustomerGroupOnCartPriceRuleForm"> + <arguments> + <argument name="customerGroupName" type="string"/> + </arguments> + <amOnPage url="{{PriceRuleNewPage.url}}" stepKey="amOnCartPriceRuleCreateCreatePage"/> + <waitForElementVisible selector="{{AdminCartPriceRulesFormSection.customerGroups}}" stepKey="waitForElementVisible"/> + <see selector="{{AdminCartPriceRulesFormSection.customerGroups}}" userInput="{{customerGroupName}}" stepKey="assertCustomerGroupPresentOnCartPriceRuleForm"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/ApplyCartRuleOnStorefrontActionGroup.xml b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/ApplyCartRuleOnStorefrontActionGroup.xml index 37e171823b11a..60ee9fb1fa1a6 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/ApplyCartRuleOnStorefrontActionGroup.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/ApplyCartRuleOnStorefrontActionGroup.xml @@ -13,6 +13,8 @@ <argument name="couponCode" type="string"/> </arguments> <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addToCart" /> + <waitForPageLoad stepKey="waitForAddToCart"/> + <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessage"/> <waitForText userInput="You added {{product.name}} to your shopping cart." stepKey="waitForText"/> <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCheckoutPage"/> <waitForPageLoad stepKey="waitForPageLoad1"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/SetCartAttributeConditionWithSegmentForCartPriceRuleActionGroup.xml b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/SetCartAttributeConditionWithSegmentForCartPriceRuleActionGroup.xml deleted file mode 100644 index ac88e2d2d8118..0000000000000 --- a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/SetCartAttributeConditionWithSegmentForCartPriceRuleActionGroup.xml +++ /dev/null @@ -1,46 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - /** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ ---> -<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> - <!--Set condition for Customer Segment--> - <actionGroup name="SetCartAttributeConditionWithSegmentForCartPriceRuleActionGroup"> - <arguments> - <argument name="attributeName" type="string"/> - <argument name="value" type="entity"/> - </arguments> - - <scrollTo selector="{{AdminCartPriceRulesFormSection.conditionsHeader}}" stepKey="scrollToActionTab"/> - <conditionalClick selector="{{AdminCartPriceRulesFormSection.conditionsHeader}}" dependentSelector="{{AdminCartPriceRulesFormSection.conditionsHeaderOpen}}" visible="false" stepKey="openActionTab"/> - <click selector="{{AdminCartPriceRulesFormSection.conditions}}" stepKey="applyRuleForConditions"/> - - <waitForPageLoad stepKey="waitForDropDownOpened"/> - <selectOption selector="{{AdminCartPriceRulesFormSection.childAttribute}}" userInput="{{attributeName}}" stepKey="selectAttribute"/> - - <waitForPageLoad time="20" stepKey="waitForOperator"/> - <click selector="{{AdminCartPriceRulesFormSection.condition('...')}}" stepKey="clickToChooseOption1"/> - <click selector="{{AdminCartPriceRulesFormSection.openList}}" stepKey="openList" /> - <waitForPageLoad time="20" stepKey="waitForGrid"/> - <fillField selector="{{AdminCartPriceRulesFormSection.searchSegmentName}}" userInput="{{value.name}}" stepKey="fillSegmentName"/> - <click selector="{{AdminCartPriceRulesFormSection.searchButton}}" stepKey="clickButtonSearch"/> - - <waitForPageLoad stepKey="waitForResults"/> - <checkOption selector="{{AdminCartPriceRulesFormSection.selectAll}}" stepKey="checkAll"/> - <waitForPageLoad stepKey="waitForChecking"/> - <moveMouseOver selector="{{AdminCartPriceRulesFormSection.setSegment}}" stepKey="moveOnButton"/> - <click selector="{{AdminCartPriceRulesFormSection.setSegment}}" stepKey="setCustomerSegment"/> - <click selector="{{AdminMainActionsSection.saveAndContinue}}" stepKey="clickSaveButton"/> - <see selector="{{AdminCartPriceRulesSection.messages}}" userInput="You saved the rule." stepKey="seeSuccessMessage"/> - </actionGroup> - <actionGroup name="SetCartAttributeConditionWhenMatchForCartPriceRuleActionGroup" extends="SetCartAttributeConditionWithSegmentForCartPriceRuleActionGroup"> - <arguments> - <argument name="operatorType" type="string" defaultValue="matches"/> - </arguments> - <click selector="{{AdminCartPriceRulesFormSection.condition('matches')}}" stepKey="clickToChooseOption" after="waitForOperator"/> - <selectOption userInput="{{operatorType}}" selector="{{AdminCartPriceRulesFormSection.conditionsOperator}}" stepKey="setOperatorType" after="clickToChooseOption"/> - </actionGroup> -</actionGroups> diff --git a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/StorefrontAddToTheCartActionGroup.xml b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/StorefrontAddToTheCartActionGroup.xml index 57ed2b67be10e..10f4dd562bf01 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/StorefrontAddToTheCartActionGroup.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/StorefrontAddToTheCartActionGroup.xml @@ -12,5 +12,6 @@ <scrollTo selector="{{StorefrontProductActionSection.addToCart}}" stepKey="scrollToAddToCartButton"/> <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addToCart"/> <waitForPageLoad stepKey="waitForPageToLoad"/> + <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessage"/> </actionGroup> </actionGroups> diff --git a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/StorefrontSalesRuleActionGroup.xml b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/StorefrontSalesRuleActionGroup.xml index 3e55eb4f26607..3963fb13ca94d 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/StorefrontSalesRuleActionGroup.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/ActionGroup/StorefrontSalesRuleActionGroup.xml @@ -51,6 +51,7 @@ <fillField selector="{{StorefrontProductActionSection.quantity}}" userInput="{{quantity}}" stepKey="fillQuantity"/> <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addProductToCart"/> <waitForPageLoad stepKey="waitForAddToCart"/> + <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessage"/> <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCartPage"/> <waitForPageLoad stepKey="waitForCartPage"/> <waitForElementVisible selector="{{CheckoutCartSummarySection.discountAmount}}" stepKey="waitForDiscountElement"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCartRulesAppliedForProductInCartTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCartRulesAppliedForProductInCartTest.xml index ab085dc5ae137..dc5b624c4eabf 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCartRulesAppliedForProductInCartTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCartRulesAppliedForProductInCartTest.xml @@ -18,6 +18,9 @@ <testCaseId value="MAGETWO-96722"/> <useCaseId value="MAGETWO-96410"/> <group value="SalesRule"/> + <skip> + <issueId value="MC-16684"/> + </skip> </annotations> <before> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleAndVerifyRuleConditionAndFreeShippingIsAppliedTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleAndVerifyRuleConditionAndFreeShippingIsAppliedTest.xml index 33184f79f6f0c..f87b02d6ff04a 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleAndVerifyRuleConditionAndFreeShippingIsAppliedTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleAndVerifyRuleConditionAndFreeShippingIsAppliedTest.xml @@ -16,6 +16,9 @@ <severity value="CRITICAL"/> <group value="SalesRule"/> <group value="mtf_migrated"/> + <skip> + <issueId value="MC-16684"/> + </skip> </annotations> <before> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleAndVerifyRuleConditionIsNotAppliedTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleAndVerifyRuleConditionIsNotAppliedTest.xml index 22628e599823d..6941e95a60c74 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleAndVerifyRuleConditionIsNotAppliedTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleAndVerifyRuleConditionIsNotAppliedTest.xml @@ -16,6 +16,9 @@ <severity value="CRITICAL"/> <group value="SalesRule"/> <group value="mtf_migrated"/> + <skip> + <issueId value="MC-16684"/> + </skip> </annotations> <before> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleEmptyFromDateTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleEmptyFromDateTest.xml index e6676dab4eb5e..f5b285f2f0286 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleEmptyFromDateTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleEmptyFromDateTest.xml @@ -85,6 +85,7 @@ <waitForPageLoad stepKey="waitForProductPageLoad"/> <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addProductToCart"/> <waitForPageLoad stepKey="waitForAddToCart"/> + <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessage"/> <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCartPage"/> <waitForPageLoad stepKey="waitForCartPage"/> <actionGroup ref="StorefrontApplyCouponActionGroup" stepKey="applyCoupon"> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForCouponCodeTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForCouponCodeTest.xml index 3deb688de9c34..712475186d5bf 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForCouponCodeTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForCouponCodeTest.xml @@ -77,6 +77,7 @@ <waitForPageLoad stepKey="waitForProductPageLoad"/> <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addProductToCart"/> <waitForPageLoad stepKey="waitForAddToCart"/> + <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessage"/> <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCartPage"/> <waitForPageLoad stepKey="waitForCartPage"/> <actionGroup ref="StorefrontApplyCouponActionGroup" stepKey="applyCoupon"> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForGeneratedCouponTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForGeneratedCouponTest.xml index 7b350c0208cc1..03dffe9f448ea 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForGeneratedCouponTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForGeneratedCouponTest.xml @@ -76,6 +76,7 @@ <waitForPageLoad stepKey="waitForProductPageLoad"/> <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addProductToCart"/> <waitForPageLoad stepKey="waitForAddToCart"/> + <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessage"/> <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCartPage"/> <waitForPageLoad stepKey="waitForCartPage"/> <conditionalClick selector="{{StorefrontSalesRuleCartCouponSection.couponHeader}}" dependentSelector="{{StorefrontSalesRuleCartCouponSection.discountBlockActive}}" visible="false" stepKey="clickCouponHeader"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForMatchingSubtotalAndVerifyRuleConditionIsAppliedTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForMatchingSubtotalAndVerifyRuleConditionIsAppliedTest.xml index ab62e51414e85..2b2834726f6bf 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForMatchingSubtotalAndVerifyRuleConditionIsAppliedTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleForMatchingSubtotalAndVerifyRuleConditionIsAppliedTest.xml @@ -16,6 +16,9 @@ <severity value="CRITICAL"/> <group value="SalesRule"/> <group value="mtf_migrated"/> + <skip> + <issueId value="MC-16684"/> + </skip> </annotations> <before> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleWithMatchingCategoryAndVerifyRuleConditionIsAppliedTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleWithMatchingCategoryAndVerifyRuleConditionIsAppliedTest.xml index e1a4ca40fd710..02ff63f0efb9f 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleWithMatchingCategoryAndVerifyRuleConditionIsAppliedTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleWithMatchingCategoryAndVerifyRuleConditionIsAppliedTest.xml @@ -16,6 +16,9 @@ <severity value="CRITICAL"/> <group value="SalesRule"/> <group value="mtf_migrated"/> + <skip> + <issueId value="MC-16684"/> + </skip> </annotations> <before> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleWithMatchingTotalWeightAndVerifyRuleConditionIsAppliedTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleWithMatchingTotalWeightAndVerifyRuleConditionIsAppliedTest.xml index c62b0dd869281..11088badb05e7 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleWithMatchingTotalWeightAndVerifyRuleConditionIsAppliedTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminCreateCartPriceRuleWithMatchingTotalWeightAndVerifyRuleConditionIsAppliedTest.xml @@ -16,6 +16,9 @@ <severity value="CRITICAL"/> <group value="SalesRule"/> <group value="mtf_migrated"/> + <skip> + <issueId value="MC-16684"/> + </skip> </annotations> <before> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminDeleteActiveSalesRuleWithComplexConditionsAndVerifyDeleteMessageTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminDeleteActiveSalesRuleWithComplexConditionsAndVerifyDeleteMessageTest.xml index 7baec93c73905..d106c086a6065 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/AdminDeleteActiveSalesRuleWithComplexConditionsAndVerifyDeleteMessageTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/AdminDeleteActiveSalesRuleWithComplexConditionsAndVerifyDeleteMessageTest.xml @@ -16,6 +16,9 @@ <severity value="CRITICAL"/> <group value="salesRule"/> <group value="mtf_migrated"/> + <skip> + <issueId value="MC-17175"/> + </skip> </annotations> <before> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/CartPriceRuleForConfigurableProductTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/CartPriceRuleForConfigurableProductTest.xml index e2687f5f16baf..1eba06126ff6f 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/CartPriceRuleForConfigurableProductTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/CartPriceRuleForConfigurableProductTest.xml @@ -122,11 +122,13 @@ <waitForPageLoad stepKey="waitForProductPageLoad1"/> <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addProductToCart1"/> <waitForPageLoad stepKey="waitForAddToCart1"/> + <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessage"/> <!-- Add the second product to the cart --> <amOnPage url="$$createConfigChildProduct2.sku$$.html" stepKey="goToProductPage2"/> <waitForPageLoad stepKey="waitForProductPageLoad2"/> <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addProductToCart2"/> <waitForPageLoad stepKey="waitForAddToCart2"/> + <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessage2"/> <!--View and edit cart--> <actionGroup ref="clickViewAndEditCartFromMiniCart" stepKey="clickViewAndEditCartFromMiniCart"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml index 0d365dc089e43..d9c578e9be334 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/EndToEndB2CGuestUserTest.xml @@ -9,6 +9,11 @@ <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="EndToEndB2CGuestUserTest"> + <annotations> + <skip> + <issueId value="MC-16684"/> + </skip> + </annotations> <before> <createData entity="ApiSalesRule" stepKey="createSalesRule"/> <createData entity="ApiSalesRuleCoupon" stepKey="createSalesRuleCoupon"> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml index 7a995b1feeeda..f816e93d4bc42 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/EndToEndB2CLoggedInUserTest.xml @@ -9,6 +9,11 @@ <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="EndToEndB2CLoggedInUserTest"> + <annotations> + <skip> + <issueId value="MC-16684"/> + </skip> + </annotations> <before> <createData entity="ApiSalesRule" stepKey="createSalesRule"/> <createData entity="ApiSalesRuleCoupon" stepKey="createSalesRuleCoupon"> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleCountry.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleCountry.xml index 045fdbb33763f..ffd50e43a2344 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleCountry.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleCountry.xml @@ -68,6 +68,7 @@ <fillField selector="{{StorefrontProductActionSection.quantity}}" userInput="1" stepKey="fillQuantity"/> <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addProductToCart"/> <waitForPageLoad stepKey="waitForAddToCart"/> + <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessage"/> <!-- Should not see the discount yet because we have not set country --> <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCartPage"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRulePostcode.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRulePostcode.xml index d8c3ef9c32b0b..765f10e44d81b 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRulePostcode.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRulePostcode.xml @@ -72,6 +72,7 @@ <fillField selector="{{StorefrontProductActionSection.quantity}}" userInput="1" stepKey="fillQuantity"/> <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addProductToCart"/> <waitForPageLoad stepKey="waitForAddToCart"/> + <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessage"/> <!-- Should not see the discount yet because we have not filled in postcode --> <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCartPage"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleQuantity.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleQuantity.xml index 51d11b4e5cb1c..cde5e351e1b70 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleQuantity.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleQuantity.xml @@ -68,6 +68,7 @@ <fillField selector="{{StorefrontProductActionSection.quantity}}" userInput="1" stepKey="fillQuantity"/> <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addProductToCart"/> <waitForPageLoad stepKey="waitForAddToCart"/> + <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessage"/> <!-- Should not see the discount yet because we have only 1 item in our cart --> <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCartPage"/> @@ -81,6 +82,7 @@ <fillField selector="{{StorefrontProductActionSection.quantity}}" userInput="1" stepKey="fillQuantity2"/> <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addProductToCart2"/> <waitForPageLoad stepKey="waitForAddToCart2"/> + <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessage2"/> <!-- Now we should see the discount because we have more than 1 item --> <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCartPage2"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleState.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleState.xml index 647f4d6e5c800..d6806e2a2966b 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleState.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleState.xml @@ -68,6 +68,7 @@ <fillField selector="{{StorefrontProductActionSection.quantity}}" userInput="1" stepKey="fillQuantity"/> <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addProductToCart"/> <waitForPageLoad stepKey="waitForAddToCart"/> + <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessage"/> <!-- Should not see the discount yet because we have not filled in postcode --> <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCartPage"/> diff --git a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleSubtotal.xml b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleSubtotal.xml index 7c9c52e1c02ac..8ff747607ea30 100644 --- a/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleSubtotal.xml +++ b/app/code/Magento/SalesRule/Test/Mftf/Test/StorefrontCartPriceRuleSubtotal.xml @@ -67,6 +67,7 @@ <fillField selector="{{StorefrontProductActionSection.quantity}}" userInput="1" stepKey="fillQuantity"/> <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addProductToCart"/> <waitForPageLoad stepKey="waitForAddToCart"/> + <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessage"/> <!-- Should not see the discount yet because we have not exceeded $200 --> <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCartPage"/> @@ -80,6 +81,7 @@ <fillField selector="{{StorefrontProductActionSection.quantity}}" userInput="1" stepKey="fillQuantity2"/> <click selector="{{StorefrontProductActionSection.addToCart}}" stepKey="addProductToCart2"/> <waitForPageLoad stepKey="waitForAddToCart2"/> + <waitForElementVisible selector="{{StorefrontMessagesSection.success}}" stepKey="waitForSuccessMessage2"/> <!-- Now we should see the discount because we exceeded $200 --> <amOnPage url="{{CheckoutCartPage.url}}" stepKey="goToCartPage2"/> diff --git a/app/code/Magento/SalesRule/etc/db_schema.xml b/app/code/Magento/SalesRule/etc/db_schema.xml index c7427e49219b5..5a4877bbf825e 100644 --- a/app/code/Magento/SalesRule/etc/db_schema.xml +++ b/app/code/Magento/SalesRule/etc/db_schema.xml @@ -68,7 +68,7 @@ comment="Usage Per Customer"/> <column xsi:type="int" name="times_used" padding="10" unsigned="true" nullable="false" identity="false" default="0" comment="Times Used"/> - <column xsi:type="timestamp" name="expiration_date" on_update="false" nullable="true" + <column xsi:type="datetime" name="expiration_date" on_update="false" nullable="true" comment="Expiration Date"/> <column xsi:type="smallint" name="is_primary" padding="5" unsigned="true" nullable="true" identity="false" comment="Is Primary"/> diff --git a/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchSuggestionByProductDescriptionTest.xml b/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchSuggestionByProductDescriptionTest.xml index 9e3ed2def2f87..63a861b697a86 100644 --- a/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchSuggestionByProductDescriptionTest.xml +++ b/app/code/Magento/Search/Test/Mftf/Test/StorefrontVerifySearchSuggestionByProductDescriptionTest.xml @@ -16,6 +16,9 @@ <severity value="CRITICAL"/> <testCaseId value="MC-14765"/> <group value="mtf_migrated"/> + <skip> + <issueId value="MC-17012"/> + </skip> </annotations> <before> diff --git a/app/code/Magento/Search/view/frontend/templates/form.mini.phtml b/app/code/Magento/Search/view/frontend/templates/form.mini.phtml index 44c8db3f1a663..dfefcee3aceae 100644 --- a/app/code/Magento/Search/view/frontend/templates/form.mini.phtml +++ b/app/code/Magento/Search/view/frontend/templates/form.mini.phtml @@ -4,7 +4,7 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile +// phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis ?> <?php /** @var $block \Magento\Framework\View\Element\Template */ @@ -12,26 +12,26 @@ $helper = $this->helper(\Magento\Search\Helper\Data::class); ?> <div class="block block-search"> - <div class="block block-title"><strong><?= /* @escapeNotVerified */ __('Search') ?></strong></div> + <div class="block block-title"><strong><?= $block->escapeHtml(__('Search')) ?></strong></div> <div class="block block-content"> - <form class="form minisearch" id="search_mini_form" action="<?= /* @escapeNotVerified */ $helper->getResultUrl() ?>" method="get"> + <form class="form minisearch" id="search_mini_form" action="<?= $block->escapeUrl($helper->getResultUrl()) ?>" method="get"> <div class="field search"> <label class="label" for="search" data-role="minisearch-label"> - <span><?= /* @escapeNotVerified */ __('Search') ?></span> + <span><?= $block->escapeHtml(__('Search')) ?></span> </label> <div class="control"> <input id="search" data-mage-init='{"quickSearch":{ "formSelector":"#search_mini_form", - "url":"<?= /* @escapeNotVerified */ $helper->getSuggestUrl()?>", + "url":"<?= $block->escapeUrl($helper->getSuggestUrl())?>", "destinationSelector":"#search_autocomplete"} }' type="text" - name="<?= /* @escapeNotVerified */ $helper->getQueryParamName() ?>" - value="<?= /* @escapeNotVerified */ $helper->getEscapedQueryText() ?>" - placeholder="<?= /* @escapeNotVerified */ __('Search entire store here...') ?>" + name="<?= $block->escapeHtmlAttr($helper->getQueryParamName()) ?>" + value="<?= $block->escapeHtmlAttr($helper->getEscapedQueryText()) ?>" + placeholder="<?= $block->escapeHtmlAttr(__('Search entire store here...')) ?>" class="input-text" - maxlength="<?= /* @escapeNotVerified */ $helper->getMaxQueryLength() ?>" + maxlength="<?= $block->escapeHtmlAttr($helper->getMaxQueryLength()) ?>" role="combobox" aria-haspopup="false" aria-autocomplete="both" @@ -46,7 +46,7 @@ $helper = $this->helper(\Magento\Search\Helper\Data::class); class="action search" aria-label="Search" > - <span><?= /* @escapeNotVerified */ __('Search') ?></span> + <span><?= $block->escapeHtml(__('Search')) ?></span> </button> </div> </form> diff --git a/app/code/Magento/Search/view/frontend/templates/term.phtml b/app/code/Magento/Search/view/frontend/templates/term.phtml index 1ffcc49314778..b06ebcfe66966 100644 --- a/app/code/Magento/Search/view/frontend/templates/term.phtml +++ b/app/code/Magento/Search/view/frontend/templates/term.phtml @@ -3,23 +3,20 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> -<?php if (sizeof($block->getTerms()) > 0): ?> +<?php if (count($block->getTerms()) > 0) : ?> <ul class="search-terms"> - <?php foreach ($block->getTerms() as $_term): ?> + <?php foreach ($block->getTerms() as $_term) : ?> <li class="item"> - <a href="<?= /* @escapeNotVerified */ $block->getSearchUrl($_term) ?>" - style="font-size:<?= /* @escapeNotVerified */ $_term->getRatio()*70+75 ?>%;"> + <a href="<?= $block->escapeUrl($block->getSearchUrl($_term)) ?>" + style="font-size:<?= /* @noEscape */ $_term->getRatio()*70+75 ?>%;"> <?= $block->escapeHtml($_term->getQueryText()) ?> </a> </li> <?php endforeach; ?> </ul> -<?php else: ?> +<?php else : ?> <div class="message notice"> - <div><?= /* @escapeNotVerified */ __('There are no search terms available.') ?></div> + <div><?= $block->escapeHtml(__('There are no search terms available.')) ?></div> </div> <?php endif; ?> diff --git a/app/code/Magento/Search/view/frontend/web/js/form-mini.js b/app/code/Magento/Search/view/frontend/web/js/form-mini.js index 5331ba3b447ac..0700806a8959d 100644 --- a/app/code/Magento/Search/view/frontend/web/js/form-mini.js +++ b/app/code/Magento/Search/view/frontend/web/js/form-mini.js @@ -128,11 +128,16 @@ define([ * @param {Boolean} isActive */ setActiveState: function (isActive) { + var searchValue; + this.searchForm.toggleClass('active', isActive); this.searchLabel.toggleClass('active', isActive); if (this.isExpandable) { this.element.attr('aria-expanded', isActive); + searchValue = this.element.val(); + this.element.val(''); + this.element.val(searchValue); } }, diff --git a/app/code/Magento/Security/Test/Mftf/Test/AdminLockAdminUserWhenCreatingNewRoleTest.xml b/app/code/Magento/Security/Test/Mftf/Test/AdminLockAdminUserWhenCreatingNewRoleTest.xml new file mode 100644 index 0000000000000..c7fb05a3b7358 --- /dev/null +++ b/app/code/Magento/Security/Test/Mftf/Test/AdminLockAdminUserWhenCreatingNewRoleTest.xml @@ -0,0 +1,96 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminLockAdminUserWhenCreatingNewRoleTest"> + <annotations> + <features value="Security"/> + <stories value="Runs Lock admin user when creating new admin role test."/> + <title value="Lock admin user when creating new admin role"/> + <description value="Runs Lock admin user when creating new admin role test."/> + <severity value="CRITICAL"/> + <testCaseId value="MC-14384" /> + <group value="security"/> + <group value="mtf_migrated"/> + </annotations> + <before> + <!-- Log in to Admin Panel --> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + </before> + <after> + <!-- Unlock Admin user --> + <magentoCLI command="admin:user:unlock {{_ENV.MAGENTO_ADMIN_USERNAME}}" stepKey="unlockAdminUser"/> + </after> + <!-- Perform add new role 6 specified number of times. --> + <actionGroup ref="AdminOpenCreateRolePageActionGroup" stepKey="openCreateRolePage"/> + + <actionGroup ref="AdminFillUserRoleFormActionGroup" stepKey="fillFieldFirstAttempt"> + <argument name="role" value="roleAdministrator" /> + <argument name="currentAdminPassword" value="{{_ENV.MAGENTO_ADMIN_PASSWORD}}INVALID" /> + </actionGroup> + <actionGroup ref="AdminClickSaveButtonOnUserRoleFormActionGroup" stepKey="saveRoleFirstAttempt"/> + <actionGroup ref="AssertMessageInAdminPanelActionGroup" stepKey="checkFirstSaveRoleError"> + <argument name="message" value="The password entered for the current user is invalid. Verify the password and try again." /> + <argument name="messageType" value="error" /> + </actionGroup> + + <actionGroup ref="AdminFillUserRoleFormActionGroup" stepKey="fillFieldSecondAttempt"> + <argument name="role" value="roleAdministrator" /> + <argument name="currentAdminPassword" value="{{_ENV.MAGENTO_ADMIN_PASSWORD}}INVALID" /> + </actionGroup> + <actionGroup ref="AdminClickSaveButtonOnUserRoleFormActionGroup" stepKey="saveRoleSecondAttempt"/> + <actionGroup ref="AssertMessageInAdminPanelActionGroup" stepKey="checkSecondSaveRoleError"> + <argument name="message" value="The password entered for the current user is invalid. Verify the password and try again." /> + <argument name="messageType" value="error" /> + </actionGroup> + + <actionGroup ref="AdminFillUserRoleFormActionGroup" stepKey="fillFieldThirdAttempt"> + <argument name="role" value="roleAdministrator" /> + <argument name="currentAdminPassword" value="{{_ENV.MAGENTO_ADMIN_PASSWORD}}INVALID" /> + </actionGroup> + <actionGroup ref="AdminClickSaveButtonOnUserRoleFormActionGroup" stepKey="saveRoleThirdAttempt"/> + <actionGroup ref="AssertMessageInAdminPanelActionGroup" stepKey="checkThirdSaveRoleError"> + <argument name="message" value="The password entered for the current user is invalid. Verify the password and try again." /> + <argument name="messageType" value="error" /> + </actionGroup> + + <actionGroup ref="AdminFillUserRoleFormActionGroup" stepKey="fillFieldFourthAttempt"> + <argument name="role" value="roleAdministrator" /> + <argument name="currentAdminPassword" value="{{_ENV.MAGENTO_ADMIN_PASSWORD}}INVALID" /> + </actionGroup> + <actionGroup ref="AdminClickSaveButtonOnUserRoleFormActionGroup" stepKey="saveRoleFourthAttempt"/> + <actionGroup ref="AssertMessageInAdminPanelActionGroup" stepKey="checkFourthSaveRoleError"> + <argument name="message" value="The password entered for the current user is invalid. Verify the password and try again." /> + <argument name="messageType" value="error" /> + </actionGroup> + + <actionGroup ref="AdminFillUserRoleFormActionGroup" stepKey="fillFieldFifthAttempt"> + <argument name="role" value="roleAdministrator" /> + <argument name="currentAdminPassword" value="{{_ENV.MAGENTO_ADMIN_PASSWORD}}INVALID" /> + </actionGroup> + <actionGroup ref="AdminClickSaveButtonOnUserRoleFormActionGroup" stepKey="saveRoleFifthAttempt"/> + <actionGroup ref="AssertMessageInAdminPanelActionGroup" stepKey="checkFifthSaveRoleError"> + <argument name="message" value="The password entered for the current user is invalid. Verify the password and try again." /> + <argument name="messageType" value="error" /> + </actionGroup> + + <actionGroup ref="AdminFillUserRoleFormActionGroup" stepKey="fillFieldSixthAttempt"> + <argument name="role" value="roleAdministrator" /> + <argument name="currentAdminPassword" value="{{_ENV.MAGENTO_ADMIN_PASSWORD}}INVALID" /> + </actionGroup> + <actionGroup ref="AdminClickSaveButtonOnUserRoleFormActionGroup" stepKey="saveRoleSixthAttempt"/> + <actionGroup ref="AssertMessageOnAdminLoginActionGroup" stepKey="checkFifthError"> + <argument name="message" value="Your account is temporarily disabled. Please try again later."/> + </actionGroup> + + <!-- Try to login as admin and check error message --> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsLockedAdmin"/> + <actionGroup ref="AssertMessageOnAdminLoginActionGroup" stepKey="checkLoginMessage"/> + </test> +</tests> diff --git a/app/code/Magento/SendFriend/Controller/Product.php b/app/code/Magento/SendFriend/Controller/Product.php index 732bcef8b957a..a184d7d8bff62 100644 --- a/app/code/Magento/SendFriend/Controller/Product.php +++ b/app/code/Magento/SendFriend/Controller/Product.php @@ -61,6 +61,7 @@ public function __construct( /** * Check if module is enabled + * * If allow only for customer - redirect to login page * * @param RequestInterface $request @@ -102,7 +103,7 @@ protected function _initProduct() } try { $product = $this->productRepository->getById($productId); - if (!$product->isVisibleInCatalog()) { + if (!$product->isVisibleInSiteVisibility() || !$product->isVisibleInCatalog()) { return false; } } catch (NoSuchEntityException $noEntityException) { diff --git a/app/code/Magento/Shipping/Block/Tracking/Popup.php b/app/code/Magento/Shipping/Block/Tracking/Popup.php index ac6575c363ceb..1eb679bd8cc76 100644 --- a/app/code/Magento/Shipping/Block/Tracking/Popup.php +++ b/app/code/Magento/Shipping/Block/Tracking/Popup.php @@ -9,6 +9,8 @@ use Magento\Framework\Stdlib\DateTime\DateTimeFormatterInterface; /** + * Tracking popup + * * @api * @since 100.0.2 */ @@ -105,13 +107,15 @@ public function formatDeliveryTime($time, $date = null) */ public function getContactUsEnabled() { - return (bool)$this->_scopeConfig->getValue( - 'contacts/contacts/enabled', + return $this->_scopeConfig->isSetFlag( + 'contact/contact/enabled', \Magento\Store\Model\ScopeInterface::SCOPE_STORE ); } /** + * Get support email + * * @return string */ public function getStoreSupportEmail() @@ -123,6 +127,8 @@ public function getStoreSupportEmail() } /** + * Get contact us url + * * @return string */ public function getContactUs() diff --git a/app/code/Magento/Shipping/view/adminhtml/templates/create/form.phtml b/app/code/Magento/Shipping/view/adminhtml/templates/create/form.phtml index be4069ccdc186..d539a44f58a63 100644 --- a/app/code/Magento/Shipping/view/adminhtml/templates/create/form.phtml +++ b/app/code/Magento/Shipping/view/adminhtml/templates/create/form.phtml @@ -3,48 +3,50 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - +//phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis +//phpcs:disable Magento2.Files.LineLength.MaxExceeded ?> -<form id="edit_form" method="post" action="<?= /* @escapeNotVerified */ $block->getSaveUrl() ?>"> +<form id="edit_form" method="post" action="<?= $block->escapeUrl($block->getSaveUrl()) ?>"> <?= $block->getBlockHtml('formkey') ?> <?php $_order = $block->getShipment()->getOrder() ?> <?= $block->getChildHtml('order_info') ?> <div class="admin__page-section"> <div class="admin__page-section-title"> - <span class="title"><?= /* @escapeNotVerified */ __('Payment & Shipping Method') ?></span> + <span class="title"><?= $block->escapeHtml(__('Payment & Shipping Method')) ?></span> </div> <div class="admin__page-section-content"> <div class="admin__page-section-item order-payment-method"> <?php /* Billing Address */ ?> <div class="admin__page-section-item-title"> - <span class="title"><?= /* @escapeNotVerified */ __('Payment Information') ?></span> + <span class="title"><?=$block->escapeHtml(__('Payment Information')) ?></span> </div> <div class="admin__page-section-item-content"> <div><?= $block->getPaymentHtml() ?></div> - <div class="order-payment-currency"><?= /* @escapeNotVerified */ __('The order was placed using %1.', $_order->getOrderCurrencyCode()) ?></div> + <div class="order-payment-currency"><?= $block->escapeHtml(__('The order was placed using %1.', $_order->getOrderCurrencyCode())) ?></div> </div> </div> <div class="admin__page-section-item order-shipping-address"> <?php /* Shipping Address */ ?> <div class="admin__page-section-item-title"> - <span class="title"><?= /* @escapeNotVerified */ __('Shipping Information') ?></span> + <span class="title"><?= $block->escapeHtml(__('Shipping Information')) ?></span> </div> <div class="admin__page-section-item-content shipping-description-wrapper"> - <div class="shipping-description-title"><?= $block->escapeHtml($_order->getShippingDescription()) ?></div> + <div class="shipping-description-title"> + <?= $block->escapeHtml($_order->getShippingDescription()) ?> + </div> <div class="shipping-description-content"> - <?= /* @escapeNotVerified */ __('Total Shipping Charges') ?>: + <?= $block->escapeHtml(__('Total Shipping Charges')) ?>: - <?php if ($this->helper('Magento\Tax\Helper\Data')->displayShippingPriceIncludingTax()): ?> + <?php if ($this->helper(Magento\Tax\Helper\Data::class)->displayShippingPriceIncludingTax()) : ?> <?php $_excl = $block->displayShippingPriceInclTax($_order); ?> - <?php else: ?> + <?php else : ?> <?php $_excl = $block->displayPriceAttribute('shipping_amount', false, ' '); ?> <?php endif; ?> <?php $_incl = $block->displayShippingPriceInclTax($_order); ?> - <?= /* @escapeNotVerified */ $_excl ?> - <?php if ($this->helper('Magento\Tax\Helper\Data')->displayShippingBothPrices() && $_incl != $_excl): ?> - (<?= /* @escapeNotVerified */ __('Incl. Tax') ?> <?= /* @escapeNotVerified */ $_incl ?>) + <?= /** @noEscape */ $_excl ?> + <?php if ($this->helper(Magento\Tax\Helper\Data::class)->displayShippingBothPrices() + && $_incl != $_excl) : ?> + (<?= $block->escapeHtml(__('Incl. Tax')) ?> <?= /** @noEscape */ $_incl ?>) <?php endif; ?> </div> </div> diff --git a/app/code/Magento/Shipping/view/adminhtml/templates/create/items.phtml b/app/code/Magento/Shipping/view/adminhtml/templates/create/items.phtml index 35e36aa5584c9..ddb5dde5dfac7 100644 --- a/app/code/Magento/Shipping/view/adminhtml/templates/create/items.phtml +++ b/app/code/Magento/Shipping/view/adminhtml/templates/create/items.phtml @@ -3,32 +3,35 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - +//phpcs:disable Squiz.ControlStructures.ControlSignature.NewlineAfterOpenBrace +//phpcs:disable Squiz.WhiteSpace.ScopeClosingBrace.ContentBefore ?> <section class="admin__page-section"> <div class="admin__page-section-title"> - <span class="title"><?= /* @escapeNotVerified */ __('Items to Ship') ?></span> + <span class="title"><?= $block->escapeHtml(__('Items to Ship')) ?></span> </div> <div class="admin__table-wrapper"> <table class="data-table admin__table-primary order-shipment-table"> <thead> <tr class="headings"> - <th class="col-product"><span><?= /* @escapeNotVerified */ __('Product') ?></span></th> - <th class="col-ordered-qty"><span><?= /* @escapeNotVerified */ __('Qty') ?></span></th> - <th class="col-qty<?php if ($block->isShipmentRegular()): ?> last<?php endif; ?>"> - <span><?= /* @escapeNotVerified */ __('Qty to Ship') ?></span> + <th class="col-product"><span><?= $block->escapeHtml(__('Product')) ?></span></th> + <th class="col-ordered-qty"><span><?= $block->escapeHtml(__('Qty')) ?></span></th> + <th class="col-qty<?php if ($block->isShipmentRegular()) : ?> last<?php endif; ?>"> + <span><?= $block->escapeHtml(__('Qty to Ship')) ?></span> </th> - <?php if (!$block->canShipPartiallyItem()): ?> - <th class="col-ship last"><span><?= /* @escapeNotVerified */ __('Ship') ?></span></th> + <?php if (!$block->canShipPartiallyItem()) : ?> + <th class="col-ship last"><span><?= $block->escapeHtml(__('Ship')) ?></span></th> <?php endif; ?> </tr> </thead> <?php $_items = $block->getShipment()->getAllItems() ?> - <?php $_i = 0; foreach ($_items as $_item): if ($_item->getOrderItem()->getParentItem()): continue; endif; $_i++ ?> - <tbody class="<?= /* @escapeNotVerified */ $_i%2 ? 'odd' : 'even' ?>"> + <?php $_i = 0; foreach ($_items as $_item) : + if ($_item->getOrderItem()->getParentItem()) : + continue; + endif; + $_i++ ?> + <tbody class="<?= $_i%2 ? 'odd' : 'even' ?>"> <?= $block->getItemHtml($_item) ?> <?= $block->getItemExtraInfoHtml($_item->getOrderItem()) ?> </tbody> @@ -39,24 +42,24 @@ <section class="admin__page-section"> <div class="admin__page-section-title"> - <span class="title"><?= /* @escapeNotVerified */ __('Shipment Total') ?></span> + <span class="title"><?= $block->escapeHtml(__('Shipment Total')) ?></span> </div> <div class="admin__page-section-content order-comments-history"> <div class="admin__page-section-item"> <div class="admin__page-section-item-title"> - <span class="title"><?= /* @escapeNotVerified */ __('Shipment Comments') ?></span> + <span class="title"><?= $block->escapeHtml(__('Shipment Comments')) ?></span> </div> <div class="admin__page-section-item-content"> <div id="order-history_form" class="admin__field"> <label class="admin__field-label" for="shipment_comment_text"> - <span><?= /* @escapeNotVerified */ __('Comment Text') ?></span></label> + <span><?= $block->escapeHtml(__('Comment Text')) ?></span></label> <div class="admin__field-control"> <textarea id="shipment_comment_text" class="admin__control-textarea" name="shipment[comment_text]" rows="3" - cols="5"><?= /* @escapeNotVerified */ $block->getShipment()->getCommentText() ?></textarea> + cols="5"><?= $block->escapeHtml($block->getShipment()->getCommentText()) ?></textarea> </div> </div> </div> @@ -64,10 +67,10 @@ </div> <div class="admin__page-section-item order-totals order-totals-actions"> <div class="admin__page-section-item-title"> - <span class="title"><?= /* @escapeNotVerified */ __('Shipment Options') ?></span> + <span class="title"><?= $block->escapeHtml(__('Shipment Options')) ?></span> </div> <div class="admin__page-section-item-content"> - <?php if ($block->canCreateShippingLabel()): ?> + <?php if ($block->canCreateShippingLabel()) : ?> <div class="field choice admin__field admin__field-option field-create"> <input id="create_shipping_label" class="admin__control-checkbox" @@ -77,7 +80,7 @@ onclick="toggleCreateLabelCheckbox();"/> <label class="admin__field-label" for="create_shipping_label"> - <span><?= /* @escapeNotVerified */ __('Create Shipping Label') ?></span></label> + <span><?= $block->escapeHtml(__('Create Shipping Label')) ?></span></label> </div> <?php endif ?> @@ -89,10 +92,10 @@ type="checkbox"/> <label class="admin__field-label" for="notify_customer"> - <span><?= /* @escapeNotVerified */ __('Append Comments') ?></span></label> + <span><?=$block->escapeHtml(__('Append Comments')) ?></span></label> </div> - <?php if ($block->canSendShipmentEmail()): ?> + <?php if ($block->canSendShipmentEmail()) : ?> <div class="field choice admin__field admin__field-option field-email"> <input id="send_email" class="admin__control-checkbox" @@ -101,7 +104,7 @@ type="checkbox"/> <label class="admin__field-label" for="send_email"> - <span><?= /* @escapeNotVerified */ __('Email Copy of Shipment') ?></span></label> + <span><?= $block->escapeHtml(__('Email Copy of Shipment')) ?></span></label> </div> <?php endif; ?> <?= $block->getChildHtml('submit_before') ?> @@ -147,7 +150,7 @@ window.toggleCreateLabelCheckbox = function() { window.submitShipment = function(btn) { if (!validQtyItems()) { alert({ - content: '<?= /* @escapeNotVerified */ __('Invalid value(s) for Qty to Ship') ?>' + content: '<?= $block->escapeJs($block->escapeHtml(__('Invalid value(s) for Qty to Ship'))) ?>' }); return; } diff --git a/app/code/Magento/Shipping/view/adminhtml/templates/create/items/renderer/default.phtml b/app/code/Magento/Shipping/view/adminhtml/templates/create/items/renderer/default.phtml index b5d2aafe18ea6..caea091eacb95 100644 --- a/app/code/Magento/Shipping/view/adminhtml/templates/create/items/renderer/default.phtml +++ b/app/code/Magento/Shipping/view/adminhtml/templates/create/items/renderer/default.phtml @@ -3,28 +3,29 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - +//phpcs:disable Squiz.ControlStructures.ControlSignature.NewlineAfterOpenBrace +//phpcs:disable Squiz.WhiteSpace.ScopeClosingBrace.ContentBefore ?> <?php $_item = $block->getItem() ?> <tr> <td class="col-product"><?= $block->getColumnHtml($_item, 'name') ?></td> <td class="col-ordered-qty"><?= $block->getColumnHtml($_item, 'qty') ?></td> - <td class="col-qty <?php if ($block->isShipmentRegular()): ?>last<?php endif; ?>"> - <?php if ($block->canShipPartiallyItem()): ?> + <td class="col-qty <?php if ($block->isShipmentRegular()) : ?>last<?php endif; ?>"> + <?php if ($block->canShipPartiallyItem()) : ?> <input type="text" class="input-text admin__control-text qty-item" - name="shipment[items][<?= /* @escapeNotVerified */ $_item->getOrderItemId() ?>]" - value="<?= /* @escapeNotVerified */ $_item->getQty()*1 ?>" /> - <?php else: ?> - <?= /* @escapeNotVerified */ $_item->getQty()*1 ?> + name="shipment[items][<?= (int) $_item->getOrderItemId() ?>]" + value="<?= /* @noEscape */ $_item->getQty()*1 ?>" /> + <?php else : ?> + <?= /* @noEscape */ $_item->getQty()*1 ?> <?php endif; ?> </td> - <?php if (!$block->canShipPartiallyItem()): ?> + <?php if (!$block->canShipPartiallyItem()) : ?> <td class="col-ship last"> - <input type="hidden" name="shipment[items][<?= /* @escapeNotVerified */ $_item->getOrderItemId() ?>]" value="0" /> - <input type="checkbox" name="shipment[items][<?= /* @escapeNotVerified */ $_item->getOrderItemId() ?>]" value="<?= /* @escapeNotVerified */ $_item->getQty()*1 ?>" checked /> + <input type="hidden" name="shipment[items][<?= (int) $_item->getOrderItemId() ?>]" value="0" /> + <input type="checkbox" + name="shipment[items][<?= (int) $_item->getOrderItemId() ?>]" + value="<?= /* @noEscape */ $_item->getQty()*1 ?>" checked /> </td> <?php endif; ?> </tr> diff --git a/app/code/Magento/Shipping/view/adminhtml/templates/order/packaging/grid.phtml b/app/code/Magento/Shipping/view/adminhtml/templates/order/packaging/grid.phtml index 1a9d44c19db40..22d546f4fb474 100644 --- a/app/code/Magento/Shipping/view/adminhtml/templates/order/packaging/grid.phtml +++ b/app/code/Magento/Shipping/view/adminhtml/templates/order/packaging/grid.phtml @@ -3,9 +3,10 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - +//phpcs:disable Squiz.ControlStructures.ControlSignature.NewlineAfterOpenBrace +//phpcs:disable Squiz.WhiteSpace.ScopeClosingBrace.ContentBefore +//phpcs:disable Squiz.Operators.IncrementDecrementUsage.NotAllowed +//phpcs:disable Squiz.PHP.NonExecutableCode.Unreachable ?> <div class="grid"> <?php $randomId = rand(); ?> @@ -19,29 +20,31 @@ id="select-items-<?= /* @noEscape */ $randomId ?>" onchange="packaging.checkAllItems(this);" class="checkbox admin__control-checkbox" - title="<?= /* @escapeNotVerified */ __('Select All') ?>"> + title="<?= $block->escapeHtmlAttr(__('Select All')) ?>"> <label for="select-items-<?= /* @noEscape */ $randomId ?>"></label> </label> </th> - <th class="data-grid-th"><?= /* @escapeNotVerified */ __('Product Name') ?></th> - <th class="data-grid-th"><?= /* @escapeNotVerified */ __('Weight') ?></th> + <th class="data-grid-th"><?= $block->escapeHtml(__('Product Name')) ?></th> + <th class="data-grid-th"><?= $block->escapeHtml(__('Weight')) ?></th> <th class="data-grid-th" <?= $block->displayCustomsValue() ? '' : 'style="display: none;"' ?>> - <?= /* @escapeNotVerified */ __('Customs Value') ?> + <?= $block->escapeHtml(__('Customs Value')) ?> </th> - <th class="data-grid-th"><?= /* @escapeNotVerified */ __('Qty Ordered') ?></th> - <th class="data-grid-th"><?= /* @escapeNotVerified */ __('Qty') ?></th> + <th class="data-grid-th"><?= $block->escapeHtml(__('Qty Ordered')) ?></th> + <th class="data-grid-th"><?= $block->escapeHtml(__('Qty')) ?></th> </tr> </thead> <tbody> <?php $i=0; ?> - <?php foreach ($block->getCollection() as $item): ?> + <?php foreach ($block->getCollection() as $item) : ?> <?php $_order = $block->getShipment()->getOrder(); $_orderItem = $_order->getItemById($item->getOrderItemId()); ?> <?php if ($item->getIsVirtual() - || ($_orderItem->isShipSeparately() && !($_orderItem->getParentItemId() || $_orderItem->getParentItem())) - || (!$_orderItem->isShipSeparately() && ($_orderItem->getParentItemId() || $_orderItem->getParentItem()))): ?> + || ($_orderItem->isShipSeparately() + && !($_orderItem->getParentItemId() || $_orderItem->getParentItem())) + || (!$_orderItem->isShipSeparately() + && ($_orderItem->getParentItemId() || $_orderItem->getParentItem()))) : ?> <?php continue; ?> <?php endif; ?> <tr class="data-grid-controls-row data-row <?= ($i++ % 2 != 0) ? '_odd-row' : '' ?>"> @@ -51,16 +54,16 @@ <input type="checkbox" name="" id="select-item-<?= /* @noEscape */ $randomId . '-' . $id ?>" - value="<?= /* @escapeNotVerified */ $id ?>" + value="<?= (int) $id ?>" class="checkbox admin__control-checkbox"> <label for="select-item-<?= /* @noEscape */ $randomId . '-' . $id ?>"></label> </label> </td> <td> - <?= /* @escapeNotVerified */ $item->getName() ?> + <?= $block->escapeHtml($item->getName()) ?> </td> <td data-role="item-weight"> - <?= /* @escapeNotVerified */ $item->getWeight() ?> + <?= $block->escapeHtml($item->getWeight()) ?> </td> <?php if ($block->displayCustomsValue()) { @@ -72,25 +75,32 @@ } ?> - <td <?= /* @escapeNotVerified */ $customsValueDisplay ?>> + <td <?= /* @noEscape */ $customsValueDisplay ?>> <input type="text" name="customs_value" - class="input-text admin__control-text <?= /* @escapeNotVerified */ $customsValueValidation ?>" - value="<?= /* @escapeNotVerified */ $block->formatPrice($item->getPrice()) ?>" + class="input-text admin__control-text <?= /* @noEscape */ $customsValueValidation ?>" + value="<?= $block->escapeHtmlAttr($block->formatPrice($item->getPrice())) ?>" size="10" onblur="packaging.recalcContainerWeightAndCustomsValue(this);"> </td> <td> - <?= /* @escapeNotVerified */ $item->getOrderItem()->getQtyOrdered()*1 ?> + <?= /* @noEscape */ $item->getOrderItem()->getQtyOrdered()*1 ?> </td> <td> - <input type="hidden" name="price" value="<?= /* @escapeNotVerified */ $item->getPrice() ?>"> + <input type="hidden" name="price" value="<?= $block->escapeHtml($item->getPrice()) ?>"> <input type="text" name="qty" - value="<?= /* @escapeNotVerified */ $item->getQty()*1 ?>" - class="input-text admin__control-text qty<?php if ($item->getOrderItem()->getIsQtyDecimal()): ?> qty-decimal<?php endif ?>">  - <button type="button" class="action-delete" data-action="package-delete-item" onclick="packaging.deleteItem(this);" style="display:none;"> - <span><?= /* @escapeNotVerified */ __('Delete') ?></span> + value="<?= /* @noEscape */ $item->getQty()*1 ?>" + class="input-text admin__control-text qty + <?php if ($item->getOrderItem()->getIsQtyDecimal()) : ?> + qty-decimal + <?php endif ?>">  + <button type="button" + class="action-delete" + data-action="package-delete-item" + onclick="packaging.deleteItem(this);" + style="display:none;"> + <span><?= $block->escapeHtml(__('Delete')) ?></span> </button> </td> </tr> diff --git a/app/code/Magento/Shipping/view/adminhtml/templates/order/packaging/packed.phtml b/app/code/Magento/Shipping/view/adminhtml/templates/order/packaging/packed.phtml index 1d9cf5688be2d..8d47f533449a7 100644 --- a/app/code/Magento/Shipping/view/adminhtml/templates/order/packaging/packed.phtml +++ b/app/code/Magento/Shipping/view/adminhtml/templates/order/packaging/packed.phtml @@ -3,18 +3,17 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - +//phpcs:disable Magento2.Files.LineLength.MaxExceeded +//phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis ?> <div id="packed_window"> -<?php foreach ($block->getPackages() as $packageId => $package): ?> +<?php foreach ($block->getPackages() as $packageId => $package) : ?> <?php $package = new \Magento\Framework\DataObject($package) ?> <?php $params = new \Magento\Framework\DataObject($package->getParams()) ?> <section class="admin__page-section"> <div class="admin__page-section-title"> - <span class="title"><?= /* @escapeNotVerified */ __('Package') . ' ' . $packageId ?></span> + <span class="title"><?= $block->escapeHtml(__('Package') . ' ' . $packageId) ?></span> </div> <div class="admin__page-section-content"> <div class="row row-gutter"> @@ -22,22 +21,24 @@ <table class="admin__table-secondary"> <tbody> <tr> - <th><?= /* @escapeNotVerified */ __('Type') ?></th> - <td><?= /* @escapeNotVerified */ $block->getContainerTypeByCode($params->getContainer()) ?></td> + <th><?= $block->escapeHtml(__('Type')) ?></th> + <td> + <?= $block->escapeHtml($block->getContainerTypeByCode($params->getContainer())) ?> + </td> </tr> <tr> - <?php if ($block->displayCustomsValue()): ?> - <th><?= /* @escapeNotVerified */ __('Customs Value') ?></th> - <td><?= /* @escapeNotVerified */ $block->displayCustomsPrice($params->getCustomsValue()) ?></td> - <?php else: ?> - <th><?= /* @escapeNotVerified */ __('Total Weight') ?></th> - <td><?= /* @escapeNotVerified */ $params->getWeight() . ' ' . $this->helper('Magento\Shipping\Helper\Carrier')->getMeasureWeightName($params->getWeightUnits()) ?></td> + <?php if ($block->displayCustomsValue()) : ?> + <th><?= $block->escapeHtml(__('Customs Value')) ?></th> + <td><?= $block->escapeHtml($block->displayCustomsPrice($params->getCustomsValue())) ?></td> + <?php else : ?> + <th><?= $block->escapeHtml(__('Total Weight')) ?></th> + <td><?= $block->escapeHtml($params->getWeight() . ' ' . $this->helper(Magento\Shipping\Helper\Carrier::class)->getMeasureWeightName($params->getWeightUnits())) ?></td> <?php endif; ?> </tr> - <?php if ($params->getSize()): ?> + <?php if ($params->getSize()) : ?> <tr> - <th><?= /* @escapeNotVerified */ __('Size') ?></th> - <td><?= /* @escapeNotVerified */ ucfirst(strtolower($params->getSize())) ?></td> + <th><?= $block->escapeHtml(__('Size')) ?></th> + <td><?= $block->escapeHtml(ucfirst(strtolower($params->getSize()))) ?></td> </tr> <?php endif; ?> </tbody> @@ -47,31 +48,31 @@ <table class="admin__table-secondary"> <tbody> <tr> - <th><?= /* @escapeNotVerified */ __('Length') ?></th> + <th><?= $block->escapeHtml(__('Length')) ?></th> <td> - <?php if ($params->getLength() != null): ?> - <?= /* @escapeNotVerified */ $params->getLength() . ' ' . $this->helper('Magento\Shipping\Helper\Carrier')->getMeasureDimensionName($params->getDimensionUnits()) ?> - <?php else: ?> + <?php if ($params->getLength() != null) : ?> + <?= $block->escapeHtml($params->getLength() . ' ' . $this->helper(Magento\Shipping\Helper\Carrier::class)->getMeasureDimensionName($params->getDimensionUnits())) ?> + <?php else : ?> -- <?php endif; ?> </td> </tr> <tr> - <th><?= /* @escapeNotVerified */ __('Width') ?></th> + <th><?= $block->escapeHtml(__('Width')) ?></th> <td> - <?php if ($params->getWidth() != null): ?> - <?= /* @escapeNotVerified */ $params->getWidth() . ' ' . $this->helper('Magento\Shipping\Helper\Carrier')->getMeasureDimensionName($params->getDimensionUnits()) ?> - <?php else: ?> + <?php if ($params->getWidth() != null) : ?> + <?= $block->escapeHtml($params->getWidth() . ' ' . $this->helper(Magento\Shipping\Helper\Carrier::class)->getMeasureDimensionName($params->getDimensionUnits())) ?> + <?php else : ?> -- <?php endif; ?> </td> </tr> <tr> - <th><?= /* @escapeNotVerified */ __('Height') ?></th> + <th><?= $block->escapeHtml(__('Height')) ?></th> <td> - <?php if ($params->getHeight() != null): ?> - <?= /* @escapeNotVerified */ $params->getHeight() . ' ' . $this->helper('Magento\Shipping\Helper\Carrier')->getMeasureDimensionName($params->getDimensionUnits()) ?> - <?php else: ?> + <?php if ($params->getHeight() != null) : ?> + <?= $block->escapeHtml($params->getHeight() . ' ' . $this->helper(Magento\Shipping\Helper\Carrier::class)->getMeasureDimensionName($params->getDimensionUnits())) ?> + <?php else : ?> -- <?php endif; ?> </td> @@ -82,26 +83,26 @@ <div class="col-m-4"> <table class="admin__table-secondary"> <tbody> - <?php if ($params->getDeliveryConfirmation() != null): ?> + <?php if ($params->getDeliveryConfirmation() != null) : ?> <tr> - <th><?= /* @escapeNotVerified */ __('Signature Confirmation') ?></th> - <td><?= /* @escapeNotVerified */ $block->getDeliveryConfirmationTypeByCode($params->getDeliveryConfirmation()) ?></td> + <th><?= $block->escapeHtml(__('Signature Confirmation')) ?></th> + <td><?= $block->escapeHtml($block->getDeliveryConfirmationTypeByCode($params->getDeliveryConfirmation())) ?></td> </tr> <?php endif; ?> - <?php if ($params->getContentType() != null): ?> + <?php if ($params->getContentType() != null) : ?> <tr> - <th><?= /* @escapeNotVerified */ __('Contents') ?></th> - <?php if ($params->getContentType() == 'OTHER'): ?> + <th><?= $block->escapeHtml(__('Contents')) ?></th> + <?php if ($params->getContentType() == 'OTHER') : ?> <td><?= $block->escapeHtml($params->getContentTypeOther()) ?></td> - <?php else: ?> - <td><?= /* @escapeNotVerified */ $block->getContentTypeByCode($params->getContentType()) ?></td> + <?php else : ?> + <td><?= $block->escapeHtml($block->getContentTypeByCode($params->getContentType())) ?></td> <?php endif; ?> </tr> <?php endif; ?> - <?php if ($params->getGirth()): ?> + <?php if ($params->getGirth()) : ?> <tr> - <th><?= /* @escapeNotVerified */ __('Girth') ?></th> - <td><?= /* @escapeNotVerified */ $params->getGirth() . ' ' . $this->helper('Magento\Shipping\Helper\Carrier')->getMeasureDimensionName($params->getGirthDimensionUnits()) ?></td> + <th><?= $block->escapeHtml(__('Girth')) ?></th> + <td><?= $block->escapeHtml($params->getGirth() . ' ' . $this->helper(Magento\Shipping\Helper\Carrier::class)->getMeasureDimensionName($params->getGirthDimensionUnits())) ?></td> </tr> <?php endif; ?> </tbody> @@ -110,19 +111,19 @@ </div> </div> <div class="admin__page-section-item-title"> - <span class="title"><?= /* @escapeNotVerified */ __('Items in the Package') ?></span> + <span class="title"><?= $block->escapeHtml(__('Items in the Package')) ?></span> </div> <div class="admin__table-wrapper"> <table class="data-table admin__table-primary"> <thead> <tr class="headings"> - <th class="col-product"><span><?= /* @escapeNotVerified */ __('Product') ?></span></th> - <th class="col-weight"><span><?= /* @escapeNotVerified */ __('Weight') ?></span></th> - <?php if ($block->displayCustomsValue()): ?> - <th class="col-custom"><span><?= /* @escapeNotVerified */ __('Customs Value') ?></span></th> + <th class="col-product"><span><?= $block->escapeHtml(__('Product')) ?></span></th> + <th class="col-weight"><span><?= $block->escapeHtml(__('Weight')) ?></span></th> + <?php if ($block->displayCustomsValue()) : ?> + <th class="col-custom"><span><?= $block->escapeHtml(__('Customs Value')) ?></span></th> <?php endif; ?> - <th class="col-qty"><span><?= /* @escapeNotVerified */ __('Qty Ordered') ?></span></th> - <th class="col-qty"><span><?= /* @escapeNotVerified */ __('Qty') ?></span></th> + <th class="col-qty"><span><?= $block->escapeHtml(__('Qty Ordered')) ?></span></th> + <th class="col-qty"><span><?= $block->escapeHtml(__('Qty')) ?></span></th> </tr> </thead> <tbody id=""> @@ -130,19 +131,21 @@ <?php $item = new \Magento\Framework\DataObject($item) ?> <tr title="#" id=""> <td class="col-product"> - <?= /* @escapeNotVerified */ $item->getName() ?> + <?= $block->escapeHtml($item->getName()) ?> </td> <td class="col-weight"> - <?= /* @escapeNotVerified */ $item->getWeight() ?> + <?= $block->escapeHtml($item->getWeight()) ?> </td> - <?php if ($block->displayCustomsValue()): ?> - <td class="col-custom"><?= /* @escapeNotVerified */ $block->displayCustomsPrice($item->getCustomsValue()) ?></td> + <?php if ($block->displayCustomsValue()) : ?> + <td class="col-custom"> + <?= $block->escapeHtml($block->displayCustomsPrice($item->getCustomsValue())) ?> + </td> <?php endif; ?> <td class="col-qty"> - <?= /* @escapeNotVerified */ $block->getQtyOrderedItem($item->getOrderItemId()) ?> + <?= $block->escapeHtml($block->getQtyOrderedItem($item->getOrderItemId())) ?> </td> <td class="col-qty"> - <?= /* @escapeNotVerified */ $item->getQty()*1 ?> + <?= /* @noEscape */ $item->getQty()*1 ?> </td> </tr> <?php endforeach; ?> @@ -163,8 +166,8 @@ "#packed_window": { "Magento_Shipping/js/packages":{ "type":"slide", - "title":"<?= /* @escapeNotVerified */ __('Packages') ?>", - "url": "<?= /* @escapeNotVerified */ $block->getPrintButton() ?>" + "title":"<?= $block->escapeHtml(__('Packages')) ?>", + "url": "<?= $block->escapeUrl($block->getPrintButton()) ?>" } } } diff --git a/app/code/Magento/Shipping/view/adminhtml/templates/order/packaging/popup.phtml b/app/code/Magento/Shipping/view/adminhtml/templates/order/packaging/popup.phtml index a1f2d2741839b..cd25cb919adb5 100644 --- a/app/code/Magento/Shipping/view/adminhtml/templates/order/packaging/popup.phtml +++ b/app/code/Magento/Shipping/view/adminhtml/templates/order/packaging/popup.phtml @@ -3,9 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - +//phpcs:disable PSR2.Methods.FunctionCallSignature.SpaceBeforeOpenBracket +//phpcs:disable Magento2.Security.IncludeFile.FoundIncludeFile ?> <?php /** @var $block \Magento\Shipping\Block\Adminhtml\Order\Packaging */ ?> <?php @@ -21,21 +20,21 @@ $girthEnabled = $block->isDisplayGirthValue() && $block->isGirthAllowed() ? 1 : "Magento_Ui/js/modal/modal" ], function(jQuery){ - window.packaging = new Packaging(<?= /* @escapeNotVerified */ $block->getConfigDataJson() ?>); + window.packaging = new Packaging(<?= /* @noEscape */ $block->getConfigDataJson() ?>); packaging.changeContainerType($$('select[name=package_container]')[0]); packaging.checkSizeAndGirthParameter( $$('select[name=package_container]')[0], - <?= /* @escapeNotVerified */ $girthEnabled ?> + <?= /* @noEscape */ $girthEnabled ?> ); packaging.setConfirmPackagingCallback(function(){ packaging.setParamsCreateLabelRequest($('edit_form').serialize(true)); packaging.sendCreateLabelRequest(); }); packaging.setLabelCreatedCallback(function(response){ - setLocation("<?php /* @escapeNotVerified */ echo $block->getUrl( + setLocation("<?php $block->escapeJs($block->escapeUrl($block->getUrl( 'sales/order/view', ['order_id' => $block->getShipment()->getOrderId()] - ); ?>"); + ))); ?>"); }); packaging.setCancelCallback(function() { if ($('create_shipping_label')) { @@ -52,23 +51,23 @@ $girthEnabled = $block->isDisplayGirthValue() && $block->isGirthAllowed() ? 1 : }); jQuery('#packaging_window').modal({ type: 'slide', - title: '<?= /* @escapeNotVerified */ __('Create Packages') ?>', + title: '<?= $block->escapeJs($block->escapeHtml(__('Create Packages'))) ?>', buttons: [{ - text: '<?= /* @escapeNotVerified */ __('Cancel') ?>', + text: '<?= $block->escapeJs($block->escapeHtml(__('Cancel'))) ?>', 'class': 'action-secondary', click: function () { packaging.cancelPackaging(); this.closeModal(); } }, { - text: '<?= /* @escapeNotVerified */ __('Save') ?>', + text: '<?= $block->escapeJs($block->escapeHtml(__('Save'))) ?>', 'attr': {'disabled':'disabled', 'data-action':'save-packages'}, 'class': 'action-primary _disabled', click: function () { packaging.confirmPackaging(); } }, { - text: '<?= /* @escapeNotVerified */ __('Add Package') ?>', + text: '<?= $block->escapeJs($block->escapeHtml(__('Add Package'))) ?>', 'attr': {'data-action':'add-packages'}, 'class': 'action-secondary', click: function () { diff --git a/app/code/Magento/Shipping/view/adminhtml/templates/order/packaging/popup_content.phtml b/app/code/Magento/Shipping/view/adminhtml/templates/order/packaging/popup_content.phtml index db0739d127b2b..f91741f439d46 100644 --- a/app/code/Magento/Shipping/view/adminhtml/templates/order/packaging/popup_content.phtml +++ b/app/code/Magento/Shipping/view/adminhtml/templates/order/packaging/popup_content.phtml @@ -3,9 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - +//phpcs:disable Magento2.Files.LineLength.MaxExceeded ?> <?php /** @var $block \Magento\Shipping\Block\Adminhtml\Order\Packaging */ ?> <div id="packaging_window"> @@ -13,14 +11,20 @@ <section class="admin__page-section" id="package_template" style="display:none;"> <div class="admin__page-section-title"> <span class="title"> - <?= /* @escapeNotVerified */ __('Package') ?> <span data-role="package-number"></span> + <?= $block->escapeHtml(__('Package')) ?> <span data-role="package-number"></span> </span> <div class="actions _primary"> - <button type="button" class="action-secondary" data-action="package-save-items" onclick="packaging.packItems(this);"> - <span><?= /* @escapeNotVerified */ __('Add Selected Product(s) to Package') ?></span> + <button type="button" + class="action-secondary" + data-action="package-save-items" + onclick="packaging.packItems(this);"> + <span><?= $block->escapeHtml(__('Add Selected Product(s) to Package')) ?></span> </button> - <button type="button" class="action-secondary" data-action="package-add-items" onclick="packaging.getItemsForPack(this);"> - <span><?= /* @escapeNotVerified */ __('Add Products to Package') ?></span> + <button type="button" + class="action-secondary" + data-action="package-add-items" + onclick="packaging.getItemsForPack(this);"> + <span><?= $block->escapeHtml(__('Add Products to Package')) ?></span> </button> </div> </div> @@ -28,23 +32,23 @@ <table class="data-table admin__control-table"> <thead> <tr> - <th class="col-type"><?= /* @escapeNotVerified */ __('Type') ?></th> - <?php if ($girthEnabled == 1): ?> - <th class="col-size"><?= /* @escapeNotVerified */ __('Size') ?></th> - <th class="col-girth"><?= /* @escapeNotVerified */ __('Girth') ?></th> + <th class="col-type"><?= $block->escapeHtml(__('Type')) ?></th> + <?php if ($girthEnabled == 1) : ?> + <th class="col-size"><?= $block->escapeHtml(__('Size')) ?></th> + <th class="col-girth"><?= $block->escapeHtml(__('Girth')) ?></th> <th> </th> <?php endif; ?> <th class="col-custom" <?= $block->displayCustomsValue() ? '' : 'style="display: none;"' ?>> - <?= /* @escapeNotVerified */ __('Customs Value') ?> + <?= $block->escapeHtml(__('Customs Value')) ?> </th> - <th class="col-total-weight"><?= /* @escapeNotVerified */ __('Total Weight') ?></th> - <th class="col-length"><?= /* @escapeNotVerified */ __('Length') ?></th> - <th class="col-width"><?= /* @escapeNotVerified */ __('Width') ?></th> - <th class="col-height"><?= /* @escapeNotVerified */ __('Height') ?></th> + <th class="col-total-weight"><?= $block->escapeHtml(__('Total Weight')) ?></th> + <th class="col-length"><?= $block->escapeHtml(__('Length')) ?></th> + <th class="col-width"><?= $block->escapeHtml(__('Width')) ?></th> + <th class="col-height"><?= $block->escapeHtml(__('Height')) ?></th> <th> </th> - <?php if ($block->getDeliveryConfirmationTypes()): ?> - <th class="col-signature"><?= /* @escapeNotVerified */ __('Signature Confirmation') ?></th> - <?php endif; ?> + <?php if ($block->getDeliveryConfirmationTypes()) : ?> + <th class="col-signature"><?= $block->escapeHtml(__('Signature Confirmation')) ?></th> + <?php endif; ?> <th class="col-actions"> </th> </tr> </thead> @@ -53,29 +57,29 @@ <td class="col-type"> <?php $containers = $block->getContainers(); ?> <select name="package_container" - onchange="packaging.changeContainerType(this);packaging.checkSizeAndGirthParameter(this, <?= /* @escapeNotVerified */ $girthEnabled ?>);" - <?php if (empty($containers)):?> - title="<?= /* @escapeNotVerified */ __('USPS domestic shipments don\'t use package types.') ?>" + onchange="packaging.changeContainerType(this);packaging.checkSizeAndGirthParameter(this, <?= $block->escapeJs($girthEnabled) ?>);" + <?php if (empty($containers)) : ?> + title="<?= $block->escapeHtmlAttr(__('USPS domestic shipments don\'t use package types.')) ?>" disabled="" class="admin__control-select disabled" - <?php else: ?> + <?php else : ?> class="admin__control-select" <?php endif; ?>> - <?php foreach ($containers as $key => $value): ?> - <option value="<?= /* @escapeNotVerified */ $key ?>" > - <?= /* @escapeNotVerified */ $value ?> + <?php foreach ($containers as $key => $value) : ?> + <option value="<?= $block->escapeHtmlAttr($key) ?>" > + <?= $block->escapeHtml($value) ?> </option> <?php endforeach; ?> </select> </td> - <?php if ($girthEnabled == 1 && !empty($sizeSource)): ?> + <?php if ($girthEnabled == 1 && !empty($sizeSource)) : ?> <td> <select name="package_size" class="admin__control-select" - onchange="packaging.checkSizeAndGirthParameter(this, <?= /* @escapeNotVerified */ $girthEnabled ?>);"> - <?php foreach ($sizeSource as $key => $value): ?> - <option value="<?= /* @escapeNotVerified */ $sizeSource[$key]['value'] ?>"> - <?= /* @escapeNotVerified */ $sizeSource[$key]['label'] ?> + onchange="packaging.checkSizeAndGirthParameter(this, <?= $block->escapeJs($girthEnabled) ?>);"> + <?php foreach ($sizeSource as $key => $value) : ?> + <option value="<?= $block->escapeHtmlAttr($sizeSource[$key]['value']) ?>"> + <?= $block->escapeHtml($sizeSource[$key]['label']) ?> </option> <?php endforeach; ?> </select> @@ -89,11 +93,11 @@ <select name="container_girth_dimension_units" class="options-units-dimensions measures admin__control-select" onchange="packaging.changeMeasures(this);"> - <option value="<?= /* @escapeNotVerified */ Zend_Measure_Length::INCH ?>" selected="selected" ><?= /* @escapeNotVerified */ __('in') ?></option> - <option value="<?= /* @escapeNotVerified */ Zend_Measure_Length::CENTIMETER ?>" ><?= /* @escapeNotVerified */ __('cm') ?></option> + <option value="<?= /* @noEscape */ Zend_Measure_Length::INCH ?>" selected="selected" ><?= $block->escapeHtml(__('in')) ?></option> + <option value="<?= /* @noEscape */ Zend_Measure_Length::CENTIMETER ?>" ><?= $block->escapeHtml(__('cm')) ?></option> </select> </td> - <?php endif; ?> + <?php endif; ?> <?php if ($block->displayCustomsValue()) { $customsValueDisplay = ''; @@ -103,13 +107,15 @@ $customsValueValidation = ''; } ?> - <td class="col-custom" <?= /* @escapeNotVerified */ $customsValueDisplay ?>> + <td class="col-custom" <?= /* @noEscape */ $customsValueDisplay ?>> <div class="admin__control-addon"> <input type="text" - class="customs-value input-text admin__control-text <?= /* @escapeNotVerified */ $customsValueValidation ?>" + class="customs-value input-text admin__control-text <?= /* @noEscape */ $customsValueValidation ?>" name="package_customs_value" /> <span class="admin__addon-suffix"> - <span class="customs-value-currency"><?= /* @escapeNotVerified */ $block->getCustomValueCurrencyCode() ?></span> + <span class="customs-value-currency"> + <?= $block->escapeHtml($block->getCustomValueCurrencyCode()) ?> + </span> </span> </div> </td> @@ -121,8 +127,8 @@ <select name="container_weight_units" class="options-units-weight measures admin__control-select" onchange="packaging.changeMeasures(this);"> - <option value="<?= /* @escapeNotVerified */ Zend_Measure_Weight::POUND ?>" selected="selected" ><?= /* @escapeNotVerified */ __('lb') ?></option> - <option value="<?= /* @escapeNotVerified */ Zend_Measure_Weight::KILOGRAM ?>" ><?= /* @escapeNotVerified */ __('kg') ?></option> + <option value="<?= /* @noEscape */ Zend_Measure_Weight::POUND ?>" selected="selected" ><?= $block->escapeHtml(__('lb')) ?></option> + <option value="<?= /* @noEscape */ Zend_Measure_Weight::KILOGRAM ?>" ><?= $block->escapeHtml(__('kg')) ?></option> </select> <span class="admin__addon-prefix"></span> </div> @@ -146,35 +152,37 @@ <select name="container_dimension_units" class="options-units-dimensions measures admin__control-select" onchange="packaging.changeMeasures(this);"> - <option value="<?= /* @escapeNotVerified */ Zend_Measure_Length::INCH ?>" selected="selected" ><?= /* @escapeNotVerified */ __('in') ?></option> - <option value="<?= /* @escapeNotVerified */ Zend_Measure_Length::CENTIMETER ?>" ><?= /* @escapeNotVerified */ __('cm') ?></option> + <option value="<?= /* @noEscape */ Zend_Measure_Length::INCH ?>" selected="selected" ><?= $block->escapeHtml(__('in')) ?></option> + <option value="<?= /* @noEscape */ Zend_Measure_Length::CENTIMETER ?>" ><?= $block->escapeHtml(__('cm')) ?></option> </select> </td> - <?php if ($block->getDeliveryConfirmationTypes()): ?> + <?php if ($block->getDeliveryConfirmationTypes()) : ?> <td> <select name="delivery_confirmation_types" class="admin__control-select"> - <?php foreach ($block->getDeliveryConfirmationTypes() as $key => $value): ?> - <option value="<?= /* @escapeNotVerified */ $key ?>" > - <?= /* @escapeNotVerified */ $value ?> + <?php foreach ($block->getDeliveryConfirmationTypes() as $key => $value) : ?> + <option value="<?= $block->escapeHtmlAttr($key) ?>" > + <?= $block->escapeHtml($value) ?> </option> <?php endforeach; ?> </select> </td> - <?php endif; ?> + <?php endif; ?> <td class="col-actions"> - <button type="button" class="action-delete DeletePackageBtn" onclick="packaging.deletePackage(this);"> - <span><?= /* @escapeNotVerified */ __('Delete Package') ?></span> + <button type="button" + class="action-delete DeletePackageBtn" + onclick="packaging.deletePackage(this);"> + <span><?= $block->escapeHtml(__('Delete Package')) ?></span> </button> </td> </tr> </tbody> </table> - <?php if ($block->getContentTypes()): ?> + <?php if ($block->getContentTypes()) : ?> <table class="data-table admin__control-table" cellspacing="0"> <thead> <tr> - <th><?= /* @escapeNotVerified */ __('Contents') ?></th> - <th><?= /* @escapeNotVerified */ __('Explanation') ?></th> + <th><?= $block->escapeHtml(__('Contents')) ?></th> + <th><?= $block->escapeHtml(__('Explanation')) ?></th> </tr> </thead> <tbody> @@ -183,9 +191,9 @@ <select name="content_type" class="admin__control-select" onchange="packaging.changeContentTypes(this);"> - <?php foreach ($block->getContentTypes() as $key => $value): ?> - <option value="<?= /* @escapeNotVerified */ $key ?>" > - <?= /* @escapeNotVerified */ $value ?> + <?php foreach ($block->getContentTypes() as $key => $value) : ?> + <option value="<?= $block->escapeHtmlAttr($key) ?>" > + <?= $block->escapeHtml($value) ?> </option> <?php endforeach; ?> </select> diff --git a/app/code/Magento/Shipping/view/adminhtml/templates/order/tracking.phtml b/app/code/Magento/Shipping/view/adminhtml/templates/order/tracking.phtml index 02cdca120fdbc..d65fa819eaeed 100644 --- a/app/code/Magento/Shipping/view/adminhtml/templates/order/tracking.phtml +++ b/app/code/Magento/Shipping/view/adminhtml/templates/order/tracking.phtml @@ -3,9 +3,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <?php /** @var $block Magento\Shipping\Block\Adminhtml\Order\Tracking */?> <script> @@ -68,8 +65,8 @@ require(['prototype'], function(){ id="trackingC<%- data.index %>" class="select admin__control-select carrier" disabled="disabled"> - <?php foreach ($block->getCarriers() as $_code => $_name): ?> - <option value="<?= /* @escapeNotVerified */ $_code ?>"><?= $block->escapeHtml($_name) ?></option> + <?php foreach ($block->getCarriers() as $_code => $_name) : ?> + <option value="<?= $block->escapeHtmlAttr($_code) ?>"><?= $block->escapeHtml($_name) ?></option> <?php endforeach; ?> </select> </td> @@ -94,7 +91,7 @@ require(['prototype'], function(){ type="button" class="action-default action-delete" onclick="trackingControl.deleteRow(event);return false"> - <span><?= /* @escapeNotVerified */ __('Delete') ?></span> + <span><?= $block->escapeHtml(__('Delete')) ?></span> </button> </td> </tr> @@ -104,10 +101,10 @@ require(['prototype'], function(){ <table class="data-table admin__control-table" id="tracking_numbers_table"> <thead> <tr class="headings"> - <th class="col-carrier"><?= /* @escapeNotVerified */ __('Carrier') ?></th> - <th class="col-title"><?= /* @escapeNotVerified */ __('Title') ?></th> - <th class="col-number"><?= /* @escapeNotVerified */ __('Number') ?></th> - <th class="col-delete"><?= /* @escapeNotVerified */ __('Action') ?></th> + <th class="col-carrier"><?= $block->escapeHtml(__('Carrier')) ?></th> + <th class="col-title"><?= $block->escapeHtml(__('Title')) ?></th> + <th class="col-number"><?= $block->escapeHtml(__('Number')) ?></th> + <th class="col-delete"><?= $block->escapeHtml(__('Action')) ?></th> </tr> </thead> <tfoot> diff --git a/app/code/Magento/Shipping/view/adminhtml/templates/order/tracking/view.phtml b/app/code/Magento/Shipping/view/adminhtml/templates/order/tracking/view.phtml index 0587d5eabe681..67587f19774c4 100644 --- a/app/code/Magento/Shipping/view/adminhtml/templates/order/tracking/view.phtml +++ b/app/code/Magento/Shipping/view/adminhtml/templates/order/tracking/view.phtml @@ -3,19 +3,19 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - +//phpcs:disable Squiz.ControlStructures.ControlSignature.NewlineAfterOpenBrace +//phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis +//phpcs:disable Magento2.Files.LineLength.MaxExceeded ?> <?php /** @var $block Magento\Shipping\Block\Adminhtml\Order\Tracking\View */ ?> <div class="admin__control-table-wrapper"> <table class="data-table admin__control-table" id="shipment_tracking_info"> <thead> <tr class="headings"> - <th class="col-carrier"><?= /* @escapeNotVerified */ __('Carrier') ?></th> - <th class="col-title"><?= /* @escapeNotVerified */ __('Title') ?></th> - <th class="col-number"><?= /* @escapeNotVerified */ __('Number') ?></th> - <th class="col-delete last"><?= /* @escapeNotVerified */ __('Action') ?></th> + <th class="col-carrier"><?= $block->escapeHtml(__('Carrier')) ?></th> + <th class="col-title"><?= $block->escapeHtml(__('Title')) ?></th> + <th class="col-number"><?= $block->escapeHtml(__('Number')) ?></th> + <th class="col-delete last"><?= $block->escapeHtml(__('Action')) ?></th> </tr> </thead> <tfoot> @@ -24,8 +24,8 @@ <select name="carrier" class="select admin__control-select" onchange="selectCarrier(this)"> - <?php foreach ($block->getCarriers() as $_code => $_name): ?> - <option value="<?= /* @escapeNotVerified */ $_code ?>"><?= $block->escapeHtml($_name) ?></option> + <?php foreach ($block->getCarriers() as $_code => $_name) : ?> + <option value="<?= $block->escapeHtmlAttr($_code) ?>"><?= $block->escapeHtml($_name) ?></option> <?php endforeach; ?> </select> </td> @@ -46,21 +46,23 @@ <td class="col-delete last"><?= $block->getSaveButtonHtml() ?></td> </tr> </tfoot> - <?php if ($_tracks = $block->getShipment()->getAllTracks()): ?> + <?php if ($_tracks = $block->getShipment()->getAllTracks()) : ?> <tbody> - <?php $i = 0; foreach ($_tracks as $_track):$i++ ?> - <tr class="<?= /* @escapeNotVerified */ ($i%2 == 0) ? 'even' : 'odd' ?>"> - <td class="col-carrier"><?= $block->escapeHtml($block->getCarrierTitle($_track->getCarrierCode())) ?></td> + <?php $i = 0; foreach ($_tracks as $_track) :$i++ ?> + <tr class="<?= /* @noEscape */ ($i%2 == 0) ? 'even' : 'odd' ?>"> + <td class="col-carrier"> + <?= $block->escapeHtml($block->getCarrierTitle($_track->getCarrierCode())) ?> + </td> <td class="col-title"><?= $block->escapeHtml($_track->getTitle()) ?></td> <td class="col-number"> - <?php if ($_track->isCustom()): ?> - <?= $block->escapeHtml($_track->getNumber()) ?> - <?php else: ?> - <a href="#" onclick="popWin('<?= /* @escapeNotVerified */ $this->helper('Magento\Shipping\Helper\Data')->getTrackingPopupUrlBySalesModel($_track) ?>','trackorder','width=800,height=600,resizable=yes,scrollbars=yes')"><?= $block->escapeHtml($_track->getNumber()) ?></a> - <div id="shipment_tracking_info_response_<?= /* @escapeNotVerified */ $_track->getId() ?>"></div> + <?php if ($_track->isCustom()) : ?> + <?= $block->escapeHtml($_track->getNumber()) ?> + <?php else : ?> + <a href="#" onclick="popWin('<?= $block->escapeJs($block->escapeUrl($this->helper(Magento\Shipping\Helper\Data::class)->getTrackingPopupUrlBySalesModel($_track))) ?>','trackorder','width=800,height=600,resizable=yes,scrollbars=yes')"><?= $block->escapeHtml($_track->getNumber()) ?></a> + <div id="shipment_tracking_info_response_<?= (int) $_track->getId() ?>"></div> <?php endif; ?> </td> - <td class="col-delete last"><button class="action-delete" type="button" onclick="deleteTrackingNumber('<?= /* @escapeNotVerified */ $block->getRemoveUrl($_track) ?>'); return false;"><span><?= /* @escapeNotVerified */ __('Delete') ?></span></button></td> + <td class="col-delete last"><button class="action-delete" type="button" onclick="deleteTrackingNumber('<?= $block->escapeJs($block->escapeUrl($block->getRemoveUrl($_track))) ?>'); return false;"><span><?= $block->escapeHtml(__('Delete')) ?></span></button></td> </tr> <?php endforeach; ?> </tbody> @@ -78,7 +80,7 @@ function selectCarrier(elem) { } function deleteTrackingNumber(url) { - if (confirm('<?= /* @escapeNotVerified */ __('Are you sure?') ?>')) { + if (confirm('<?= $block->escapeJs($block->escapeHtml(__('Are you sure?'))) ?>')) { submitAndReloadArea($('shipment_tracking_info').parentNode, url) } } diff --git a/app/code/Magento/Shipping/view/adminhtml/templates/order/view/info.phtml b/app/code/Magento/Shipping/view/adminhtml/templates/order/view/info.phtml index b7281f16cd353..720b34983551d 100644 --- a/app/code/Magento/Shipping/view/adminhtml/templates/order/view/info.phtml +++ b/app/code/Magento/Shipping/view/adminhtml/templates/order/view/info.phtml @@ -3,39 +3,40 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - +//phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis ?> <?php /** @var $block \Magento\Shipping\Block\Adminhtml\View */ ?> <?php $order = $block->getOrder() ?> -<?php if ($order->getIsVirtual()) : return '';endif; ?> +<?php if ($order->getIsVirtual()) : + return ''; +endif; ?> <?php /* Shipping Method */ ?> <div class="admin__page-section-item order-shipping-method"> <div class="admin__page-section-item-title"> - <span class="title"><?= /* @escapeNotVerified */ __('Shipping & Handling Information') ?></span> + <span class="title"><?= $block->escapeHtml(__('Shipping & Handling Information')) ?></span> </div> <div class="admin__page-section-item-content"> <?php if ($order->getTracksCollection()->count()) : ?> - <p><a href="#" id="linkId" onclick="popWin('<?= /* @escapeNotVerified */ $this->helper('Magento\Shipping\Helper\Data')->getTrackingPopupUrlBySalesModel($order) ?>','trackorder','width=800,height=600,resizable=yes,scrollbars=yes')" title="<?= /* @escapeNotVerified */ __('Track Order') ?>"><?= /* @escapeNotVerified */ __('Track Order') ?></a></p> + <p><a href="#" id="linkId" onclick="popWin('<?= $block->escapeJs($block->escapeUrl($this->helper(Magento\Shipping\Helper\Data::class)->getTrackingPopupUrlBySalesModel($order))) ?>','trackorder','width=800,height=600,resizable=yes,scrollbars=yes')" title="<?= $block->escapeHtmlAttr(__('Track Order')) ?>"><?= $block->escapeHtml(__('Track Order')) ?></a></p> <?php endif; ?> - <?php if ($order->getShippingDescription()): ?> + <?php if ($order->getShippingDescription()) : ?> <strong><?= $block->escapeHtml($order->getShippingDescription()) ?></strong> - <?php if ($this->helper('Magento\Tax\Helper\Data')->displayShippingPriceIncludingTax()): ?> + <?php if ($this->helper(Magento\Tax\Helper\Data::class)->displayShippingPriceIncludingTax()) : ?> <?php $_excl = $block->displayShippingPriceInclTax($order); ?> - <?php else: ?> + <?php else : ?> <?php $_excl = $block->displayPriceAttribute('shipping_amount', false, ' '); ?> <?php endif; ?> <?php $_incl = $block->displayShippingPriceInclTax($order); ?> - <?= /* @escapeNotVerified */ $_excl ?> - <?php if ($this->helper('Magento\Tax\Helper\Data')->displayShippingBothPrices() && $_incl != $_excl): ?> - (<?= /* @escapeNotVerified */ __('Incl. Tax') ?> <?= /* @escapeNotVerified */ $_incl ?>) + <?= /** @noEscape */ $_excl ?> + <?php if ($this->helper(Magento\Tax\Helper\Data::class)->displayShippingBothPrices() + && $_incl != $_excl) : ?> + (<?= $block->escapeHtml(__('Incl. Tax')) ?> <?= /** @noEscape */ $_incl ?>) <?php endif; ?> - <?php else: ?> - <?= /* @escapeNotVerified */ __('No shipping information available') ?> + <?php else : ?> + <?= $block->escapeHtml(__('No shipping information available')) ?> <?php endif; ?> </div> </div> diff --git a/app/code/Magento/Shipping/view/adminhtml/templates/view/form.phtml b/app/code/Magento/Shipping/view/adminhtml/templates/view/form.phtml index 32805ec0a3495..f105562151082 100644 --- a/app/code/Magento/Shipping/view/adminhtml/templates/view/form.phtml +++ b/app/code/Magento/Shipping/view/adminhtml/templates/view/form.phtml @@ -3,11 +3,11 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile /** * @var \Magento\Shipping\Block\Adminhtml\View\Form $block */ +//phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis +//phpcs:disable Magento2.Files.LineLength.MaxExceeded $order = $block->getShipment()->getOrder(); ?> <?= $block->getChildHtml('order_info'); ?> @@ -34,7 +34,7 @@ $order = $block->getShipment()->getOrder(); </div> <div class="admin__page-section-item-content"> <div class="shipping-description-wrapper"> - <?php if ($block->getShipment()->getTracksCollection()->count()): ?> + <?php if ($block->getShipment()->getTracksCollection()->count()) : ?> <p> <a href="#" id="linkId" onclick="popWin('<?= $block->escapeUrl($this->helper(\Magento\Shipping\Helper\Data::class)->getTrackingPopupUrlBySalesModel($block->getShipment())); ?>','trackshipment','width=800,height=600,resizable=yes,scrollbars=yes')" title="<?= $block->escapeHtml(__('Track this shipment')); ?>"> @@ -48,27 +48,27 @@ $order = $block->getShipment()->getOrder(); <?= $block->escapeHtml(__('Total Shipping Charges')); ?>: - <?php if ($this->helper(\Magento\Tax\Helper\Data::class)->displayShippingPriceIncludingTax()): ?> + <?php if ($this->helper(\Magento\Tax\Helper\Data::class)->displayShippingPriceIncludingTax()) : ?> <?php $excl = $block->displayShippingPriceInclTax($order); ?> - <?php else: ?> + <?php else : ?> <?php $excl = $block->displayPriceAttribute('shipping_amount', false, ' '); ?> <?php endif; ?> <?php $incl = $block->displayShippingPriceInclTax($order); ?> <?= /* @noEscape */ $excl; ?> - <?php if ($this->helper(\Magento\Tax\Helper\Data::class)->displayShippingBothPrices() && $incl != $excl): ?> + <?php if ($this->helper(\Magento\Tax\Helper\Data::class)->displayShippingBothPrices() && $incl != $excl) : ?> (<?= $block->escapeHtml(__('Incl. Tax')); ?> <?= /* @noEscape */ $incl; ?>) <?php endif; ?> </div> <p> - <?php if ($block->canCreateShippingLabel()): ?> + <?php if ($block->canCreateShippingLabel()) : ?> <?= /* @noEscape */ $block->getCreateLabelButton(); ?> <?php endif ?> - <?php if ($block->getShipment()->getShippingLabel()): ?> + <?php if ($block->getShipment()->getShippingLabel()) : ?> <?= /* @noEscape */ $block->getPrintLabelButton(); ?> <?php endif ?> - <?php if ($block->getShipment()->getPackages()): ?> + <?php if ($block->getShipment()->getPackages()) : ?> <?= /* @noEscape */ $block->getShowPackagesButton(); ?> <?php endif ?> </p> @@ -85,10 +85,7 @@ $order = $block->getShipment()->getOrder(); window.packaging.sendCreateLabelRequest(); }); window.packaging.setLabelCreatedCallback(function () { - setLocation("<?php echo $block->escapeUrl($block->getUrl( - 'adminhtml/order_shipment/view', - ['shipment_id' => $block->getShipment()->getId()]) - ); ?>"); + setLocation("<?php $block->escapeUrl($block->getUrl('adminhtml/order_shipment/view', ['shipment_id' => $block->getShipment()->getId()])); ?>"); }); }; diff --git a/app/code/Magento/Shipping/view/adminhtml/templates/view/items.phtml b/app/code/Magento/Shipping/view/adminhtml/templates/view/items.phtml index 8dddfaedda4e5..fa9fe7a84221b 100644 --- a/app/code/Magento/Shipping/view/adminhtml/templates/view/items.phtml +++ b/app/code/Magento/Shipping/view/adminhtml/templates/view/items.phtml @@ -3,24 +3,27 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <div class="admin__table-wrapper"> <table class="data-table admin__table-primary order-shipment-table"> <thead> <tr class="headings"> - <th class="col-product"><span><?= /* @escapeNotVerified */ __('Product') ?></span></th> - <th class="col-qty last"><span><?= /* @escapeNotVerified */ __('Qty Shipped') ?></span></th> + <th class="col-product"><span><?= $block->escapeHtml(__('Product')) ?></span></th> + <th class="col-qty last"><span><?= $block->escapeHtml(__('Qty Shipped')) ?></span></th> </tr> </thead> <?php $_items = $block->getShipment()->getAllItems() ?> - <?php $_i = 0; foreach ($_items as $_item): if ($_item->getOrderItem()->getParentItem()): continue; endif; $_i++ ?> - <tbody class="<?= /* @escapeNotVerified */ $_i%2 ? 'odd' : 'even' ?>"> - <?= $block->getItemHtml($_item) ?> - <?= $block->getItemExtraInfoHtml($_item->getOrderItem()) ?> - </tbody> + <?php $_i = 0; foreach ($_items as $_item) : + if (!empty($_item->getOrderItem())) : + if ($_item->getOrderItem()->getParentItem()) : + continue; + endif; + $_i++ ?> + <tbody class="<?= /* @noEscape */ $_i%2 ? 'odd' : 'even' ?>"> + <?= $block->getItemHtml($_item) ?> + <?= $block->getItemExtraInfoHtml($_item->getOrderItem()) ?> + </tbody> + <?php endif; ?> <?php endforeach; ?> </table> </div> diff --git a/app/code/Magento/Shipping/view/adminhtml/templates/view/items/renderer/default.phtml b/app/code/Magento/Shipping/view/adminhtml/templates/view/items/renderer/default.phtml index c035295f77d45..3a41eabd20c08 100644 --- a/app/code/Magento/Shipping/view/adminhtml/templates/view/items/renderer/default.phtml +++ b/app/code/Magento/Shipping/view/adminhtml/templates/view/items/renderer/default.phtml @@ -3,12 +3,9 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <?php $_item = $block->getItem() ?> <tr class="border"> <td class="col-product"><?= $block->getColumnHtml($_item, 'name') ?></td> - <td class="col-qty last"><?= /* @escapeNotVerified */ $_item->getQty()*1 ?></td> + <td class="col-qty last"><?= /* @noEscape */ $_item->getQty()*1 ?></td> </tr> diff --git a/app/code/Magento/Shipping/view/frontend/templates/items.phtml b/app/code/Magento/Shipping/view/frontend/templates/items.phtml index ebc6163a7bd06..f0f1423ed47a2 100644 --- a/app/code/Magento/Shipping/view/frontend/templates/items.phtml +++ b/app/code/Magento/Shipping/view/frontend/templates/items.phtml @@ -3,9 +3,10 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - +//phpcs:disable Squiz.ControlStructures.ControlSignature.NewlineAfterOpenBrace +//phpcs:disable Squiz.WhiteSpace.ScopeClosingBrace.ContentBefore +//phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis +//phpcs:disable Magento2.Files.LineLength.MaxExceeded ?> <?php /** @var $block \Magento\Shipping\Block\Items */ ?> <?php $_order = $block->getOrder() ?> @@ -13,67 +14,71 @@ <?php if ($_order->getTracksCollection()->count()) : ?> <?= $block->getChildHtml('track-all-link') ?> <?php endif; ?> - <a href="<?= /* @escapeNotVerified */ $block->getPrintAllShipmentsUrl($_order) ?>" + <a href="<?= $block->escapeUrl($block->getPrintAllShipmentsUrl($_order)) ?>" onclick="this.target='_blank'" class="action print"> - <span><?= /* @escapeNotVerified */ __('Print All Shipments') ?></span> + <span><?= $block->escapeHtml(__('Print All Shipments')) ?></span> </a> </div> -<?php foreach ($_order->getShipmentsCollection() as $_shipment): ?> -<div class="order-title"> - <strong><?= /* @escapeNotVerified */ __('Shipment #') ?><?= /* @escapeNotVerified */ $_shipment->getIncrementId() ?></strong> - <a href="<?= /* @escapeNotVerified */ $block->getPrintShipmentUrl($_shipment) ?>" - onclick="this.target='_blank'" - class="action print"> - <span><?= /* @escapeNotVerified */ __('Print Shipment') ?></span> - </a> - <a href="#" - data-mage-init='{"popupWindow": {"windowURL":"<?= /* @escapeNotVerified */ $this->helper('Magento\Shipping\Helper\Data')->getTrackingPopupUrlBySalesModel($_shipment) ?>","windowName":"trackshipment","width":800,"height":600,"top":0,"left":0,"resizable":1,"scrollbars":1}}' - title="<?= /* @escapeNotVerified */ __('Track this shipment') ?>" - class="action track"> - <span><?= /* @escapeNotVerified */ __('Track this shipment') ?></span> - </a> -</div> -<?php $tracks = $_shipment->getTracksCollection(); ?> -<?php if ($tracks->count()): ?> - <dl class="order-tracking" id="my-tracking-table-<?= /* @escapeNotVerified */ $_shipment->getId() ?>"> - <dt class="tracking-title"> - <?= /* @escapeNotVerified */ __('Tracking Number(s):') ?> - </dt> - <dd class="tracking-content"> - <?php - $i = 1; - $_size = $tracks->count(); - foreach ($tracks as $track): ?> - <?php if ($track->isCustom()): ?><?= $block->escapeHtml($track->getNumber()) ?><?php else: ?><a - href="#" - data-mage-init='{"popupWindow": {"windowURL":"<?= /* @escapeNotVerified */ $this->helper('Magento\Shipping\Helper\Data')->getTrackingPopupUrlBySalesModel($track) ?>","windowName":"trackorder","width":800,"height":600,"left":0,"top":0,"resizable":1,"scrollbars":1}}' - class="action track"><span><?= $block->escapeHtml($track->getNumber()) ?></span> - </a><?php endif; ?><?php if ($i != $_size): ?>, <?php endif; ?> - <?php $i++; - endforeach; ?> - </dd> - </dl> -<?php endif; ?> -<div class="table-wrapper order-items-shipment"> - <table class="data table table-order-items shipment" id="my-shipment-table-<?= /* @escapeNotVerified */ $_shipment->getId() ?>"> - <caption class="table-caption"><?= /* @escapeNotVerified */ __('Items Shipped') ?></caption> - <thead> - <tr> - <th class="col name"><?= /* @escapeNotVerified */ __('Product Name') ?></th> - <th class="col sku"><?= /* @escapeNotVerified */ __('SKU') ?></th> - <th class="col qty"><?= /* @escapeNotVerified */ __('Qty Shipped') ?></th> - </tr> - </thead> - <?php $_items = $_shipment->getAllItems(); ?> - <?php foreach ($_items as $_item): ?> - <?php if (!$_item->getOrderItem()->getParentItem()) : ?> - <tbody> - <?= $block->getItemHtml($_item) ?> - </tbody> - <?php endif; ?> - <?php endforeach; ?> - </table> -</div> -<?= $block->getCommentsHtml($_shipment) ?> +<?php foreach ($_order->getShipmentsCollection() as $_shipment) : ?> + <div class="order-title"> + <strong><?= $block->escapeHtml(__('Shipment #')) ?><?= $block->escapeHtml($_shipment->getIncrementId()) ?></strong> + <a href="<?= $block->escapeUrl($block->getPrintShipmentUrl($_shipment)) ?>" + onclick="this.target='_blank'" + class="action print"> + <span><?= $block->escapeHtml(__('Print Shipment')) ?></span> + </a> + <a href="#" + data-mage-init='{"popupWindow": {"windowURL":"<?= $block->escapeUrl($this->helper(Magento\Shipping\Helper\Data::class)->getTrackingPopupUrlBySalesModel($_shipment)) ?>","windowName":"trackshipment","width":800,"height":600,"top":0,"left":0,"resizable":1,"scrollbars":1}}' + title="<?= $block->escapeHtml(__('Track this shipment')) ?>" + class="action track"> + <span><?= $block->escapeHtml(__('Track this shipment')) ?></span> + </a> + </div> + <?php $tracks = $_shipment->getTracksCollection(); ?> + <?php if ($tracks->count()) : ?> + <dl class="order-tracking" id="my-tracking-table-<?= (int) $_shipment->getId() ?>"> + <dt class="tracking-title"> + <?= $block->escapeHtml(__('Tracking Number(s):')) ?> + </dt> + <dd class="tracking-content"> + <?php + $i = 1; + $_size = $tracks->count(); + foreach ($tracks as $track) : ?> + <?php if ($track->isCustom()) : ?> + <?= $block->escapeHtml($track->getNumber()) ?> + <?php else : ?> + <a href="#" + data-mage-init='{"popupWindow": {"windowURL":"<?= $block->escapeUrl($this->helper(Magento\Shipping\Helper\Data::class)->getTrackingPopupUrlBySalesModel($track)) ?>","windowName":"trackorder","width":800,"height":600,"left":0,"top":0,"resizable":1,"scrollbars":1}}' + class="action track"><span><?= $block->escapeHtml($track->getNumber()) ?></span> + </a> + <?php endif; ?> + <?php if ($i != $_size) : ?>, <?php endif; ?> + <?php $i++; + endforeach; ?> + </dd> + </dl> + <?php endif; ?> + <div class="table-wrapper order-items-shipment"> + <table class="data table table-order-items shipment" id="my-shipment-table-<?= (int) $_shipment->getId() ?>"> + <caption class="table-caption"><?= $block->escapeHtml(__('Items Shipped')) ?></caption> + <thead> + <tr> + <th class="col name"><?= $block->escapeHtml(__('Product Name')) ?></th> + <th class="col sku"><?= $block->escapeHtml(__('SKU')) ?></th> + <th class="col qty"><?= $block->escapeHtml(__('Qty Shipped')) ?></th> + </tr> + </thead> + <?php $_items = $_shipment->getAllItems(); ?> + <?php foreach ($_items as $_item) : ?> + <?php if (!$_item->getOrderItem()->getParentItem()) : ?> + <tbody> + <?= $block->getItemHtml($_item) ?> + </tbody> + <?php endif; ?> + <?php endforeach; ?> + </table> + </div> + <?= $block->getCommentsHtml($_shipment) ?> <?php endforeach; ?> diff --git a/app/code/Magento/Shipping/view/frontend/templates/order/shipment.phtml b/app/code/Magento/Shipping/view/frontend/templates/order/shipment.phtml index 512c06626d758..da251da3d35d4 100644 --- a/app/code/Magento/Shipping/view/frontend/templates/order/shipment.phtml +++ b/app/code/Magento/Shipping/view/frontend/templates/order/shipment.phtml @@ -8,8 +8,8 @@ <?= $block->getChildHtml('shipment_items') ?> <div class="actions-toolbar"> <div class="secondary"> - <a href="<?= /* @escapeNotVerified */ $block->getBackUrl() ?>" class="action back"> - <span><?= /* @escapeNotVerified */ $block->getBackTitle() ?></span> + <a href="<?= $block->escapeUrl($block->getBackUrl()) ?>" class="action back"> + <span><?= $block->escapeHtml($block->getBackTitle()) ?></span> </a> </div> </div> diff --git a/app/code/Magento/Shipping/view/frontend/templates/tracking/details.phtml b/app/code/Magento/Shipping/view/frontend/templates/tracking/details.phtml index e8584d8f6ad51..7dca84565e674 100644 --- a/app/code/Magento/Shipping/view/frontend/templates/tracking/details.phtml +++ b/app/code/Magento/Shipping/view/frontend/templates/tracking/details.phtml @@ -4,9 +4,8 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - /** @var $block \Magento\Framework\View\Element\Template */ +//phpcs:disable Magento2.Files.LineLength.MaxExceeded $parentBlock = $block->getParentBlock(); $track = $block->getData('track'); @@ -24,18 +23,18 @@ $number = is_object($track) ? $track->getTracking() : $track['number']; <table class="data table order tracking" id="tracking-table-popup-<?= $block->escapeHtml($number) ?>"> <caption class="table-caption"><?= $block->escapeHtml(__('Order tracking')) ?></caption> <tbody> - <?php if (is_object($track)): ?> + <?php if (is_object($track)) : ?> <tr> <th class="col label" scope="row"><?= $block->escapeHtml(__('Tracking Number:')) ?></th> <td class="col value"><?= $block->escapeHtml($number) ?></td> </tr> - <?php if ($track->getCarrierTitle()): ?> + <?php if ($track->getCarrierTitle()) : ?> <tr> <th class="col label" scope="row"><?= $block->escapeHtml(__('Carrier:')) ?></th> <td class="col value"><?= $block->escapeHtml($track->getCarrierTitle()) ?></td> </tr> <?php endif; ?> - <?php if ($track->getErrorMessage()): ?> + <?php if ($track->getErrorMessage()) : ?> <tr> <th class="col label" scope="row"><?= $block->escapeHtml(__('Error:')) ?></th> <td class="col error"> @@ -51,12 +50,12 @@ $number = is_object($track) ? $track->getTracking() : $track['number']; <a href="mailto:<?= /* @noEscape */ $email ?>"><?= /* @noEscape */ $email ?></a> </td> </tr> - <?php elseif ($track->getTrackSummary()): ?> + <?php elseif ($track->getTrackSummary()) : ?> <tr> <th class="col label" scope="row"><?= $block->escapeHtml(__('Info:')) ?></th> <td class="col value"><?= $block->escapeHtml($track->getTrackSummary()) ?></td> </tr> - <?php elseif ($track->getUrl()): ?> + <?php elseif ($track->getUrl()) : ?> <tr> <th class="col label" scope="row"><?= $block->escapeHtml(__('Track:')) ?></th> <td class="col value"> @@ -65,9 +64,9 @@ $number = is_object($track) ? $track->getTracking() : $track['number']; </a> </td> </tr> - <?php else: ?> - <?php foreach ($fields as $title => $property): ?> - <?php if (!empty($track->$property())): ?> + <?php else : ?> + <?php foreach ($fields as $title => $property) : ?> + <?php if (!empty($track->$property())) : ?> <tr> <th class="col label" scope="row"><?= /* @noEscape */ $block->escapeHtml(__($title . ':')) ?></th> <td class="col value"><?= $block->escapeHtml($track->$property()) ?></td> @@ -75,7 +74,7 @@ $number = is_object($track) ? $track->getTracking() : $track['number']; <?php endif;?> <?php endforeach; ?> - <?php if ($track->getDeliverydate()): ?> + <?php if ($track->getDeliverydate()) : ?> <tr> <th class="col label" scope="row"><?= $block->escapeHtml($parentBlock->getDeliveryDateTitle()->getTitle($track)) ?></th> <td class="col value"> @@ -84,7 +83,7 @@ $number = is_object($track) ? $track->getTracking() : $track['number']; </tr> <?php endif; ?> <?php endif; ?> - <?php elseif (isset($track['title']) && isset($track['number']) && $track['number']): ?> + <?php elseif (isset($track['title']) && isset($track['number']) && $track['number']) : ?> <?php /* if the tracking is custom value */ ?> <tr> <th class="col label" scope="row"> diff --git a/app/code/Magento/Shipping/view/frontend/templates/tracking/link.phtml b/app/code/Magento/Shipping/view/frontend/templates/tracking/link.phtml index 188e24ee11515..24658649d0a0c 100644 --- a/app/code/Magento/Shipping/view/frontend/templates/tracking/link.phtml +++ b/app/code/Magento/Shipping/view/frontend/templates/tracking/link.phtml @@ -3,13 +3,19 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <?php /** @var $block \Magento\Shipping\Block\Tracking\Link */ ?> <?php $order = $block->getOrder() ?> -<a href="#" class="action track" title="<?= /* @escapeNotVerified */ $block->getLabel() ?>" - data-mage-init='{"popupWindow": {"windowURL":"<?= /* @escapeNotVerified */ $block->getWindowUrl($order) ?>","windowName":"trackorder","width":800,"height":600,"left":0,"top":0,"resizable":1,"scrollbars":1}}'> - <span><?= /* @escapeNotVerified */ $block->getLabel() ?></span> +<a href="#" class="action track" title="<?= $block->escapeHtmlAttr($block->getLabel()) ?>" + data-mage-init='{"popupWindow": { + "windowURL":"<?= $block->escapeUrl($block->getWindowUrl($order)) ?>", + "windowName":"trackorder", + "width":800, + "height":600, + "left":0, + "top":0, + "resizable":1, + "scrollbars":1 + }}'> + <span><?= $block->escapeHtml($block->getLabel()) ?></span> </a> diff --git a/app/code/Magento/Shipping/view/frontend/templates/tracking/popup.phtml b/app/code/Magento/Shipping/view/frontend/templates/tracking/popup.phtml index eb888595c7f97..ee8a834044aa7 100644 --- a/app/code/Magento/Shipping/view/frontend/templates/tracking/popup.phtml +++ b/app/code/Magento/Shipping/view/frontend/templates/tracking/popup.phtml @@ -6,20 +6,19 @@ use Magento\Framework\View\Element\Template; -// @codingStandardsIgnoreFile - /** @var $block \Magento\Shipping\Block\Tracking\Popup */ +//phpcs:disable Magento2.Files.LineLength.MaxExceeded $results = $block->getTrackingInfo(); ?> <div class="page tracking"> - <?php if (!empty($results)): ?> - <?php foreach ($results as $shipId => $result): ?> - <?php if ($shipId): ?> + <?php if (!empty($results)) : ?> + <?php foreach ($results as $shipId => $result) : ?> + <?php if ($shipId) : ?> <div class="order subtitle caption"><?= /* @noEscape */ $block->escapeHtml(__('Shipment #')) . $shipId ?></div> <?php endif; ?> - <?php if (!empty($result)): ?> - <?php foreach ($result as $counter => $track): ?> + <?php if (!empty($result)) : ?> + <?php foreach ($result as $counter => $track) : ?> <div class="table-wrapper"> <?php $shipmentBlockIdentifier = $shipId . '.' . $counter; @@ -27,12 +26,11 @@ $results = $block->getTrackingInfo(); 'track' => $track, 'template' => 'Magento_Shipping::tracking/details.phtml', 'storeSupportEmail' => $block->getStoreSupportEmail() - ] - ); + ]); ?> <?= /* @noEscape */ $block->getChildHtml('shipping.tracking.details.' . $shipmentBlockIdentifier) ?> </div> - <?php if (is_object($track) && !empty($track->getProgressdetail())): ?> + <?php if (is_object($track) && !empty($track->getProgressdetail())) : ?> <?php $block->addChild('shipping.tracking.progress.' . $shipmentBlockIdentifier, Template::class, [ 'track' => $track, @@ -42,13 +40,13 @@ $results = $block->getTrackingInfo(); <?= /* @noEscape */ $block->getChildHtml('shipping.tracking.progress.' . $shipmentBlockIdentifier) ?> <?php endif; ?> <?php endforeach; ?> - <?php else: ?> + <?php else : ?> <div class="message info empty"> <div><?= $block->escapeHtml(__('There is no tracking available for this shipment.')) ?></div> </div> <?php endif; ?> <?php endforeach; ?> - <?php else: ?> + <?php else : ?> <div class="message info empty"> <div><?= $block->escapeHtml(__('There is no tracking available.')) ?></div> </div> diff --git a/app/code/Magento/Shipping/view/frontend/templates/tracking/progress.phtml b/app/code/Magento/Shipping/view/frontend/templates/tracking/progress.phtml index 237eba09ff802..e15c39367529f 100644 --- a/app/code/Magento/Shipping/view/frontend/templates/tracking/progress.phtml +++ b/app/code/Magento/Shipping/view/frontend/templates/tracking/progress.phtml @@ -1,10 +1,8 @@ <?php /** -* Copyright © Magento, Inc. All rights reserved. -* See COPYING.txt for license details. -*/ - -// @codingStandardsIgnoreFile + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ /** @var $block \Magento\Framework\View\Element\Template */ $parentBlock = $block->getParentBlock(); @@ -22,9 +20,13 @@ $track = $block->getData('track'); </tr> </thead> <tbody> - <?php foreach ($track->getProgressdetail() as $detail): ?> - <?php $detailDate = (!empty($detail['deliverydate']) ? $parentBlock->formatDeliveryDate($detail['deliverydate'] . ' ' . $detail['deliverytime']) : ''); ?> - <?php $detailTime = (!empty($detail['deliverytime']) ? $parentBlock->formatDeliveryTime($detail['deliverytime'], $detail['deliverydate']) : ''); ?> + <?php foreach ($track->getProgressdetail() as $detail) : ?> + <?php $detailDate = (!empty($detail['deliverydate']) ? + $parentBlock->formatDeliveryDate($detail['deliverydate'] . ' ' . $detail['deliverytime']) : + ''); ?> + <?php $detailTime = (!empty($detail['deliverytime']) ? + $parentBlock->formatDeliveryTime($detail['deliverytime'], $detail['deliverydate']) : + ''); ?> <tr> <td data-th="<?= $block->escapeHtml(__('Location')) ?>" class="col location"> <?= (!empty($detail['deliverylocation']) ? $block->escapeHtml($detail['deliverylocation']) : '') ?> diff --git a/app/code/Magento/Sitemap/Model/ResourceModel/Catalog/Product.php b/app/code/Magento/Sitemap/Model/ResourceModel/Catalog/Product.php index 1419fa375a117..9f4f698458cf8 100644 --- a/app/code/Magento/Sitemap/Model/ResourceModel/Catalog/Product.php +++ b/app/code/Magento/Sitemap/Model/ResourceModel/Catalog/Product.php @@ -194,7 +194,6 @@ protected function _addFilter($storeId, $attributeCode, $value, $type = '=') break; default: return false; - break; } $attribute = $this->_getAttribute($attributeCode); @@ -310,10 +309,6 @@ public function getCollection($storeId) } $connection = $this->getConnection(); - $urlsConfigCondition = ''; - if ($this->isCategoryProductURLsConfig($storeId)) { - $urlsConfigCondition = 'NOT '; - } $this->_select = $connection->select()->from( ['e' => $this->getMainTable()], @@ -324,9 +319,7 @@ public function getCollection($storeId) [] )->joinLeft( ['url_rewrite' => $this->getTable('url_rewrite')], - 'e.entity_id = url_rewrite.entity_id AND url_rewrite.is_autogenerated = 1 ' - . 'AND NULLIF(url_rewrite.metadata,"") IS ' - . $urlsConfigCondition . 'NULL' + 'e.entity_id = url_rewrite.entity_id AND url_rewrite.is_autogenerated = 1 AND url_rewrite.metadata IS NULL' . $connection->quoteInto(' AND url_rewrite.store_id = ?', $store->getId()) . $connection->quoteInto(' AND url_rewrite.entity_type = ?', ProductUrlRewriteGenerator::ENTITY_TYPE), ['url' => 'request_path'] @@ -490,20 +483,4 @@ private function getProductImageUrl($image) { return $this->imageUrlBuilder->getUrl($image, 'product_page_image_large'); } - - /** - * Return Use Categories Path for Product URLs config value - * - * @param null|string $storeId - * - * @return bool - */ - private function isCategoryProductURLsConfig($storeId) - { - return $this->scopeConfig->isSetFlag( - HelperProduct::XML_PATH_PRODUCT_URL_USE_CATEGORY, - ScopeInterface::SCOPE_STORE, - $storeId - ); - } } diff --git a/app/code/Magento/Store/Model/StoreManager.php b/app/code/Magento/Store/Model/StoreManager.php index 0fce3a5217058..c3137150c8081 100644 --- a/app/code/Magento/Store/Model/StoreManager.php +++ b/app/code/Magento/Store/Model/StoreManager.php @@ -69,7 +69,7 @@ class StoreManager implements /** * Default store code * - * @var string + * @var string|int|\Magento\Store\Api\Data\StoreInterface */ protected $currentStoreId = null; diff --git a/app/code/Magento/Store/Model/StoreManagerInterface.php b/app/code/Magento/Store/Model/StoreManagerInterface.php index f440515f23e0b..6f9aed16b39f1 100644 --- a/app/code/Magento/Store/Model/StoreManagerInterface.php +++ b/app/code/Magento/Store/Model/StoreManagerInterface.php @@ -117,7 +117,7 @@ public function getGroups($withDefault = false); /** * Set current default store * - * @param string $store + * @param string|int|\Magento\Store\Api\Data\StoreInterface $store * @return void */ public function setCurrentStore($store); diff --git a/app/code/Magento/Store/Test/Mftf/Data/StoreConfigData.xml b/app/code/Magento/Store/Test/Mftf/Data/StoreConfigData.xml index 4333446925474..9f4de2ffb5723 100644 --- a/app/code/Magento/Store/Test/Mftf/Data/StoreConfigData.xml +++ b/app/code/Magento/Store/Test/Mftf/Data/StoreConfigData.xml @@ -21,4 +21,19 @@ <data key="label">Yes</data> <data key="value">1</data> </entity> + <entity name="MinifyJavaScriptFilesDisableConfigData"> + <!-- Default value --> + <data key="path">dev/js/minify_files</data> + <data key="scope">admin</data> + <data key="scope_id">0</data> + <data key="label">No</data> + <data key="value">0</data> + </entity> + <entity name="MinifyJavaScriptFilesEnableConfigData"> + <data key="path">dev/js/minify_files</data> + <data key="scope">admin</data> + <data key="scope_id">0</data> + <data key="label">Yes</data> + <data key="value">1</data> + </entity> </entities> diff --git a/app/code/Magento/Store/Test/Mftf/Test/StorefrontAddStoreCodeInUrlTest.xml b/app/code/Magento/Store/Test/Mftf/Test/StorefrontAddStoreCodeInUrlTest.xml index 95f5a9cd2d669..eaebc7fdaf74a 100644 --- a/app/code/Magento/Store/Test/Mftf/Test/StorefrontAddStoreCodeInUrlTest.xml +++ b/app/code/Magento/Store/Test/Mftf/Test/StorefrontAddStoreCodeInUrlTest.xml @@ -11,9 +11,11 @@ <test name="StorefrontAddStoreCodeInUrlTest"> <annotations> <features value="Backend"/> + <stories value="Admin Panel URL with Store Code"/> <title value="Store code should be added to storefront URL if the corresponding configuration is enabled"/> <description value="Store code should be added to storefront URL if the corresponding configuration is enabled"/> - <testCaseId value="MC-15944" /> + <testCaseId value="MC-15944"/> + <severity value="CRITICAL"/> <group value="store"/> <group value="mtf_migrated"/> </annotations> diff --git a/app/code/Magento/Swatches/Block/Product/Renderer/Configurable.php b/app/code/Magento/Swatches/Block/Product/Renderer/Configurable.php index 6143b8e659059..0848f566f67bb 100644 --- a/app/code/Magento/Swatches/Block/Product/Renderer/Configurable.php +++ b/app/code/Magento/Swatches/Block/Product/Renderer/Configurable.php @@ -182,6 +182,9 @@ public function getJsonSwatchConfig() $attributeDataArray ); } + if (isset($attributeDataArray['additional_data'])) { + $config[$attributeId]['additional_data'] = $attributeDataArray['additional_data']; + } } return $this->jsonEncoder->encode($config); @@ -189,6 +192,7 @@ public function getJsonSwatchConfig() /** * Get number of swatches from config to show on product listing. + * * Other swatches can be shown after click button 'Show more' * * @return string @@ -228,6 +232,8 @@ public function getProduct() } /** + * Get swatch attributes data. + * * @return array */ protected function getSwatchAttributesData() @@ -236,6 +242,8 @@ protected function getSwatchAttributesData() } /** + * Init isProductHasSwatchAttribute. + * * @deprecated 100.1.5 Method isProductHasSwatchAttribute() is used instead of this. * * @codeCoverageIgnore @@ -364,6 +372,8 @@ protected function getVariationMedia($attributeCode, $optionId) } /** + * Get swatch product image. + * * @param Product $childProduct * @param string $imageType * @return string @@ -384,6 +394,8 @@ protected function getSwatchProductImage(Product $childProduct, $imageType) } /** + * Check if product have image. + * * @param Product $product * @param string $imageType * @return bool @@ -394,6 +406,8 @@ protected function isProductHasImage(Product $product, $imageType) } /** + * Get configurable options ids. + * * @param array $attributeData * @return array * @since 100.0.3 @@ -453,8 +467,8 @@ protected function getRendererTemplate() } /** + * @inheritDoc * @deprecated 100.1.5 Now is used _toHtml() directly - * @return string */ protected function getHtmlOutput() { @@ -462,6 +476,8 @@ protected function getHtmlOutput() } /** + * Get media callback url. + * * @return string */ public function getMediaCallback() diff --git a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByImageSwatchTest.xml b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByImageSwatchTest.xml index 5347a1a1f870f..b1ae06428c0ab 100644 --- a/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByImageSwatchTest.xml +++ b/app/code/Magento/Swatches/Test/Mftf/Test/StorefrontFilterByImageSwatchTest.xml @@ -41,6 +41,9 @@ <!-- Select visual swatch --> <selectOption selector="{{AttributePropertiesSection.InputType}}" userInput="swatch_visual" stepKey="selectInputType"/> + <!-- Set Update Product Preview Image to Yes--> + <selectOption selector="{{AttributePropertiesSection.UpdateProductPreviewImage}}" userInput="Yes" stepKey="setUpdateProductPreviewImage"/> + <!-- This hack is because the same <input type="file"> is re-purposed used for all uploads. --> <executeJS function="HTMLInputElement.prototype.click = function() { if(this.type !== 'file') HTMLElement.prototype.click.call(this); };" stepKey="disableClick"/> diff --git a/app/code/Magento/Swatches/view/adminhtml/templates/catalog/product/attribute/js.phtml b/app/code/Magento/Swatches/view/adminhtml/templates/catalog/product/attribute/js.phtml index e63e9f4b2adad..7e62814556ba9 100644 --- a/app/code/Magento/Swatches/view/adminhtml/templates/catalog/product/attribute/js.phtml +++ b/app/code/Magento/Swatches/view/adminhtml/templates/catalog/product/attribute/js.phtml @@ -4,14 +4,13 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - +// phpcs:disable Magento2.Templates.ThisInTemplate ?> <script type="text/x-magento-init"> { "*": { "swatchesProductAttributes": { - "hiddenFields": <?= /* @escapeNotVerified */ json_encode($this->helper('Magento\Catalog\Helper\Data')->getAttributeHiddenFields()) ?> + "hiddenFields": <?= /* @noEscape */ json_encode($this->helper(Magento\Catalog\Helper\Data::class)->getAttributeHiddenFields()) ?> } } } diff --git a/app/code/Magento/Swatches/view/adminhtml/templates/catalog/product/attribute/text.phtml b/app/code/Magento/Swatches/view/adminhtml/templates/catalog/product/attribute/text.phtml index e00c41d371c9e..d436dc8d1ef3a 100644 --- a/app/code/Magento/Swatches/view/adminhtml/templates/catalog/product/attribute/text.phtml +++ b/app/code/Magento/Swatches/view/adminhtml/templates/catalog/product/attribute/text.phtml @@ -4,8 +4,6 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - /** @var $block \Magento\Swatches\Block\Adminhtml\Attribute\Edit\Options\Text */ $stores = $block->getStoresSortedBySortOrder(); @@ -20,9 +18,8 @@ $stores = $block->getStoresSortedBySortOrder(); <tr id="swatch-text-options-table"> <th class="col-draggable"></th> <th class="col-default"><span><?= $block->escapeHtml(__('Is Default')) ?></span></th> - <?php foreach ($stores as $_store): ?> - <th class="col-swatch col-swatch-min-width col-<%- data.id %> - <?php if ($_store->getId() == \Magento\Store\Model\Store::DEFAULT_STORE_ID): ?> _required<?php endif; ?>" + <?php foreach ($stores as $_store) : ?> + <th class="col-swatch col-swatch-min-width col-<%- data.id %><?= ($_store->getId() == \Magento\Store\Model\Store::DEFAULT_STORE_ID) ? ' _required' : '' ?>" colspan="2"> <span><?= $block->escapeHtml($_store->getName()) ?></span> </th> @@ -41,7 +38,7 @@ $stores = $block->getStoresSortedBySortOrder(); </tr> <tr> <th colspan="<?= (int)$colTotal ?>" class="col-actions-add"> - <?php if (!$block->getReadOnly() && !$block->canManageOptionDefaultOnly()):?> + <?php if (!$block->getReadOnly() && !$block->canManageOptionDefaultOnly()) : ?> <button id="add_new_swatch_text_option_button" title="<?= $block->escapeHtml(__('Add Swatch')) ?>" type="button" class="action- scalable add"> @@ -57,44 +54,44 @@ $stores = $block->getStoresSortedBySortOrder(); <script id="swatch-text-row-template" type="text/x-magento-template"> <tr> <td class="col-draggable"> - <?php if (!$block->getReadOnly() && !$block->canManageOptionDefaultOnly()): ?> + <?php if (!$block->getReadOnly() && !$block->canManageOptionDefaultOnly()) : ?> <div data-role="draggable-handle" class="draggable-handle" - title="<?= $block->escapeHtml(__('Sort Option')) ?>"></div> + title="<?= $block->escapeHtmlAttr(__('Sort Option')) ?>"></div> <?php endif; ?> <input data-role="order" type="hidden" name="optiontext[order][<%- data.id %>]" value="<%- data.sort_order %>" - <?php if ($block->getReadOnly() || $block->canManageOptionDefaultOnly()): ?> disabled="disabled"<?php endif; ?> + <?= ($block->getReadOnly() || $block->canManageOptionDefaultOnly()) ? ' disabled="disabled"' : '' ?> /> </td> <td class="col-default"> <input class="input-radio" type="<%- data.intype %>" name="defaulttext[]" - value="<%- data.id %>" <%- data.checked %><?php if ($block->getReadOnly()):?>disabled="disabled"<?php endif;?>/> + value="<%- data.id %>" <%- data.checked %><?= ($block->getReadOnly()) ? ' disabled="disabled"' : '' ?>/> </td> - <?php foreach ($stores as $_store): ?> + <?php foreach ($stores as $_store) : ?> <?php $storeId = (int)$_store->getId(); ?> <td class="col-swatch col-swatch-min-width col-<%- data.id %>"> <input class="input-text swatch-text-field-<?= /* @noEscape */ $storeId ?> - <?php if ($storeId == \Magento\Store\Model\Store::DEFAULT_STORE_ID): ?> required-option required-unique<?php endif; ?>" + <?= ($storeId == \Magento\Store\Model\Store::DEFAULT_STORE_ID) ? ' required-option required-unique' : '' ?>" name="swatchtext[value][<%- data.id %>][<?= /* @noEscape */ $storeId ?>]" type="text" value="<%- data.swatch<?= /* @noEscape */ $storeId ?> %>" - placeholder="<?= $block->escapeHtml(__("Swatch")) ?>"/> + placeholder="<?= $block->escapeHtmlAttr(__("Swatch")) ?>"/> </td> <td class="col-swatch-min-width swatch-col-<%- data.id %>"> <input name="optiontext[value][<%- data.id %>][<?= /* @noEscape */ $storeId ?>]" value="<%- data.store<?= /* @noEscape */ $storeId ?> %>" - class="input-text<?php if ($storeId == \Magento\Store\Model\Store::DEFAULT_STORE_ID): ?> required-option<?php endif; ?>" - type="text" <?php if ($block->getReadOnly() || $block->canManageOptionDefaultOnly()):?> disabled="disabled"<?php endif;?> - placeholder="<?= $block->escapeHtml(__("Description")) ?>"/> + class="input-text<?= ($storeId == \Magento\Store\Model\Store::DEFAULT_STORE_ID) ? ' required-option' : '' ?>" + type="text" <?= ($block->getReadOnly() || $block->canManageOptionDefaultOnly()) ? ' disabled="disabled"' : ''?> + placeholder="<?= $block->escapeHtmlAttr(__("Description")) ?>"/> </td> <?php endforeach; ?> <td id="delete_button_swatch_container_<%- data.id %>" class="col-delete"> <input type="hidden" class="delete-flag" name="optiontext[delete][<%- data.id %>]" value="" /> - <?php if (!$block->getReadOnly() && !$block->canManageOptionDefaultOnly()):?> - <button title="<?= $block->escapeHtml(__('Delete')) ?>" type="button" + <?php if (!$block->getReadOnly() && !$block->canManageOptionDefaultOnly()) : ?> + <button title="<?= $block->escapeHtmlAttr(__('Delete')) ?>" type="button" class="action- scalable delete delete-option"> <span><?= $block->escapeHtml(__('Delete')) ?></span> </button> @@ -105,10 +102,10 @@ $stores = $block->getStoresSortedBySortOrder(); <script type="text/x-magento-init"> { "*": { - "Magento_Swatches/js/text": <?= /* @escapeNotVerified */ $block->getJsonConfig() ?> , + "Magento_Swatches/js/text": <?= /* @noEscape */ $block->getJsonConfig() ?> , "Magento_Catalog/catalog/product/attribute/unique-validate": { "element": "required-text-swatch-unique", - "message": "<?= $block->escapeHtml(__("The value of Admin must be unique.")) ?>" + "message": "<?= $block->escapeJs($block->escapeHtml(__("The value of Admin must be unique."))) ?>" } } } diff --git a/app/code/Magento/Swatches/view/adminhtml/templates/catalog/product/attribute/visual.phtml b/app/code/Magento/Swatches/view/adminhtml/templates/catalog/product/attribute/visual.phtml index 5b971d9a99a3b..b7b1ab5fc5323 100644 --- a/app/code/Magento/Swatches/view/adminhtml/templates/catalog/product/attribute/visual.phtml +++ b/app/code/Magento/Swatches/view/adminhtml/templates/catalog/product/attribute/visual.phtml @@ -4,15 +4,13 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - /** @var $block \Magento\Swatches\Block\Adminhtml\Attribute\Edit\Options\Visual */ $stores = $block->getStoresSortedBySortOrder(); ?> <fieldset class="admin__fieldset fieldset"> <legend class="legend"> - <span><?= $block->escapeHtml( __('Manage Swatch (Values of Your Attribute)')) ?></span> + <span><?= $block->escapeHtml(__('Manage Swatch (Values of Your Attribute)')) ?></span> </legend><br /> <div class="admin__control-table-wrapper" id="swatch-visual-options-panel"> <table class="data-table clearfix" cellspacing="0"> @@ -21,13 +19,12 @@ $stores = $block->getStoresSortedBySortOrder(); <th class="col-draggable"></th> <th class="col-default"><span><?= $block->escapeHtml(__('Is Default')) ?></span></th> <th><span><?= $block->escapeHtml(__('Swatch')) ?></span></th> - <?php foreach ($stores as $_store): ?> - <th<?php if ($_store->getId() == \Magento\Store\Model\Store::DEFAULT_STORE_ID): ?> class="_required"<?php endif; ?>> + <?php foreach ($stores as $_store) : ?> + <th<?= ($_store->getId() == \Magento\Store\Model\Store::DEFAULT_STORE_ID) ? ' class="_required"' : '' ?> <span><?= $block->escapeHtml($_store->getName()) ?></span> </th> - <?php endforeach; - $colTotal = count($stores) * 2 + 3; - ?> + <?php endforeach; ?> + <?php $colTotal = count($stores) * 2 + 3; ?> <th class="col-delete"> </th> </tr> </thead> @@ -41,7 +38,7 @@ $stores = $block->getStoresSortedBySortOrder(); </tr> <tr> <th colspan="<?= (int)$colTotal ?>" class="col-actions-add"> - <?php if (!$block->getReadOnly() && !$block->canManageOptionDefaultOnly()): ?> + <?php if (!$block->getReadOnly() && !$block->canManageOptionDefaultOnly()) : ?> <button id="add_new_swatch_visual_option_button" title="<?= $block->escapeHtml(__('Add Swatch')) ?>" type="button" class="action- scalable add"> @@ -57,14 +54,14 @@ $stores = $block->getStoresSortedBySortOrder(); <script id="swatch-visual-row-template" type="text/x-magento-template"> <tr> <td class="col-draggable"> - <?php if (!$block->getReadOnly() && !$block->canManageOptionDefaultOnly()): ?> + <?php if (!$block->getReadOnly() && !$block->canManageOptionDefaultOnly()) : ?> <div data-role="draggable-handle" class="draggable-handle" title="<?= $block->escapeHtml(__('Sort Option')) ?>"></div> <?php endif; ?> - <input data-role="order" type="hidden" name="optionvisual[order][<%- data.id %>]" value="<%- data.sort_order %>" <?php if ($block->getReadOnly() || $block->canManageOptionDefaultOnly()): ?> disabled="disabled"<?php endif; ?>/> + <input data-role="order" type="hidden" name="optionvisual[order][<%- data.id %>]" value="<%- data.sort_order %>" <?= ($block->getReadOnly() || $block->canManageOptionDefaultOnly()) ? ' disabled="disabled"' : '' ?>/> </td> <td class="col-default"> - <input class="input-radio" type="<%- data.intype %>" name="defaultvisual[]" value="<%- data.id %>" <%- data.checked %><?php if ($block->getReadOnly()):?>disabled="disabled"<?php endif;?>/> + <input class="input-radio" type="<%- data.intype %>" name="defaultvisual[]" value="<%- data.id %>" <%- data.checked %><?= ($block->getReadOnly()) ? ' disabled="disabled"' : '' ?>/> </td> <td class="swatches-visual-col col-default <%- data.empty_class %>"> <?php //@todo add logic getting swatch value from db */ ?> @@ -88,17 +85,17 @@ $stores = $block->getStoresSortedBySortOrder(); </div> </div> </td> - <?php foreach ($stores as $_store): ?> + <?php foreach ($stores as $_store) : ?> <td class="swatch-col-<%- data.id %>"> <input name="optionvisual[value][<%- data.id %>][<?= (int)$_store->getId() ?>]" value="<%- data.store<?= (int) $_store->getId() ?> %>" - class="input-text<?php if ($_store->getId() == \Magento\Store\Model\Store::DEFAULT_STORE_ID): ?> required-option required-unique<?php endif; ?>" - type="text" <?php if ($block->getReadOnly() || $block->canManageOptionDefaultOnly()): ?> disabled="disabled"<?php endif; ?>/> + class="input-text<?= ($_store->getId() == \Magento\Store\Model\Store::DEFAULT_STORE_ID) ? ' required-option required-unique' : '' ?>" + type="text" <?= ($block->getReadOnly() || $block->canManageOptionDefaultOnly()) ? ' disabled="disabled"' : '' ?>/> </td> <?php endforeach; ?> <td id="delete_button_swatch_container_<%- data.id %>" class="col-delete"> <input type="hidden" class="delete-flag" name="optionvisual[delete][<%- data.id %>]" value="" /> - <?php if (!$block->getReadOnly() && !$block->canManageOptionDefaultOnly()):?> + <?php if (!$block->getReadOnly() && !$block->canManageOptionDefaultOnly()) : ?> <button title="<?= $block->escapeHtml(__('Delete')) ?>" type="button" class="action- scalable delete delete-option"> <span><?= $block->escapeHtml(__('Delete')) ?></span> diff --git a/app/code/Magento/Swatches/view/adminhtml/templates/catalog/product/edit/attribute/steps/attributes_values.phtml b/app/code/Magento/Swatches/view/adminhtml/templates/catalog/product/edit/attribute/steps/attributes_values.phtml index 9cbda396b2624..adb13e7f24c34 100644 --- a/app/code/Magento/Swatches/view/adminhtml/templates/catalog/product/edit/attribute/steps/attributes_values.phtml +++ b/app/code/Magento/Swatches/view/adminhtml/templates/catalog/product/edit/attribute/steps/attributes_values.phtml @@ -4,12 +4,10 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - /* @var $block \Magento\ConfigurableProduct\Block\Adminhtml\Product\Steps\AttributeValues */ ?> -<div data-bind="scope: '<?= /* @escapeNotVerified */ $block->getComponentName() ?>'"> - <h2 class="steps-wizard-title"><?= /* @escapeNotVerified */ __('Step 2: Attribute Values') ?></h2> +<div data-bind="scope: '<?= $block->escapeHtmlAttr($block->getComponentName()) ?>'"> + <h2 class="steps-wizard-title"><?= $block->escapeHtml(__('Step 2: Attribute Values')) ?></h2> <div class="steps-wizard-info"> <span><?= $block->escapeHtml(__('Select from the following attribute values for this product. Each unique combination of values creates a unique product SKU.')) ?></span> </div> @@ -25,7 +23,7 @@ <div class="attribute-entity-title" data-bind="text: label"></div> <div class="attribute-options-block"> (<span class="attribute-length" data-bind="text: $data.options().length"></span> - <?= /* @escapeNotVerified */ __('Options') ?>) + <?= $block->escapeHtml(__('Options')) ?>) </div> </div> @@ -34,19 +32,19 @@ class="action-select-all action-tertiary" data-bind="click: $parent.selectAllAttributes" title="<?= $block->escapeHtml(__('Select All')) ?>"> - <span><?= /* @escapeNotVerified */ __('Select All') ?></span> + <span><?= $block->escapeHtml(__('Select All')) ?></span> </button> <button type="button" class="action-deselect-all action-tertiary" data-bind="click: $parent.deSelectAllAttributes" title="<?= $block->escapeHtml(__('Deselect All')) ?>"> - <span><?= /* @escapeNotVerified */ __('Deselect All') ?></span> + <span><?=$block->escapeHtml(__('Deselect All')) ?></span> </button> <button type="button" class="action-remove-all action-tertiary" data-bind="click: $parent.removeAttribute.bind($parent)" title="<?= $block->escapeHtml(__('Remove Attribute')) ?>"> - <span><?= /* @escapeNotVerified */ __('Remove Attribute') ?></span> + <span><?= $block->escapeHtml(__('Remove Attribute')) ?></span> </button> </div> </div> @@ -74,14 +72,14 @@ title="<?= $block->escapeHtml(__('Save Option')) ?>" data-action="save" data-bind="click: $parents[1].saveOption.bind($parent)"> - <span><?= /* @escapeNotVerified */ __('Save Option') ?></span> + <span><?= $block->escapeHtml(__('Save Option')) ?></span> </button> <button type="button" class="action-remove" title="<?= $block->escapeHtml(__('Remove Option')) ?>" data-action="remove" data-bind="click: $parents[1].removeOption.bind($parent)"> - <span><?= /* @escapeNotVerified */ __('Remove Option') ?></span> + <span><?= $block->escapeHtml(__('Remove Option')) ?></span> </button> </div> </li> @@ -91,7 +89,7 @@ type="button" data-action="addOption" data-bind="click: $parent.createOption, visible: canCreateOption"> - <span><?= /* @escapeNotVerified */ __('Create New Value') ?></span> + <span><?= $block->escapeHtml(__('Create New Value')) ?></span> </button> </div> </div> @@ -101,11 +99,11 @@ "*": { "Magento_Ui/js/core/app": { "components": { - "<?= /* @escapeNotVerified */ $block->getComponentName() ?>": { + "<?= $block->escapeJs($block->getComponentName()) ?>": { "component": "Magento_ConfigurableProduct/js/variations/steps/attributes_values", - "appendTo": "<?= /* @escapeNotVerified */ $block->getParentComponentName() ?>", - "optionsUrl": "<?= /* @escapeNotVerified */ $block->getUrl('catalog/product_attribute/getAttributes') ?>", - "createOptionsUrl": "<?= /* @escapeNotVerified */ $block->getUrl('catalog/product_attribute/createOptions') ?>" + "appendTo": "<?= $block->escapeJs($block->getParentComponentName()) ?>", + "optionsUrl": "<?= $block->escapeJs($block->escapeUrl($block->getUrl('catalog/product_attribute/getAttributes'))) ?>", + "createOptionsUrl": "<?= $block->escapeJs($block->escapeUrl($block->getUrl('catalog/product_attribute/createOptions'))) ?>" } } } diff --git a/app/code/Magento/Swatches/view/frontend/templates/product/layered/renderer.phtml b/app/code/Magento/Swatches/view/frontend/templates/product/layered/renderer.phtml index d817000f7bc46..93c19c8068f7b 100644 --- a/app/code/Magento/Swatches/view/frontend/templates/product/layered/renderer.phtml +++ b/app/code/Magento/Swatches/view/frontend/templates/product/layered/renderer.phtml @@ -4,68 +4,69 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile +// phpcs:disable PSR2.ControlStructures.SwitchDeclaration +// phpcs:disable Generic.WhiteSpace.ScopeIndent /** @var $block \Magento\Swatches\Block\LayeredNavigation\RenderLayered */ ?> <?php $swatchData = $block->getSwatchData(); ?> -<div class="swatch-attribute swatch-layered <?= /* @escapeNotVerified */ $swatchData['attribute_code'] ?>" - attribute-code="<?= /* @escapeNotVerified */ $swatchData['attribute_code'] ?>" - attribute-id="<?= /* @escapeNotVerified */ $swatchData['attribute_id'] ?>"> +<div class="swatch-attribute swatch-layered <?= $block->escapeHtmlAttr($swatchData['attribute_code']) ?>" + attribute-code="<?= $block->escapeHtmlAttr($swatchData['attribute_code']) ?>" + attribute-id="<?= $block->escapeHtmlAttr($swatchData['attribute_id']) ?>"> <div class="swatch-attribute-options clearfix"> - <?php foreach ($swatchData['options'] as $option => $label): ?> - <a href="<?= /* @escapeNotVerified */ $label['link'] ?>" - aria-label="<?= /* @escapeNotVerified */ $label['label'] ?>" + <?php foreach ($swatchData['options'] as $option => $label) : ?> + <a href="<?= $block->escapeUrl($label['link']) ?>" + aria-label="<?= $block->escapeHtmlAttr($label['label']) ?>" class="swatch-option-link-layered"> - <?php if (isset($swatchData['swatches'][$option]['type'])): ?> + <?php if (isset($swatchData['swatches'][$option]['type'])) : ?> <?php switch ($swatchData['swatches'][$option]['type']) { case '3': ?> - <div class="swatch-option <?= /* @escapeNotVerified */ $label['custom_style'] ?>" + <div class="swatch-option <?= $block->escapeHtmlAttr($label['custom_style']) ?>" tabindex="-1" option-type="3" - option-id="<?= /* @escapeNotVerified */ $option ?>" - option-label="<?= /* @escapeNotVerified */ $label['label'] ?>" + option-id="<?= $block->escapeHtmlAttr($option) ?>" + option-label="<?= $block->escapeHtmlAttr($label['label']) ?>" option-tooltip-thumb="" option-tooltip-value="" ></div> - <?php break; + <?php break; case '2': ?> <?php $swatchThumbPath = $block->getSwatchPath('swatch_thumb', $swatchData['swatches'][$option]['value']); ?> <?php $swatchImagePath = $block->getSwatchPath('swatch_image', $swatchData['swatches'][$option]['value']); ?> - <div class="swatch-option image <?= /* @escapeNotVerified */ $label['custom_style'] ?>" + <div class="swatch-option image <?= $block->escapeHtmlAttr($label['custom_style']) ?>" tabindex="-1" option-type="2" - option-id="<?= /* @escapeNotVerified */ $option ?>" - option-label="<?= /* @escapeNotVerified */ $label['label'] ?>" - option-tooltip-thumb="<?= /* @escapeNotVerified */ $swatchThumbPath ?>" + option-id="<?= $block->escapeHtmlAttr($option) ?>" + option-label="<?= $block->escapeHtmlAttr($label['label']) ?>" + option-tooltip-thumb="<?= $block->escapeUrl($swatchThumbPath) ?>" option-tooltip-value="" - style="background: url(<?= /* @escapeNotVerified */ $swatchImagePath ?>) no-repeat center; background-size: initial;"></div> - <?php break; + style="background: url(<?= $block->escapeUrl($swatchImagePath) ?>) no-repeat center; background-size: initial;"></div> + <?php break; case '1': ?> - <div class="swatch-option color <?= /* @escapeNotVerified */ $label['custom_style'] ?>" + <div class="swatch-option color <?= $block->escapeHtmlAttr($label['custom_style']) ?>" tabindex="-1" option-type="1" - option-id="<?= /* @escapeNotVerified */ $option ?>" - option-label="<?= /* @escapeNotVerified */ $label['label'] ?>" + option-id="<?= $block->escapeHtmlAttr($option) ?>" + option-label="<?= $block->escapeHtmlAttr($label['label']) ?>" option-tooltip-thumb="" - option-tooltip-value="<?= /* @escapeNotVerified */ $swatchData['swatches'][$option]['value'] ?>" - style="background: <?= /* @escapeNotVerified */ $swatchData['swatches'][$option]['value'] ?> no-repeat center; background-size: initial;"></div> - <?php break; + option-tooltip-value="<?= $block->escapeHtmlAttr($swatchData['swatches'][$option]['value']) ?>" + style="background: <?= $block->escapeHtmlAttr($swatchData['swatches'][$option]['value']) ?> no-repeat center; background-size: initial;"></div> + <?php break; case '0': default: ?> - <div class="swatch-option text <?= /* @escapeNotVerified */ $label['custom_style'] ?>" - tabindex="-1" - option-type="0" - option-id="<?= /* @escapeNotVerified */ $option ?>" - option-label="<?= /* @escapeNotVerified */ $label['label'] ?>" - option-tooltip-thumb="" - option-tooltip-value="" - ><?= /* @escapeNotVerified */ $swatchData['swatches'][$option]['value'] ?></div> - <?php break; + <div class="swatch-option text <?= $block->escapeHtmlAttr($label['custom_style']) ?>" + tabindex="-1" + option-type="0" + option-id="<?= $block->escapeHtmlAttr($option) ?>" + option-label="<?= $block->escapeHtmlAttr($label['label']) ?>" + option-tooltip-thumb="" + option-tooltip-value="" + ><?= $block->escapeHtml($swatchData['swatches'][$option]['value']) ?></div> + <?php break; } ?> <?php endif; ?> </a> @@ -75,7 +76,7 @@ <script> require(["jquery", "jquery/ui", "Magento_Swatches/js/swatch-renderer"], function ($) { - $('.swatch-layered.<?= /* @escapeNotVerified */ $swatchData['attribute_code'] ?>') + $('.swatch-layered.<?= $block->escapeJs($swatchData['attribute_code']) ?>') .find('[option-type="1"], [option-type="2"], [option-type="0"], [option-type="3"]') .SwatchRendererTooltip(); }); diff --git a/app/code/Magento/Swatches/view/frontend/templates/product/listing/renderer.phtml b/app/code/Magento/Swatches/view/frontend/templates/product/listing/renderer.phtml index c30c96fc890f7..777277a15d8cd 100644 --- a/app/code/Magento/Swatches/view/frontend/templates/product/listing/renderer.phtml +++ b/app/code/Magento/Swatches/view/frontend/templates/product/listing/renderer.phtml @@ -8,20 +8,20 @@ /** @var $block \Magento\Swatches\Block\Product\Renderer\Listing\Configurable */ $productId = $block->getProduct()->getId(); ?> -<div class="swatch-opt-<?= /* @escapeNotVerified */ $productId ?>" - data-role="swatch-option-<?= /* @escapeNotVerified */ $productId ?>"></div> +<div class="swatch-opt-<?= $block->escapeHtmlAttr($productId) ?>" + data-role="swatch-option-<?= $block->escapeHtmlAttr($productId) ?>"></div> <script type="text/x-magento-init"> { - "[data-role=swatch-option-<?= /* @escapeNotVerified */ $productId ?>]": { + "[data-role=swatch-option-<?= $block->escapeJs($productId) ?>]": { "Magento_Swatches/js/swatch-renderer": { "selectorProduct": ".product-item-details", "onlySwatches": true, "enableControlLabel": false, - "numberToShow": <?= /* @escapeNotVerified */ $block->getNumberSwatchesPerProduct(); ?>, - "jsonConfig": <?= /* @escapeNotVerified */ $block->getJsonConfig(); ?>, - "jsonSwatchConfig": <?= /* @escapeNotVerified */ $block->getJsonSwatchConfig(); ?>, - "mediaCallback": "<?= /* @escapeNotVerified */ $block->getMediaCallback() ?>", + "numberToShow": <?= $block->escapeJs($block->getNumberSwatchesPerProduct()) ?>, + "jsonConfig": <?= /* @noEscape */ $block->getJsonConfig() ?>, + "jsonSwatchConfig": <?= /* @noEscape */ $block->getJsonSwatchConfig() ?>, + "mediaCallback": "<?= $block->escapeJs($block->escapeUrl($block->getMediaCallback())) ?>", "jsonSwatchImageSizeConfig": <?= /* @noEscape */ $block->getJsonSwatchSizeConfig() ?> } } @@ -30,11 +30,11 @@ $productId = $block->getProduct()->getId(); <script type="text/x-magento-init"> { - "[data-role=priceBox][data-price-box=product-id-<?= /* @escapeNotVerified */ $productId ?>]": { + "[data-role=priceBox][data-price-box=product-id-<?= $block->escapeJs($productId) ?>]": { "priceBox": { "priceConfig": { - "priceFormat": <?= /* @escapeNotVerified */ $block->getPriceFormatJson(); ?>, - "prices": <?= /* @escapeNotVerified */ $block->getPricesJson(); ?> + "priceFormat": <?= /* @noEscape */ $block->getPriceFormatJson(); ?>, + "prices": <?= /* @noEscape */ $block->getPricesJson(); ?> } } } diff --git a/app/code/Magento/Swatches/view/frontend/templates/product/view/renderer.phtml b/app/code/Magento/Swatches/view/frontend/templates/product/view/renderer.phtml index ebf47925434cd..c85a6908413b5 100644 --- a/app/code/Magento/Swatches/view/frontend/templates/product/view/renderer.phtml +++ b/app/code/Magento/Swatches/view/frontend/templates/product/view/renderer.phtml @@ -3,8 +3,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile -//"swatchRenderer": { ?> <?php /** @var $block \Magento\Swatches\Block\Product\Renderer\Configurable */ ?> <div class="swatch-opt" data-role="swatch-options"></div> @@ -13,13 +11,11 @@ { "[data-role=swatch-options]": { "Magento_Swatches/js/swatch-renderer": { - "jsonConfig": <?= /* @escapeNotVerified */ $swatchOptions = $block->getJsonConfig() ?>, - "jsonSwatchConfig": <?php /* @escapeNotVerified */ - echo $swatchOptions = $block->getJsonSwatchConfig(); ?>, - "mediaCallback": "<?= /* @escapeNotVerified */ $block->getMediaCallback() ?>", - "gallerySwitchStrategy": "<?php /* @escapeNotVerified */ echo $block->getVar('gallery_switch_strategy', - 'Magento_ConfigurableProduct') ?: 'replace'; ?>", - "jsonSwatchImageSizeConfig": <?php /* @noEscape */ echo $block->getJsonSwatchSizeConfig() ?> + "jsonConfig": <?= /* @noEscape */ $swatchOptions = $block->getJsonConfig() ?>, + "jsonSwatchConfig": <?= /* @noEscape */ $swatchOptions = $block->getJsonSwatchConfig() ?>, + "mediaCallback": "<?= $block->escapeJs($block->escapeUrl($block->getMediaCallback())) ?>", + "gallerySwitchStrategy": "<?= $block->escapeJs($block->getVar('gallery_switch_strategy', 'Magento_ConfigurableProduct')) ?: 'replace'; ?>", + "jsonSwatchImageSizeConfig": <?= /* @noEscape */ $block->getJsonSwatchSizeConfig() ?> } }, "*" : { diff --git a/app/code/Magento/Swatches/view/frontend/web/js/swatch-renderer.js b/app/code/Magento/Swatches/view/frontend/web/js/swatch-renderer.js index cc77c07ad39e6..6e028ec53c122 100644 --- a/app/code/Magento/Swatches/view/frontend/web/js/swatch-renderer.js +++ b/app/code/Magento/Swatches/view/frontend/web/js/swatch-renderer.js @@ -713,7 +713,8 @@ define([ $wrapper = $this.parents('.' + $widget.options.classes.attributeOptionsWrapper), $label = $parent.find('.' + $widget.options.classes.attributeSelectedOptionLabelClass), attributeId = $parent.attr('attribute-id'), - $input = $parent.find('.' + $widget.options.classes.attributeInput); + $input = $parent.find('.' + $widget.options.classes.attributeInput), + checkAdditionalData = JSON.parse(this.options.jsonSwatchConfig[attributeId]['additional_data']); if ($widget.inProductList) { $input = $widget.productForm.find( @@ -753,7 +754,10 @@ define([ $widget.options.jsonConfig.optionPrices ]); - $widget._loadMedia(); + if (checkAdditionalData['update_product_preview_image'] === '1') { + $widget._loadMedia(); + } + $input.trigger('change'); }, @@ -1254,7 +1258,7 @@ define([ dataMergeStrategy: this.options.gallerySwitchStrategy }); } - + gallery.first(); } else if (justAnImage && justAnImage.img) { context.find('.product-image-photo').attr('src', justAnImage.img); } diff --git a/app/code/Magento/Tax/Block/Adminhtml/Rate/Form.php b/app/code/Magento/Tax/Block/Adminhtml/Rate/Form.php index 450203486f364..f25b2c1cf50c5 100644 --- a/app/code/Magento/Tax/Block/Adminhtml/Rate/Form.php +++ b/app/code/Magento/Tax/Block/Adminhtml/Rate/Form.php @@ -15,6 +15,8 @@ use Magento\Tax\Controller\RegistryConstants; /** + * Tax rate form. + * * @api * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @since 100.0.2 @@ -108,7 +110,7 @@ public function __construct( } /** - * @return void + * @inheritdoc */ protected function _construct() { @@ -117,6 +119,8 @@ protected function _construct() } /** + * Prepare form before rendering HTML. + * * @return $this * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) @@ -130,8 +134,9 @@ protected function _prepareForm() if ($taxRateId) { $taxRateDataObject = $this->_taxRateRepository->get($taxRateId); } + // phpcs:ignore Magento2.CodeAnalysis.EmptyBlock } catch (NoSuchEntityException $e) { - /* tax rate not found */ + //tax rate not found// } $sessionFormValues = (array)$this->_coreRegistry->registry(RegistryConstants::CURRENT_TAX_RATE_FORM_DATA); @@ -176,7 +181,10 @@ protected function _prepareForm() } $legend = $this->getShowLegend() ? __('Tax Rate Information') : ''; - $fieldset = $form->addFieldset('base_fieldset', ['legend' => $legend, 'class' => 'form-inline']); + $fieldset = $form->addFieldset( + 'base_fieldset', + ['legend' => $legend, 'class' => 'admin__scope-old form-inline'] + ); if (isset($formData['tax_calculation_rate_id']) && $formData['tax_calculation_rate_id'] > 0) { $fieldset->addField( diff --git a/app/code/Magento/Tax/Test/Mftf/ActionGroup/AdminCreateTaxRuleActionGroup.xml b/app/code/Magento/Tax/Test/Mftf/ActionGroup/AdminCreateTaxRuleActionGroup.xml new file mode 100644 index 0000000000000..fe24af478ff5e --- /dev/null +++ b/app/code/Magento/Tax/Test/Mftf/ActionGroup/AdminCreateTaxRuleActionGroup.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminCreateTaxRuleActionGroup"> + <arguments> + <argument name="taxRate" type="entity"/> + <argument name="taxRule" type="entity"/> + </arguments> + <!-- Create Tax Rule --> + <amOnPage url="{{AdminTaxRuleGridPage.url}}" stepKey="goToTaxRulePage"/> + <waitForPageLoad stepKey="waitForTaxRatePage"/> + <click selector="{{AdminGridMainControls.add}}" stepKey="addNewTaxRate"/> + <fillField selector="{{AdminTaxRulesSection.ruleName}}" userInput="{{taxRule.code}}" stepKey="fillRuleName"/> + <click selector="{{AdminTaxRulesSection.selectTaxRate(taxRate.code)}}" stepKey="selectTaxRate"/> + <click selector="{{AdminTaxRuleFormSection.additionalSettings}}" stepKey="clickAdditionalSettings"/> + <fillField userInput="{{taxRule.priority}}" selector="{{AdminTaxRuleFormSection.priority}}" stepKey="fillPriority"/> + <fillField userInput="{{taxRule.position}}" selector="{{AdminTaxRuleFormSection.sortOrder}}" stepKey="fillPosition"/> + <waitForLoadingMaskToDisappear stepKey="waitForLoading"/> + <click selector="{{AdminStoresMainActionsSection.saveButton}}" stepKey="clickSave"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Tax/Test/Mftf/Data/TaxRateData.xml b/app/code/Magento/Tax/Test/Mftf/Data/TaxRateData.xml index 4409ea0a21df6..a497a1776b5ee 100644 --- a/app/code/Magento/Tax/Test/Mftf/Data/TaxRateData.xml +++ b/app/code/Magento/Tax/Test/Mftf/Data/TaxRateData.xml @@ -110,4 +110,11 @@ <data key="tax_region_id">51</data> <data key="rate">6</data> </entity> + <entity name="USFullTaxRate" type="taxRate"> + <data key="code" unique="suffix">Tax Rate</data> + <data key="tax_country_id">US</data> + <data key="tax_postcode">*</data> + <data key="zip_is_range">0</data> + <data key="rate">10</data> + </entity> </entities> diff --git a/app/code/Magento/Tax/Test/Mftf/Section/AdminTaxRulesSection.xml b/app/code/Magento/Tax/Test/Mftf/Section/AdminTaxRulesSection.xml index 46d92e30395e0..7f721d4079c27 100644 --- a/app/code/Magento/Tax/Test/Mftf/Section/AdminTaxRulesSection.xml +++ b/app/code/Magento/Tax/Test/Mftf/Section/AdminTaxRulesSection.xml @@ -34,5 +34,6 @@ <element name="popUpDialogOK" type="button" selector="//*[@class='modal-footer']//*[contains(text(),'OK')]"/> <element name="taxRateMultiSelectItems" type="block" selector=".mselect-list-item"/> <element name="taxRateNumber" type="button" selector="//div[@data-ui-id='tax-rate-form-fieldset-element-form-field-tax-rate']//div[@class='mselect-items-wrapper']//label[{{var}}]" parameterized="true"/> + <element name="selectTaxRate" type="input" selector="//span[text()='{{taxCode}}']" parameterized="true"/> </section> </sections> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminDeleteTaxRuleTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminDeleteTaxRuleTest.xml index 72adf7b0dae1e..e446c80669c4b 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/AdminDeleteTaxRuleTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminDeleteTaxRuleTest.xml @@ -17,6 +17,9 @@ <severity value="CRITICAL"/> <group value="tax"/> <group value="mtf_migrated"/> + <skip> + <issueId value="MC-16684"/> + </skip> </annotations> <before> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/AdminUpdateTaxRuleWithFixedZipUtahTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/AdminUpdateTaxRuleWithFixedZipUtahTest.xml index a96a57cbfec55..9c63a362c7e41 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/AdminUpdateTaxRuleWithFixedZipUtahTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/AdminUpdateTaxRuleWithFixedZipUtahTest.xml @@ -17,6 +17,9 @@ <severity value="CRITICAL"/> <group value="tax"/> <group value="mtf_migrated"/> + <skip> + <issueId value="MC-16684"/> + </skip> </annotations> <before> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/CheckCreditMemoTotalsTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/CheckCreditMemoTotalsTest.xml index 83fcfbff6de62..38cc687ff53a4 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/CheckCreditMemoTotalsTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/CheckCreditMemoTotalsTest.xml @@ -18,6 +18,9 @@ <testCaseId value="MAGETWO-95175"/> <group value="creditMemo"/> <group value="tax"/> + <skip> + <issueId value="MC-17140"/> + </skip> </annotations> <before> <!--Create category and product--> diff --git a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest.xml b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest.xml index 05ced7e61b3b7..345777f0aa0b0 100644 --- a/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest.xml +++ b/app/code/Magento/Tax/Test/Mftf/Test/StorefrontTaxQuoteCartTest.xml @@ -17,6 +17,9 @@ <severity value="CRITICAL"/> <testCaseId value="MC-295"/> <group value="Tax"/> + <skip> + <issueId value="MC-16684"/> + </skip> </annotations> <before> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> diff --git a/app/code/Magento/Tax/view/adminhtml/templates/items/price/row.phtml b/app/code/Magento/Tax/view/adminhtml/templates/items/price/row.phtml index 5e914fdc5a558..4d680dc072e52 100644 --- a/app/code/Magento/Tax/view/adminhtml/templates/items/price/row.phtml +++ b/app/code/Magento/Tax/view/adminhtml/templates/items/price/row.phtml @@ -4,30 +4,28 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - +// phpcs:disable Magento2.Templates.ThisInTemplate ?> <?php /** @var \Magento\Tax\Block\Adminhtml\Items\Price\Renderer $block */ $_item = $block->getItem(); ?> - -<?php if ($block->displayBothPrices() || $block->displayPriceExclTax()): ?> +<?php if ($block->displayBothPrices() || $block->displayPriceExclTax()) : ?> <div class="price-excl-tax"> - <?php if ($block->displayBothPrices()): ?> - <span class="label"><?= /* @escapeNotVerified */ __('Excl. Tax') ?>:</span> + <?php if ($block->displayBothPrices()) : ?> + <span class="label"><?= $block->escapeHtml(__('Excl. Tax')) ?>:</span> <?php endif; ?> - <?= /* @escapeNotVerified */ $block->displayPrices($_item->getBaseRowTotal(), $_item->getRowTotal()) ?> + <?= /* @noEscape */ $block->displayPrices($_item->getBaseRowTotal(), $_item->getRowTotal()) ?> </div> <?php endif; ?> -<?php if ($block->displayBothPrices() || $block->displayPriceInclTax()): ?> +<?php if ($block->displayBothPrices() || $block->displayPriceInclTax()) : ?> <div class="price-incl-tax"> - <?php if ($block->displayBothPrices()): ?> - <span class="label"><?= /* @escapeNotVerified */ __('Incl. Tax') ?>:</span> + <?php if ($block->displayBothPrices()) : ?> + <span class="label"><?= $block->escapeHtml(__('Incl. Tax')) ?>:</span> <?php endif; ?> - <?php $_incl = $this->helper('Magento\Checkout\Helper\Data')->getSubtotalInclTax($_item); ?> - <?php $_baseIncl = $this->helper('Magento\Checkout\Helper\Data')->getBaseSubtotalInclTax($_item); ?> - <?= /* @escapeNotVerified */ $block->displayPrices($_baseIncl, $_incl) ?> + <?php $_incl = $this->helper(\Magento\Checkout\Helper\Data::class)->getSubtotalInclTax($_item); ?> + <?php $_baseIncl = $this->helper(\Magento\Checkout\Helper\Data::class)->getBaseSubtotalInclTax($_item); ?> + <?= /* @noEscape */ $block->displayPrices($_baseIncl, $_incl) ?> </div> <?php endif; ?> diff --git a/app/code/Magento/Tax/view/adminhtml/templates/items/price/total.phtml b/app/code/Magento/Tax/view/adminhtml/templates/items/price/total.phtml index 501ec61d34815..f8a32492a566d 100644 --- a/app/code/Magento/Tax/view/adminhtml/templates/items/price/total.phtml +++ b/app/code/Magento/Tax/view/adminhtml/templates/items/price/total.phtml @@ -3,9 +3,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <?php /** @var \Magento\Tax\Block\Adminhtml\Items\Price\Renderer $block */ @@ -13,4 +10,4 @@ $_item = $block->getItem(); ?> -<?= /* @escapeNotVerified */ $block->displayPrices($block->getBaseTotalAmount($_item), $block->getTotalAmount($_item)) ?> +<?= /* @noEscape */ $block->displayPrices($block->getBaseTotalAmount($_item), $block->getTotalAmount($_item)) ?> diff --git a/app/code/Magento/Tax/view/adminhtml/templates/items/price/unit.phtml b/app/code/Magento/Tax/view/adminhtml/templates/items/price/unit.phtml index 82d82192d45e4..817a6264e5eae 100644 --- a/app/code/Magento/Tax/view/adminhtml/templates/items/price/unit.phtml +++ b/app/code/Magento/Tax/view/adminhtml/templates/items/price/unit.phtml @@ -4,8 +4,7 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - +// phpcs:disable Magento2.Templates.ThisInTemplate ?> <?php /** @var \Magento\Tax\Block\Adminhtml\Items\Price\Renderer $block */ @@ -13,24 +12,24 @@ $_item = $block->getItem(); ?> -<?php if ($this->helper('Magento\Tax\Helper\Data')->displaySalesBothPrices() || $this->helper('Magento\Tax\Helper\Data')->displaySalesPriceExclTax()): ?> +<?php if ($this->helper(\Magento\Tax\Helper\Data::class)->displaySalesBothPrices() || $this->helper(\Magento\Tax\Helper\Data::class)->displaySalesPriceExclTax()) : ?> <div class="price-excl-tax"> - <?php if ($this->helper('Magento\Tax\Helper\Data')->displaySalesBothPrices()): ?> - <span class="label"><?= /* @escapeNotVerified */ __('Excl. Tax') ?>:</span> + <?php if ($this->helper(\Magento\Tax\Helper\Data::class)->displaySalesBothPrices()) : ?> + <span class="label"><?= $block->escapeHtml(__('Excl. Tax')) ?>:</span> <?php endif; ?> - <?= /* @escapeNotVerified */ $block->displayPrices($_item->getBasePrice(), $_item->getPrice()) ?> + <?= /* @noEscape */ $block->displayPrices($_item->getBasePrice(), $_item->getPrice()) ?> </div> <?php endif; ?> -<?php if ($this->helper('Magento\Tax\Helper\Data')->displaySalesBothPrices() || $this->helper('Magento\Tax\Helper\Data')->displaySalesPriceInclTax()): ?> +<?php if ($this->helper(\Magento\Tax\Helper\Data::class)->displaySalesBothPrices() || $this->helper(\Magento\Tax\Helper\Data::class)->displaySalesPriceInclTax()) : ?> <div class="price-incl-tax"> - <?php if ($this->helper('Magento\Tax\Helper\Data')->displaySalesBothPrices()): ?> - <span class="label"><?= /* @escapeNotVerified */ __('Incl. Tax') ?>:</span> + <?php if ($this->helper(\Magento\Tax\Helper\Data::class)->displaySalesBothPrices()) : ?> + <span class="label"><?= $block->escapeHtml(__('Incl. Tax')) ?>:</span> <?php endif; ?> - <?php $_incl = $this->helper('Magento\Checkout\Helper\Data')->getPriceInclTax($_item); ?> - <?php $_baseIncl = $this->helper('Magento\Checkout\Helper\Data')->getBasePriceInclTax($_item); ?> + <?php $_incl = $this->helper(\Magento\Checkout\Helper\Data::class)->getPriceInclTax($_item); ?> + <?php $_baseIncl = $this->helper(\Magento\Checkout\Helper\Data::class)->getBasePriceInclTax($_item); ?> - <?= /* @escapeNotVerified */ $block->displayPrices($_baseIncl, $_incl) ?> + <?= /* @noEscape */ $block->displayPrices($_baseIncl, $_incl) ?> </div> <?php endif; ?> diff --git a/app/code/Magento/Tax/view/adminhtml/templates/order/create/items/price/row.phtml b/app/code/Magento/Tax/view/adminhtml/templates/order/create/items/price/row.phtml index 17f7f39529a74..eeaf769542033 100644 --- a/app/code/Magento/Tax/view/adminhtml/templates/order/create/items/price/row.phtml +++ b/app/code/Magento/Tax/view/adminhtml/templates/order/create/items/price/row.phtml @@ -4,8 +4,7 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - +// phpcs:disable Magento2.Templates.ThisInTemplate ?> <?php /** @var \Magento\Tax\Block\Adminhtml\Items\Price\Renderer $block */ @@ -13,17 +12,17 @@ $_item = $block->getItem(); ?> -<?php if ($block->displayPriceExclTax() || $block->displayBothPrices()): ?> - <?php if ($block->displayBothPrices($block->getStore())): ?> - <span class="label"><?= /* @escapeNotVerified */ __('Excl. Tax') ?>:</span> +<?php if ($block->displayPriceExclTax() || $block->displayBothPrices()) : ?> + <?php if ($block->displayBothPrices($block->getStore())) : ?> + <span class="label"><?= $block->escapeHtml(__('Excl. Tax')) ?>:</span> <?php endif; ?> - <?= /* @escapeNotVerified */ $block->formatPrice($_item->getRowTotal()) ?> + <?= /* @noEscape */ $block->formatPrice($_item->getRowTotal()) ?> <?php endif; ?> -<?php if ($block->displayPriceInclTax() || $block->displayBothPrices()): ?> - <?php if ($block->displayBothPrices()): ?> - <br /><span class="label"><?= /* @escapeNotVerified */ __('Incl. Tax') ?>:</span> +<?php if ($block->displayPriceInclTax() || $block->displayBothPrices()) : ?> + <?php if ($block->displayBothPrices()) : ?> + <br /><span class="label"><?= $block->escapeHtml(__('Incl. Tax')) ?>:</span> <?php endif; ?> - <?php $_incl = $this->helper('Magento\Checkout\Helper\Data')->getSubtotalInclTax($_item); ?> - <?= /* @escapeNotVerified */ $block->formatPrice($_incl) ?> + <?php $_incl = $this->helper(\Magento\Checkout\Helper\Data::class)->getSubtotalInclTax($_item); ?> + <?= /* @noEscape */ $block->formatPrice($_incl) ?> <?php endif; ?> diff --git a/app/code/Magento/Tax/view/adminhtml/templates/order/create/items/price/total.phtml b/app/code/Magento/Tax/view/adminhtml/templates/order/create/items/price/total.phtml index 860b4662fd369..462a1a65f97c3 100644 --- a/app/code/Magento/Tax/view/adminhtml/templates/order/create/items/price/total.phtml +++ b/app/code/Magento/Tax/view/adminhtml/templates/order/create/items/price/total.phtml @@ -3,9 +3,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <?php /** @var \Magento\Tax\Block\Adminhtml\Items\Price\Renderer $block */ @@ -13,19 +10,19 @@ $_item = $block->getItem(); ?> -<?php if ($block->displayPriceExclTax() || $block->displayBothPrices()): ?> +<?php if ($block->displayPriceExclTax() || $block->displayBothPrices()) : ?> <?php $_rowTotalWithoutDiscount = $_item->getRowTotal() - $_item->getTotalDiscountAmount(); ?> - <?php if ($block->displayBothPrices()): ?> - <span class="label"><?= /* @escapeNotVerified */ __('Excl. Tax') ?>:</span> + <?php if ($block->displayBothPrices()) : ?> + <span class="label"><?= $block->escapeHtml(__('Excl. Tax')) ?>:</span> <?php endif; ?> - <?= /* @escapeNotVerified */ $block->formatPrice(max(0, $_rowTotalWithoutDiscount)) ?> + <?= /* @noEscape */ $block->formatPrice(max(0, $_rowTotalWithoutDiscount)) ?> <?php endif; ?> -<?php if ($block->displayPriceInclTax() || $block->displayBothPrices()): ?> - <?php if ($block->displayBothPrices($block->getStore())): ?> - <br /><span class="label"><?= /* @escapeNotVerified */ __('Incl. Tax') ?>:</span> +<?php if ($block->displayPriceInclTax() || $block->displayBothPrices()) : ?> + <?php if ($block->displayBothPrices($block->getStore())) : ?> + <br /><span class="label"><?= $block->escapeHtml(__('Incl. Tax')) ?>:</span> <?php endif; ?> <?php $_incl = $block->getTotalAmount($_item); ?> - <?= /* @escapeNotVerified */ $block->formatPrice($_incl) ?> + <?= /* @noEscape */ $block->formatPrice($_incl) ?> <?php endif; ?> diff --git a/app/code/Magento/Tax/view/adminhtml/templates/order/create/items/price/unit.phtml b/app/code/Magento/Tax/view/adminhtml/templates/order/create/items/price/unit.phtml index 2846307979df0..066b6a04fded4 100644 --- a/app/code/Magento/Tax/view/adminhtml/templates/order/create/items/price/unit.phtml +++ b/app/code/Magento/Tax/view/adminhtml/templates/order/create/items/price/unit.phtml @@ -4,8 +4,7 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - +// phpcs:disable Magento2.Templates.ThisInTemplate ?> <?php /** @var \Magento\Tax\Block\Adminhtml\Items\Price\Renderer $block */ @@ -13,18 +12,18 @@ $_item = $block->getItem(); ?> -<?php if ($block->displayPriceExclTax() || $block->displayBothPrices()): ?> - <?php if ($block->displayBothPrices()): ?> - <span class="label"><?= /* @escapeNotVerified */ __('Excl. Tax') ?>:</span> +<?php if ($block->displayPriceExclTax() || $block->displayBothPrices()) : ?> + <?php if ($block->displayBothPrices()) : ?> + <span class="label"><?= $block->escapeHtml(__('Excl. Tax')) ?>:</span> <?php endif; ?> - <?= /* @escapeNotVerified */ $block->formatPrice($_item->getCalculationPrice()) ?> + <?= /* @noEscape */ $block->formatPrice($_item->getCalculationPrice()) ?> <?php endif; ?> -<?php if ($block->displayPriceInclTax() || $block->displayBothPrices()): ?> - <?php if ($block->displayBothPrices()): ?> - <br /><span class="label"><?= /* @escapeNotVerified */ __('Incl. Tax') ?>:</span> +<?php if ($block->displayPriceInclTax() || $block->displayBothPrices()) : ?> + <?php if ($block->displayBothPrices()) : ?> + <br /><span class="label"><?= $block->escapeHtml(__('Incl. Tax')) ?>:</span> <?php endif; ?> - <?php $_incl = $this->helper('Magento\Checkout\Helper\Data')->getPriceInclTax($_item); ?> - <?= /* @escapeNotVerified */ $block->formatPrice($_incl) ?> + <?php $_incl = $this->helper(\Magento\Checkout\Helper\Data::class)->getPriceInclTax($_item); ?> + <?= /* @noEscape */ $block->formatPrice($_incl) ?> <?php endif; ?> diff --git a/app/code/Magento/Tax/view/adminhtml/templates/rate/form.phtml b/app/code/Magento/Tax/view/adminhtml/templates/rate/form.phtml index 30b5e954da5d2..304020c3af279 100644 --- a/app/code/Magento/Tax/view/adminhtml/templates/rate/form.phtml +++ b/app/code/Magento/Tax/view/adminhtml/templates/rate/form.phtml @@ -3,9 +3,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <div class="entry-edit form-inline"> <?= $block->getFormHtml() ?> diff --git a/app/code/Magento/Tax/view/adminhtml/templates/rate/js.phtml b/app/code/Magento/Tax/view/adminhtml/templates/rate/js.phtml index 16c23624c38e6..fec108d53948f 100644 --- a/app/code/Magento/Tax/view/adminhtml/templates/rate/js.phtml +++ b/app/code/Magento/Tax/view/adminhtml/templates/rate/js.phtml @@ -4,8 +4,7 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - +// phpcs:disable Magento2.Templates.ThisInTemplate ?> <script> require([ @@ -13,7 +12,7 @@ require([ "mage/adminhtml/form" ], function(jQuery){ - var updater = new RegionUpdater('tax_country_id', 'tax_region', 'tax_region_id', <?= /* @escapeNotVerified */ $this->helper('Magento\Directory\Helper\Data')->getRegionJson() ?>, 'disable'); + var updater = new RegionUpdater('tax_country_id', 'tax_region', 'tax_region_id', <?= /* @noEscape */ $this->helper(\Magento\Directory\Helper\Data::class)->getRegionJson() ?>, 'disable'); updater.disableRegionValidation(); (function ($) { diff --git a/app/code/Magento/Tax/view/adminhtml/templates/rate/title.phtml b/app/code/Magento/Tax/view/adminhtml/templates/rate/title.phtml index 682968e34479b..7dd6f6cee8c63 100644 --- a/app/code/Magento/Tax/view/adminhtml/templates/rate/title.phtml +++ b/app/code/Magento/Tax/view/adminhtml/templates/rate/title.phtml @@ -3,21 +3,20 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <fieldset id="tax-rate-titles-table" class="admin__fieldset"> <?php $_labels = $block->getTitles() ?> - <?php foreach ($block->getStores() as $_store): ?> + <?php foreach ($block->getStores() as $_store) : ?> <div class="admin__field"> - <label class="admin__field-label"><span><?= /* @escapeNotVerified */ $_store->getName() ?></span></label> + <label class="admin__field-label"> + <span><?= $block->escapeHtml($_store->getName()) ?></span> + </label> <div class="admin__field-control"> <input - class="admin__control-text<?php if ($_store->getId() == 0): ?> required-entry<?php endif; ?>" + class="admin__control-text<?php if ($_store->getId() == 0) : ?> required-entry<?php endif; ?>" type="text" - name="title[<?= /* @escapeNotVerified */ $_store->getId() ?>]" - value="<?= /* @escapeNotVerified */ $_labels[$_store->getId()] ?>" /> + name="title[<?= (int) $_store->getId() ?>]" + value="<?= $block->escapeHtmlAttr($_labels[(int) $_store->getId()]) ?>" /> </div> </div> <?php endforeach; ?> @@ -25,8 +24,8 @@ <div class="messages"> <div class="message message-notice"> <div> - <strong><?= /* @escapeNotVerified */ __('Note:') ?></strong> - <?= /* @escapeNotVerified */ __('Leave this field empty if you wish to use the tax identifier.') ?> + <strong><?= $block->escapeHtml(__('Note:')) ?></strong> + <?= $block->escapeHtml(__('Leave this field empty if you wish to use the tax identifier.')) ?> </div> </div> </div> diff --git a/app/code/Magento/Tax/view/adminhtml/templates/rule/edit.phtml b/app/code/Magento/Tax/view/adminhtml/templates/rule/edit.phtml index fced077015f4e..81bdd874ead6c 100644 --- a/app/code/Magento/Tax/view/adminhtml/templates/rule/edit.phtml +++ b/app/code/Magento/Tax/view/adminhtml/templates/rule/edit.phtml @@ -4,8 +4,6 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - /** @var $block \Magento\Tax\Block\Adminhtml\Rule\Edit\Form */ ?> <script> @@ -78,7 +76,7 @@ require([ $.ajax({ type: "POST", data: {id:id}, - url: '<?= /* @escapeNotVerified */ $block->getTaxRateLoadUrl() ?>', + url: '<?= $block->escapeJs($block->escapeUrl($block->getTaxRateLoadUrl())) ?>', success: function(result, status) { $('body').trigger('processStop'); if (result.success) { @@ -95,14 +93,14 @@ require([ }); else alert({ - content: '<?= /* @escapeNotVerified */ __('An error occurred') ?>' + content: '<?= $block->escapeJs($block->escapeHtml(__('An error occurred'))) ?>' }); } }, error: function () { $('body').trigger('processStop'); alert({ - content: '<?= /* @escapeNotVerified */ __('An error occurred') ?>' + content: '<?= $block->escapeJs($block->escapeHtml(__('An error occurred'))) ?>' }); }, dataType: "json" @@ -113,9 +111,9 @@ require([ var options = { mselectContainer: '#tax_rate + section.mselect-list', toggleAddButton:false, - addText: '<?= /* @escapeNotVerified */ __('Add New Tax Rate') ?>', + addText: '<?= $block->escapeJs($block->escapeHtml(__('Add New Tax Rate'))) ?>', parse: null, - nextPageUrl: '<?php echo $block->escapeHtml($block->getTaxRatesPageUrl())?>', + nextPageUrl: '<?= $block->escapeHtml($block->getTaxRatesPageUrl()) ?>', selectedValues: this.settings.selected_values, mselectInputSubmitCallback: function (value, options) { var select = $('#tax_rate'); @@ -138,7 +136,7 @@ require([ var taxRate = $('#tax_rate'), taxRateField = taxRate.parent(), taxRateForm = $('#tax-rate-form'), - taxRateFormElement = $('#<?= /* @escapeNotVerified */ \Magento\Tax\Block\Adminhtml\Rate\Form::FORM_ELEMENT_ID ?>'); + taxRateFormElement = $('#<?= /* @noEscape */ \Magento\Tax\Block\Adminhtml\Rate\Form::FORM_ELEMENT_ID ?>'); if (!this.isEntityEditable) { // Override default layout of editable multiselect @@ -162,7 +160,7 @@ require([ taxRateField.find('.mselect-list') .on('click.mselect-edit', '.mselect-edit', this.edit) .on("click.mselect-delete", ".mselect-delete", function () { - if (!confirm('<?= /* @escapeNotVerified */ __('Do you really want to delete this tax rate?') ?>')) { + if (!confirm('<?= $block->escapeJs(__('Do you really want to delete this tax rate?')) ?>')) { return; } @@ -178,7 +176,7 @@ require([ form_key: $('input[name="form_key"]').val() }, dataType: 'json', - url: '<?= /* @escapeNotVerified */ $block->getTaxRateDeleteUrl() ?>', + url: '<?= $block->escapeJs($block->escapeUrl($block->getTaxRateDeleteUrl())) ?>', success: function(result, status) { $('body').trigger('processStop'); if (result.success) { @@ -196,14 +194,14 @@ require([ }); else alert({ - content: '<?= /* @escapeNotVerified */ __('An error occurred') ?>' + content: '<?= $block->escapeJs($block->escapeHtml(__('An error occurred'))) ?>' }); } }, error: function () { $('body').trigger('processStop'); alert({ - content: '<?= /* @escapeNotVerified */ __('An error occurred') ?>' + content: '<?= $block->escapeJs($block->escapeHtml(__('An error occurred'))) ?>' }); } }; @@ -224,15 +222,15 @@ require([ taxRateFormElement.mage('form').mage('validation'); taxRateForm.dialogRates({ - title: '<?= /* @escapeNotVerified */ __('Tax Rate') ?>', + title: '<?= $block->escapeJs($block->escapeHtml(__('Tax Rate'))) ?>', type: 'slide', - id: '<?= /* @escapeNotVerified */ $block->getJsId() ?>', + id: '<?= /* @noEscape */ $block->getJsId() ?>', modalClass: 'tax-rate-popup', closed: function () { taxRateFormElement.data('validation').clearError(); }, buttons: [{ - text: '<?= /* @escapeNotVerified */ __('Save') ?>', + text: '<?= $block->escapeJs($block->escapeHtml(__('Save'))) ?>', 'class': 'action-save action-primary', click: function() { this.updateItemRate(); @@ -252,7 +250,7 @@ require([ type: 'POST', data: itemRateData, dataType: 'json', - url: '<?= /* @escapeNotVerified */ $block->getTaxRateSaveUrl() ?>', + url: '<?= $block->escapeJs($block->escapeUrl($block->getTaxRateSaveUrl())) ?>', success: function(result, status) { $('body').trigger('processStop'); if (result.success) { @@ -277,14 +275,14 @@ require([ }); else alert({ - content: '<?= /* @escapeNotVerified */ __('An error occurred') ?>' + content: '<?= $block->escapeJs($block->escapeHtml(__('An error occurred'))) ?>' }); } }, error: function () { $('body').trigger('processStop'); alert({ - content: '<?= /* @escapeNotVerified */ __('An error occurred') ?>' + content: '<?= $block->escapeJs($block->escapeHtml(__('An error occurred'))) ?>' }); } }; diff --git a/app/code/Magento/Tax/view/adminhtml/templates/rule/rate/form.phtml b/app/code/Magento/Tax/view/adminhtml/templates/rule/rate/form.phtml index c0d185c36b0d6..f09af05303f36 100644 --- a/app/code/Magento/Tax/view/adminhtml/templates/rule/rate/form.phtml +++ b/app/code/Magento/Tax/view/adminhtml/templates/rule/rate/form.phtml @@ -10,8 +10,7 @@ <div class="grid-loader"></div> </div> -<div class="form-inline" id="<?= /* @escapeNotVerified */ $block->getNameInLayout() ?>" style="display:none"> +<div class="form-inline" id="<?= $block->escapeHtmlAttr($block->getNameInLayout()) ?>" style="display:none"> <?= $block->getFormHtml() ?> <?= $block->getChildHtml('form_after') ?> </div> - diff --git a/app/code/Magento/Tax/view/adminhtml/templates/toolbar/class/add.phtml b/app/code/Magento/Tax/view/adminhtml/templates/toolbar/class/add.phtml index ed7e543989e78..a674424f723ca 100644 --- a/app/code/Magento/Tax/view/adminhtml/templates/toolbar/class/add.phtml +++ b/app/code/Magento/Tax/view/adminhtml/templates/toolbar/class/add.phtml @@ -3,9 +3,11 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +// @deprecated +// @codingStandardsIgnoreFile ?> <div data-mage-init='{"floatingHeader": {}}' class="page-actions"> - <button type="button" onclick="window.location.href='<?= /* @escapeNotVerified */ $createUrl ?>'"> - <?= /* @escapeNotVerified */ __('Add New Class') ?> + <button type="button" onclick="window.location.href='<?= $block->escapeUrl($createUrl) ?>'"> + <?= $block->escapeHtml(__('Add New Class')) ?> </button> </div> diff --git a/app/code/Magento/Tax/view/adminhtml/templates/toolbar/class/save.phtml b/app/code/Magento/Tax/view/adminhtml/templates/toolbar/class/save.phtml index e0255d52e4dc3..cfe5a7d14785a 100644 --- a/app/code/Magento/Tax/view/adminhtml/templates/toolbar/class/save.phtml +++ b/app/code/Magento/Tax/view/adminhtml/templates/toolbar/class/save.phtml @@ -3,22 +3,21 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - +// @deprecated // @codingStandardsIgnoreFile - ?> <div data-mage-init='{"floatingHeader": {}}' class="page-actions"> <?= $block->getBackButtonHtml() ?> <?= $block->getResetButtonHtml() ?> <?= $block->getSaveButtonHtml() ?> </div> -<?php if ($form): ?> -<?= $form->toHtml() ?> -<script> -require(['jquery', "mage/mage"], function(jQuery){ +<?php if ($form) : ?> + <?= $form->toHtml() ?> + <script> + require(['jquery', "mage/mage"], function(jQuery){ - jQuery('#<?= /* @escapeNotVerified */ $form->getForm()->getId() ?>').mage('form').mage('validation'); + jQuery('#<?= $block->escapeJs($form->getForm()->getId()) ?>').mage('form').mage('validation'); -}); -</script> + }); + </script> <?php endif; ?> diff --git a/app/code/Magento/Tax/view/adminhtml/templates/toolbar/rate/add.phtml b/app/code/Magento/Tax/view/adminhtml/templates/toolbar/rate/add.phtml index 902c6932f0ae1..c0928f4723b50 100644 --- a/app/code/Magento/Tax/view/adminhtml/templates/toolbar/rate/add.phtml +++ b/app/code/Magento/Tax/view/adminhtml/templates/toolbar/rate/add.phtml @@ -3,8 +3,5 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <?= $block->getChildHtml('grid') ?> diff --git a/app/code/Magento/Tax/view/adminhtml/templates/toolbar/rate/save.phtml b/app/code/Magento/Tax/view/adminhtml/templates/toolbar/rate/save.phtml index 3128b82b69e60..58c79bbfe9715 100644 --- a/app/code/Magento/Tax/view/adminhtml/templates/toolbar/rate/save.phtml +++ b/app/code/Magento/Tax/view/adminhtml/templates/toolbar/rate/save.phtml @@ -3,48 +3,44 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> -<?php if ($form): ?> -<?= $form->toHtml() ?> - -<script> -require([ - "jquery", - "mage/mage" -], function($){ +<?php if ($form) : ?> + <?= $form->toHtml() ?> + <script> + require([ + "jquery", + "mage/mage" + ], function($){ - $('#<?= /* @escapeNotVerified */ $form->getForm()->getId() ?>').mage('form').mage('validation'); + $('#<?= $block->escapeJs($form->getForm()->getId()) ?>').mage('form').mage('validation'); - $(document).ready(function () { - 'use strict'; + $(document).ready(function () { + 'use strict'; - $('.field-zip_from').addClass('ignore-validate'); - $('.field-zip_to').addClass('ignore-validate'); + $('.field-zip_from').addClass('ignore-validate'); + $('.field-zip_to').addClass('ignore-validate'); - $('#zip_is_range').on('change.zipRange', function(){ + $('#zip_is_range').on('change.zipRange', function(){ - var elem = $(this), - zipFrom =$('.field-zip_from'), - zipTo =$('.field-zip_to'), - zipCode =$('.field-tax_postcode'); + var elem = $(this), + zipFrom =$('.field-zip_from'), + zipTo =$('.field-zip_to'), + zipCode =$('.field-tax_postcode'); - if (elem.is(':checked')) { - zipCode.addClass('hidden').addClass('ignore-validate'); - zipFrom.removeClass('hidden').removeClass('ignore-validate'); - zipTo.removeClass('hidden').removeClass('ignore-validate'); + if (elem.is(':checked')) { + zipCode.addClass('hidden').addClass('ignore-validate'); + zipFrom.removeClass('hidden').removeClass('ignore-validate'); + zipTo.removeClass('hidden').removeClass('ignore-validate'); - } else { - zipCode.removeClass('hidden').removeClass('ignore-validate'); - zipFrom.addClass('hidden').addClass('ignore-validate'); - zipTo.addClass('hidden').addClass('ignore-validate'); - } + } else { + zipCode.removeClass('hidden').removeClass('ignore-validate'); + zipFrom.addClass('hidden').addClass('ignore-validate'); + zipTo.addClass('hidden').addClass('ignore-validate'); + } + }); }); - }); -}); -</script> + }); + </script> <?php endif; ?> diff --git a/app/code/Magento/Tax/view/adminhtml/templates/toolbar/rule/add.phtml b/app/code/Magento/Tax/view/adminhtml/templates/toolbar/rule/add.phtml index b3d8a01e1eff1..b0ad610272f80 100644 --- a/app/code/Magento/Tax/view/adminhtml/templates/toolbar/rule/add.phtml +++ b/app/code/Magento/Tax/view/adminhtml/templates/toolbar/rule/add.phtml @@ -3,9 +3,11 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +// @deprecated +// @codingStandardsIgnoreFile ?> <div data-mage-init='{"floatingHeader": {}}' class="page-actions"> - <button type="button" onclick="window.location.href='<?= /* @escapeNotVerified */ $createUrl ?>'"> - <?= /* @escapeNotVerified */ __('Add New Tax Rule') ?> + <button type="button" onclick="window.location.href='<?= $block->escapeUrl($createUrl) ?>'"> + <?= $block->escapeHtml(__('Add New Tax Rule')) ?> </button> </div> diff --git a/app/code/Magento/Tax/view/adminhtml/templates/toolbar/rule/save.phtml b/app/code/Magento/Tax/view/adminhtml/templates/toolbar/rule/save.phtml index 10c0e796ea4f1..7c5de950469ab 100644 --- a/app/code/Magento/Tax/view/adminhtml/templates/toolbar/rule/save.phtml +++ b/app/code/Magento/Tax/view/adminhtml/templates/toolbar/rule/save.phtml @@ -3,9 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - +// @deprecated // @codingStandardsIgnoreFile - ?> <div data-mage-init='{"floatingHeader": {}}' class="page-actions"> <?= $block->getBackButtonHtml() ?> @@ -13,13 +12,13 @@ <?= $block->getSaveButtonHtml() ?> <?= $block->getDeleteButtonHtml() ?> </div> -<?php if ($form): ?> -<?= $form->toHtml() ?> -<script> -require(['jquery', "mage/mage"], function(jQuery){ +<?php if ($form) : ?> + <?= $form->toHtml() ?> + <script> + require(['jquery', "mage/mage"], function(jQuery){ - jQuery('#<?= /* @escapeNotVerified */ $form->getForm()->getId() ?>').mage('form').mage('validation'); + jQuery('#<?= $block->escapeJs($form->getForm()->getId()) ?>').mage('form').mage('validation'); -}); -</script> + }); + </script> <?php endif; ?> diff --git a/app/code/Magento/Tax/view/base/templates/pricing/adjustment.phtml b/app/code/Magento/Tax/view/base/templates/pricing/adjustment.phtml index c6d3d51ff22a8..e87d1c9eb96aa 100644 --- a/app/code/Magento/Tax/view/base/templates/pricing/adjustment.phtml +++ b/app/code/Magento/Tax/view/base/templates/pricing/adjustment.phtml @@ -3,18 +3,15 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <?php /** @var \Magento\Tax\Pricing\Render\Adjustment $block */ ?> -<?php if ($block->displayBothPrices()): ?> - <span id="<?= /* @escapeNotVerified */ $block->buildIdWithPrefix('price-excluding-tax-') ?>" - data-label="<?= $block->escapeHtml(__('Excl. Tax')) ?>" - data-price-amount="<?= /* @escapeNotVerified */ $block->getRawAmount() ?>" +<?php if ($block->displayBothPrices()) : ?> + <span id="<?= $block->escapeHtmlAttr($block->buildIdWithPrefix('price-excluding-tax-')) ?>" + data-label="<?= $block->escapeHtmlAttr(__('Excl. Tax')) ?>" + data-price-amount="<?= /* @noEscape */ $block->getRawAmount() ?>" data-price-type="basePrice" class="price-wrapper price-excluding-tax"> - <span class="price"><?= /* @escapeNotVerified */ $block->getDisplayAmountExclTax() ?></span></span> + <span class="price"><?= /* @noEscape */ $block->getDisplayAmountExclTax() ?></span></span> <?php endif; ?> diff --git a/app/code/Magento/Tax/view/base/templates/pricing/adjustment/bundle.phtml b/app/code/Magento/Tax/view/base/templates/pricing/adjustment/bundle.phtml index 999af81186fae..41430c0fbcfa8 100644 --- a/app/code/Magento/Tax/view/base/templates/pricing/adjustment/bundle.phtml +++ b/app/code/Magento/Tax/view/base/templates/pricing/adjustment/bundle.phtml @@ -3,19 +3,16 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <?php /** @var \Magento\Tax\Pricing\Render\Adjustment $block */ ?> -<?php if ($block->displayPriceIncludingTax()): ?> - <?= /* @escapeNotVerified */ $block->getDisplayAmount() ?> -<?php elseif ($block->displayPriceExcludingTax()): ?> - <?= /* @escapeNotVerified */ $block->getDisplayAmountExclTax() ?> -<?php elseif ($block->displayBothPrices()): ?> - <?= /* @escapeNotVerified */ $block->getDisplayAmount() ?> - <?php if ($block->getDisplayAmountExclTax() !== $block->getDisplayAmount()): ?> - (+<?= /* @escapeNotVerified */ $block->getDisplayAmountExclTax() ?> Excl. Tax) +<?php if ($block->displayPriceIncludingTax()) : ?> + <?= /* @noEscape */ $block->getDisplayAmount() ?> +<?php elseif ($block->displayPriceExcludingTax()) : ?> + <?= /* @noEscape */ $block->getDisplayAmountExclTax() ?> +<?php elseif ($block->displayBothPrices()) : ?> + <?= /* @noEscape */ $block->getDisplayAmount() ?> + <?php if ($block->getDisplayAmountExclTax() !== $block->getDisplayAmount()) : ?> + (+<?= /* @noEscape */ $block->getDisplayAmountExclTax() ?> <?= $block->escapeHtml(__('Excl. Tax')) ?>) <?php endif; ?> <?php endif; ?> diff --git a/app/code/Magento/Tax/view/frontend/templates/checkout/cart/item/price/sidebar.phtml b/app/code/Magento/Tax/view/frontend/templates/checkout/cart/item/price/sidebar.phtml index 9395bd47ca3e7..74db5c24aecd0 100644 --- a/app/code/Magento/Tax/view/frontend/templates/checkout/cart/item/price/sidebar.phtml +++ b/app/code/Magento/Tax/view/frontend/templates/checkout/cart/item/price/sidebar.phtml @@ -4,19 +4,19 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile +// phpcs:disable Magento2.Templates.ThisInTemplate /** @var $block \Magento\Tax\Block\Item\Price\Renderer */ ?> <?php $_item = $block->getItem() ?> - <?php if ($block->displayPriceInclTax() || $block->displayBothPrices()): ?> - <span class="price-wrapper price-including-tax" data-label="<?= /* @escapeNotVerified */ __('Incl. Tax') ?>"> + <?php if ($block->displayPriceInclTax() || $block->displayBothPrices()) : ?> + <span class="price-wrapper price-including-tax" data-label="<?= $block->escapeHtmlAttr(__('Incl. Tax')) ?>"> <?php $_incl = $_item->getPriceInclTax(); ?> - <?= /* @escapeNotVerified */ $this->helper('Magento\Checkout\Helper\Data')->formatPrice($_incl) ?> + <?= /* @noEscape */ $this->helper(\Magento\Checkout\Helper\Data::class)->formatPrice($_incl) ?> </span> <?php endif; ?> - <?php if ($block->displayPriceExclTax() || $block->displayBothPrices()): ?> - <span class="price-wrapper price-excluding-tax" data-label="<?= /* @escapeNotVerified */ __('Excl. Tax') ?>"> - <?= /* @escapeNotVerified */ $this->helper('Magento\Checkout\Helper\Data')->formatPrice($_item->getCalculationPrice()) ?> + <?php if ($block->displayPriceExclTax() || $block->displayBothPrices()) : ?> + <span class="price-wrapper price-excluding-tax" data-label="<?= $block->escapeHtmlAttr(__('Excl. Tax')) ?>"> + <?= /* @noEscape */ $this->helper(\Magento\Checkout\Helper\Data::class)->formatPrice($_item->getCalculationPrice()) ?> </span> <?php endif; ?> diff --git a/app/code/Magento/Tax/view/frontend/templates/checkout/grandtotal.phtml b/app/code/Magento/Tax/view/frontend/templates/checkout/grandtotal.phtml index 5bcbde0e24c4e..df177b6180511 100644 --- a/app/code/Magento/Tax/view/frontend/templates/checkout/grandtotal.phtml +++ b/app/code/Magento/Tax/view/frontend/templates/checkout/grandtotal.phtml @@ -4,37 +4,41 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile +// phpcs:disable Magento2.Templates.ThisInTemplate /** * @var $block \Magento\Tax\Block\Checkout\Grandtotal */ ?> -<?php if ($block->includeTax() && $block->getTotalExclTax() >= 0):?> -<tr class="grand totals excl"> - <th style="<?= /* @escapeNotVerified */ $block->getStyle() ?>" class="mark" colspan="<?= /* @escapeNotVerified */ $block->getColspan() ?>" scope="row"> - <strong><?= /* @escapeNotVerified */ __('Grand Total Excl. Tax') ?></strong> - </th> - <td style="<?= /* @escapeNotVerified */ $block->getStyle() ?>" class="amount" data-th="<?= $block->escapeHtml(__('Grand Total Excl. Tax')) ?>"> - <strong><?= /* @escapeNotVerified */ $this->helper('Magento\Checkout\Helper\Data')->formatPrice($block->getTotalExclTax()) ?></strong> - </td> -</tr> -<?= /* @escapeNotVerified */ $block->renderTotals('taxes', $block->getColspan()) ?> -<tr class="grand totals incl"> - <th style="<?= /* @escapeNotVerified */ $block->getStyle() ?>" class="mark" colspan="<?= /* @escapeNotVerified */ $block->getColspan() ?>" scope="row"> - <strong><?= /* @escapeNotVerified */ __('Grand Total Incl. Tax') ?></strong> - </th> - <td style="<?= /* @escapeNotVerified */ $block->getStyle() ?>" class="amount" data-th="<?= $block->escapeHtml(__('Grand Total Incl. Tax')) ?>"> - <strong><?= /* @escapeNotVerified */ $this->helper('Magento\Checkout\Helper\Data')->formatPrice($block->getTotal()->getValue()) ?></strong> - </td> -</tr> -<?php else:?> -<tr class="grand totals"> - <th style="<?= /* @escapeNotVerified */ $block->getStyle() ?>" class="mark" colspan="<?= /* @escapeNotVerified */ $block->getColspan() ?>" scope="row"> - <strong><?= /* @escapeNotVerified */ $block->getTotal()->getTitle() ?></strong> - </th> - <td style="<?= /* @escapeNotVerified */ $block->getStyle() ?>" class="amount" data-th="<?= $block->escapeHtml($block->getTotal()->getTitle()) ?>"> - <strong><?= /* @escapeNotVerified */ $this->helper('Magento\Checkout\Helper\Data')->formatPrice($block->getTotal()->getValue()) ?></strong> - </td> -</tr> -<?php endif;?> +<?php +$style = $block->escapeHtmlAttr($block->getStyle()); +$colspan = (int) $block->getColspan(); +?> +<?php if ($block->includeTax() && $block->getTotalExclTax() >= 0) : ?> + <tr class="grand totals excl"> + <th style="<?= /* @noEscape */ $style ?>" class="mark" colspan="<?= /* @noEscape */ $colspan ?>" scope="row"> + <strong><?= $block->escapeHtml(__('Grand Total Excl. Tax')) ?></strong> + </th> + <td style="<?= /* @noEscape */ $style ?>" class="amount" data-th="<?= $block->escapeHtmlAttr(__('Grand Total Excl. Tax')) ?>"> + <strong><?= /* @noEscape */ $this->helper(\Magento\Checkout\Helper\Data::class)->formatPrice($block->getTotalExclTax()) ?></strong> + </td> + </tr> + <?= /* @noEscape */ $block->renderTotals('taxes', $colspan) ?> + <tr class="grand totals incl"> + <th style="<?= /* @noEscape */ $style ?>" class="mark" colspan="<?= /* @noEscape */ $colspan ?>" scope="row"> + <strong><?= $block->escapeHtml(__('Grand Total Incl. Tax')) ?></strong> + </th> + <td style="<?= /* @noEscape */ $style ?>" class="amount" data-th="<?= $block->escapeHtmlAttr(__('Grand Total Incl. Tax')) ?>"> + <strong><?= /* @noEscape */ $this->helper(\Magento\Checkout\Helper\Data::class)->formatPrice($block->getTotal()->getValue()) ?></strong> + </td> + </tr> +<?php else : ?> + <tr class="grand totals"> + <th style="<?= /* @noEscape */ $style ?>" class="mark" colspan="<?= /* @noEscape */ $colspan ?>" scope="row"> + <strong><?= $block->escapeHtml($block->getTotal()->getTitle()) ?></strong> + </th> + <td style="<?= /* @noEscape */ $style ?>" class="amount" data-th="<?= $block->escapeHtmlAttr($block->getTotal()->getTitle()) ?>"> + <strong><?= /* @noEscape */ $this->helper(\Magento\Checkout\Helper\Data::class)->formatPrice($block->getTotal()->getValue()) ?></strong> + </td> + </tr> +<?php endif; ?> diff --git a/app/code/Magento/Tax/view/frontend/templates/checkout/shipping.phtml b/app/code/Magento/Tax/view/frontend/templates/checkout/shipping.phtml index 203dd72041296..3f5a55e5fa325 100644 --- a/app/code/Magento/Tax/view/frontend/templates/checkout/shipping.phtml +++ b/app/code/Magento/Tax/view/frontend/templates/checkout/shipping.phtml @@ -4,48 +4,52 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile +// phpcs:disable Magento2.Templates.ThisInTemplate /** * @var $block \Magento\Tax\Block\Checkout\Shipping * @see \Magento\Tax\Block\Checkout\Shipping */ ?> -<?php if ($block->displayShipping()):?> - <?php if ($block->displayBoth()):?> +<?php if ($block->displayShipping()) : ?> + <?php + $style = $block->escapeHtmlAttr($block->getStyle()); + $colspan = (int) $block->getColspan(); + ?> + <?php if ($block->displayBoth()) : ?> <tr class="totals shipping excl"> - <th style="<?= /* @escapeNotVerified */ $block->getStyle() ?>" class="mark" colspan="<?= /* @escapeNotVerified */ $block->getColspan() ?>" scope="row"> - <?= /* @escapeNotVerified */ $block->getExcludeTaxLabel() ?> + <th style="<?= /* @noEscape */ $style ?>" class="mark" colspan="<?= /* @noEscape */ $colspan ?>" scope="row"> + <?= $block->escapeHtml($block->getExcludeTaxLabel()) ?> </th> - <td style="<?= /* @escapeNotVerified */ $block->getStyle() ?>" class="amount" data-th="<?= $block->escapeHtml($block->getExcludeTaxLabel()) ?>"> - <?= /* @escapeNotVerified */ $this->helper('Magento\Checkout\Helper\Data')->formatPrice($block->getShippingExcludeTax()) ?> + <td style="<?= /* @noEscape */ $style ?>" class="amount" data-th="<?= $block->escapeHtmlAttr($block->getExcludeTaxLabel()) ?>"> + <?= /* @noEscape */ $this->helper(\Magento\Checkout\Helper\Data::class)->formatPrice($block->getShippingExcludeTax()) ?> </td> </tr> <tr class="totals shipping incl"> - <th style="<?= /* @escapeNotVerified */ $block->getStyle() ?>" class="mark" colspan="<?= /* @escapeNotVerified */ $block->getColspan() ?>" scope="row"> - <?= /* @escapeNotVerified */ $block->getIncludeTaxLabel() ?> + <th style="<?= /* @noEscape */ $style ?>" class="mark" colspan="<?= /* @noEscape */ $colspan ?>" scope="row"> + <?= $block->escapeHtml($block->getIncludeTaxLabel()) ?> </th> - <td style="<?= /* @escapeNotVerified */ $block->getStyle() ?>" class="amount" data-th="<?= $block->escapeHtml($block->getIncludeTaxLabel()) ?>"> - <?= /* @escapeNotVerified */ $this->helper('Magento\Checkout\Helper\Data')->formatPrice($block->getShippingIncludeTax()) ?> + <td style="<?= /* @noEscape */ $style ?>" class="amount" data-th="<?= $block->escapeHtmlAttr($block->getIncludeTaxLabel()) ?>"> + <?= /* @noEscape */ $this->helper(\Magento\Checkout\Helper\Data::class)->formatPrice($block->getShippingIncludeTax()) ?> </td> </tr> <?php elseif ($block->displayIncludeTax()) : ?> <tr class="totals shipping incl"> - <th style="<?= /* @escapeNotVerified */ $block->getStyle() ?>" class="mark" colspan="<?= /* @escapeNotVerified */ $block->getColspan() ?>" scope="row"> - <?= /* @escapeNotVerified */ $block->getTotal()->getTitle() ?> + <th style="<?= /* @noEscape */ $style ?>" class="mark" colspan="<?= /* @noEscape */ $colspan ?>" scope="row"> + <?= $block->escapeHtml($block->getTotal()->getTitle()) ?> </th> - <td style="<?= /* @escapeNotVerified */ $block->getStyle() ?>" class="amount" data-th="<?= $block->escapeHtml($block->getTotal()->getTitle()) ?>"> - <?= /* @escapeNotVerified */ $this->helper('Magento\Checkout\Helper\Data')->formatPrice($block->getShippingIncludeTax()) ?> + <td style="<?= /* @noEscape */ $style ?>" class="amount" data-th="<?= $block->escapeHtmlAttr($block->getTotal()->getTitle()) ?>"> + <?= /* @noEscape */ $this->helper(\Magento\Checkout\Helper\Data::class)->formatPrice($block->getShippingIncludeTax()) ?> </td> </tr> - <?php else:?> + <?php else : ?> <tr class="totals shipping excl"> - <th style="<?= /* @escapeNotVerified */ $block->getStyle() ?>" class="mark" colspan="<?= /* @escapeNotVerified */ $block->getColspan() ?>" scope="row"> + <th style="<?= /* @noEscape */ $style ?>" class="mark" colspan="<?= /* @noEscape */ $colspan ?>" scope="row"> <?= $block->escapeHtml($block->getTotal()->getTitle()) ?> </th> - <td style="<?= /* @escapeNotVerified */ $block->getStyle() ?>" class="amount" data-th="<?= $block->escapeHtml($block->getTotal()->getTitle()) ?>"> - <?= /* @escapeNotVerified */ $this->helper('Magento\Checkout\Helper\Data')->formatPrice($block->getShippingExcludeTax()) ?> + <td style="<?= /* @noEscape */ $style ?>" class="amount" data-th="<?= $block->escapeHtmlAttr($block->getTotal()->getTitle()) ?>"> + <?= /* @noEscape */ $this->helper(\Magento\Checkout\Helper\Data::class)->formatPrice($block->getShippingExcludeTax()) ?> </td> </tr> - <?php endif;?> + <?php endif; ?> <?php endif; ?> diff --git a/app/code/Magento/Tax/view/frontend/templates/checkout/shipping/price.phtml b/app/code/Magento/Tax/view/frontend/templates/checkout/shipping/price.phtml index 2aedc85221344..6c91f8cec2dce 100644 --- a/app/code/Magento/Tax/view/frontend/templates/checkout/shipping/price.phtml +++ b/app/code/Magento/Tax/view/frontend/templates/checkout/shipping/price.phtml @@ -3,25 +3,22 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <?php /** @var $block \Magento\Tax\Block\Checkout\Shipping\Price */ ?> <?php $_excl = $block->getShippingPriceExclTax(); ?> <?php $_incl = $block->getShippingPriceInclTax(); ?> -<?php if ($block->displayShippingPriceExclTax()): ?> - <span class="price"><?= /* @escapeNotVerified */ $_excl ?></span> -<?php else: ?> -<?php if ($block->displayShippingBothPrices() && $_incl != $_excl): ?> - <span class="price-including-tax" data-label="<?= /* @escapeNotVerified */ __('Incl. Tax') ?>"> -<?php endif; ?> - <span class="price"><?= /* @escapeNotVerified */ $_incl ?></span> -<?php if ($block->displayShippingBothPrices() && $_incl != $_excl): ?> - </span> -<?php endif; ?> +<?php if ($block->displayShippingPriceExclTax()) : ?> + <span class="price"><?= /* @noEscape */ $_excl ?></span> +<?php else : ?> + <?php if ($block->displayShippingBothPrices() && $_incl != $_excl) : ?> + <span class="price-including-tax" data-label="<?= $block->escapeHtmlAttr(__('Incl. Tax')) ?>"> + <?php endif; ?> + <span class="price"><?= /* @noEscape */ $_incl ?></span> + <?php if ($block->displayShippingBothPrices() && $_incl != $_excl) : ?> + </span> + <?php endif; ?> <?php endif; ?> -<?php if ($block->displayShippingBothPrices() && $_incl != $_excl): ?> - <span class="price-excluding-tax" data-label="<?= /* @escapeNotVerified */ __('Excl. Tax') ?>"><span class="price"><?= /* @escapeNotVerified */ $_excl ?></span></span> +<?php if ($block->displayShippingBothPrices() && $_incl != $_excl) : ?> + <span class="price-excluding-tax" data-label="<?= $block->escapeHtmlAttr(__('Excl. Tax')) ?>"><span class="price"><?= /* @noEscape */ $_excl ?></span></span> <?php endif; ?> diff --git a/app/code/Magento/Tax/view/frontend/templates/checkout/subtotal.phtml b/app/code/Magento/Tax/view/frontend/templates/checkout/subtotal.phtml index d295ba7bc7bbc..010a7b8dcfe4a 100644 --- a/app/code/Magento/Tax/view/frontend/templates/checkout/subtotal.phtml +++ b/app/code/Magento/Tax/view/frontend/templates/checkout/subtotal.phtml @@ -4,37 +4,41 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile +// phpcs:disable Magento2.Templates.ThisInTemplate /** * @var $block \Magento\Tax\Block\Checkout\Subtotal * @see \Magento\Tax\Block\Checkout\Subtotal */ ?> -<?php if ($block->displayBoth()):?> +<?php +$style = $block->escapeHtmlAttr($block->getStyle()); +$colspan = (int) $block->getColspan(); +?> +<?php if ($block->displayBoth()) : ?> <tr class="totals sub excl"> - <th style="<?= /* @escapeNotVerified */ $block->getStyle() ?>" class="mark" colspan="<?= /* @escapeNotVerified */ $block->getColspan() ?>" scope="row"> - <?= /* @escapeNotVerified */ __('Subtotal (Excl. Tax)') ?> + <th style="<?= /* @noEscape */ $style ?>" class="mark" colspan="<?= /* @noEscape */ $colspan ?>" scope="row"> + <?= $block->escapeHtml(__('Subtotal (Excl. Tax)')) ?> </th> - <td style="<?= /* @escapeNotVerified */ $block->getStyle() ?>" class="amount" data-th="<?= $block->escapeHtml(__('Subtotal (Excl. Tax)')) ?>"> - <?= /* @escapeNotVerified */ $this->helper('Magento\Checkout\Helper\Data')->formatPrice($block->getTotal()->getValueExclTax()) ?> + <td style="<?= /* @noEscape */ $style ?>" class="amount" data-th="<?= $block->escapeHtmlAttr(__('Subtotal (Excl. Tax)')) ?>"> + <?= /* @noEscape */ $this->helper(\Magento\Checkout\Helper\Data::class)->formatPrice($block->getTotal()->getValueExclTax()) ?> </td> </tr> <tr class="totals sub incl"> - <th style="<?= /* @escapeNotVerified */ $block->getStyle() ?>" class="mark" colspan="<?= /* @escapeNotVerified */ $block->getColspan() ?>" scope="row"> - <?= /* @escapeNotVerified */ __('Subtotal (Incl. Tax)') ?> + <th style="<?= /* @noEscape */ $style ?>" class="mark" colspan="<?= /* @noEscape */ $colspan ?>" scope="row"> + <?= $block->escapeHtml(__('Subtotal (Incl. Tax)')) ?> </th> - <td style="<?= /* @escapeNotVerified */ $block->getStyle() ?>" class="amount" data-th="<?= $block->escapeHtml(__('Subtotal (Incl. Tax)')) ?>"> - <?= /* @escapeNotVerified */ $this->helper('Magento\Checkout\Helper\Data')->formatPrice($block->getTotal()->getValueInclTax()) ?> + <td style="<?= /* @noEscape */ $style ?>" class="amount" data-th="<?= $block->escapeHtmlAttr(__('Subtotal (Incl. Tax)')) ?>"> + <?= /* @noEscape */ $this->helper(\Magento\Checkout\Helper\Data::class)->formatPrice($block->getTotal()->getValueInclTax()) ?> </td> </tr> <?php else : ?> <tr class="totals sub"> - <th style="<?= /* @escapeNotVerified */ $block->getStyle() ?>" class="mark" colspan="<?= /* @escapeNotVerified */ $block->getColspan() ?>" scope="row"> - <?= /* @escapeNotVerified */ $block->getTotal()->getTitle() ?> + <th style="<?= /* @noEscape */ $style ?>" class="mark" colspan="<?= /* @noEscape */ $colspan ?>" scope="row"> + <?= $block->escapeHtml($block->getTotal()->getTitle()) ?> </th> - <td style="<?= /* @escapeNotVerified */ $block->getStyle() ?>" class="amount" data-th="<?= $block->escapeHtml($block->getTotal()->getTitle()) ?>"> - <?= /* @escapeNotVerified */ $this->helper('Magento\Checkout\Helper\Data')->formatPrice($block->getTotal()->getValue()) ?> + <td style="<?= /* @noEscape */ $style ?>" class="amount" data-th="<?= $block->escapeHtmlAttr($block->getTotal()->getTitle()) ?>"> + <?= /* @noEscape */ $this->helper(\Magento\Checkout\Helper\Data::class)->formatPrice($block->getTotal()->getValue()) ?> </td> </tr> -<?php endif;?> +<?php endif; ?> diff --git a/app/code/Magento/Tax/view/frontend/templates/checkout/tax.phtml b/app/code/Magento/Tax/view/frontend/templates/checkout/tax.phtml index 50fa6c425c223..0329db406fa16 100644 --- a/app/code/Magento/Tax/view/frontend/templates/checkout/tax.phtml +++ b/app/code/Magento/Tax/view/frontend/templates/checkout/tax.phtml @@ -4,7 +4,7 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile +// phpcs:disable Magento2.Templates.ThisInTemplate /** * @var $block \Magento\Tax\Block\Checkout\Tax @@ -13,56 +13,52 @@ ?> <?php $_value = $block->getTotal()->getValue(); - $_style = $block->getTotal()->getStyle(); -?> -<?php global $taxIter; $taxIter++; ?> + $_style = $block->escapeHtmlAttr($block->getTotal()->getStyle()); -<?php $attributes = 'class="totals-tax"'; - if ($this->helper('Magento\Tax\Helper\Data')->displayFullSummary() && $_value != 0) { - $attributes = 'class="totals-tax-summary" data-mage-init=\'{"toggleAdvanced": {"selectorsToggleClass": "shown", "baseToggleClass": "expanded", "toggleContainers": ".totals-tax-details"}}\''; - } + +if ($this->helper(\Magento\Tax\Helper\Data::class)->displayFullSummary() && $_value != 0) { + $attributes = 'class="totals-tax-summary" data-mage-init=\'{"toggleAdvanced": {"selectorsToggleClass": "shown", "baseToggleClass": "expanded", "toggleContainers": ".totals-tax-details"}}\''; +} ?> -<tr <?= /* @escapeNotVerified */ $attributes ?>> - <th style="<?= /* @escapeNotVerified */ $_style ?>" class="mark" colspan="<?= /* @escapeNotVerified */ $block->getColspan() ?>" scope="row"> - <?php if ($this->helper('Magento\Tax\Helper\Data')->displayFullSummary()): ?> - <span class="detailed"><?= /* @escapeNotVerified */ $block->getTotal()->getTitle() ?></span> - <?php else: ?> - <?= /* @escapeNotVerified */ $block->getTotal()->getTitle() ?> - <?php endif;?> +<tr <?= /* @noEscape */ $attributes ?>> + <th style="<?= /* @noEscape */ $_style ?>" class="mark" colspan="<?= (int) $block->getColspan() ?>" scope="row"> + <?php if ($this->helper(\Magento\Tax\Helper\Data::class)->displayFullSummary()) : ?> + <span class="detailed"><?= $block->escapeHtml($block->getTotal()->getTitle()) ?></span> + <?php else : ?> + <?= $block->escapeHtml($block->getTotal()->getTitle()) ?> + <?php endif; ?> </th> - <td style="<?= /* @escapeNotVerified */ $_style ?>" class="amount" data-th="<?= $block->escapeHtml($block->getTotal()->getTitle()) ?>"> - <?= /* @escapeNotVerified */ $this->helper('Magento\Checkout\Helper\Data')->formatPrice($_value) ?> + <td style="<?= /* @noEscape */ $_style ?>" class="amount" data-th="<?= $block->escapeHtmlAttr($block->getTotal()->getTitle()) ?>"> + <?= /* @noEscape */ $this->helper(\Magento\Checkout\Helper\Data::class)->formatPrice($_value) ?> </td> </tr> -<?php if ($this->helper('Magento\Tax\Helper\Data')->displayFullSummary() && $_value != 0): ?> - <?php foreach ($block->getTotal()->getFullInfo() as $info): ?> - <?php if (isset($info['hidden']) && $info['hidden']) { - continue; - } ?> +<?php if ($this->helper(\Magento\Tax\Helper\Data::class)->displayFullSummary() && $_value != 0) : ?> + <?php foreach ($block->getTotal()->getFullInfo() as $info) : ?> + <?php if (isset($info['hidden']) && $info['hidden']) { continue; } ?> <?php $percent = $info['percent']; ?> <?php $amount = $info['amount']; ?> <?php $rates = $info['rates']; ?> <?php $isFirst = 1; ?> - <?php foreach ($rates as $rate): ?> - <tr class="totals-tax-details details-<?= /* @escapeNotVerified */ $taxIter ?>"> - <th class="mark" style="<?= /* @escapeNotVerified */ $_style ?>" colspan="<?= /* @escapeNotVerified */ $block->getColspan() ?>" scope="row"> + <?php foreach ($rates as $rate) : ?> + <tr class="totals-tax-details"> + <th class="mark" style="<?= /* @noEscape */ $_style ?>" colspan="<?= (int) $block->getColspan() ?>" scope="row"> <?= $block->escapeHtml($rate['title']) ?> - <?php if (!is_null($rate['percent'])): ?> - (<?= (float)$rate['percent'] ?>%) + <?php if ($rate['percent'] !== null) : ?> + (<?= (float) $rate['percent'] ?>%) <?php endif; ?> </th> - <?php if ($isFirst): ?> - <td style="<?= /* @escapeNotVerified */ $_style ?>" class="amount" rowspan="<?= count($rates) ?>" - data-th="<?= $block->escapeHtml($rate['title']) ?><?php if (!is_null($rate['percent'])): ?>(<?= (float)$rate['percent'] ?>%)<?php endif; ?>"> - <?= /* @escapeNotVerified */ $this->helper('Magento\Checkout\Helper\Data')->formatPrice($amount) ?> + <?php if ($isFirst) : ?> + <td style="<?= /* @noEscape */ $_style ?>" class="amount" rowspan="<?= count($rates) ?>" + data-th="<?= $block->escapeHtmlAttr($rate['title']) ?><?php if ($rate['percent'] !== null) : ?>(<?= (float) $rate['percent'] ?>%)<?php endif; ?>"> + <?= /* @noEscape */ $this->helper(\Magento\Checkout\Helper\Data::class)->formatPrice($amount) ?> </td> <?php endif; ?> </tr> <?php $isFirst = 0; ?> <?php endforeach; ?> <?php endforeach; ?> -<?php endif;?> +<?php endif; ?> diff --git a/app/code/Magento/Tax/view/frontend/templates/email/items/price/row.phtml b/app/code/Magento/Tax/view/frontend/templates/email/items/price/row.phtml index bd6841268d509..991fbf9c5c81f 100644 --- a/app/code/Magento/Tax/view/frontend/templates/email/items/price/row.phtml +++ b/app/code/Magento/Tax/view/frontend/templates/email/items/price/row.phtml @@ -4,8 +4,7 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - +// phpcs:disable Magento2.Templates.ThisInTemplate ?> <?php /** @var \Magento\Tax\Block\Item\Price\Renderer $block */ @@ -15,18 +14,18 @@ $_item = $block->getItem(); $_order = $_item->getOrder(); ?> -<?php if ($block->displayPriceExclTax() || $block->displayBothPrices()): ?> - <?php if ($block->displayBothPrices()): ?> - <span class="label"><?= /* @escapeNotVerified */ __('Excl. Tax') ?>:</span> +<?php if ($block->displayPriceExclTax() || $block->displayBothPrices()) : ?> + <?php if ($block->displayBothPrices()) : ?> + <span class="label"><?= $block->escapeHtml(__('Excl. Tax')) ?>:</span> <?php endif; ?> - <?= /* @escapeNotVerified */ $_order->formatPrice($_item->getRowTotal()) ?> + <?= /* @noEscape */ $_order->formatPrice($_item->getRowTotal()) ?> <?php endif; ?> -<?php if ($block->displayPriceInclTax() || $block->displayBothPrices()): ?> - <?php if ($block->displayBothPrices()): ?> - <br /><span class="label"><?= /* @escapeNotVerified */ __('Incl. Tax') ?>:</span> +<?php if ($block->displayPriceInclTax() || $block->displayBothPrices()) : ?> + <?php if ($block->displayBothPrices()) : ?> + <br /><span class="label"><?= $block->escapeHtml(__('Incl. Tax')) ?>:</span> <?php endif; ?> - <?php $_incl = $this->helper('Magento\Checkout\Helper\Data')->getSubtotalInclTax($_item); ?> - <?= /* @escapeNotVerified */ $_order->formatPrice($_incl) ?> + <?php $_incl = $this->helper(\Magento\Checkout\Helper\Data::class)->getSubtotalInclTax($_item); ?> + <?= /* @noEscape */ $_order->formatPrice($_incl) ?> <?php endif; ?> diff --git a/app/code/Magento/Tax/view/frontend/templates/item/price/row.phtml b/app/code/Magento/Tax/view/frontend/templates/item/price/row.phtml index 856909cbb4240..12aa494f74e7a 100644 --- a/app/code/Magento/Tax/view/frontend/templates/item/price/row.phtml +++ b/app/code/Magento/Tax/view/frontend/templates/item/price/row.phtml @@ -4,24 +4,22 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - /** @var $block \Magento\Tax\Block\Item\Price\Renderer */ $_item = $block->getItem(); ?> -<?php if (($block->displayPriceInclTax() || $block->displayBothPrices()) && !$_item->getNoSubtotal()): ?> - <span class="price-including-tax" data-label="<?= $block->escapeHtml(__('Incl. Tax')) ?>"> +<?php if (($block->displayPriceInclTax() || $block->displayBothPrices()) && !$_item->getNoSubtotal()) : ?> + <span class="price-including-tax" data-label="<?= $block->escapeHtmlAttr(__('Incl. Tax')) ?>"> <span class="cart-price"> - <?= /* @escapeNotVerified */ $block->formatPrice($_item->getRowTotalInclTax()) ?> + <?= /* @noEscape */ $block->formatPrice($_item->getRowTotalInclTax()) ?> </span> </span> <?php endif; ?> -<?php if (($block->displayPriceExclTax() || $block->displayBothPrices()) && !$_item->getNoSubtotal()): ?> - <span class="price-excluding-tax" data-label="<?= $block->escapeHtml(__('Excl. Tax')) ?>"> +<?php if (($block->displayPriceExclTax() || $block->displayBothPrices()) && !$_item->getNoSubtotal()) : ?> + <span class="price-excluding-tax" data-label="<?= $block->escapeHtmlAttr(__('Excl. Tax')) ?>"> <span class="cart-price"> - <?= /* @escapeNotVerified */ $block->formatPrice($_item->getRowTotal()) ?> + <?= /* @noEscape */ $block->formatPrice($_item->getRowTotal()) ?> </span> </span> <?php endif; ?> diff --git a/app/code/Magento/Tax/view/frontend/templates/item/price/total_after_discount.phtml b/app/code/Magento/Tax/view/frontend/templates/item/price/total_after_discount.phtml index f0066d9024139..3356c2514c20f 100644 --- a/app/code/Magento/Tax/view/frontend/templates/item/price/total_after_discount.phtml +++ b/app/code/Magento/Tax/view/frontend/templates/item/price/total_after_discount.phtml @@ -4,10 +4,8 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - /** @var \Magento\Tax\Block\Item\Price\Renderer $block */ $_item = $block->getItem(); ?> <?php $_order = $block->getItem()->getOrderItem()->getOrder() ?> -<?= /* @escapeNotVerified */ $_order->formatPrice($block->getTotalAmount($_item)) ?> +<?= /* @noEscape */ $_order->formatPrice($block->getTotalAmount($_item)) ?> diff --git a/app/code/Magento/Tax/view/frontend/templates/item/price/unit.phtml b/app/code/Magento/Tax/view/frontend/templates/item/price/unit.phtml index 5fd1fed49c33c..e5fd756e2f373 100644 --- a/app/code/Magento/Tax/view/frontend/templates/item/price/unit.phtml +++ b/app/code/Magento/Tax/view/frontend/templates/item/price/unit.phtml @@ -4,26 +4,23 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - /** @var $block \Magento\Tax\Block\Item\Price\Renderer */ $_item = $block->getItem(); ?> - -<?php if ($block->displayPriceInclTax() || $block->displayBothPrices()): ?> - <span class="price-including-tax" data-label="<?= $block->escapeHtml(__('Incl. Tax')) ?>"> +<?php if ($block->displayPriceInclTax() || $block->displayBothPrices()) : ?> + <span class="price-including-tax" data-label="<?= $block->escapeHtmlAttr(__('Incl. Tax')) ?>"> <?php $_incl = $_item->getPriceInclTax(); ?> <span class="cart-price"> - <?= /* @escapeNotVerified */ $block->formatPrice($_incl) ?> + <?= /* @noEscape */ $block->formatPrice($_incl) ?> </span> </span> <?php endif; ?> -<?php if ($block->displayPriceExclTax() || $block->displayBothPrices()): ?> - <span class="price-excluding-tax" data-label="<?= $block->escapeHtml(__('Excl. Tax')) ?>"> +<?php if ($block->displayPriceExclTax() || $block->displayBothPrices()) : ?> + <span class="price-excluding-tax" data-label="<?= $block->escapeHtmlAttr(__('Excl. Tax')) ?>"> <span class="cart-price"> - <?= /* @escapeNotVerified */ $block->formatPrice($block->getItemDisplayPriceExclTax()) ?> + <?= /* @noEscape */ $block->formatPrice($block->getItemDisplayPriceExclTax()) ?> </span> </span> <?php endif; ?> diff --git a/app/code/Magento/Tax/view/frontend/templates/order/tax.phtml b/app/code/Magento/Tax/view/frontend/templates/order/tax.phtml index b329f00973b5a..6115ed5ee9311 100644 --- a/app/code/Magento/Tax/view/frontend/templates/order/tax.phtml +++ b/app/code/Magento/Tax/view/frontend/templates/order/tax.phtml @@ -4,54 +4,55 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - +// phpcs:disable Magento2.Templates.ThisInTemplate +// phpcs:disable Squiz.PHP.GlobalKeyword.NotAllowed ?> <?php $_order = $block->getOrder(); $_source = $block->getSource(); - $_fullInfo = $this->helper('Magento\Tax\Helper\Data')->getCalculatedTaxes($_source); - global $taxIter; $taxIter++; + $_fullInfo = $this->helper(\Magento\Tax\Helper\Data::class)->getCalculatedTaxes($_source); + global $taxIter; + $taxIter++; ?> -<?php if ($_fullInfo && $block->displayFullSummary()): ?> - <?php foreach ($_fullInfo as $info): ?> +<?php if ($_fullInfo && $block->displayFullSummary()) : ?> + <?php foreach ($_fullInfo as $info) : ?> <?php $percent = $info['percent']; $amount = $info['tax_amount']; $baseAmount = $info['base_tax_amount']; $title = $info['title']; ?> - <tr class="totals tax details details-<?= /* @escapeNotVerified */ $taxIter ?> <?= ($block->getIsPlaneMode()) ? ' plane' : '' ?>"> - <td <?= /* @escapeNotVerified */ $block->getLabelProperties() ?>> + <tr class="totals tax details details-<?= (int) $taxIter ?><?= ($block->getIsPlaneMode()) ? ' plane' : '' ?>"> + <td <?= /* @noEscape */ $block->getLabelProperties() ?>> <?= $block->escapeHtml($title) ?> - <?php if (!is_null($percent)): ?> - (<?= (float)$percent ?>%) + <?php if ($percent !== null) : ?> + (<?= (float) $percent ?>%) <?php endif; ?> <br /> </td> - <td <?= /* @escapeNotVerified */ $block->getValueProperties() ?> rowspan="1"> - <?= /* @escapeNotVerified */ $_order->formatPrice($amount) ?> + <td <?= /* @noEscape */ $block->getValueProperties() ?> rowspan="1"> + <?= /* @noEscape */ $_order->formatPrice($amount) ?> </td> </tr> <?php endforeach; ?> -<?php endif;?> +<?php endif; ?> -<?php if ($block->displayFullSummary() && $_fullInfo && !$block->getIsPlaneMode()): ?> +<?php if ($block->displayFullSummary() && $_fullInfo && !$block->getIsPlaneMode()) : ?> <tr class="totals-tax-summary"> -<?php elseif ($block->displayFullSummary() && $_fullInfo && $block->getIsPlaneMode()): ?> +<?php elseif ($block->displayFullSummary() && $_fullInfo && $block->getIsPlaneMode()) : ?> <tr class="totals-tax-summary plane"> -<?php else: ?> +<?php else : ?> <tr class="totals-tax"> <?php endif; ?> - <th <?= /* @escapeNotVerified */ $block->getLabelProperties() ?> scope="row"> - <?php if ($block->displayFullSummary()): ?> - <div class="detailed"><?= /* @escapeNotVerified */ __('Tax') ?></div> - <?php else: ?> - <?= /* @escapeNotVerified */ __('Tax') ?> - <?php endif;?> + <th <?= /* @noEscape */ $block->getLabelProperties() ?> scope="row"> + <?php if ($block->displayFullSummary()) : ?> + <div class="detailed"><?= $block->escapeHtml(__('Tax')) ?></div> + <?php else : ?> + <?= $block->escapeHtml(__('Tax')) ?> + <?php endif; ?> </th> - <td <?= /* @escapeNotVerified */ $block->getValueProperties() ?> data-th="<?= $block->escapeHtml(__('Tax')) ?>"> - <?= /* @escapeNotVerified */ $_order->formatPrice($_source->getTaxAmount()) ?> + <td <?= /* @noEscape */ $block->getValueProperties() ?> data-th="<?= $block->escapeHtmlAttr(__('Tax')) ?>"> + <?= /* @noEscape */ $_order->formatPrice($_source->getTaxAmount()) ?> </td> </tr> diff --git a/app/code/Magento/TaxImportExport/view/adminhtml/templates/importExport.phtml b/app/code/Magento/TaxImportExport/view/adminhtml/templates/importExport.phtml index f1d41e0c969a2..7473612252bb2 100644 --- a/app/code/Magento/TaxImportExport/view/adminhtml/templates/importExport.phtml +++ b/app/code/Magento/TaxImportExport/view/adminhtml/templates/importExport.phtml @@ -4,24 +4,30 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - +/** @var $block \Magento\TaxImportExport\Block\Adminhtml\Rate\ImportExport */ ?> <div class="import-export-tax-rates"> - <?php if (!$block->getIsReadonly()): ?> + <?php if (!$block->getIsReadonly()) :?> <div class="import-tax-rates"> - <?php if ($block->getUseContainer()): ?> - <form id="import-form" class="admin__fieldset" action="<?= /* @escapeNotVerified */ $block->getUrl('tax/rate/importPost') ?>" method="post" enctype="multipart/form-data"> + <?php if ($block->getUseContainer()) :?> + <form id="import-form" + class="admin__fieldset" + action="<?= $block->escapeUrl($block->getUrl('tax/rate/importPost')) ?>" + method="post" + enctype="multipart/form-data"> <?php endif; ?> <?= $block->getBlockHtml('formkey') ?> <div class="fieldset admin__field"> - <label for="import_rates_file" class="admin__field-label"><span><?= /* @escapeNotVerified */ __('Import Tax Rates') ?></span></label> + <label for="import_rates_file" class="admin__field-label"><span><?= $block->escapeHtml(__('Import Tax Rates')) ?></span></label> <div class="admin__field-control"> - <input type="file" id="import_rates_file" name="import_rates_file" class="input-file required-entry"/> + <input type="file" + id="import_rates_file" + name="import_rates_file" + class="input-file required-entry"/> <?= $block->getButtonHtml(__('Import Tax Rates'), '', 'import-submit') ?> </div> </div> - <?php if ($block->getUseContainer()): ?> + <?php if ($block->getUseContainer()) :?> </form> <?php endif; ?> <script> @@ -44,18 +50,22 @@ require(['jquery', "mage/mage", "loadingPopup"], function(jQuery){ </script> </div> <?php endif; ?> - <div class="export-tax-rates <?php if ($block->getIsReadonly()): ?>box-left<?php else: ?>box-right<?php endif; ?>"> - <?php if ($block->getUseContainer()): ?> - <form id="export_form" class="admin__fieldset" action="<?= /* @escapeNotVerified */ $block->getUrl('tax/rate/exportPost') ?>" method="post" enctype="multipart/form-data"> + <div class="export-tax-rates <?= ($block->getIsReadonly()) ? 'box-left' : 'box-right' ?>"> + <?php if ($block->getUseContainer()) :?> + <form id="export_form" + class="admin__fieldset" + action="<?= $block->escapeUrl($block->getUrl('tax/rate/exportPost')) ?>" + method="post" + enctype="multipart/form-data"> <?php endif; ?> <?= $block->getBlockHtml('formkey') ?> <div class="fieldset admin__field"> - <span class="admin__field-label"><span><?= /* @escapeNotVerified */ __('Export Tax Rates') ?></span></span> + <span class="admin__field-label"><span><?= $block->escapeHtml(__('Export Tax Rates')) ?></span></span> <div class="admin__field-control"> <?= $block->getButtonHtml(__('Export Tax Rates'), "this.form.submit()") ?> </div> </div> - <?php if ($block->getUseContainer()): ?> + <?php if ($block->getUseContainer()) :?> </form> <?php endif; ?> </div> diff --git a/app/code/Magento/Theme/Block/Html/Topmenu.php b/app/code/Magento/Theme/Block/Html/Topmenu.php index 242947d19b321..fd8aaa7708cf3 100644 --- a/app/code/Magento/Theme/Block/Html/Topmenu.php +++ b/app/code/Magento/Theme/Block/Html/Topmenu.php @@ -5,9 +5,12 @@ */ namespace Magento\Theme\Block\Html; +use Magento\Backend\Model\Menu; use Magento\Framework\Data\Tree\Node; +use Magento\Framework\Data\Tree\Node\Collection; use Magento\Framework\Data\Tree\NodeFactory; use Magento\Framework\Data\TreeFactory; +use Magento\Framework\DataObject; use Magento\Framework\DataObject\IdentityInterface; use Magento\Framework\View\Element\Template; @@ -29,7 +32,7 @@ class Topmenu extends Template implements IdentityInterface /** * Top menu data tree * - * @var \Magento\Framework\Data\Tree\Node + * @var Node */ protected $_menu; @@ -89,28 +92,35 @@ public function getHtml($outermostClass = '', $childrenWrapClass = '', $limit = $this->getMenu()->setOutermostClass($outermostClass); $this->getMenu()->setChildrenWrapClass($childrenWrapClass); - $html = $this->_getHtml($this->getMenu(), $childrenWrapClass, $limit); + $transportObject = new DataObject( + [ + 'html' => $this->_getHtml( + $this->getMenu(), + $childrenWrapClass, + $limit + ) + ] + ); - $transportObject = new \Magento\Framework\DataObject(['html' => $html]); $this->_eventManager->dispatch( 'page_block_html_topmenu_gethtml_after', ['menu' => $this->getMenu(), 'transportObject' => $transportObject] ); - $html = $transportObject->getHtml(); - return $html; + + return $transportObject->getHtml(); } /** * Count All Subnavigation Items * - * @param \Magento\Backend\Model\Menu $items + * @param Menu $items * @return int */ protected function _countItems($items) { $total = $items->count(); foreach ($items as $item) { - /** @var $item \Magento\Backend\Model\Menu\Item */ + /** @var $item Menu\Item */ if ($item->hasChildren()) { $total += $this->_countItems($item->getChildren()); } @@ -121,7 +131,7 @@ protected function _countItems($items) /** * Building Array with Column Brake Stops * - * @param \Magento\Backend\Model\Menu $items + * @param Menu $items * @param int $limit * @return array|void * @@ -164,7 +174,7 @@ protected function _columnBrake($items, $limit) /** * Add sub menu HTML code for current menu item * - * @param \Magento\Framework\Data\Tree\Node $child + * @param Node $child * @param string $childLevel * @param string $childrenWrapClass * @param int $limit @@ -192,17 +202,14 @@ protected function _addSubMenu($child, $childLevel, $childrenWrapClass, $limit) /** * Recursively generates top menu html from data that is specified in $menuTree * - * @param \Magento\Framework\Data\Tree\Node $menuTree + * @param Node $menuTree * @param string $childrenWrapClass * @param int $limit * @param array $colBrakes * @return string - * - * @SuppressWarnings(PHPMD.CyclomaticComplexity) - * @SuppressWarnings(PHPMD.NPathComplexity) */ protected function _getHtml( - \Magento\Framework\Data\Tree\Node $menuTree, + Node $menuTree, $childrenWrapClass, $limit, array $colBrakes = [] @@ -210,41 +217,31 @@ protected function _getHtml( $html = ''; $children = $menuTree->getChildren(); - $parentLevel = $menuTree->getLevel(); - $childLevel = $parentLevel === null ? 0 : $parentLevel + 1; + $childLevel = $this->getChildLevel($menuTree->getLevel()); + $this->removeChildrenWithoutActiveParent($children, $childLevel); $counter = 1; - $itemPosition = 1; $childrenCount = $children->count(); $parentPositionClass = $menuTree->getPositionClass(); $itemPositionClassPrefix = $parentPositionClass ? $parentPositionClass . '-' : 'nav-'; - /** @var \Magento\Framework\Data\Tree\Node $child */ + /** @var Node $child */ foreach ($children as $child) { - if ($childLevel === 0 && $child->getData('is_parent_active') === false) { - continue; - } $child->setLevel($childLevel); - $child->setIsFirst($counter == 1); - $child->setIsLast($counter == $childrenCount); + $child->setIsFirst($counter === 1); + $child->setIsLast($counter === $childrenCount); $child->setPositionClass($itemPositionClassPrefix . $counter); $outermostClassCode = ''; $outermostClass = $menuTree->getOutermostClass(); - if ($childLevel == 0 && $outermostClass) { + if ($childLevel === 0 && $outermostClass) { $outermostClassCode = ' class="' . $outermostClass . '" '; - $currentClass = $child->getClass(); - - if (empty($currentClass)) { - $child->setClass($outermostClass); - } else { - $child->setClass($currentClass . ' ' . $outermostClass); - } + $this->setCurrentClass($child, $outermostClass); } - if (is_array($colBrakes) && count($colBrakes) && $colBrakes[$counter]['colbrake']) { + if ($this->shouldAddNewColumn($colBrakes, $counter)) { $html .= '</ul></li><li class="column"><ul>'; } @@ -257,11 +254,10 @@ protected function _getHtml( $childrenWrapClass, $limit ) . '</li>'; - $itemPosition++; $counter++; } - if (is_array($colBrakes) && count($colBrakes) && $limit) { + if (is_array($colBrakes) && !empty($colBrakes) && $limit) { $html = '<li class="column"><ul>' . $html . '</ul></li>'; } @@ -271,14 +267,13 @@ protected function _getHtml( /** * Generates string with all attributes that should be present in menu item element * - * @param \Magento\Framework\Data\Tree\Node $item + * @param Node $item * @return string */ - protected function _getRenderedMenuItemAttributes(\Magento\Framework\Data\Tree\Node $item) + protected function _getRenderedMenuItemAttributes(Node $item) { $html = ''; - $attributes = $this->_getMenuItemAttributes($item); - foreach ($attributes as $attributeName => $attributeValue) { + foreach ($this->_getMenuItemAttributes($item) as $attributeName => $attributeValue) { $html .= ' ' . $attributeName . '="' . str_replace('"', '\"', $attributeValue) . '"'; } return $html; @@ -287,27 +282,26 @@ protected function _getRenderedMenuItemAttributes(\Magento\Framework\Data\Tree\N /** * Returns array of menu item's attributes * - * @param \Magento\Framework\Data\Tree\Node $item + * @param Node $item * @return array */ - protected function _getMenuItemAttributes(\Magento\Framework\Data\Tree\Node $item) + protected function _getMenuItemAttributes(Node $item) { - $menuItemClasses = $this->_getMenuItemClasses($item); - return ['class' => implode(' ', $menuItemClasses)]; + return ['class' => implode(' ', $this->_getMenuItemClasses($item))]; } /** * Returns array of menu item's classes * - * @param \Magento\Framework\Data\Tree\Node $item + * @param Node $item * @return array */ - protected function _getMenuItemClasses(\Magento\Framework\Data\Tree\Node $item) + protected function _getMenuItemClasses(Node $item) { - $classes = []; - - $classes[] = 'level' . $item->getLevel(); - $classes[] = $item->getPositionClass(); + $classes = [ + 'level' . $item->getLevel(), + $item->getPositionClass(), + ]; if ($item->getIsCategory()) { $classes[] = 'category-item'; @@ -375,7 +369,7 @@ protected function getCacheTags() /** * Get menu object. * - * Creates \Magento\Framework\Data\Tree\Node root node object. + * Creates Tree root node object. * The creation logic was moved from class constructor into separate method. * * @return Node @@ -394,4 +388,61 @@ public function getMenu() } return $this->_menu; } + + /** + * Remove children from collection when the parent is not active + * + * @param Collection $children + * @param int $childLevel + * @return void + */ + private function removeChildrenWithoutActiveParent(Collection $children, int $childLevel): void + { + /** @var Node $child */ + foreach ($children as $child) { + if ($childLevel === 0 && $child->getData('is_parent_active') === false) { + $children->delete($child); + } + } + } + + /** + * Retrieve child level based on parent level + * + * @param int $parentLevel + * + * @return int + */ + private function getChildLevel($parentLevel): int + { + return $parentLevel === null ? 0 : $parentLevel + 1; + } + + /** + * Check if new column should be added. + * + * @param array $colBrakes + * @param int $counter + * @return bool + */ + private function shouldAddNewColumn(array $colBrakes, int $counter): bool + { + return count($colBrakes) && $colBrakes[$counter]['colbrake']; + } + + /** + * Set current class. + * + * @param Node $child + * @param string $outermostClass + */ + private function setCurrentClass(Node $child, string $outermostClass): void + { + $currentClass = $child->getClass(); + if (empty($currentClass)) { + $child->setClass($outermostClass); + } else { + $child->setClass($currentClass . ' ' . $outermostClass); + } + } } diff --git a/app/code/Magento/Theme/Model/Design/Backend/File.php b/app/code/Magento/Theme/Model/Design/Backend/File.php index 27636b4d574cd..a9aaaedb726d6 100644 --- a/app/code/Magento/Theme/Model/Design/Backend/File.php +++ b/app/code/Magento/Theme/Model/Design/Backend/File.php @@ -21,6 +21,7 @@ use Magento\Framework\UrlInterface; use Magento\MediaStorage\Model\File\UploaderFactory; use Magento\Theme\Model\Design\Config\FileUploader\FileProcessor; +use Magento\MediaStorage\Helper\File\Storage\Database; /** * File Backend @@ -39,6 +40,11 @@ class File extends BackendFile */ private $mime; + /** + * @var Database + */ + private $databaseHelper; + /** * @param Context $context * @param Registry $registry @@ -51,6 +57,7 @@ class File extends BackendFile * @param AbstractResource|null $resource * @param AbstractDb|null $resourceCollection * @param array $data + * @param Database $databaseHelper * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -64,7 +71,8 @@ public function __construct( UrlInterface $urlBuilder, AbstractResource $resource = null, AbstractDb $resourceCollection = null, - array $data = [] + array $data = [], + Database $databaseHelper = null ) { parent::__construct( $context, @@ -79,6 +87,7 @@ public function __construct( $data ); $this->urlBuilder = $urlBuilder; + $this->databaseHelper = $databaseHelper ?: ObjectManager::getInstance()->get(Database::class); } /** @@ -103,7 +112,7 @@ public function beforeSave() $this->setValue($file); return $this; } - + //phpcs:ignore Magento2.Functions.DiscouragedFunction $this->updateMediaDirectory(basename($file), $value['url']); @@ -261,6 +270,10 @@ private function updateMediaDirectory(string $filename, string $url) $mediaPath, $destinationMediaPath ); + $this->databaseHelper->renameFile( + $mediaPath, + $destinationMediaPath + ); } if ($result) { if ($mediaPath === $tmpMediaPath) { diff --git a/app/code/Magento/Theme/Test/Unit/Model/Design/Backend/FileTest.php b/app/code/Magento/Theme/Test/Unit/Model/Design/Backend/FileTest.php index b49b3cb797651..94a6ab0ec565e 100644 --- a/app/code/Magento/Theme/Test/Unit/Model/Design/Backend/FileTest.php +++ b/app/code/Magento/Theme/Test/Unit/Model/Design/Backend/FileTest.php @@ -28,6 +28,11 @@ class FileTest extends \PHPUnit\Framework\TestCase */ private $mime; + /** + * @var \Magento\MediaStorage\Helper\File\Storage\Database|\PHPUnit_Framework_MockObject_MockObject + */ + private $databaseHelper; + public function setUp() { $context = $this->getMockObject(\Magento\Framework\Model\Context::class); @@ -55,6 +60,17 @@ public function setUp() ->disableOriginalConstructor() ->getMock(); + $this->databaseHelper = $this->getMockBuilder(\Magento\MediaStorage\Helper\File\Storage\Database::class) + ->disableOriginalConstructor() + ->getMock(); + + $abstractResource = $this->getMockBuilder(\Magento\Framework\Model\ResourceModel\AbstractResource::class) + ->getMockForAbstractClass(); + + $abstractDb = $this->getMockBuilder(\Magento\Framework\Data\Collection\AbstractDb::class) + ->disableOriginalConstructor() + ->getMockForAbstractClass(); + $this->fileBackend = new File( $context, $registry, @@ -63,7 +79,11 @@ public function setUp() $uploaderFactory, $requestData, $filesystem, - $this->urlBuilder + $this->urlBuilder, + $abstractResource, + $abstractDb, + [], + $this->databaseHelper ); $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); @@ -196,6 +216,11 @@ public function testBeforeSave($fileName) ] ); + $this->databaseHelper->expects($this->once()) + ->method('renameFile') + ->with($expectedTmpMediaPath, '/' . $expectedFileName) + ->willReturn(true); + $this->mediaDirectory->expects($this->once()) ->method('copyFile') ->with($expectedTmpMediaPath, '/' . $expectedFileName) diff --git a/app/code/Magento/Theme/i18n/en_US.csv b/app/code/Magento/Theme/i18n/en_US.csv index c8c586f0bc684..40b219a15e04d 100644 --- a/app/code/Magento/Theme/i18n/en_US.csv +++ b/app/code/Magento/Theme/i18n/en_US.csv @@ -96,6 +96,7 @@ Phrase,Phrase testMessage,testMessage Edit,Edit "We found no files.","We found no files." +"thumbnail","thumbnail" "Browse Files","Browse Files" Scope:,Scope: Remove,Remove diff --git a/app/code/Magento/Theme/view/adminhtml/templates/browser/content.phtml b/app/code/Magento/Theme/view/adminhtml/templates/browser/content.phtml index 4ce0d766ee3f3..b129498d0d42b 100644 --- a/app/code/Magento/Theme/view/adminhtml/templates/browser/content.phtml +++ b/app/code/Magento/Theme/view/adminhtml/templates/browser/content.phtml @@ -3,8 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -?> -<?php + /** * Wysiwyg Images content template * diff --git a/app/code/Magento/Theme/view/adminhtml/templates/browser/content/files.phtml b/app/code/Magento/Theme/view/adminhtml/templates/browser/content/files.phtml index e5f0efd7917be..6b350cd625445 100644 --- a/app/code/Magento/Theme/view/adminhtml/templates/browser/content/files.phtml +++ b/app/code/Magento/Theme/view/adminhtml/templates/browser/content/files.phtml @@ -4,25 +4,21 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - -?> - -<?php /** @var $block \Magento\Theme\Block\Adminhtml\Wysiwyg\Files\Content\Files */ ?> -<?php if ($block->getFilesCount() > 0): ?> - <?php foreach ($block->getFiles() as $file): ?> - <div class="filecnt file-font" id="<?= /* @escapeNotVerified */ $file['id'] ?>"> +<?php if ($block->getFilesCount() > 0) : ?> + <?php foreach ($block->getFiles() as $file) : ?> + <div class="filecnt file-font" id="<?= $block->escapeHtmlAttr($file['id']) ?>"> <p class="nm"> - <?= /* @escapeNotVerified */ $file['text'] ?> - <?php if (isset($file['thumbnailParams'])): ?> - <img src="<?= /* @escapeNotVerified */ $block->getUrl('*/*/previewImage', $file['thumbnailParams']) ?>"> + <?= $block->escapeHtml($file['text']) ?> + <?php if (isset($file['thumbnailParams'])) : ?> + <img src="<?= $block->escapeUrl($block->getUrl('*/*/previewImage', $file['thumbnailParams'])) ?>" + alt="<?= $block->escapeHtmlAttr(__('thumbnail')) ?>"> <?php endif; ?> </p> </div> <?php endforeach; ?> -<?php else: ?> - <?= /* @escapeNotVerified */ __('We found no files.') ?> +<?php else : ?> + <?= $block->escapeHtml(__('We found no files.')) ?> <?php endif; ?> diff --git a/app/code/Magento/Theme/view/adminhtml/templates/browser/content/uploader.phtml b/app/code/Magento/Theme/view/adminhtml/templates/browser/content/uploader.phtml index 722ade94d97fd..67c9084b2756e 100644 --- a/app/code/Magento/Theme/view/adminhtml/templates/browser/content/uploader.phtml +++ b/app/code/Magento/Theme/view/adminhtml/templates/browser/content/uploader.phtml @@ -4,17 +4,14 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile -?> -<?php /** @var $block \Magento\Theme\Block\Adminhtml\Wysiwyg\Files\Content\Uploader */ ?> <div id="<?= $block->getHtmlId() ?>" class="uploader"> <span class="fileinput-button form-buttons"> - <span><?= /* @escapeNotVerified */ __('Browse Files') ?></span> - <input id="fileupload" type="file" name="<?= /* @escapeNotVerified */ $block->getConfig()->getFileField() ?>" - data-url="<?= /* @escapeNotVerified */ $block->getConfig()->getUrl() ?>" multiple> + <span><?= $block->escapeHtml(__('Browse Files')) ?></span> + <input id="fileupload" type="file" name="<?= $block->escapeHtmlAttr($block->getConfig()->getFileField()) ?>" + data-url="<?= $block->escapeUrl($block->getConfig()->getUrl()) ?>" multiple> </span> <div class="clear"></div> <script id="<?= $block->getHtmlId() ?>-template" type="text/x-magento-template"> @@ -44,7 +41,7 @@ require([ form_key: FORM_KEY }, sequentialUploads: true, - maxFileSize: <?= /* @escapeNotVerified */ $block->getFileSizeService()->getMaxFileSize() ?> , + maxFileSize: <?= $block->escapeJs($block->getFileSizeService()->getMaxFileSize()) ?> , add: function (e, data) { var progressTmpl = mageTemplate('#<?= $block->getHtmlId() ?>-template'), fileSize, diff --git a/app/code/Magento/Theme/view/adminhtml/templates/design/config/edit/scope.phtml b/app/code/Magento/Theme/view/adminhtml/templates/design/config/edit/scope.phtml index 7bd5c70f0bfda..639eb749d3e71 100644 --- a/app/code/Magento/Theme/view/adminhtml/templates/design/config/edit/scope.phtml +++ b/app/code/Magento/Theme/view/adminhtml/templates/design/config/edit/scope.phtml @@ -4,12 +4,10 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - /* @var $block \Magento\Theme\Block\Adminhtml\Design\Config\Edit\Scope */ ?> <div class="store-view"> - <span class="store-switcher-label"><?= /* @escapeNotVerified */ __('Scope:') ?></span> - <span class="store-switcher-value"><?= /* @escapeNotVerified */ $block->getScopeTitle() ?></span> + <span class="store-switcher-label"><?= $block->escapeHtml(__('Scope:')) ?></span> + <span class="store-switcher-value"><?= $block->escapeHtml($block->getScopeTitle()) ?></span> </div> diff --git a/app/code/Magento/Theme/view/adminhtml/templates/tabs/css.phtml b/app/code/Magento/Theme/view/adminhtml/templates/tabs/css.phtml index 1c1a7ea204b2b..902daf98182f0 100644 --- a/app/code/Magento/Theme/view/adminhtml/templates/tabs/css.phtml +++ b/app/code/Magento/Theme/view/adminhtml/templates/tabs/css.phtml @@ -3,10 +3,9 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +/** @var $block \Magento\Theme\Block\Adminhtml\System\Design\Theme\Edit\Tab\Css */ ?> -<?php /** @var $block \Magento\Theme\Block\Adminhtml\System\Design\Theme\Edit\Tab\Css */ ?> - <?= $block->getFormHtml() ?> <script> @@ -20,7 +19,7 @@ require([ $( '#css_file_uploader' ).fileupload({ dataType: 'json', replaceFileInput: false, - url : '<?= /* @escapeNotVerified */ $block->getUrl('*/system_design_theme/uploadcss') ?>', + url : '<?= $block->escapeJs($block->escapeUrl($block->getUrl('*/system_design_theme/uploadcss'))) ?>', acceptFileTypes: /(.|\/)(css)$/i, /** diff --git a/app/code/Magento/Theme/view/adminhtml/templates/tabs/fieldset/js.phtml b/app/code/Magento/Theme/view/adminhtml/templates/tabs/fieldset/js.phtml index 992bd73bf0b4a..b50f68cd9353b 100644 --- a/app/code/Magento/Theme/view/adminhtml/templates/tabs/fieldset/js.phtml +++ b/app/code/Magento/Theme/view/adminhtml/templates/tabs/fieldset/js.phtml @@ -4,9 +4,9 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile +// phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis +/** @var $block \Magento\Backend\Block\Widget\Form\Renderer\Fieldset */ ?> -<?php /** @var $block \Magento\Backend\Block\Widget\Form\Renderer\Fieldset */ ?> <div id="js-file-uploader" class="uploader"> </div> @@ -32,7 +32,7 @@ id="remove_js_files_<%- data.id %>" name="js_removed_files[]" value="<%- data.id %>" /> - <label for="remove_js_files_<%- data.id %>"><?= /* @escapeNotVerified */ __('Remove') ?></label> + <label for="remove_js_files_<%- data.id %>"><?= $block->escapeHtml(__('Remove')) ?></label> </div> </div> @@ -60,7 +60,9 @@ jQuery(function($) { }); $('body').trigger( 'refreshJsList', - {jsList: <?= /* @escapeNotVerified */ $this->helper('Magento\Framework\Json\Helper\Data')->jsonEncode($block->getJsFiles()) ?>} + { + jsList: <?= /* @noEscape */ $this->helper(Magento\Framework\Json\Helper\Data::class)->jsonEncode($block->getJsFiles()) ?> + } ); }); diff --git a/app/code/Magento/Theme/view/adminhtml/templates/tabs/js.phtml b/app/code/Magento/Theme/view/adminhtml/templates/tabs/js.phtml index b13e1320d0bd3..1b4633d0965f3 100644 --- a/app/code/Magento/Theme/view/adminhtml/templates/tabs/js.phtml +++ b/app/code/Magento/Theme/view/adminhtml/templates/tabs/js.phtml @@ -3,8 +3,9 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + +/** @var $block \Magento\Theme\Block\Adminhtml\System\Design\Theme\Edit\Tab\Js */ ?> -<?php /** @var $block \Magento\Theme\Block\Adminhtml\System\Design\Theme\Edit\Tab\Js */ ?> <?= $block->getFormHtml() ?> <script> @@ -21,7 +22,7 @@ require([ dataType: 'json', replaceFileInput: false, sequentialUploads: true, - url: '<?= /* @escapeNotVerified */ $block->getJsUploadUrl() ?>', + url: '<?= $block->escapeJs($block->escapeUrl($block->getJsUploadUrl())) ?>', /** * Add data diff --git a/app/code/Magento/Theme/view/adminhtml/templates/title.phtml b/app/code/Magento/Theme/view/adminhtml/templates/title.phtml index e3a85b64586d0..6cefec065c1fd 100644 --- a/app/code/Magento/Theme/view/adminhtml/templates/title.phtml +++ b/app/code/Magento/Theme/view/adminhtml/templates/title.phtml @@ -4,17 +4,15 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - /** * @var $block \Magento\Theme\Block\Html\Title */ -$titleId = ($block->getTitleId()) ? ' id="' . $block->getTitleId() . '"' : ''; +$titleIdHtml = ($block->getTitleId()) ? ' id="' . $block->escapeHtmlAttr($block->getTitleId()) . '"' : ''; $titleClass = ($block->getTitleClass()) ? ' ' . $block->getTitleClass() : ''; -$title = $block->escapeHtml($block->getPageTitle()); +$title = $block->getPageTitle(); ?> -<div class="page-title-wrapper<?= /* @escapeNotVerified */ $titleClass ?>"> - <h1 class="page-title"<?= /* @escapeNotVerified */ $titleId ?>><?= /* @escapeNotVerified */ $title ?></h1> +<div class="page-title-wrapper<?= $block->escapeHtmlAttr($titleClass) ?>"> + <h1 class="page-title"<?= /* @noEscape */ $titleIdHtml ?>><?= $block->escapeHtml($title) ?></h1> <?= $block->getChildHtml() ?> </div> diff --git a/app/code/Magento/Theme/view/base/templates/root.phtml b/app/code/Magento/Theme/view/base/templates/root.phtml index 4923ce9ded7df..16948d097ab97 100644 --- a/app/code/Magento/Theme/view/base/templates/root.phtml +++ b/app/code/Magento/Theme/view/base/templates/root.phtml @@ -3,18 +3,17 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <!doctype html> -<html <?= /* @escapeNotVerified */ $htmlAttributes ?>> - <head <?= /* @escapeNotVerified */ $headAttributes ?>> - <?= /* @escapeNotVerified */ $requireJs ?> - <?= /* @escapeNotVerified */ $headContent ?> - <?= /* @escapeNotVerified */ $headAdditional ?> +<html <?= /* @noEscape */ $htmlAttributes ?>> + <head <?= /* @noEscape */ $headAttributes ?>> + <?= /* @noEscape */ $requireJs ?> + <?= /* @noEscape */ $headContent ?> + <?= /* @noEscape */ $headAdditional ?> </head> - <body data-container="body" data-mage-init='{"loaderAjax": {}, "loader": { "icon": "<?= /* @escapeNotVerified */ $loaderIcon ?>"}}' <?= /* @escapeNotVerified */ $bodyAttributes ?>> - <?= /* @escapeNotVerified */ $layoutContent ?> + <body data-container="body" + data-mage-init='{"loaderAjax": {}, "loader": { "icon": "<?= /* @noEscape */ $loaderIcon ?>"}}' + <?= /* @noEscape */ $bodyAttributes ?>> + <?= /* @noEscape */ $layoutContent ?> </body> </html> diff --git a/app/code/Magento/Theme/view/frontend/templates/callouts/left_col.phtml b/app/code/Magento/Theme/view/frontend/templates/callouts/left_col.phtml index 0df0fcfbc5e83..fb11be34a618f 100644 --- a/app/code/Magento/Theme/view/frontend/templates/callouts/left_col.phtml +++ b/app/code/Magento/Theme/view/frontend/templates/callouts/left_col.phtml @@ -3,19 +3,22 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <div class="block block-banner"> <div class="block-content"> - <?php if (strtolower(substr($block->getLinkUrl(), 0, 4)) === 'http'): ?> - <a href="<?= /* @escapeNotVerified */ $block->getLinkUrl() ?>" title="<?= /* @escapeNotVerified */ __($block->getImgAlt()) ?>"> - <?php elseif ($block->getLinkUrl()): ?> - <a href="<?= /* @escapeNotVerified */ $block->getUrl($block->getLinkUrl()) ?>" title="<?= /* @escapeNotVerified */ __($block->getImgAlt()) ?>"> + <?php if (strtolower(substr($block->getLinkUrl(), 0, 4)) === 'http') : ?> + <a href="<?= $block->escapeUrl($block->getLinkUrl()) ?>" + title="<?= $block->escapeHtmlAttr(__($block->getImgAlt())) ?>"> + <?php elseif ($block->getLinkUrl()) : ?> + <a href="<?= $block->escapeUrl($block->getUrl($block->getLinkUrl())) ?>" + title="<?= $block->escapeHtmlAttr(__($block->getImgAlt())) ?>"> <?php endif; ?> - <img src="<?= /* @escapeNotVerified */ $block->getViewFileUrl($block->getImgSrc()) ?>"<?php if (!$block->getLinkUrl()): ?> title="<?= /* @escapeNotVerified */ __($block->getImgAlt()) ?>"<?php endif; ?> alt="<?= /* @escapeNotVerified */ __($block->getImgAlt()) ?>" /> - <?php if ($block->getLinkUrl()): ?> + <img src="<?= $block->escapeUrl($block->getViewFileUrl($block->getImgSrc())) ?>" + <?php if (!$block->getLinkUrl()) : ?> + title="<?= $block->escapeHtmlAttr(__($block->getImgAlt())) ?>" + <?php endif; ?> + alt="<?= $block->escapeHtmlAttr(__($block->getImgAlt())) ?>" /> + <?php if ($block->getLinkUrl()) : ?> </a> <?php endif ?> </div> diff --git a/app/code/Magento/Theme/view/frontend/templates/callouts/right_col.phtml b/app/code/Magento/Theme/view/frontend/templates/callouts/right_col.phtml index 0df0fcfbc5e83..fb11be34a618f 100644 --- a/app/code/Magento/Theme/view/frontend/templates/callouts/right_col.phtml +++ b/app/code/Magento/Theme/view/frontend/templates/callouts/right_col.phtml @@ -3,19 +3,22 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <div class="block block-banner"> <div class="block-content"> - <?php if (strtolower(substr($block->getLinkUrl(), 0, 4)) === 'http'): ?> - <a href="<?= /* @escapeNotVerified */ $block->getLinkUrl() ?>" title="<?= /* @escapeNotVerified */ __($block->getImgAlt()) ?>"> - <?php elseif ($block->getLinkUrl()): ?> - <a href="<?= /* @escapeNotVerified */ $block->getUrl($block->getLinkUrl()) ?>" title="<?= /* @escapeNotVerified */ __($block->getImgAlt()) ?>"> + <?php if (strtolower(substr($block->getLinkUrl(), 0, 4)) === 'http') : ?> + <a href="<?= $block->escapeUrl($block->getLinkUrl()) ?>" + title="<?= $block->escapeHtmlAttr(__($block->getImgAlt())) ?>"> + <?php elseif ($block->getLinkUrl()) : ?> + <a href="<?= $block->escapeUrl($block->getUrl($block->getLinkUrl())) ?>" + title="<?= $block->escapeHtmlAttr(__($block->getImgAlt())) ?>"> <?php endif; ?> - <img src="<?= /* @escapeNotVerified */ $block->getViewFileUrl($block->getImgSrc()) ?>"<?php if (!$block->getLinkUrl()): ?> title="<?= /* @escapeNotVerified */ __($block->getImgAlt()) ?>"<?php endif; ?> alt="<?= /* @escapeNotVerified */ __($block->getImgAlt()) ?>" /> - <?php if ($block->getLinkUrl()): ?> + <img src="<?= $block->escapeUrl($block->getViewFileUrl($block->getImgSrc())) ?>" + <?php if (!$block->getLinkUrl()) : ?> + title="<?= $block->escapeHtmlAttr(__($block->getImgAlt())) ?>" + <?php endif; ?> + alt="<?= $block->escapeHtmlAttr(__($block->getImgAlt())) ?>" /> + <?php if ($block->getLinkUrl()) : ?> </a> <?php endif ?> </div> diff --git a/app/code/Magento/Theme/view/frontend/templates/html/absolute_footer.phtml b/app/code/Magento/Theme/view/frontend/templates/html/absolute_footer.phtml index 708f0e0ef19f2..76f237064a904 100644 --- a/app/code/Magento/Theme/view/frontend/templates/html/absolute_footer.phtml +++ b/app/code/Magento/Theme/view/frontend/templates/html/absolute_footer.phtml @@ -4,4 +4,4 @@ * See COPYING.txt for license details. */ ?> -<?= /* @escapeNotVerified */ $block->getMiscellaneousHtml(); +<?= /* @noEscape */ $block->getMiscellaneousHtml(); diff --git a/app/code/Magento/Theme/view/frontend/templates/html/block.phtml b/app/code/Magento/Theme/view/frontend/templates/html/block.phtml index 8b28a8cd4e32d..e8021e2defe0b 100644 --- a/app/code/Magento/Theme/view/frontend/templates/html/block.phtml +++ b/app/code/Magento/Theme/view/frontend/templates/html/block.phtml @@ -3,13 +3,12 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> -<div class="block <?= /* @escapeNotVerified */ $block->getBlockCss() ?>"> - <div class="block-title <?= /* @escapeNotVerified */ $block->getBlockCss() ?>-title"><strong><?= /* @escapeNotVerified */ $block->getBlockTitle() ?></strong></div> - <div class="block-content <?= /* @escapeNotVerified */ $block->getBlockCss() ?>-content"> +<div class="block <?= $block->escapeHtmlAttr($block->getBlockCss()) ?>"> + <div class="block-title <?= $block->escapeHtmlAttr($block->getBlockCss()) ?>-title"> + <strong><?= $block->escapeHtml($block->getBlockTitle()) ?></strong> + </div> + <div class="block-content <?= $block->escapeHtmlAttr($block->getBlockCss()) ?>-content"> <?= $block->getChildHtml() ?> </div> </div> diff --git a/app/code/Magento/Theme/view/frontend/templates/html/breadcrumbs.phtml b/app/code/Magento/Theme/view/frontend/templates/html/breadcrumbs.phtml index 710c622ce26c4..ce8e0fd75097f 100644 --- a/app/code/Magento/Theme/view/frontend/templates/html/breadcrumbs.phtml +++ b/app/code/Magento/Theme/view/frontend/templates/html/breadcrumbs.phtml @@ -3,20 +3,20 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <?php if ($crumbs && is_array($crumbs)) : ?> <div class="breadcrumbs"> <ul class="items"> <?php foreach ($crumbs as $crumbName => $crumbInfo) : ?> - <li class="item <?= /* @escapeNotVerified */ $crumbName ?>"> + <li class="item <?= $block->escapeHtmlAttr($crumbName) ?>"> <?php if ($crumbInfo['link']) : ?> - <a href="<?= /* @escapeNotVerified */ $crumbInfo['link'] ?>" title="<?= $block->escapeHtml($crumbInfo['title']) ?>"><?= $block->escapeHtml($crumbInfo['label']) ?></a> + <a href="<?= $block->escapeUrl($crumbInfo['link']) ?>" + title="<?= $block->escapeHtml($crumbInfo['title']) ?>"> + <?= $block->escapeHtml($crumbInfo['label']) ?> + </a> <?php elseif ($crumbInfo['last']) : ?> <strong><?= $block->escapeHtml($crumbInfo['label']) ?></strong> - <?php else: ?> + <?php else : ?> <?= $block->escapeHtml($crumbInfo['label']) ?> <?php endif; ?> </li> diff --git a/app/code/Magento/Theme/view/frontend/templates/html/bugreport.phtml b/app/code/Magento/Theme/view/frontend/templates/html/bugreport.phtml index f147b7085945f..2adbb28b9c59a 100644 --- a/app/code/Magento/Theme/view/frontend/templates/html/bugreport.phtml +++ b/app/code/Magento/Theme/view/frontend/templates/html/bugreport.phtml @@ -5,9 +5,9 @@ */ ?> <small class="bugs"> - <span><?= /* @escapeNotVerified */ __('Help Us Keep Magento Healthy') ?></span> + <span><?= $block->escapeHtml(__('Help Us Keep Magento Healthy')) ?></span> <a href="http://www.magentocommerce.com/bug-tracking" - target="_blank" title="<?= /* @escapeNotVerified */ __('Report All Bugs') ?>"> - <?= /* @escapeNotVerified */ __('Report All Bugs') ?> + target="_blank" title="<?= $block->escapeHtmlAttr(__('Report All Bugs')) ?>"> + <?= $block->escapeHtml(__('Report All Bugs')) ?> </a> </small> diff --git a/app/code/Magento/Theme/view/frontend/templates/html/collapsible.phtml b/app/code/Magento/Theme/view/frontend/templates/html/collapsible.phtml index c3ad4a89399a0..1976870a57492 100644 --- a/app/code/Magento/Theme/view/frontend/templates/html/collapsible.phtml +++ b/app/code/Magento/Theme/view/frontend/templates/html/collapsible.phtml @@ -3,16 +3,17 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> -<div class="block <?= /* @escapeNotVerified */ $block->getBlockCss() ?>"> - <div class="title <?= /* @escapeNotVerified */ $block->getBlockCss() ?>-title" data-mage-init='{"toggleAdvanced": {"toggleContainers": "#<?= /* @escapeNotVerified */ $block->getBlockCss() ?>", "selectorsToggleClass": "active"}}'> - <strong><?= /* @escapeNotVerified */ __($block->getBlockTitle()) ?></strong> +<div class="block <?= $block->escapeHtmlAttr($block->getBlockCss()) ?>"> + <div class="title <?= $block->escapeHtmlAttr($block->getBlockCss()) ?>-title" + data-mage-init='{"toggleAdvanced": {"toggleContainers": "#<?= $block->escapeHtmlAttr($block->getBlockCss()) ?>", "selectorsToggleClass": "active"}}'> + <strong> + <?= $block->escapeHtml(__($block->getBlockTitle())) ?> + </strong> </div> - <div class="content <?= /* @escapeNotVerified */ $block->getBlockCss() ?>-content" id="<?= /* @escapeNotVerified */ $block->getBlockCss() ?>"> + <div class="content <?= $block->escapeHtmlAttr($block->getBlockCss()) ?>-content" + id="<?= $block->escapeHtmlAttr($block->getBlockCss()) ?>"> <?= $block->getChildHtml() ?> </div> </div> diff --git a/app/code/Magento/Theme/view/frontend/templates/html/container.phtml b/app/code/Magento/Theme/view/frontend/templates/html/container.phtml index bad5acc209b5f..4ca5983081a6f 100644 --- a/app/code/Magento/Theme/view/frontend/templates/html/container.phtml +++ b/app/code/Magento/Theme/view/frontend/templates/html/container.phtml @@ -3,8 +3,5 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> -<?= $block->getChildHtml() ?> +<?= $block->getChildHtml(); diff --git a/app/code/Magento/Theme/view/frontend/templates/html/copyright.phtml b/app/code/Magento/Theme/view/frontend/templates/html/copyright.phtml index af2e43d51fa58..672ff32734f40 100644 --- a/app/code/Magento/Theme/view/frontend/templates/html/copyright.phtml +++ b/app/code/Magento/Theme/view/frontend/templates/html/copyright.phtml @@ -5,5 +5,5 @@ */ ?> <small class="copyright"> - <span><?= /* @escapeNotVerified */ $block->getCopyright() ?></span> + <span><?= $block->escapeHtml($block->getCopyright()) ?></span> </small> diff --git a/app/code/Magento/Theme/view/frontend/templates/html/footer.phtml b/app/code/Magento/Theme/view/frontend/templates/html/footer.phtml index 093663e3bb71b..d7fbc2979ea40 100644 --- a/app/code/Magento/Theme/view/frontend/templates/html/footer.phtml +++ b/app/code/Magento/Theme/view/frontend/templates/html/footer.phtml @@ -3,17 +3,14 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <div class="footer-container"> <div class="footer"> <?= $block->getChildHtml() ?> - <p class="bugs"><?= /* @escapeNotVerified */ __('Help Us Keep Magento Healthy') ?> - <a + <p class="bugs"><?= $block->escapeHtml(__('Help Us Keep Magento Healthy')) ?> - <a href="http://www.magentocommerce.com/bug-tracking" - target="_blank"><strong><?= /* @escapeNotVerified */ __('Report All Bugs') ?></strong></a> + target="_blank"><strong><?= $block->escapeHtml(__('Report All Bugs')) ?></strong></a> </p> - <address><?= /* @escapeNotVerified */ $block->getCopyright() ?></address> + <address><?= $block->escapeHtml($block->getCopyright()) ?></address> </div> </div> diff --git a/app/code/Magento/Theme/view/frontend/templates/html/header.phtml b/app/code/Magento/Theme/view/frontend/templates/html/header.phtml index 1103ae28741c6..cbefb82f23e33 100644 --- a/app/code/Magento/Theme/view/frontend/templates/html/header.phtml +++ b/app/code/Magento/Theme/view/frontend/templates/html/header.phtml @@ -4,42 +4,37 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - /** * @var \Magento\Theme\Block\Html\Header $block */ $welcomeMessage = $block->getWelcome(); ?> -<?php switch ($block->getShowPart()): - case 'welcome': ?> - <li class="greet welcome" data-bind="scope: 'customer'"> - <!-- ko if: customer().fullname --> - <span class="logged-in" data-bind="text: new String('<?= $block->escapeHtml(__('Welcome, %1!', '%1')) ?>').replace('%1', customer().fullname)"> - </span> - <!-- /ko --> - <!-- ko ifnot: customer().fullname --> - <span class="not-logged-in" data-bind='html:"<?= $block->escapeHtml($welcomeMessage) ?>"'></span> - <?= $block->getBlockHtml('header.additional') ?> - <!-- /ko --> - </li> - <script type="text/x-magento-init"> - { - "*": { - "Magento_Ui/js/core/app": { - "components": { - "customer": { - "component": "Magento_Customer/js/view/customer" - } +<?php if ($block->getShowPart() == 'welcome') : ?> + <li class="greet welcome" data-bind="scope: 'customer'"> + <!-- ko if: customer().fullname --> + <span class="logged-in" + data-bind="text: new String('<?= $block->escapeHtml(__('Welcome, %1!', '%1')) ?>').replace('%1', customer().fullname)"> + </span> + <!-- /ko --> + <!-- ko ifnot: customer().fullname --> + <span class="not-logged-in" + data-bind='html:"<?= $block->escapeHtml($welcomeMessage) ?>"'></span> + <?= $block->getBlockHtml('header.additional') ?> + <!-- /ko --> + </li> + <script type="text/x-magento-init"> + { + "*": { + "Magento_Ui/js/core/app": { + "components": { + "customer": { + "component": "Magento_Customer/js/view/customer" } } } } - </script> - <?php break; ?> - - <?php case 'other': ?> - <?= $block->getChildHtml() ?> - <?php break; ?> - -<?php endswitch; ?> + } + </script> +<?php elseif ($block->getShowPart() == 'other') :?> + <?= $block->getChildHtml() ?> +<?php endif ?> diff --git a/app/code/Magento/Theme/view/frontend/templates/html/header/logo.phtml b/app/code/Magento/Theme/view/frontend/templates/html/header/logo.phtml index 9c34dfea3218b..99beea5681f2f 100644 --- a/app/code/Magento/Theme/view/frontend/templates/html/header/logo.phtml +++ b/app/code/Magento/Theme/view/frontend/templates/html/header/logo.phtml @@ -4,23 +4,21 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - /** * @var \Magento\Theme\Block\Html\Header\Logo $block */ +$storeName = $block->getThemeName() ? $block->getThemeName() : $block->getLogoAlt() ?> -<?php $storeName = $block->getThemeName() ? $block->getThemeName() : $block->getLogoAlt();?> -<span data-action="toggle-nav" class="action nav-toggle"><span><?= /* @escapeNotVerified */ __('Toggle Nav') ?></span></span> +<span data-action="toggle-nav" class="action nav-toggle"><span><?= $block->escapeHtml(__('Toggle Nav')) ?></span></span> <a class="logo" - href="<?= $block->getUrl('') ?>" - title="<?= /* @escapeNotVerified */ $storeName ?>" + href="<?= $block->escapeUrl($block->getUrl('')) ?>" + title="<?= $block->escapeHtmlAttr($storeName) ?>" aria-label="store logo"> - <img src="<?= /* @escapeNotVerified */ $block->getLogoSrc() ?>" + <img src="<?= $block->escapeUrl($block->getLogoSrc()) ?>" title="<?= $block->escapeHtmlAttr($block->getLogoAlt()) ?>" alt="<?= $block->escapeHtmlAttr($block->getLogoAlt()) ?>" - <?= $block->getLogoWidth() ? 'width="' . $block->getLogoWidth() . '"' : '' ?> - <?= $block->getLogoHeight() ? 'height="' . $block->getLogoHeight() . '"' : '' ?> + <?= $block->getLogoWidth() ? 'width="' . $block->escapeHtmlAttr($block->getLogoWidth()) . '"' : '' ?> + <?= $block->getLogoHeight() ? 'height="' . $block->escapeHtmlAttr($block->getLogoHeight()) . '"' : '' ?> /> </a> diff --git a/app/code/Magento/Theme/view/frontend/templates/html/notices.phtml b/app/code/Magento/Theme/view/frontend/templates/html/notices.phtml index 720ee44007954..1414c21c6e9bc 100644 --- a/app/code/Magento/Theme/view/frontend/templates/html/notices.phtml +++ b/app/code/Magento/Theme/view/frontend/templates/html/notices.phtml @@ -4,32 +4,28 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - -?> -<?php /** * @var $block \Magento\Theme\Block\Html\Notices */ ?> -<?php if ($block->displayNoscriptNotice()): ?> +<?php if ($block->displayNoscriptNotice()) : ?> <noscript> <div class="message global noscript"> <div class="content"> <p> - <strong><?= /* @escapeNotVerified */ __('JavaScript seems to be disabled in your browser.') ?></strong> - <span><?= /* @escapeNotVerified */ __('For the best experience on our site, be sure to turn on Javascript in your browser.') ?></span> + <strong><?= $block->escapeHtml(__('JavaScript seems to be disabled in your browser.')) ?></strong> + <span><?= $block->escapeHtml(__('For the best experience on our site, be sure to turn on Javascript in your browser.')) ?></span> </p> </div> </div> </noscript> <?php endif; ?> -<?php if ($block->displayNoLocalStorageNotice()): ?> +<?php if ($block->displayNoLocalStorageNotice()) : ?> <div class="notice global site local_storage" style="display: none;"> <div class="content"> <p> - <strong><?= /* @escapeNotVerified */ __('Local Storage seems to be disabled in your browser.') ?></strong><br /> - <?= /* @escapeNotVerified */ __('For the best experience on our site, be sure to turn on Local Storage in your browser.') ?> + <strong><?= $block->escapeHtml(__('Local Storage seems to be disabled in your browser.')) ?></strong><br /> + <?= $block->escapeHtml(__('For the best experience on our site, be sure to turn on Local Storage in your browser.')) ?> </p> </div> </div> @@ -51,10 +47,10 @@ require(['jquery'], function(jQuery){ }); </script> <?php endif; ?> -<?php if ($block->displayDemoNotice()): ?> +<?php if ($block->displayDemoNotice()) : ?> <div class="message global demo"> <div class="content"> - <p><?= /* @escapeNotVerified */ __('This is a demo store. No orders will be fulfilled.') ?></p> + <p><?= $block->escapeHtml(__('This is a demo store. No orders will be fulfilled.')) ?></p> </div> </div> <?php endif; ?> diff --git a/app/code/Magento/Theme/view/frontend/templates/html/pager.phtml b/app/code/Magento/Theme/view/frontend/templates/html/pager.phtml index 69edfa081df65..bd50fa39d4099 100644 --- a/app/code/Magento/Theme/view/frontend/templates/html/pager.phtml +++ b/app/code/Magento/Theme/view/frontend/templates/html/pager.phtml @@ -4,108 +4,110 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - -?> -<?php /** * Pager template * * @see \Magento\Theme\Block\Html\Pager */ ?> -<?php if ($block->getCollection()->getSize()): ?> +<?php if ($block->getCollection()->getSize()) : ?> - <?php if ($block->getUseContainer()): ?> + <?php if ($block->getUseContainer()) : ?> <div class="pager"> <?php endif ?> - <?php if ($block->getShowAmounts()): ?> + <?php if ($block->getShowAmounts()) : ?> <p class="toolbar-amount"> <span class="toolbar-number"> - <?php if ($block->getLastPageNum()>1): ?> - <?= /* @escapeNotVerified */ __('Items %1 to %2 of %3 total', $block->getFirstNum(), $block->getLastNum(), $block->getTotalNum()) ?> - <?php elseif ($block->getTotalNum() == 1): ?> - <?= /* @escapeNotVerified */ __('%1 Item', $block->getTotalNum()) ?> - <?php else: ?> - <?= /* @escapeNotVerified */ __('%1 Item(s)', $block->getTotalNum()) ?> + <?php if ($block->getLastPageNum()>1) : ?> + <?= $block->escapeHtml(__('Items %1 to %2 of %3 total', $block->getFirstNum(), $block->getLastNum(), $block->getTotalNum())) ?> + <?php elseif ($block->getTotalNum() == 1) : ?> + <?= $block->escapeHtml(__('%1 Item', $block->getTotalNum())) ?> + <?php else : ?> + <?= $block->escapeHtml(__('%1 Item(s)', $block->getTotalNum())) ?> <?php endif; ?> </span> </p> <?php endif ?> - <?php if ($block->getLastPageNum()>1): ?> + <?php if ($block->getLastPageNum()>1) : ?> <div class="pages"> - <strong class="label pages-label" id="paging-label"><?= /* @escapeNotVerified */ __('Page') ?></strong> + <strong class="label pages-label" id="paging-label"><?= $block->escapeHtml(__('Page')) ?></strong> <ul class="items pages-items" aria-labelledby="paging-label"> - <?php if (!$block->isFirstPage()): ?> + <?php if (!$block->isFirstPage()) : ?> <li class="item pages-item-previous"> <?php $text = $block->getAnchorTextForPrevious() ? $block->getAnchorTextForPrevious() : '';?> - <a class="<?= /* @escapeNotVerified */ $text ? 'link ' : 'action ' ?> previous" href="<?= /* @escapeNotVerified */ $block->getPreviousPageUrl() ?>" title="<?= /* @escapeNotVerified */ $text ? $text : __('Previous') ?>"> - <span class="label"><?= /* @escapeNotVerified */ __('Page') ?></span> - <span><?= /* @escapeNotVerified */ $text ? $text : __('Previous') ?></span> + <a class="<?= $block->escapeHtmlAttr($text ? 'link ' : 'action ') ?> previous" + href="<?= $block->escapeUrl($block->getPreviousPageUrl()) ?>" + title="<?= $block->escapeHtmlAttr($text ? $text : __('Previous')) ?>"> + <span class="label"><?= $block->escapeHtml(__('Page')) ?></span> + <span><?= $block->escapeHtml($text ? $text : __('Previous')) ?></span> </a> </li> <?php endif;?> - <?php if ($block->canShowFirst()): ?> + <?php if ($block->canShowFirst()) : ?> <li class="item"> - <a class="page first" href="<?= /* @escapeNotVerified */ $block->getFirstPageUrl() ?>"> - <span class="label"><?= /* @escapeNotVerified */ __('Page') ?></span> + <a class="page first" href="<?= $block->escapeUrl($block->getFirstPageUrl()) ?>"> + <span class="label"><?= $block->escapeHtml(__('Page')) ?></span> <span>1</span> </a> </li> <?php endif;?> - <?php if ($block->canShowPreviousJump()): ?> + <?php if ($block->canShowPreviousJump()) : ?> <li class="item"> - <a class="page previous jump" title="" href="<?= /* @escapeNotVerified */ $block->getPreviousJumpUrl() ?>"> + <a class="page previous jump" + title="" + href="<?= $block->escapeUrl($block->getPreviousJumpUrl()) ?>"> <span>...</span> </a> </li> <?php endif;?> - <?php foreach ($block->getFramePages() as $_page): ?> - <?php if ($block->isPageCurrent($_page)): ?> + <?php foreach ($block->getFramePages() as $_page) : ?> + <?php if ($block->isPageCurrent($_page)) : ?> <li class="item current"> <strong class="page"> - <span class="label"><?= /* @escapeNotVerified */ __('You\'re currently reading page') ?></span> - <span><?= /* @escapeNotVerified */ $_page ?></span> + <span class="label"><?= $block->escapeHtml(__('You\'re currently reading page')) ?></span> + <span><?= $block->escapeHtml($_page) ?></span> </strong> </li> - <?php else: ?> + <?php else : ?> <li class="item"> - <a href="<?= /* @escapeNotVerified */ $block->getPageUrl($_page) ?>" class="page"> - <span class="label"><?= /* @escapeNotVerified */ __('Page') ?></span> - <span><?= /* @escapeNotVerified */ $_page ?></span> + <a href="<?= $block->escapeUrl($block->getPageUrl($_page)) ?>" class="page"> + <span class="label"><?= $block->escapeHtml(__('Page')) ?></span> + <span><?= $block->escapeHtml($_page) ?></span> </a> </li> <?php endif;?> <?php endforeach;?> - <?php if ($block->canShowNextJump()): ?> + <?php if ($block->canShowNextJump()) : ?> <li class="item"> - <a class="page next jump" title="" href="<?= /* @escapeNotVerified */ $block->getNextJumpUrl() ?>"> + <a class="page next jump" title="" href="<?= $block->escapeUrl($block->getNextJumpUrl()) ?>"> <span>...</span> </a> </li> <?php endif;?> - <?php if ($block->canShowLast()): ?> + <?php if ($block->canShowLast()) : ?> <li class="item"> - <a class="page last" href="<?= /* @escapeNotVerified */ $block->getLastPageUrl() ?>"> - <span class="label"><?= /* @escapeNotVerified */ __('Page') ?></span> - <span><?= /* @escapeNotVerified */ $block->getLastPageNum() ?></span> + <a class="page last" href="<?= $block->escapeUrl($block->getLastPageUrl()) ?>"> + <span class="label"><?= $block->escapeHtml(__('Page')) ?></span> + <span><?= $block->escapeHtml($block->getLastPageNum()) ?></span> </a> </li> <?php endif;?> - <?php if (!$block->isLastPage()): ?> + <?php if (!$block->isLastPage()) : ?> <li class="item pages-item-next"> <?php $text = $block->getAnchorTextForNext() ? $block->getAnchorTextForNext() : '';?> - <a class="<?= /* @escapeNotVerified */ $text ? 'link ' : 'action ' ?> next" href="<?= /* @escapeNotVerified */ $block->getNextPageUrl() ?>" title="<?= /* @escapeNotVerified */ $text ? $text : __('Next') ?>"> - <span class="label"><?= /* @escapeNotVerified */ __('Page') ?></span> - <span><?= /* @escapeNotVerified */ $text ? $text : __('Next') ?></span> + <a class="<?= /* @noEscape */ $text ? 'link ' : 'action ' ?> next" + href="<?= $block->escapeUrl($block->getNextPageUrl()) ?>" + title="<?= $block->escapeHtmlAttr($text ? $text : __('Next')) ?>"> + <span class="label"><?= $block->escapeHtml(__('Page')) ?></span> + <span><?= $block->escapeHtml($text ? $text : __('Next')) ?></span> </a> </li> <?php endif;?> @@ -113,22 +115,23 @@ </div> <?php endif; ?> - <?php if ($block->isShowPerPage()): ?> + <?php if ($block->isShowPerPage()) : ?> <div class="limiter"> - <strong class="limiter-label"><?= /* @escapeNotVerified */ __('Show') ?></strong> + <strong class="limiter-label"><?= $block->escapeHtml(__('Show')) ?></strong> <select id="limiter" data-mage-init='{"redirectUrl": {"event":"change"}}' class="limiter-options"> - <?php foreach ($block->getAvailableLimit() as $_key => $_limit): ?> - <option value="<?= /* @escapeNotVerified */ $block->getLimitUrl($_key) ?>"<?php if ($block->isLimitCurrent($_key)): ?> + <?php foreach ($block->getAvailableLimit() as $_key => $_limit) : ?> + <option value="<?= $block->escapeHtmlAttr($block->getLimitUrl($_key)) ?>" + <?php if ($block->isLimitCurrent($_key)) : ?> selected="selected"<?php endif ?>> - <?= /* @escapeNotVerified */ $_limit ?> + <?= $block->escapeHtml($_limit) ?> </option> <?php endforeach; ?> </select> - <span class="limiter-text"><?= /* @escapeNotVerified */ __('per page') ?></span> + <span class="limiter-text"><?= $block->escapeHtml(__('per page')) ?></span> </div> <?php endif ?> - <?php if ($block->getUseContainer()): ?> + <?php if ($block->getUseContainer()) : ?> </div> <?php endif ?> diff --git a/app/code/Magento/Theme/view/frontend/templates/html/sections.phtml b/app/code/Magento/Theme/view/frontend/templates/html/sections.phtml index 0c31164f4459e..a414a9d248162 100644 --- a/app/code/Magento/Theme/view/frontend/templates/html/sections.phtml +++ b/app/code/Magento/Theme/view/frontend/templates/html/sections.phtml @@ -4,36 +4,40 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - -?> -<?php - /** -* General template for displaying group of blocks devided into sections -*/ + * General template for displaying group of blocks divided into sections + */ $group = $block->getGroupName(); $groupCss = $block->getGroupCss(); $groupBehavior = $block->getGroupBehaviour() ? $block->getGroupBehaviour() : '{"tabs":{"openedState":"active"}}'; ?> -<?php if ($detailedInfoGroup = $block->getGroupChildNames($group, 'getChildHtml')):?> - <div class="sections <?= /* @escapeNotVerified */ $groupCss ?>"> +<?php if ($detailedInfoGroup = $block->getGroupChildNames($group, 'getChildHtml')) :?> + <div class="sections <?= $block->escapeHtmlAttr($groupCss) ?>"> <?php $layout = $block->getLayout(); ?> - <div class="section-items <?= /* @escapeNotVerified */ $groupCss ?>-items" data-mage-init='<?= /* @escapeNotVerified */ $groupBehavior ?>'> - <?php foreach ($detailedInfoGroup as $name):?> + <div class="section-items <?= $block->escapeHtmlAttr($groupCss) ?>-items" + data-mage-init='<?= $block->escapeHtmlAttr($groupBehavior) ?>'> + <?php foreach ($detailedInfoGroup as $name) :?> <?php $html = $layout->renderElement($name); - if (!trim($html) && ($block->getUseForce() != true)) { - continue; - } + if (!trim($html) && ($block->getUseForce() != true)) { + continue; + } $alias = $layout->getElementAlias($name); $label = $block->getChildData($alias, 'title'); ?> - <div class="section-item-title <?= /* @escapeNotVerified */ $groupCss ?>-item-title" data-role="collapsible"> - <a class="<?= /* @escapeNotVerified */ $groupCss ?>-item-switch" data-toggle="switch" href="#<?= /* @escapeNotVerified */ $alias ?>"><?= /* @escapeNotVerified */ $label ?></a> + <div class="section-item-title <?= $block->escapeHtmlAttr($groupCss) ?>-item-title" + data-role="collapsible"> + <a class="<?= $block->escapeHtmlAttr($groupCss) ?>-item-switch" + data-toggle="switch" href="#<?= $block->escapeHtmlAttr($alias) ?>"> + <?= $block->escapeHtml($label) ?> + </a> + </div> + <div class="section-item-content <?= $block->escapeHtmlAttr($groupCss) ?>-item-content" + id="<?= $block->escapeHtmlAttr($alias) ?>" + data-role="content"> + <?= /* @noEscape */ $html ?> </div> - <div class="section-item-content <?= /* @escapeNotVerified */ $groupCss ?>-item-content" id="<?= /* @escapeNotVerified */ $alias ?>" data-role="content"><?= /* @escapeNotVerified */ $html ?></div> <?php endforeach;?> </div> </div> diff --git a/app/code/Magento/Theme/view/frontend/templates/html/skip.phtml b/app/code/Magento/Theme/view/frontend/templates/html/skip.phtml index 198943530f9bb..82ea40ef3424d 100644 --- a/app/code/Magento/Theme/view/frontend/templates/html/skip.phtml +++ b/app/code/Magento/Theme/view/frontend/templates/html/skip.phtml @@ -4,8 +4,12 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile $target = $block->getTarget(); $label = $block->getLabel(); ?> -<a class="action skip <?= /* @escapeNotVerified */ $target ?>" href="#<?= /* @escapeNotVerified */ $target ?>"><span><?= /* @escapeNotVerified */ $label ?></span></a> +<a class="action skip <?= $block->escapeHtmlAttr($target) ?>" + href="#<?= $block->escapeHtmlAttr($target) ?>"> + <span> + <?= $block->escapeHtml($label) ?> + </span> +</a> diff --git a/app/code/Magento/Theme/view/frontend/templates/html/skiptarget.phtml b/app/code/Magento/Theme/view/frontend/templates/html/skiptarget.phtml index 8a7f35183f3e7..9c85e06afdc10 100644 --- a/app/code/Magento/Theme/view/frontend/templates/html/skiptarget.phtml +++ b/app/code/Magento/Theme/view/frontend/templates/html/skiptarget.phtml @@ -4,8 +4,6 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - $target_id = $block->getTargetId(); ?> -<a id="<?= /* @escapeNotVerified */ $target_id ?>" tabindex="-1"></a> +<a id="<?= $block->escapeHtmlAttr($target_id) ?>" tabindex="-1"></a> diff --git a/app/code/Magento/Theme/view/frontend/templates/html/title.phtml b/app/code/Magento/Theme/view/frontend/templates/html/title.phtml index 69ff4c8909f8c..c4961448323fb 100644 --- a/app/code/Magento/Theme/view/frontend/templates/html/title.phtml +++ b/app/code/Magento/Theme/view/frontend/templates/html/title.phtml @@ -4,26 +4,27 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - /** * @var $block \Magento\Theme\Block\Html\Title */ $cssClass = $block->getCssClass() ? ' ' . $block->getCssClass() : ''; -$title = ''; +$titleHtml = ''; if (trim($block->getPageHeading())) { - $title = '<span class="base" data-ui-id="page-title-wrapper" ' . $block->getAddBaseAttribute() . '>' - . $block->escapeHtml($block->getPageHeading()) . '</span>'; + $titleHtml = '<span class="base" data-ui-id="page-title-wrapper" ' + . $block->getAddBaseAttribute() + . '>' + . $block->escapeHtml($block->getPageHeading()) + . '</span>'; } ?> -<?php if ($title): ?> -<div class="page-title-wrapper<?= /* @escapeNotVerified */ $cssClass ?>"> +<?php if ($titleHtml) : ?> +<div class="page-title-wrapper<?= $block->escapeHtmlAttr($cssClass) ?>"> <h1 class="page-title" - <?php if ($block->getId()): ?> id="<?= /* @escapeNotVerified */ $block->getId() ?>" <?php endif; ?> - <?php if ($block->getAddBaseAttributeAria()): ?> - aria-labelledby="<?= /* @escapeNotVerified */ $block->getAddBaseAttributeAria() ?>" + <?php if ($block->getId()) : ?> id="<?= $block->escapeHtmlAttr($block->getId()) ?>" <?php endif; ?> + <?php if ($block->getAddBaseAttributeAria()) : ?> + aria-labelledby="<?= $block->escapeHtmlAttr($block->getAddBaseAttributeAria()) ?>" <?php endif; ?>> - <?= /* @escapeNotVerified */ $title ?> + <?= /* @noEscape */ $titleHtml ?> </h1> <?= $block->getChildHtml() ?> </div> diff --git a/app/code/Magento/Theme/view/frontend/templates/html/topmenu.phtml b/app/code/Magento/Theme/view/frontend/templates/html/topmenu.phtml index 129fd755637ac..5c9748deeeabe 100644 --- a/app/code/Magento/Theme/view/frontend/templates/html/topmenu.phtml +++ b/app/code/Magento/Theme/view/frontend/templates/html/topmenu.phtml @@ -4,22 +4,19 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - -?> -<?php /** * Top menu for store * * @var $block \Magento\Theme\Block\Html\Topmenu */ + +$columnsLimit = $block->getColumnsLimit() ?: 0; +$_menuHtml = $block->getHtml('level-top', 'submenu', $columnsLimit) ?> -<?php $columnsLimit = $block->getColumnsLimit() ?: 0; ?> -<?php $_menu = $block->getHtml('level-top', 'submenu', $columnsLimit) ?> <nav class="navigation" data-action="navigation"> <ul data-mage-init='{"menu":{"responsive":true, "expanded":true, "position":{"my":"left top","at":"left bottom"}}}'> - <?= /* @escapeNotVerified */ $_menu ?> - <?= /* @escapeNotVerified */ $block->getChildHtml() ?> + <?= /* @noEscape */ $_menuHtml?> + <?= $block->getChildHtml() ?> </ul> </nav> diff --git a/app/code/Magento/Theme/view/frontend/templates/js/calendar.phtml b/app/code/Magento/Theme/view/frontend/templates/js/calendar.phtml index 7d18ed17303b9..fcc6f092ff18c 100644 --- a/app/code/Magento/Theme/view/frontend/templates/js/calendar.phtml +++ b/app/code/Magento/Theme/view/frontend/templates/js/calendar.phtml @@ -3,8 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -?> -<?php + /** * Calendar localization script. Should be put into page header. * @@ -21,20 +20,20 @@ require([ //<![CDATA[ $.extend(true, $, { calendarConfig: { - dayNames: <?= /* @escapeNotVerified */ $days['wide'] ?>, - dayNamesMin: <?= /* @escapeNotVerified */ $days['abbreviated'] ?>, - monthNames: <?= /* @escapeNotVerified */ $months['wide'] ?>, - monthNamesShort: <?= /* @escapeNotVerified */ $months['abbreviated'] ?>, - infoTitle: "<?= /* @escapeNotVerified */ __('About the calendar') ?>", - firstDay: <?= /* @escapeNotVerified */ $firstDay ?>, - closeText: "<?= /* @escapeNotVerified */ __('Close') ?>", - currentText: "<?= /* @escapeNotVerified */ __('Go Today') ?>", - prevText: "<?= /* @escapeNotVerified */ __('Previous') ?>", - nextText: "<?= /* @escapeNotVerified */ __('Next') ?>", - weekHeader: "<?= /* @escapeNotVerified */ __('WK') ?>", - timeText: "<?= /* @escapeNotVerified */ __('Time') ?>", - hourText: "<?= /* @escapeNotVerified */ __('Hour') ?>", - minuteText: "<?= /* @escapeNotVerified */ __('Minute') ?>", + dayNames: <?= $block->escapeJs($days['wide']) ?>, + dayNamesMin: <?= $block->escapeJs($days['abbreviated']) ?>, + monthNames: <?= $block->escapeJs($months['wide']) ?>, + monthNamesShort: <?= $block->escapeJs($months['abbreviated']) ?>, + infoTitle: "<?= $block->escapeJs(__('About the calendar')) ?>", + firstDay: <?= $block->escapeJs($firstDay) ?>, + closeText: "<?= $block->escapeJs(__('Close')) ?>", + currentText: "<?= $block->escapeJs(__('Go Today')) ?>", + prevText: "<?= $block->escapeJs(__('Previous')) ?>", + nextText: "<?= $block->escapeJs(__('Next')) ?>", + weekHeader: "<?= $block->escapeJs(__('WK')) ?>", + timeText: "<?= $block->escapeJs(__('Time')) ?>", + hourText: "<?= $block->escapeJs(__('Hour')) ?>", + minuteText: "<?= $block->escapeJs(__('Minute')) ?>", dateFormat: $.datepicker.RFC_2822, showOn: "button", showAnim: "", @@ -51,7 +50,7 @@ require([ } }); - enUS = <?= /* @escapeNotVerified */ $enUS ?>; // en_US locale reference + enUS = <?= $block->escapeJs($enUS) ?>; // en_US locale reference //]]> }); diff --git a/app/code/Magento/Theme/view/frontend/templates/js/components.phtml b/app/code/Magento/Theme/view/frontend/templates/js/components.phtml index bad5acc209b5f..4ca5983081a6f 100644 --- a/app/code/Magento/Theme/view/frontend/templates/js/components.phtml +++ b/app/code/Magento/Theme/view/frontend/templates/js/components.phtml @@ -3,8 +3,5 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> -<?= $block->getChildHtml() ?> +<?= $block->getChildHtml(); diff --git a/app/code/Magento/Theme/view/frontend/templates/js/cookie.phtml b/app/code/Magento/Theme/view/frontend/templates/js/cookie.phtml index 72d93003c1ae7..7ecfd18d0d3b0 100644 --- a/app/code/Magento/Theme/view/frontend/templates/js/cookie.phtml +++ b/app/code/Magento/Theme/view/frontend/templates/js/cookie.phtml @@ -3,8 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -?> -<?php + /** * Cookie settings initialization script * @@ -17,10 +16,10 @@ "*": { "mage/cookies": { "expires": null, - "path": "<?= /* @escapeNotVerified */ $block->getPath() ?>", - "domain": "<?= /* @escapeNotVerified */ $block->getDomain() ?>", + "path": "<?= $block->escapeJs($block->getPath()) ?>", + "domain": "<?= $block->escapeJs($block->getDomain()) ?>", "secure": false, - "lifetime": "<?= /* @escapeNotVerified */ $block->getLifetime() ?>" + "lifetime": "<?= $block->escapeJs($block->getLifetime()) ?>" } } } diff --git a/app/code/Magento/Theme/view/frontend/templates/link.phtml b/app/code/Magento/Theme/view/frontend/templates/link.phtml index 9fdcbcb367056..32f5447aa0aac 100644 --- a/app/code/Magento/Theme/view/frontend/templates/link.phtml +++ b/app/code/Magento/Theme/view/frontend/templates/link.phtml @@ -4,16 +4,14 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - /** * @var $block \Magento\Framework\View\Element\Html\Link */ ?> -<?php if (!$block->getIsDisabled()): ?> +<?php if (!$block->getIsDisabled()) : ?> <li> - <a href="<?= $block->escapeHtml($block->getHref()) ?>" - <?php if ($title = $block->getTitle()):?> title="<?= $block->escapeHtml(__($title)) ?>"<?php endif;?>> + <a href="<?= $block->escapeUrl($block->getHref()) ?>" + <?php if ($title = $block->getTitle()) : ?> title="<?= $block->escapeHtmlAttr(__($title)) ?>"<?php endif;?>> <?= $block->escapeHtml(__($block->getLabel())) ?> </a> </li> diff --git a/app/code/Magento/Theme/view/frontend/templates/page/js/require_js.phtml b/app/code/Magento/Theme/view/frontend/templates/page/js/require_js.phtml index 2a4b07ee6396f..ad998c56b963f 100644 --- a/app/code/Magento/Theme/view/frontend/templates/page/js/require_js.phtml +++ b/app/code/Magento/Theme/view/frontend/templates/page/js/require_js.phtml @@ -7,6 +7,6 @@ <script> var BASE_URL = '<?= $block->escapeUrl($block->getBaseUrl()) ?>'; var require = { - "baseUrl": "<?= /* @escapeNotVerified */ $block->getViewFileUrl('/') ?>" + "baseUrl": "<?= $block->escapeUrl($block->getViewFileUrl('/')) ?>" }; </script> diff --git a/app/code/Magento/Theme/view/frontend/templates/template.phtml b/app/code/Magento/Theme/view/frontend/templates/template.phtml index ba3d1c939cb02..7884c3e08f5d2 100644 --- a/app/code/Magento/Theme/view/frontend/templates/template.phtml +++ b/app/code/Magento/Theme/view/frontend/templates/template.phtml @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + /** @var $block \Magento\Framework\View\Element\Template */ ?> <?= $block->getChildHtml('', false); diff --git a/app/code/Magento/Theme/view/frontend/templates/text.phtml b/app/code/Magento/Theme/view/frontend/templates/text.phtml index 7d00235c0362c..d4032908a445e 100644 --- a/app/code/Magento/Theme/view/frontend/templates/text.phtml +++ b/app/code/Magento/Theme/view/frontend/templates/text.phtml @@ -4,12 +4,18 @@ * See COPYING.txt for license details. */ -$attributes = $block->getCssClass() ? ' class="' . $block->getCssClass() . '"' : ''; +$attributes = $block->getCssClass() ? ' class="' . $block->escapeHtmlAttr($block->getCssClass()) . '"' : ''; $attr = $block->getAttributes(); if (!empty($attr)) { foreach ($block->getAttributes() as $attribute => $value) { - $attributes .= ' ' . $attribute . '="' . $value . '"'; + $attributes .= ' ' . $block->escapeHtml($attribute) . '="' . $block->escapeHtmlAttr($value) . '"'; } } -echo - '<' . $block->getTag() . $attributes . '>' . $block->getText() . '</' . $block->getTag() . '>'; +/* @noEscape */ echo '<' + . $block->escapeHtml($block->getTag()) + . $attributes + . '>' + . $block->escapeHtml($block->getText()) + . '</' + . $block->escapeHtml($block->getTag()) + . '>'; diff --git a/app/code/Magento/Tinymce3/Test/Mftf/Test/AdminSwitchWYSIWYGOptionsTest.xml b/app/code/Magento/Tinymce3/Test/Mftf/Test/AdminSwitchWYSIWYGOptionsTest.xml index 02b8d3686fd68..e15886e026c4c 100644 --- a/app/code/Magento/Tinymce3/Test/Mftf/Test/AdminSwitchWYSIWYGOptionsTest.xml +++ b/app/code/Magento/Tinymce3/Test/Mftf/Test/AdminSwitchWYSIWYGOptionsTest.xml @@ -16,7 +16,7 @@ <title value="Admin should able to switch between versions of TinyMCE"/> <description value="Admin should able to switch between versions of TinyMCE"/> <severity value="CRITICAL"/> - <testCaseId value="MAGETWO-82936"/> + <testCaseId value="MC-6114"/> </annotations> <before> <actionGroup ref="LoginActionGroup" stepKey="loginGetFromGeneralFile"/> diff --git a/app/code/Magento/Tinymce3/view/base/web/tiny_mce/themes/advanced/js/source_editor.js b/app/code/Magento/Tinymce3/view/base/web/tiny_mce/themes/advanced/js/source_editor.js index 9cf6b1a29cdaf..e90ee4d99628d 100644 --- a/app/code/Magento/Tinymce3/view/base/web/tiny_mce/themes/advanced/js/source_editor.js +++ b/app/code/Magento/Tinymce3/view/base/web/tiny_mce/themes/advanced/js/source_editor.js @@ -10,8 +10,9 @@ function onLoadInit() { tinyMCEPopup.resizeToInnerSize(); // Remove Gecko spellchecking - if (tinymce.isGecko) - document.body.spellcheck = tinyMCEPopup.editor.getParam("gecko_spellcheck"); + if (tinymce.isGecko) { + document.body.spellcheck = tinyMCEPopup.editor.getParam("gecko_spellcheck", false); + } document.getElementById('htmlSource').value = tinyMCEPopup.editor.getContent({source_view : true}); diff --git a/app/code/Magento/Translation/Model/Js/PreProcessor.php b/app/code/Magento/Translation/Model/Js/PreProcessor.php index e85d022627621..a96cdf5e32591 100644 --- a/app/code/Magento/Translation/Model/Js/PreProcessor.php +++ b/app/code/Magento/Translation/Model/Js/PreProcessor.php @@ -3,6 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Translation\Model\Js; use Magento\Framework\App\AreaList; @@ -92,6 +94,6 @@ public function translate($content) */ protected function replaceCallback($matches) { - return '"' . __($matches[1]) . '"'; + return '\'' . __($matches['translate']) . '\''; } } diff --git a/app/code/Magento/Translation/Test/Unit/Model/Js/PreProcessorTest.php b/app/code/Magento/Translation/Test/Unit/Model/Js/PreProcessorTest.php deleted file mode 100644 index 713086f298806..0000000000000 --- a/app/code/Magento/Translation/Test/Unit/Model/Js/PreProcessorTest.php +++ /dev/null @@ -1,85 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -namespace Magento\Translation\Test\Unit\Model\Js; - -use Magento\Translation\Model\Js\PreProcessor; -use Magento\Translation\Model\Js\Config; -use Magento\Framework\App\AreaList; -use Magento\Framework\TranslateInterface; - -class PreProcessorTest extends \PHPUnit\Framework\TestCase -{ - /** - * @var PreProcessor - */ - protected $model; - - /** - * @var Config|\PHPUnit_Framework_MockObject_MockObject - */ - protected $configMock; - - /** - * @var AreaList|\PHPUnit_Framework_MockObject_MockObject - */ - protected $areaListMock; - - /** - * @var TranslateInterface|\PHPUnit_Framework_MockObject_MockObject - */ - protected $translateMock; - - protected function setUp() - { - $this->configMock = $this->createMock(\Magento\Translation\Model\Js\Config::class); - $this->areaListMock = $this->createMock(\Magento\Framework\App\AreaList::class); - $this->translateMock = $this->getMockForAbstractClass(\Magento\Framework\TranslateInterface::class); - $this->model = new PreProcessor($this->configMock, $this->areaListMock, $this->translateMock); - } - - public function testGetData() - { - $asset = $this->createMock(\Magento\Framework\View\Asset\File::class); - $chain = $this->createMock(\Magento\Framework\View\Asset\PreProcessor\Chain::class); - $context = $this->createMock(\Magento\Framework\View\Asset\File\FallbackContext::class); - $originalContent = 'content$.mage.__("hello1")content'; - $translatedContent = 'content"hello1"content'; - $patterns = ['~\$\.mage\.__\([\'|\"](.+?)[\'|\"]\)~']; - $areaCode = 'adminhtml'; - $area = $this->createMock(\Magento\Framework\App\Area::class); - - $chain->expects($this->once()) - ->method('getAsset') - ->willReturn($asset); - $asset->expects($this->once()) - ->method('getContext') - ->willReturn($context); - $context->expects($this->once()) - ->method('getAreaCode') - ->willReturn($areaCode); - - $this->configMock->expects($this->once()) - ->method('isEmbeddedStrategy') - ->willReturn(true); - $chain->expects($this->once()) - ->method('getContent') - ->willReturn($originalContent); - $this->configMock->expects($this->once()) - ->method('getPatterns') - ->willReturn($patterns); - - $this->areaListMock->expects($this->once()) - ->method('getArea') - ->with($areaCode) - ->willReturn($area); - - $chain->expects($this->once()) - ->method('setContent') - ->with($translatedContent); - - $this->model->process($chain); - } -} diff --git a/app/code/Magento/Translation/etc/di.xml b/app/code/Magento/Translation/etc/di.xml index 6d3ca03953cf9..93ca7c7b6b736 100644 --- a/app/code/Magento/Translation/etc/di.xml +++ b/app/code/Magento/Translation/etc/di.xml @@ -65,8 +65,8 @@ <argument name="patterns" xsi:type="array"> <item name="i18n_translation" xsi:type="string"><![CDATA[~i18n\:\s*(["'])(.*?)(?<!\\)\1~]]></item> <item name="translate_wrapping" xsi:type="string"><![CDATA[~translate\=("')([^\'].*?)\'\"~]]></item> - <item name="mage_translation_widget" xsi:type="string"><![CDATA[~(?:\$|jQuery)\.mage\.__\((?s)[^'"]*?(['"])(.+?)(?<!\\)\1(?s).*?\)~]]></item> - <item name="mage_translation_static" xsi:type="string"><![CDATA[~\$t\((?s)[^'"]*?(["'])(.+?)\1(?s).*?\)~]]></item> + <item name="mage_translation_widget" xsi:type="string"><![CDATA[~(?s)(?:\$|jQuery)\.mage\.__\(\s*(['"])(?<translate>.+?)(?<!\\)\1\s*(*SKIP)\)\s*(?s)~]]></item> + <item name="mage_translation_static" xsi:type="string"><![CDATA[~(?s)\$t\(\s*(['"])(?<translate>.+?)(?<!\\)\1\s*(*SKIP)\)(?s)~]]></item> <item name="translate_args" xsi:type="string"><![CDATA[~translate args\=("|'|"')([^\'].*?)('"|'|")~]]></item> </argument> </arguments> diff --git a/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminGridFilterApplyActionGroup.xml b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminGridFilterApplyActionGroup.xml new file mode 100644 index 0000000000000..9f0b7da28fffc --- /dev/null +++ b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminGridFilterApplyActionGroup.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminGridFilterApplyActionGroup"> + <click selector="{{AdminDataGridFilterSection.apply}}" stepKey="applyFilters" /> + <waitForPageLoad stepKey="waitForFiltersReset" /> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminGridFilterFillInputFieldActionGroup.xml b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminGridFilterFillInputFieldActionGroup.xml new file mode 100644 index 0000000000000..7cf05ab8c186b --- /dev/null +++ b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminGridFilterFillInputFieldActionGroup.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminGridFilterFillInputFieldActionGroup"> + <arguments> + <argument name="filterInputName" type="string"/> + <argument name="filterValue" type="string"/> + </arguments> + + <conditionalClick selector="{{AdminDataGridFilterSection.filterExpand}}" dependentSelector="{{AdminDataGridFilterSection.filterForm}}" visible="false" stepKey="openFiltersFormIfNecessary" /> + <waitForElementVisible selector="{{AdminDataGridFilterSection.filterForm}}" stepKey="waitForFormVisible" /> + <fillField userInput="{{filterValue}}" selector="{{AdminDataGridFilterSection.inputFieldByNameAttr(filterInputName)}}" stepKey="fillFilterInputField" /> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminGridFilterResetActionGroup.xml b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminGridFilterResetActionGroup.xml new file mode 100644 index 0000000000000..8430975a119a8 --- /dev/null +++ b/app/code/Magento/Ui/Test/Mftf/ActionGroup/AdminGridFilterResetActionGroup.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminGridFilterResetActionGroup"> + <scrollToTopOfPage stepKey="scrollToTop" /> + <conditionalClick selector="{{AdminDataGridFilterSection.clear}}" dependentSelector="{{AdminDataGridFilterSection.clear}}" visible="true" stepKey="clearExistingFilters" /> + <waitForPageLoad stepKey="waitForFiltersReset" /> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/Ui/Test/Mftf/Section/AdminDataGridFilterSection.xml b/app/code/Magento/Ui/Test/Mftf/Section/AdminDataGridFilterSection.xml new file mode 100644 index 0000000000000..6ae4ebafa0df1 --- /dev/null +++ b/app/code/Magento/Ui/Test/Mftf/Section/AdminDataGridFilterSection.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> + +<sections xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Page/etc/SectionObject.xsd"> + <section name="AdminDataGridFilterSection"> + <element name="filterForm" type="block" selector="[data-part='filter-form']" /> + <element name="filterExpand" type="button" selector="//div[@class='admin__data-grid-header'][(not(ancestor::*[@class='sticky-header']) and not(contains(@style,'visibility: hidden'))) or (ancestor::*[@class='sticky-header' and not(contains(@style,'display: none'))])]//button[@data-action='grid-filter-expand']" /> + <element name="inputFieldByNameAttr" type="input" selector="//*[@data-part='filter-form']//input[@name='{{inputNameAttr}}']" parameterized="true" /> + <element name="apply" type="button" selector="//*[@data-part='filter-form']//button[@data-action='grid-filter-apply']" /> + <element name="clear" type="button" selector=".admin__data-grid-header [data-action='grid-filter-reset']" /> + </section> +</sections> diff --git a/app/code/Magento/Ui/Test/Mftf/Section/AdminDataGridTableSection.xml b/app/code/Magento/Ui/Test/Mftf/Section/AdminDataGridTableSection.xml index a2b7ba8c1ffd5..8a61bf76cc5ea 100644 --- a/app/code/Magento/Ui/Test/Mftf/Section/AdminDataGridTableSection.xml +++ b/app/code/Magento/Ui/Test/Mftf/Section/AdminDataGridTableSection.xml @@ -19,5 +19,7 @@ <element name="gridCell" type="text" selector="//tr[{{row}}]//td[count(//div[@data-role='grid-wrapper']//tr//th[contains(., '{{column}}')]/preceding-sibling::th) +1 ]" parameterized="true"/> <element name="rowViewAction" type="button" selector=".data-grid tbody > tr:nth-of-type({{row}}) .action-menu-item" parameterized="true" timeout="30"/> <element name="dataGridEmpty" type="block" selector=".data-grid-tr-no-data td"/> + <element name="rowTemplateStrict" type="block" selector="//tbody/tr[td[*[text()[normalize-space()='{{text}}']]]]" parameterized="true" /> + <element name="rowTemplate" type="block" selector="//tbody/tr[td[*[contains(.,normalize-space('{{text}}'))]]]" parameterized="true" /> </section> </sections> diff --git a/app/code/Magento/Ui/view/base/templates/control/button/default.phtml b/app/code/Magento/Ui/view/base/templates/control/button/default.phtml index 0307af4f749d4..856bdd67a5bc6 100644 --- a/app/code/Magento/Ui/view/base/templates/control/button/default.phtml +++ b/app/code/Magento/Ui/view/base/templates/control/button/default.phtml @@ -4,16 +4,12 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - -?> -<?php /** * @var $block \Magento\Ui\Component\Control\Button */ ?> <?= $block->getBeforeHtml() ?> -<button <?= /* @escapeNotVerified */ $block->getAttributesHtml(), $block->getUiId() ?>> - <span><?= /* @escapeNotVerified */ $block->getLabel() ?></span> +<button <?= /* @noEscape */ $block->getAttributesHtml(), /* @noEscape */ $block->getUiId() ?>> + <span><?= $block->escapeHtml($block->getLabel()) ?></span> </button> <?= $block->getAfterHtml() ?> diff --git a/app/code/Magento/Ui/view/base/templates/control/button/split.phtml b/app/code/Magento/Ui/view/base/templates/control/button/split.phtml index 757b1d3a4e425..08230184d5a4d 100644 --- a/app/code/Magento/Ui/view/base/templates/control/button/split.phtml +++ b/app/code/Magento/Ui/view/base/templates/control/button/split.phtml @@ -4,10 +4,6 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - -?> -<?php /** @var $block \Magento\Ui\Component\Control\SplitButton */ ?> @@ -15,21 +11,21 @@ <button <?= $block->getButtonAttributesHtml() ?>> <span><?= $block->escapeHtml($block->getLabel()) ?></span> </button> - <?php if ($block->hasSplit()): ?> + <?php if ($block->hasSplit()) : ?> <button <?= $block->getToggleAttributesHtml() ?>> - <span><?= /* @escapeNotVerified */ __('Select'); ?></span> + <span><?= $block->escapeHtml(__('Select')) ?></span> </button> - <?php if (!$block->getDisabled()): ?> - <ul class="dropdown-menu" <?= /* @escapeNotVerified */ $block->getUiId("dropdown-menu") ?>> - <?php foreach ($block->getOptions() as $key => $option): ?> + <?php if (!$block->getDisabled()) : ?> + <ul class="dropdown-menu" <?= /* @noEscape */ $block->getUiId("dropdown-menu") ?>> + <?php foreach ($block->getOptions() as $key => $option) : ?> <li> <span <?= $block->getOptionAttributesHtml($key, $option) ?>> <?= $block->escapeHtml($option['label']) ?> </span> - <?php if (isset($option['hint'])): ?> - <div class="tooltip" <?= /* @escapeNotVerified */ $block->getUiId('item', $key, 'tooltip') ?>> - <a href="<?= $block->escapeHtml($option['hint']['href']) ?>" class="help"> + <?php if (isset($option['hint'])) : ?> + <div class="tooltip" <?= /* @noEscape */ $block->getUiId('item', $key, 'tooltip') ?>> + <a href="<?= $block->escapeUrl($option['hint']['href']) ?>" class="help"> <?= $block->escapeHtml($option['hint']['label']) ?> </a> </div> @@ -46,4 +42,4 @@ "Magento_Ui/js/grid/controls/button/split": {} } } -</script> \ No newline at end of file +</script> diff --git a/app/code/Magento/Ui/view/base/templates/form/default.phtml b/app/code/Magento/Ui/view/base/templates/form/default.phtml index 97f8ec52ca0d0..bb14cd4171f9b 100644 --- a/app/code/Magento/Ui/view/base/templates/form/default.phtml +++ b/app/code/Magento/Ui/view/base/templates/form/default.phtml @@ -4,17 +4,18 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile /** * @var \Magento\Ui\Component\Form $block */ -/* @escapeNotVerified */ echo $block->renderChildComponent('before_form'); ?> -<div data-role="spinner" data-component="<?= /* @escapeNotVerified */ $block->getName() ?>.areas" class="admin__data-grid-loading-mask"> +<?=/* @noEscape */ $block->renderChildComponent('before_form') ?> +<div data-role="spinner" + data-component="<?= $block->escapeHtmlAttr($block->getName()) ?>.areas" + class="admin__data-grid-loading-mask"> <div class="grid-loader"></div> </div> -<div data-bind="scope: '<?= /* @escapeNotVerified */ $block->getName() ?>.areas'" class="entry-edit form-inline"> +<div data-bind="scope: '<?= $block->escapeHtmlAttr($block->getName()) ?>.areas'" + class="entry-edit form-inline"> <!-- ko template: getTemplate() --><!-- /ko --> </div> -<?php -echo $block->renderChildComponent('after_form'); +<?= /* @noEscape */ $block->renderChildComponent('after_form') ?> diff --git a/app/code/Magento/Ui/view/base/templates/label/default.phtml b/app/code/Magento/Ui/view/base/templates/label/default.phtml index db434adf92ee9..cb334a3e3f385 100644 --- a/app/code/Magento/Ui/view/base/templates/label/default.phtml +++ b/app/code/Magento/Ui/view/base/templates/label/default.phtml @@ -5,5 +5,5 @@ */ ?> <span> - <?= /* @escapeNotVerified */ $block->getData('label') ?> + <?= $block->escapeHtml($block->getData('label')) ?> </span> diff --git a/app/code/Magento/Ui/view/base/templates/layout/tabs/default.phtml b/app/code/Magento/Ui/view/base/templates/layout/tabs/default.phtml index 58e9ed5d0508f..9192c86847c4b 100644 --- a/app/code/Magento/Ui/view/base/templates/layout/tabs/default.phtml +++ b/app/code/Magento/Ui/view/base/templates/layout/tabs/default.phtml @@ -3,10 +3,11 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + /** * @var \Magento\Ui\Component\Layout\Tabs $block */ ?> -<div data-bind="scope: '<?= /* @escapeNotVerified */ $block->getDataScope() ?>.sections' " class="ui-tabs"> +<div data-bind="scope: '<?= $block->escapeHtmlAttr($block->getDataScope()) ?>.sections' " class="ui-tabs"> <!-- ko template: getTemplate() --><!-- /ko --> </div> diff --git a/app/code/Magento/Ui/view/base/templates/layout/tabs/nav/default.phtml b/app/code/Magento/Ui/view/base/templates/layout/tabs/nav/default.phtml index b81e0bcc8f2a4..202834c83124c 100644 --- a/app/code/Magento/Ui/view/base/templates/layout/tabs/nav/default.phtml +++ b/app/code/Magento/Ui/view/base/templates/layout/tabs/nav/default.phtml @@ -3,10 +3,11 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + /** * @var \Magento\Ui\Component\Layout\Tabs\Nav $block */ ?> -<div data-bind="scope: '<?= /* @escapeNotVerified */ $block->getDataScope() ?>.sections' " class="ui-tabs"> +<div data-bind="scope: '<?= $block->escapeHtmlAttr($block->getDataScope()) ?>.sections' " class="ui-tabs"> <!-- ko template: getTemplate() --><!-- /ko --> </div> diff --git a/app/code/Magento/Ui/view/base/templates/logger.phtml b/app/code/Magento/Ui/view/base/templates/logger.phtml index d064704ab6aed..7466781a606f1 100644 --- a/app/code/Magento/Ui/view/base/templates/logger.phtml +++ b/app/code/Magento/Ui/view/base/templates/logger.phtml @@ -3,13 +3,13 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile + +/** @var $block \Magento\Ui\Block\Logger */ ?> -<?php /** @var $block \Magento\Ui\Block\Logger */ ?> -<?php if ($block->isLoggingEnabled()): ?> +<?php if ($block->isLoggingEnabled()) : ?> <script> window.onerror = function(msg, url, line) { - var key = "<?= /* @escapeNotVerified */ $block->getSessionStorageKey() ?>"; + var key = "<?= $block->escapeJs($block->getSessionStorageKey()) ?>"; var errors = {}; if (sessionStorage.getItem(key)) { errors = JSON.parse(sessionStorage.getItem(key)); diff --git a/app/code/Magento/Ui/view/base/templates/stepswizard.phtml b/app/code/Magento/Ui/view/base/templates/stepswizard.phtml index 78e73e0cd9a69..3513cf4c0edee 100644 --- a/app/code/Magento/Ui/view/base/templates/stepswizard.phtml +++ b/app/code/Magento/Ui/view/base/templates/stepswizard.phtml @@ -4,20 +4,22 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - +// phpcs:disable Magento2.Templates.ThisInTemplate.FoundThis /** @var $block \Magento\Ui\Block\Component\StepsWizard */ ?> -<div data-role="steps-wizard-main" class="steps-wizard <?= /* @noEscape */ $block->getData('config/dataScope') ?>" data-bind="scope: '<?= /* @escapeNotVerified */ $block->getComponentName() ?>'"> +<div data-role="steps-wizard-main" + class="steps-wizard <?= /* @noEscape */ $block->getData('config/dataScope') ?>" + data-bind="scope: '<?= $block->escapeHtmlAttr($block->getComponentName()) ?>'"> <div data-role="messages" class="messages"></div> <div data-role="steps-wizard-controls" class="steps-wizard-navigation"> <ul class="nav-bar"> - <?php foreach ($block->getSteps() as $step): ?> - <li data-role="collapsible" data-bind="css: { 'active': selectedStep() == '<?= /* @escapeNotVerified */ $step->getComponentName() ?>'}"> - <a href="#<?= /* @escapeNotVerified */ $step->getComponentName() ?>" + <?php foreach ($block->getSteps() as $step) : ?> + <li data-role="collapsible" + data-bind="css: { 'active': selectedStep() == '<?= $block->escapeHtmlAttr($step->getComponentName()) ?>'}"> + <a href="#<?= $block->escapeHtmlAttr($step->getComponentName()) ?>" data-bind="click: showSpecificStep"> - <?= /* @escapeNotVerified */ $step->getCaption() ?> + <?= $block->escapeHtml($step->getCaption()) ?> </a> </li> <?php endforeach; ?> @@ -26,30 +28,30 @@ <div class="action-wrap" data-role="closeBtn"> <button type="button" class="action-cancel action-tertiary" data-bind="click: close"> - <span><?= /* @escapeNotVerified */ __('Cancel') ?></span> + <span><?= $block->escapeHtml(__('Cancel')) ?></span> </button> </div> <div class="action-wrap action-wrap-prev" data-role="step-wizard-prev"> <button type="button" class="action-default action-back-step" data-bind="click: back, css: { 'disabled': disabled}"> - <span><?= /* @escapeNotVerified */ __('Back') ?></span> + <span><?= $block->escapeHtml(__('Back')) ?></span> </button> </div> <div class="action-wrap action-wrap-next" data-role="step-wizard-next"> <button type="button" class="action-default action-primary action-next-step" data-bind="click: next"> - <span><?= /* @escapeNotVerified */ __('Next') ?></span> + <span><?= $block->escapeHtml(__('Next')) ?></span> </button> </div> </div> </div> <div data-role="steps-wizard-tab"> - <?php foreach ($block->getSteps() as $step): ?> + <?php foreach ($block->getSteps() as $step) : ?> <div data-bind="visible: selectedStep() == $element.id, css: {'no-display':false}" - class="content no-display" id="<?= /* @escapeNotVerified */ $step->getComponentName() ?>" + class="content no-display" id="<?= $block->escapeHtmlAttr($step->getComponentName()) ?>" data-role="content"> - <?= /* @escapeNotVerified */ $step->getContent() ?> + <?= /* @noEscape */ $step->getContent() ?> </div> <?php endforeach; ?> </div> @@ -60,10 +62,10 @@ "*": { "Magento_Ui/js/core/app": { "components": { - "<?= /* @escapeNotVerified */ $block->getComponentName() ?>": { + "<?= $block->escapeJs($block->getComponentName()) ?>": { "component": "Magento_Ui/js/lib/step-wizard", - "initData": <?= /* @escapeNotVerified */ $this->helper("Magento\Framework\Json\Helper\Data")->jsonEncode($block->getInitData()) ?>, - "stepsNames": <?= /* @escapeNotVerified */ $this->helper("Magento\Framework\Json\Helper\Data")->jsonEncode($block->getStepComponents()) ?>, + "initData": <?= /* @noEscape */ $this->helper(Magento\Framework\Json\Helper\Data::class)->jsonEncode($block->getInitData()) ?>, + "stepsNames": <?= /* @noEscape */ $this->helper(Magento\Framework\Json\Helper\Data::class)->jsonEncode($block->getStepComponents()) ?>, "modalClass": "<?= /* @noEscape */ $block->getData('config/dataScope') ?>" } } diff --git a/app/code/Magento/Ui/view/base/templates/wysiwyg/active_editor.phtml b/app/code/Magento/Ui/view/base/templates/wysiwyg/active_editor.phtml index 22607921296c7..a7c279c431665 100644 --- a/app/code/Magento/Ui/view/base/templates/wysiwyg/active_editor.phtml +++ b/app/code/Magento/Ui/view/base/templates/wysiwyg/active_editor.phtml @@ -1,21 +1,16 @@ <?php - /** * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - /** @var Magento\Ui\Block\Wysiwyg\ActiveEditor $block */ - ?> - <script> require.config({ map: { '*': { - wysiwygAdapter: '<?php /* @noEscape */ echo $block->getWysiwygAdapterPath(); ?>' + wysiwygAdapter: '<?= /* @noEscape */ $block->getWysiwygAdapterPath() ?>' } } }); diff --git a/app/code/Magento/Ui/view/base/web/js/form/element/abstract.js b/app/code/Magento/Ui/view/base/web/js/form/element/abstract.js index ca3d383accca1..c7fab7da4567e 100755 --- a/app/code/Magento/Ui/view/base/web/js/form/element/abstract.js +++ b/app/code/Magento/Ui/view/base/web/js/form/element/abstract.js @@ -296,7 +296,7 @@ define([ this.validation[rule] = options; } - changed = utils.compare(rules, this.validation).equal; + changed = !utils.compare(rules, this.validation).equal; if (changed) { this.required(!!rules['required-entry']); diff --git a/app/code/Magento/Ui/view/base/web/js/form/form.js b/app/code/Magento/Ui/view/base/web/js/form/form.js index ea6c57f63bdf1..3692108675bc7 100644 --- a/app/code/Magento/Ui/view/base/web/js/form/form.js +++ b/app/code/Magento/Ui/view/base/web/js/form/form.js @@ -339,6 +339,7 @@ define([ */ reset: function () { this.source.trigger('data.reset'); + $('[data-bind*=datepicker]').val(''); }, /** diff --git a/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js b/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js index 97b47f77beeab..6f0dd01f33291 100644 --- a/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js +++ b/app/code/Magento/Ui/view/base/web/js/lib/validation/rules.js @@ -803,6 +803,14 @@ define([ }, $.mage.__('Please enter a valid date.') ], + 'validate-date-range': [ + function (value, params) { + var fromDate = $('input[name*="' + params + '"]').val(); + + return moment.utc(value).unix() >= moment.utc(fromDate).unix() || isNaN(moment.utc(value).unix()); + }, + $.mage.__('Make sure the To Date is later than or the same as the From Date.') + ], 'validate-identifier': [ function (value) { return utils.isEmptyNoTrim(value) || /^[a-z0-9][a-z0-9_\/-]+(\.[a-z0-9_-]+)?$/.test(value); diff --git a/app/code/Magento/Ups/Test/Mftf/Test/DefaultConfigForUPSTypeTest.xml b/app/code/Magento/Ups/Test/Mftf/Test/DefaultConfigForUPSTypeTest.xml index 51db704a7abc7..9caa32609c487 100644 --- a/app/code/Magento/Ups/Test/Mftf/Test/DefaultConfigForUPSTypeTest.xml +++ b/app/code/Magento/Ups/Test/Mftf/Test/DefaultConfigForUPSTypeTest.xml @@ -9,9 +9,11 @@ xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="DefaultConfigForUPSTypeTest"> <annotations> + <features value="Ups"/> + <stories value="UPS configuration"/> <title value="Default Configuration for UPS Type"/> + <stories value="UPS"/> <description value="Default Configuration for UPS Type"/> - <features value="Ups"/> <severity value="MAJOR"/> <testCaseId value="MAGETWO-99012"/> <useCaseId value="MAGETWO-98947"/> diff --git a/app/code/Magento/Ups/etc/adminhtml/system.xml b/app/code/Magento/Ups/etc/adminhtml/system.xml index f427c5960123f..8b9dc30a0188b 100644 --- a/app/code/Magento/Ups/etc/adminhtml/system.xml +++ b/app/code/Magento/Ups/etc/adminhtml/system.xml @@ -13,6 +13,9 @@ <field id="access_license_number" translate="label" type="obscure" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="0"> <label>Access License Number</label> <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> + <depends> + <field id="carriers/ups/active">1</field> + </depends> </field> <field id="active" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> <label>Enabled for Checkout</label> @@ -84,6 +87,9 @@ <field id="password" translate="label" type="obscure" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="0"> <label>Password</label> <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> + <depends> + <field id="carriers/ups/active">1</field> + </depends> </field> <field id="pickup" translate="label" type="select" sortOrder="80" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> <label>Pickup Method</label> @@ -113,6 +119,9 @@ <field id="username" translate="label" type="obscure" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="0"> <label>User ID</label> <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model> + <depends> + <field id="carriers/ups/active">1</field> + </depends> </field> <field id="negotiated_active" translate="label" type="select" sortOrder="40" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1"> <label>Enable Negotiated Rates</label> diff --git a/app/code/Magento/UrlRewrite/Controller/Router.php b/app/code/Magento/UrlRewrite/Controller/Router.php index ce432f24365a6..9c1e184cbc506 100644 --- a/app/code/Magento/UrlRewrite/Controller/Router.php +++ b/app/code/Magento/UrlRewrite/Controller/Router.php @@ -43,7 +43,7 @@ class Router implements \Magento\Framework\App\RouterInterface protected $response; /** - * @var \Magento\UrlRewrite\Model\UrlFinderInterface + * @var UrlFinderInterface */ protected $urlFinder; @@ -72,7 +72,6 @@ public function __construct( * Match corresponding URL Rewrite and modify request. * * @param RequestInterface|HttpRequest $request - * * @return ActionInterface|null */ public function match(RequestInterface $request) @@ -104,6 +103,8 @@ public function match(RequestInterface $request) } /** + * Process redirect + * * @param RequestInterface $request * @param UrlRewrite $rewrite * @@ -121,6 +122,8 @@ protected function processRedirect($request, $rewrite) } /** + * Redirect to target URL + * * @param RequestInterface|HttpRequest $request * @param string $url * @param int $code @@ -135,15 +138,19 @@ protected function redirect($request, $url, $code) } /** + * Find rewrite based on request data + * * @param string $requestPath * @param int $storeId * @return UrlRewrite|null */ protected function getRewrite($requestPath, $storeId) { - return $this->urlFinder->findOneByData([ - UrlRewrite::REQUEST_PATH => ltrim($requestPath, '/'), - UrlRewrite::STORE_ID => $storeId, - ]); + return $this->urlFinder->findOneByData( + [ + UrlRewrite::REQUEST_PATH => ltrim($requestPath, '/'), + UrlRewrite::STORE_ID => $storeId, + ] + ); } } diff --git a/app/code/Magento/UrlRewrite/Model/CompositeUrlFinder.php b/app/code/Magento/UrlRewrite/Model/CompositeUrlFinder.php new file mode 100644 index 0000000000000..b463a41af8404 --- /dev/null +++ b/app/code/Magento/UrlRewrite/Model/CompositeUrlFinder.php @@ -0,0 +1,116 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\UrlRewrite\Model; + +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\ObjectManagerInterface; +use Magento\UrlRewrite\Model\MergeDataProviderFactory; + +/** + * Class CompositeUrlFinder + */ +class CompositeUrlFinder implements UrlFinderInterface +{ + /** + * @var ObjectManagerInterface + */ + private $objectManager; + + /** + * @var array + */ + private $children = []; + + /** + * @var MergeDataProviderFactory + */ + private $mergeDataProviderFactory; + + /** + * @var ScopeConfigInterface + */ + private $config; + + /** + * @param array $children + * @param ObjectManagerInterface $objectManager + * @param MergeDataProviderFactory $mergeDataProviderFactory + * @param ScopeConfigInterface $config + */ + public function __construct( + array $children, + ObjectManagerInterface $objectManager, + MergeDataProviderFactory $mergeDataProviderFactory, + ScopeConfigInterface $config + ) { + $this->children = $children; + $this->objectManager = $objectManager; + $this->mergeDataProviderFactory = $mergeDataProviderFactory; + $this->config = $config; + } + + /** + * Check config value of generate_category_product_rewrites + * + * @return bool + */ + private function isCategoryRewritesEnabled(): bool + { + return (bool)$this->config->getValue('catalog/seo/generate_category_product_rewrites'); + } + + /** + * @inheritdoc + */ + public function findAllByData(array $data) + { + $isDynamicRewrites = !$this->isCategoryRewritesEnabled(); + + $mergeDataProvider = $this->mergeDataProviderFactory->create(); + foreach ($this->getChildren() as $child) { + $urlFinder = $this->objectManager->get($child['class']); + $rewrites = $urlFinder->findAllByData($data); + if (!$isDynamicRewrites) { + return $rewrites; + } + $mergeDataProvider->merge($rewrites); + } + return $mergeDataProvider->getData(); + } + + /** + * @inheritdoc + */ + public function findOneByData(array $data) + { + foreach ($this->getChildren() as $child) { + $urlFinder = $this->objectManager->get($child['class']); + $rewrite = $urlFinder->findOneByData($data); + if (!empty($rewrite)) { + return $rewrite; + } + } + return null; + } + + /** + * Get children in sorted order + * + * @return array + */ + private function getChildren(): array + { + uasort( + $this->children, + function ($first, $second) { + return (int)$first['sortOrder'] <=> (int)$second['sortOrder']; + } + ); + return $this->children; + } +} diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTest.xml index 2c2dd48caeaa9..44fad061d7656 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTest.xml @@ -14,7 +14,7 @@ <title value="Url Rewrites Correctly Generated for Multiple Storeviews During Product Import"/> <description value="Check Url Rewrites Correctly Generated for Multiple Storeviews During Product Import."/> <severity value="CRITICAL"/> - <testCaseId value="MAGETWO-68980"/> + <testCaseId value="MC-12656"/> <group value="urlRewrite"/> </annotations> <before> @@ -114,4 +114,142 @@ <seeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue('category-dutch/productformagetwo68980-dutch.html')}}" stepKey="seeUrlInRequestPathColumn5"/> <seeElement selector="{{AdminUrlRewriteIndexSection.targetPathColumnValue(catalog/product/view/id/$grabProductIdFromUrl/category/$$createCategory.id$$)}}" stepKey="seeUrlInTargetPathColumn5"/> </test> + <test name="AdminCheckUrlRewritesCorrectlyGeneratedForMultipleStoreviewsDuringProductImportTestWithConfigurationTurnedOff"> + <annotations> + <features value="Url Rewrite"/> + <stories value="Url Rewrites for Multiple Storeviews"/> + <title value="Url Rewrites Correctly Generated for Multiple Storeviews During Product Import With Configuration Turned Off"/> + <description value="Check Url Rewrites Correctly Generated for Multiple Storeviews During Product Import."/> + <severity value="CRITICAL"/> + <testCaseId value="MC-6802"/> + <group value="urlRewrite"/> + </annotations> + <before> + <!-- Set the configuration for Generate "category/product" URL Rewrites to Yes (default)--> + <comment userInput="Enable SEO configuration setting to generate category/product URL Rewrites" stepKey="commentEnableUrlRewriteConfig" /> + <magentoCLI command="config:set catalog/seo/generate_category_product_rewrites 1" stepKey="enableGenerateUrlRewrite"/> + <!--Flush cache--> + <magentoCLI command="cache:flush" stepKey="cleanCache1"/> + + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <!-- Create Store View EN --> + <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreViewEn"> + <argument name="customStore" value="customStoreENNotUnique"/> + </actionGroup> + <!-- Create Store View NL --> + <actionGroup ref="AdminCreateStoreViewActionGroup" stepKey="createStoreViewNl"> + <argument name="customStore" value="customStoreNLNotUnique"/> + </actionGroup> + <createData entity="ApiCategory" stepKey="createCategory"> + <field key="name">category-admin</field> + </createData> + + <!-- Set the configuration for Generate "category/product" URL Rewrites to No--> + <comment userInput="Disable SEO configuration setting to generate category/product URL Rewrites" stepKey="commentDisableUrlRewriteConfig" /> + <magentoCLI command="config:set catalog/seo/generate_category_product_rewrites 0" stepKey="disableGenerateUrlRewrite"/> + <!--Flush cache--> + <magentoCLI command="cache:flush" stepKey="cleanCache"/> + </before> + <after> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <actionGroup ref="deleteProductByName" stepKey="deleteImportedProduct"> + <argument name="sku" value="productformagetwo68980"/> + <argument name="name" value="productformagetwo68980"/> + </actionGroup> + <actionGroup ref="clearFiltersAdminDataGrid" stepKey="clearFiltersIfSet"/> + <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteStoreViewEn"> + <argument name="customStore" value="customStoreENNotUnique"/> + </actionGroup> + <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteStoreViewNl"> + <argument name="customStore" value="customStoreNLNotUnique"/> + </actionGroup> + <actionGroup ref="logout" stepKey="logout"/> + <magentoCLI command="config:set catalog/seo/generate_category_product_rewrites 1" stepKey="resetConfigurationSetting"/> + <!--Flush cache--> + <magentoCLI command="cache:flush" stepKey="cleanCache2"/> + </after> + <actionGroup ref="switchCategoryStoreView" stepKey="switchToStoreViewEn"> + <argument name="Store" value="customStoreENNotUnique.name"/> + <argument name="CatName" value="$$createCategory.name$$"/> + </actionGroup> + <uncheckOption selector="{{AdminCategoryBasicFieldSection.categoryNameUseDefault}}" stepKey="uncheckUseDefaultValueENStoreView"/> + <fillField selector="{{AdminCategoryBasicFieldSection.CategoryNameInput}}" userInput="category-english" stepKey="changeNameField"/> + <click selector="{{AdminCategorySEOSection.SectionHeader}}" stepKey="clickOnSectionHeader"/> + <actionGroup ref="ChangeSeoUrlKeyForSubCategory" stepKey="changeSeoUrlKeyENStoreView"> + <argument name="value" value="category-english"/> + </actionGroup> + <actionGroup ref="switchCategoryStoreView" stepKey="switchToStoreViewNl"> + <argument name="Store" value="customStoreNLNotUnique.name"/> + <argument name="CatName" value="$$createCategory.name$$"/> + </actionGroup> + <uncheckOption selector="{{AdminCategoryBasicFieldSection.categoryNameUseDefault}}" stepKey="uncheckUseDefaultValue1"/> + <fillField selector="{{AdminCategoryBasicFieldSection.CategoryNameInput}}" userInput="category-dutch" stepKey="changeNameFieldNLStoreView"/> + <click selector="{{AdminCategorySEOSection.SectionHeader}}" stepKey="clickOnSectionHeader2"/> + <actionGroup ref="ChangeSeoUrlKeyForSubCategory" stepKey="changeSeoUrlKeyNLStoreView"> + <argument name="value" value="category-dutch"/> + </actionGroup> + <amOnPage url="{{AdminImportIndexPage.url}}" stepKey="navigateToSystemImport"/> + <selectOption selector="{{AdminImportMainSection.entityType}}" userInput="Products" stepKey="selectProductsOption"/> + <waitForElementVisible selector="{{AdminImportMainSection.importBehavior}}" stepKey="waitForImportBehaviorElementVisible"/> + <selectOption selector="{{AdminImportMainSection.importBehavior}}" userInput="Add/Update" stepKey="selectAddUpdateOption"/> + <attachFile selector="{{AdminImportMainSection.selectFileToImport}}" userInput="import_updated.csv" stepKey="attachFileForImport"/> + <click selector="{{AdminImportHeaderSection.checkDataButton}}" stepKey="clickCheckDataButton"/> + <see selector="{{AdminMessagesSection.notice}}" userInput="Checked rows: 3, checked entities: 1, invalid rows: 0, total errors: 0" stepKey="assertNotice"/> + <see selector="{{AdminMessagesSection.successMessage}}" userInput="File is valid! To start import process press "Import" button" stepKey="assertSuccessMessage"/> + <click selector="{{AdminImportMainSection.importButton}}" stepKey="clickImportButton"/> + <see selector="{{AdminMessagesSection.successMessage}}" userInput="Import successfully done" stepKey="assertSuccessMessage1"/> + <see selector="{{AdminMessagesSection.notice}}" userInput="Created: 1, Updated: 0, Deleted: 0" stepKey="assertNotice1"/> + <actionGroup ref="SearchForProductOnBackendByNameActionGroup" stepKey="searchForProductOnBackend"> + <argument name="productName" value="productformagetwo68980"/> + </actionGroup> + <click selector="{{AdminProductGridSection.productRowBySku('productformagetwo68980')}}" stepKey="clickOnProductRow"/> + <grabFromCurrentUrl regex="~/id/(\d+)/~" stepKey="grabProductIdFromUrl"/> + <amOnPage url="{{AdminUrlRewriteIndexPage.url}}" stepKey="goToUrlRewritesIndexPage"/> + + <fillField selector="{{AdminUrlRewriteIndexSection.requestPathFilter}}" userInput="category-english.html" stepKey="inputCategoryUrlForENStoreView"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickSearchButton"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue('category-english.html')}}" stepKey="seeUrlInRequestPathColumn"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.targetPathColumnValue(catalog/category/view/id/$$createCategory.id$$)}}" stepKey="seeUrlInTargetPathColumn"/> + + <fillField selector="{{AdminUrlRewriteIndexSection.requestPathFilter}}" userInput="category-dutch.html" stepKey="inputCategoryUrlForNLStoreView"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickSearchButton1"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue('category-dutch.html')}}" stepKey="seeUrlInRequestPathColumn1"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.targetPathColumnValue(catalog/category/view/id/$$createCategory.id$$)}}" stepKey="seeUrlInTargetPathColumn1"/> + + <fillField selector="{{AdminUrlRewriteIndexSection.requestPathFilter}}" userInput="productformagetwo68980-english.html" stepKey="inputProductUrlForENStoreView"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickSearchButton2"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue('productformagetwo68980-english.html')}}" stepKey="seeUrlInRequestPathColumn2"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.targetPathColumnValue('catalog/product/view/id/$grabProductIdFromUrl')}}" stepKey="seeUrlInTargetPathColumn2"/> + <dontSeeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue('category-english/productformagetwo68980-english.html')}}" stepKey="seeUrlInRequestPathColumn4"/> + <dontSeeElement selector="{{AdminUrlRewriteIndexSection.targetPathColumnValue(catalog/product/view/id/$grabProductIdFromUrl/category/$$createCategory.id$$)}}" stepKey="seeUrlInTargetPathColumn4"/> + + <fillField selector="{{AdminUrlRewriteIndexSection.requestPathFilter}}" userInput="productformagetwo68980-dutch.html" stepKey="inputProductUrlForENStoreView1"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickSearchButton3"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue('productformagetwo68980-dutch.html')}}" stepKey="seeUrlInRequestPathColumn3"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.targetPathColumnValue('catalog/product/view/id/$grabProductIdFromUrl')}}" stepKey="seeUrlInTargetPathColumn3"/> + <dontSeeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue('category-dutch/productformagetwo68980-dutch.html')}}" stepKey="seeUrlInRequestPathColumn5"/> + <dontSeeElement selector="{{AdminUrlRewriteIndexSection.targetPathColumnValue(catalog/product/view/id/$grabProductIdFromUrl/category/$$createCategory.id$$)}}" stepKey="seeUrlInTargetPathColumn5"/> + + <!-- Switch StoreView --> + <amOnPage url="{{StorefrontHomePage.url}}" stepKey="amOnProduct4Page"/> + <actionGroup ref="StorefrontSwitchStoreViewActionGroup" stepKey="switchToCustomStoreView"> + <argument name="storeView" value="customStoreENNotUnique"/> + </actionGroup> + + <amOnPage url="/productformagetwo68980-english.html" stepKey="navigateToProductPage"/> + <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="productformagetwo68980-english" stepKey="seeProductName"/> + <amOnPage url="/category-english/productformagetwo68980-english.html" stepKey="navigateToProductPage2"/> + <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="productformagetwo68980-english" stepKey="seeProductName2"/> + + <!-- Switch StoreView --> + <amOnPage url="{{StorefrontHomePage.url}}" stepKey="amOnProduct4Page2"/> + <actionGroup ref="StorefrontSwitchStoreViewActionGroup" stepKey="switchToCustomStoreView2"> + <argument name="storeView" value="customStoreNLNotUnique"/> + </actionGroup> + + <amOnPage url="/productformagetwo68980-dutch.html" stepKey="navigateToProductPage3"/> + <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="productformagetwo68980-dutch" stepKey="seeProductName3"/> + <amOnPage url="/category-dutch/productformagetwo68980-dutch.html" stepKey="navigateToProductPage4"/> + <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="productformagetwo68980-dutch" stepKey="seeProductName4"/> + </test> </tests> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductWithSeveralWebsitesAndCheckURLRewritesTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductWithSeveralWebsitesAndCheckURLRewritesTest.xml index 83c1e5c0a5e0a..8712edb69e499 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductWithSeveralWebsitesAndCheckURLRewritesTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminCreateProductWithSeveralWebsitesAndCheckURLRewritesTest.xml @@ -16,6 +16,9 @@ <severity value="CRITICAL"/> <group value="urlRewrite"/> <group value="mtf_migrated"/> + <skip> + <issueId value="MC-17181"/> + </skip> </annotations> <before> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminGenerateUrlRewritesForProductInCategoriesSwitchOffTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminGenerateUrlRewritesForProductInCategoriesSwitchOffTest.xml new file mode 100644 index 0000000000000..884c7778786a3 --- /dev/null +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminGenerateUrlRewritesForProductInCategoriesSwitchOffTest.xml @@ -0,0 +1,73 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + <test name="AdminGenerateUrlRewritesForProductInCategoriesSwitchOffTest"> + <annotations> + <features value="Url Rewrite"/> + <stories value="Url Rewrites cleared in case of switching configuration off"/> + <title value="Clear Url Rewrites after configuration turned off"/> + <description value="Check Url Rewrites for product in categories is correctly cleared after configuration turned off"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-16330"/> + <group value="urlRewrite"/> + </annotations> + + <before> + <!-- Set the configuration for Generate "category/product" URL Rewrites--> + <comment userInput="Enable config to generate category/product URL Rewrites" stepKey="commentEnableConfig" /> + <magentoCLI command="config:set catalog/seo/generate_category_product_rewrites 1" stepKey="enableGenerateUrlRewrite"/> + <!--Flush cache--> + <magentoCLI command="cache:flush" stepKey="cleanCache1"/> + + <actionGroup ref="LoginAsAdmin" stepKey="login"/> + <createData entity="SimpleSubCategory" stepKey="createCategory"/> + <createData entity="SimpleProduct" stepKey="createSimpleProduct"> + <requiredEntity createDataKey="createCategory"/> + </createData> + </before> + <after> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteProduct"/> + + <!-- Set the configuration for Generate "category/product" URL Rewrites--> + <comment userInput="Enable config to generate category/product URL Rewrites" stepKey="commentEnableConfig" /> + <magentoCLI command="config:set catalog/seo/generate_category_product_rewrites 1" stepKey="enableGenerateUrlRewrite"/> + <!--Flush cache--> + <magentoCLI command="cache:flush" stepKey="cleanCache1"/> + + <actionGroup ref="logout" stepKey="logout"/> + </after> + + <!-- 1. Open Marketing - SEO & Search - URL Rewrites --> + <amOnPage url="{{AdminUrlRewriteIndexPage.url}}" stepKey="amOnUrlRewriteIndexPage"/> + <fillField selector="{{AdminUrlRewriteIndexSection.requestPathFilter}}" userInput="$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="inputProductName"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickSearchButton"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue($createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeValue1"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue($createCategory.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeValue2"/> + + <!-- 2. Set the configuration for Generate "category/product" URL Rewrites to No--> + <amOnPage url="{{CatalogConfigPage.url}}" stepKey="amOnCatalogConfigPage"/> + <conditionalClick selector="{{CatalogSection.seo}}" dependentSelector="{{CatalogSection.CheckIfSeoTabExpand}}" visible="true" stepKey="expandSeoTab" /> + <waitForElementVisible selector="{{CatalogSection.GenerateUrlRewrites}}" stepKey="GenerateUrlRewritesSelect"/> + <selectOption userInput="0" selector="{{CatalogSection.GenerateUrlRewrites}}" stepKey="selectUrlGenerationNo" /> + <waitForElementVisible selector="{{GenerateUrlRewritesConfirm.title}}" stepKey="waitForConfirmModal"/> + <click selector="{{GenerateUrlRewritesConfirm.ok}}" stepKey="confirmSwitchingGenerationOff"/> + <click selector="{{CatalogSection.save}}" stepKey="saveConfig" /> + <waitForPageLoad stepKey="waitForSavingSystemConfiguration"/> + + <!-- 3. Flush cache--> + <magentoCLI command="cache:flush" stepKey="cleanCache"/> + + <!-- 4. Open Marketing - SEO & Search - URL Rewrites --> + <amOnPage url="{{AdminUrlRewriteIndexPage.url}}" stepKey="amOnUrlRewriteIndexPage2"/> + <fillField selector="{{AdminUrlRewriteIndexSection.requestPathFilter}}" userInput="$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="inputProductName2"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickSearchButton2"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue($createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue1"/> + <dontSeeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue($createCategory.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="dontSeeValue2"/> + </test> +</tests> diff --git a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest.xml b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest.xml index 0d9df9176d2b5..b708b63599173 100644 --- a/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest.xml +++ b/app/code/Magento/UrlRewrite/Test/Mftf/Test/AdminUrlRewritesForProductInAnchorCategoriesTest.xml @@ -14,7 +14,7 @@ <title value="Url-rewrites for product in anchor categories"/> <description value="For a product with category that has parent anchor categories, the rewrites is created when the category/product is saved."/> <severity value="CRITICAL"/> - <testCaseId value="MAGETWO-69826"/> + <testCaseId value="MC-16568"/> <group value="urlRewrite"/> </annotations> @@ -32,6 +32,9 @@ <createData entity="ApiSimpleProduct" stepKey="createSimpleProduct"> <requiredEntity createDataKey="simpleSubCategory3"/> </createData> + <!-- Set the configuration for Generate "category/product" URL Rewrites--> + <comment userInput="Enable config to generate category/product URL Rewrites " stepKey="commentEnableConfig" /> + <magentoCLI command="config:set catalog/seo/generate_category_product_rewrites 1" stepKey="enableGenerateUrlRewrite"/> </before> <after> <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> @@ -41,7 +44,6 @@ <!-- Steps --> <!-- 1. Log in to Admin --> <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> - <!-- 2. Open Marketing - SEO & Search - URL Rewrites --> <amOnPage url="{{AdminUrlRewriteIndexPage.url}}" stepKey="amOnUrlRewriteIndexPage"/> <fillField selector="{{AdminUrlRewriteIndexSection.requestPathFilter}}" userInput="$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="inputProductName"/> @@ -76,4 +78,204 @@ <seeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue($simpleSubCategory1.custom_attributes[url_key]$-new/$simpleSubCategory2.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue6"/> <seeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue($simpleSubCategory1.custom_attributes[url_key]$-new/$simpleSubCategory2.custom_attributes[url_key]$/$simpleSubCategory3.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue7"/> </test> + + <test name="AdminUrlRewritesForProductInAnchorCategoriesTestWithConfigurationTurnedOff"> + <annotations> + <features value="Url Rewrite"/> + <stories value="Url-rewrites for product in anchor categories"/> + <title value="Url-rewrites for product in anchor categories with configuration turned off"/> + <description value="For a product with category that has parent anchor categories, the rewrites is created when the category/product is saved."/> + <severity value="CRITICAL"/> + <testCaseId value="MC-6844"/> + <group value="urlRewrite"/> + </annotations> + + <!-- Preconditions--> + <!-- Create 3 categories --> + <before> + <!-- Set the configuration for Generate "category/product" URL Rewrites to Yes (default)--> + <comment userInput="Enable SEO configuration setting to generate category/product URL Rewrites" stepKey="commentEnableUrlRewriteConfig" /> + <magentoCLI command="config:set catalog/seo/generate_category_product_rewrites 1" stepKey="enableGenerateUrlRewrite"/> + <!--Flush cache--> + <magentoCLI command="cache:flush" stepKey="cleanCache1"/> + + <createData entity="SimpleSubCategory" stepKey="simpleSubCategory1"/> + <createData entity="SubCategoryWithParent" stepKey="simpleSubCategory2"> + <requiredEntity createDataKey="simpleSubCategory1"/> + </createData> + <createData entity="SubCategoryWithParent" stepKey="simpleSubCategory3"> + <requiredEntity createDataKey="simpleSubCategory2"/> + </createData> + <!-- Create Simple product 1 and assign it to Category 3 --> + <createData entity="ApiSimpleProduct" stepKey="createSimpleProduct"> + <requiredEntity createDataKey="simpleSubCategory3"/> + </createData> + <!-- Set the configuration for Generate "category/product" URL Rewrites to No--> + <comment userInput="Disable SEO configuration setting to generate category/product URL Rewrites" stepKey="commentDisableUrlRewriteConfig" /> + <magentoCLI command="config:set catalog/seo/generate_category_product_rewrites 0" stepKey="disableGenerateUrlRewrite"/> + <!--Flush cache--> + <magentoCLI command="cache:flush" stepKey="cleanCache"/> + </before> + <after> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> + <deleteData createDataKey="simpleSubCategory1" stepKey="deletesimpleSubCategory1"/> + <amOnPage url="{{AdminLogoutPage.url}}" stepKey="amOnLogoutPage"/> + <magentoCLI command="config:set catalog/seo/generate_category_product_rewrites 1" stepKey="resetConfigurationSetting"/> + <!--Flush cache--> + <magentoCLI command="cache:flush" stepKey="cleanCache2"/> + </after> + <!-- Steps --> + <!-- 1. Log in to Admin --> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <!-- 2. Open Marketing - SEO & Search - URL Rewrites --> + <amOnPage url="{{AdminUrlRewriteIndexPage.url}}" stepKey="amOnUrlRewriteIndexPage"/> + <fillField selector="{{AdminUrlRewriteIndexSection.requestPathFilter}}" userInput="$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="inputProductName"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickSearchButton"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue($createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeValue1"/> + <dontSeeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue($simpleSubCategory1.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeValue2"/> + <dontSeeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue($simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeValue3"/> + <dontSeeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue($simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$simpleSubCategory3.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeValue4"/> + + <!-- 3. Edit Category 1 for DEFAULT Store View: --> + <actionGroup ref="switchCategoryStoreView" stepKey="switchStoreView"> + <argument name="Store" value="_defaultStore.name"/> + <argument name="CatName" value="$$simpleSubCategory1.name$$"/> + </actionGroup> + <click selector="{{AdminCategorySEOSection.SectionHeader}}" stepKey="openSeoSection2"/> + <uncheckOption selector="{{AdminCategorySEOSection.UrlKeyDefaultValueCheckbox}}" stepKey="uncheckRedirect2"/> + <fillField selector="{{AdminCategorySEOSection.UrlKeyInput}}" userInput="$simpleSubCategory1.custom_attributes[url_key]$-new" stepKey="changeURLKey"/> + <checkOption selector="{{AdminCategorySEOSection.UrlKeyRedirectCheckbox}}" stepKey="checkUrlKeyRedirect"/> + <!-- 4. Save Category 1 --> + <click selector="{{AdminCategoryMainActionsSection.SaveButton}}" stepKey="saveCategory"/> + <seeElement selector="{{AdminCategoryMessagesSection.SuccessMessage}}" stepKey="assertSuccessMessageAfterSaved"/> + + <!-- 5. Open Marketing - SEO & Search - URL Rewrites --> + <amOnPage url="{{AdminUrlRewriteIndexPage.url}}" stepKey="amOnUrlRewriteIndexPage2"/> + <fillField selector="{{AdminUrlRewriteIndexSection.requestPathFilter}}" userInput="$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="inputProductName2"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickSearchButton2"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue($createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue1"/> + <dontSeeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue($simpleSubCategory1.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue2"/> + <dontSeeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue($simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue3"/> + <dontSeeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue($simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$simpleSubCategory3.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue4"/> + <dontSeeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue($simpleSubCategory1.custom_attributes[url_key]$-new/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue5"/> + <dontSeeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue($simpleSubCategory1.custom_attributes[url_key]$-new/$simpleSubCategory2.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue6"/> + <dontSeeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue($simpleSubCategory1.custom_attributes[url_key]$-new/$simpleSubCategory2.custom_attributes[url_key]$/$simpleSubCategory3.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeInListValue7"/> + + <amOnPage url="/$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="navigateToProductPage"/> + <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$createSimpleProduct.name$$" stepKey="seeProductName"/> + + <amOnPage url="/$simpleSubCategory1.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="navigateToProductPage2"/> + <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$createSimpleProduct.name$$" stepKey="seeProductName2"/> + <amOnPage url="/$simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="navigateToProductPage3"/> + <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$createSimpleProduct.name$$" stepKey="seeProductName3"/> + <amOnPage url="/$simpleSubCategory1.custom_attributes[url_key]$/$simpleSubCategory2.custom_attributes[url_key]$/$simpleSubCategory3.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="navigateToProductPage4"/> + <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$createSimpleProduct.name$$" stepKey="seeProductName4"/> + + <amOnPage url="/$simpleSubCategory1.custom_attributes[url_key]$-new/$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="navigateToProductPage5"/> + <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$createSimpleProduct.name$$" stepKey="seeProductName5"/> + <amOnPage url="/$simpleSubCategory1.custom_attributes[url_key]$-new/$simpleSubCategory2.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="navigateToProductPage6"/> + <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$createSimpleProduct.name$$" stepKey="seeProductName6"/> + <amOnPage url="/$simpleSubCategory1.custom_attributes[url_key]$-new/$simpleSubCategory2.custom_attributes[url_key]$/$simpleSubCategory3.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="navigateToProductPage7"/> + <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$createSimpleProduct.name$$" stepKey="seeProductName7"/> + </test> + + <test name="AdminUrlRewritesForProductInAnchorCategoriesTestAllStoreView" extends="AdminUrlRewritesForProductInAnchorCategoriesTest"> + <annotations> + <features value="Url Rewrite"/> + <stories value="Url-rewrites for product in anchor categories for all store views"/> + <title value="Url-rewrites for product in anchor categories for all store views"/> + <description value="Verify that Saving category do not delete UrlRewrites for subcategories and all products in them."/> + <severity value="CRITICAL"/> + <testCaseId value="MC-16681"/> + <group value="urlRewrite"/> + </annotations> + <before> + <remove keyForRemoval="createSimpleProduct"/> + <!-- Create Simple product 1 and assign it to all the threee categories above --> + <createData entity="ApiSimpleProduct" stepKey="createSimpleProduct" after="simpleSubCategory3"> + <requiredEntity createDataKey="simpleSubCategory1"/> + <requiredEntity createDataKey="simpleSubCategory2"/> + <requiredEntity createDataKey="simpleSubCategory3"/> + </createData> + </before> + <remove keyForRemoval="switchStoreView"/> + <!-- 3. Edit Category 1 for All store view: --> + <actionGroup ref="navigateToCreatedCategory" stepKey="goToCategoryPage" after="seeValue4"> + <argument name="Category" value="$$simpleSubCategory1$$"/> + </actionGroup> + <remove keyForRemoval="uncheckRedirect2"/> + </test> + + <test name="AdminUrlRewritesForProductInAnchorCategoriesTestAllStoreViewWithConfigurationTurnedOff" extends="AdminUrlRewritesForProductInAnchorCategoriesTestWithConfigurationTurnedOff"> + <annotations> + <features value="Url Rewrite"/> + <stories value="Url-rewrites for product in anchor categories for all store views"/> + <title value="Url-rewrites for product in anchor categories for all store views with configuration turned off"/> + <description value="Url-rewrites for product in anchor categories for all store views with configuration turned off"/> + <severity value="CRITICAL"/> + <testCaseId value="MC-6964"/> + <group value="urlRewrite"/> + </annotations> + <before> + <remove keyForRemoval="createSimpleProduct"/> + <!-- Create Simple product 1 and assign it to all the threee categories above --> + <createData entity="ApiSimpleProduct" stepKey="createSimpleProduct" after="simpleSubCategory3"> + <requiredEntity createDataKey="simpleSubCategory1"/> + <requiredEntity createDataKey="simpleSubCategory2"/> + <requiredEntity createDataKey="simpleSubCategory3"/> + </createData> + </before> + <remove keyForRemoval="switchStoreView"/> + <!-- 3. Edit Category 1 for All store view: --> + <actionGroup ref="navigateToCreatedCategory" stepKey="goToCategoryPage" after="seeValue4"> + <argument name="Category" value="$$simpleSubCategory1$$"/> + </actionGroup> + <remove keyForRemoval="uncheckRedirect2"/> + </test> + + <test name="AdminUrlRewritesForProductsWithConfigurationTurnedOff"> + <annotations> + <features value="Url Rewrite"/> + <stories value="No Url-rewrites for product if configuration to generate url rewrite for Generate 'category/product' URL Rewrites is enabled "/> + <title value="No auto generated of request path for simple product when assigned to subCategory"/> + <description value="No auto generated of request path when SEO configuration to Generate url rewrite for Generate 'category/product' URL Rewrites is set to No"/> + <severity value="CRITICAL"/> + <testCaseId value="MAGETWO-94803"/> + <group value="urlRewrite"/> + </annotations> + <before> + <!-- Set the configuration for Generate "category/product" URL Rewrites to Yes (default)--> + <comment userInput="Enable SEO configuration setting to generate category/product URL Rewrites" stepKey="commentEnableUrlRewriteConfig" /> + <magentoCLI command="config:set catalog/seo/generate_category_product_rewrites 1" stepKey="enableGenerateUrlRewrite"/> + <!--Flush cache--> + <magentoCLI command="cache:flush" stepKey="cleanCache1"/> + <createData entity="SimpleSubCategory" stepKey="simpleSubCategory1"/> + <!-- Create Simple product 1 and assign it to Category 1 --> + <createData entity="ApiSimpleProduct" stepKey="createSimpleProduct"> + <requiredEntity createDataKey="simpleSubCategory1"/> + </createData> + <!-- Set the configuration for Generate "category/product" URL Rewrites to No--> + <comment userInput="Disable SEO configuration setting to generate category/product URL Rewrites" stepKey="commentDisableUrlRewriteConfig" /> + <magentoCLI command="config:set catalog/seo/generate_category_product_rewrites 0" stepKey="disableGenerateUrlRewrite"/> + <!--Flush cache--> + <magentoCLI command="cache:flush" stepKey="cleanCache"/> + </before> + <after> + <deleteData createDataKey="createSimpleProduct" stepKey="deleteSimpleProduct"/> + <deleteData createDataKey="simpleSubCategory1" stepKey="deletesimpleSubCategory1"/> + <magentoCLI command="config:set catalog/seo/generate_category_product_rewrites 1" stepKey="resetConfigurationSetting"/> + <!--Flush cache--> + <magentoCLI command="cache:flush" stepKey="cleanCache2"/> + </after> + <!-- 1. Log in to Admin --> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <!-- 2. Open Marketing - SEO & Search - URL Rewrites --> + <amOnPage url="{{AdminUrlRewriteIndexPage.url}}" stepKey="amOnUrlRewriteIndexPage"/> + <fillField selector="{{AdminUrlRewriteIndexSection.requestPathFilter}}" userInput="$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="inputProductName5"/> + <click selector="{{AdminDataGridHeaderSection.applyFilters}}" stepKey="clickSearchButton5"/> + <seeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue($createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="seeProducturl"/> + <dontSeeElement selector="{{AdminUrlRewriteIndexSection.requestPathColumnValue($simpleSubCategory1.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html)}}" stepKey="dontSeeCategoryProducturlKey"/> + <amOnPage url="/$simpleSubCategory1.custom_attributes[url_key]$/$createSimpleProduct.custom_attributes[url_key]$.html" stepKey="navigateToProductPage"/> + <see selector="{{StorefrontProductInfoMainSection.productName}}" userInput="$$createSimpleProduct.name$$" stepKey="seeProductName"/> + </test> </tests> diff --git a/app/code/Magento/UrlRewrite/Test/Unit/Controller/RouterTest.php b/app/code/Magento/UrlRewrite/Test/Unit/Controller/RouterTest.php index 642ca0f9af6d1..b22fc55938468 100644 --- a/app/code/Magento/UrlRewrite/Test/Unit/Controller/RouterTest.php +++ b/app/code/Magento/UrlRewrite/Test/Unit/Controller/RouterTest.php @@ -45,6 +45,7 @@ class RouterTest extends \PHPUnit\Framework\TestCase */ protected function setUp() { + $objectManager = new ObjectManager($this); $this->actionFactory = $this->createMock(\Magento\Framework\App\ActionFactory::class); $this->url = $this->createMock(\Magento\Framework\UrlInterface::class); $this->storeManager = $this->createMock(\Magento\Store\Model\StoreManagerInterface::class); @@ -59,7 +60,7 @@ protected function setUp() \Magento\Store\Model\Store::class )->disableOriginalConstructor()->getMock(); - $this->router = (new ObjectManager($this))->getObject( + $this->router = $objectManager->getObject( \Magento\UrlRewrite\Controller\Router::class, [ 'actionFactory' => $this->actionFactory, @@ -139,15 +140,17 @@ public function testRewriteAfterStoreSwitcher() $this->urlFinder ->expects($this->any()) ->method('findOneByData') - ->willReturnMap([ + ->willReturnMap( [ [ - UrlRewrite::REQUEST_PATH => $initialRequestPath, - UrlRewrite::STORE_ID => $currentStoreId, - ], - $urlRewrite, + [ + UrlRewrite::REQUEST_PATH => $initialRequestPath, + UrlRewrite::STORE_ID => $currentStoreId, + ], + $urlRewrite, + ] ] - ]); + ); $this->actionFactory ->expects($this->once()) ->method('create') @@ -203,21 +206,23 @@ public function testNoRewriteAfterStoreSwitcherWhenOldRewriteEqualsToNewOne() $urlRewrite->expects($this->any())->method('getRequestPath')->will($this->returnValue('old-request-path')); $this->urlFinder->expects($this->any())->method('findOneByData')->will( - $this->returnValueMap([ - [ - [UrlRewrite::REQUEST_PATH => 'request-path', UrlRewrite::STORE_ID => 'old-store-id'], - $oldUrlRewrite, - ], + $this->returnValueMap( [ [ - UrlRewrite::ENTITY_TYPE => 'entity-type', - UrlRewrite::ENTITY_ID => 'entity-id', - UrlRewrite::STORE_ID => 'current-store-id', - UrlRewrite::IS_AUTOGENERATED => 1, + [UrlRewrite::REQUEST_PATH => 'request-path', UrlRewrite::STORE_ID => 'old-store-id'], + $oldUrlRewrite, ], - $urlRewrite - ], - ]) + [ + [ + UrlRewrite::ENTITY_TYPE => 'entity-type', + UrlRewrite::ENTITY_ID => 'entity-id', + UrlRewrite::STORE_ID => 'current-store-id', + UrlRewrite::IS_AUTOGENERATED => 1, + ], + $urlRewrite + ], + ] + ) ); $this->assertNull($this->router->match($this->request)); diff --git a/app/code/Magento/UrlRewrite/etc/di.xml b/app/code/Magento/UrlRewrite/etc/di.xml index 26055efbd2ba8..e09c48ff89141 100644 --- a/app/code/Magento/UrlRewrite/etc/di.xml +++ b/app/code/Magento/UrlRewrite/etc/di.xml @@ -16,4 +16,14 @@ </argument> </arguments> </type> + <type name="Magento\UrlRewrite\Model\CompositeUrlFinder"> + <arguments> + <argument name="children" xsi:type="array"> + <item name="default" xsi:type="array"> + <item name="class" xsi:type="string">Magento\UrlRewrite\Model\Storage\DbStorage</item> + <item name="sortOrder" xsi:type="number">10</item> + </item> + </argument> + </arguments> + </type> </config> diff --git a/app/code/Magento/UrlRewrite/etc/frontend/di.xml b/app/code/Magento/UrlRewrite/etc/frontend/di.xml index bc5b6aa767fa8..3978b0ee977c5 100644 --- a/app/code/Magento/UrlRewrite/etc/frontend/di.xml +++ b/app/code/Magento/UrlRewrite/etc/frontend/di.xml @@ -17,4 +17,5 @@ </argument> </arguments> </type> + <preference for="Magento\UrlRewrite\Model\UrlFinderInterface" type="Magento\UrlRewrite\Model\CompositeUrlFinder"/> </config> diff --git a/app/code/Magento/UrlRewrite/view/adminhtml/templates/categories.phtml b/app/code/Magento/UrlRewrite/view/adminhtml/templates/categories.phtml index baff22b342b06..632d9f6f85fd0 100644 --- a/app/code/Magento/UrlRewrite/view/adminhtml/templates/categories.phtml +++ b/app/code/Magento/UrlRewrite/view/adminhtml/templates/categories.phtml @@ -31,4 +31,4 @@ } </script> -<?php endif; ?> \ No newline at end of file +<?php endif; ?> diff --git a/app/code/Magento/UrlRewriteGraphQl/Model/Resolver/EntityUrl.php b/app/code/Magento/UrlRewriteGraphQl/Model/Resolver/EntityUrl.php index c842d660a6176..6e6c915959a16 100644 --- a/app/code/Magento/UrlRewriteGraphQl/Model/Resolver/EntityUrl.php +++ b/app/code/Magento/UrlRewriteGraphQl/Model/Resolver/EntityUrl.php @@ -8,6 +8,7 @@ namespace Magento\UrlRewriteGraphQl\Model\Resolver; use Magento\Framework\GraphQl\Exception\GraphQlInputException; +use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException; use Magento\Framework\GraphQl\Schema\Type\ResolveInfo; use Magento\Framework\GraphQl\Config\Element\Field; use Magento\Framework\GraphQl\Query\ResolverInterface; @@ -73,6 +74,11 @@ public function resolve( $url = $customUrl ?: $url; $urlRewrite = $this->findCanonicalUrl($url); if ($urlRewrite) { + if (!$urlRewrite->getEntityId()) { + throw new GraphQlNoSuchEntityException( + __('No such entity found with matching URL key: %url', ['url' => $url]) + ); + } $result = [ 'id' => $urlRewrite->getEntityId(), 'canonical_url' => $urlRewrite->getTargetPath(), @@ -100,6 +106,9 @@ private function findCanonicalUrl(string $requestPath) : ?\Magento\UrlRewrite\Se if (!$urlRewrite) { $urlRewrite = $this->findUrlFromTargetPath($requestPath); } + if ($urlRewrite && !$urlRewrite->getEntityId() && !$urlRewrite->getIsAutogenerated()) { + $urlRewrite = $this->findUrlFromTargetPath($urlRewrite->getTargetPath()); + } return $urlRewrite; } diff --git a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminClickSaveButtonOnUserRoleFormActionGroup.xml b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminClickSaveButtonOnUserRoleFormActionGroup.xml new file mode 100644 index 0000000000000..70724e00feb57 --- /dev/null +++ b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminClickSaveButtonOnUserRoleFormActionGroup.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminClickSaveButtonOnUserRoleFormActionGroup"> + <click selector="{{AdminCreateRoleSection.save}}" stepKey="saveRole"/> + <waitForPageLoad stepKey="waitForSaveResultLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminFillUserRoleFormActionGroup.xml b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminFillUserRoleFormActionGroup.xml new file mode 100644 index 0000000000000..e1e7b26c87b11 --- /dev/null +++ b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminFillUserRoleFormActionGroup.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminFillUserRoleFormActionGroup"> + <arguments> + <argument name="role" type="entity" /> + <argument name="currentAdminPassword" type="string" defaultValue="{{_ENV.MAGENTO_ADMIN_PASSWORD}}" /> + </arguments> + + <fillField selector="{{AdminCreateRoleSection.name}}" userInput="{{role.name}}" stepKey="fillRoleName"/> + <fillField selector="{{AdminCreateRoleSection.password}}" userInput="{{currentAdminPassword}}" stepKey="fillCurrentUserPassword"/> + + <click selector="{{AdminCreateRoleSection.roleResources}}" stepKey="clickToOpenRoleResources"/> + <waitForPageLoad stepKey="waitForRoleResourceTab" /> + + <selectOption userInput="{{role.resourceAccess}}" selector="{{AdminCreateRoleSection.resourceAccess}}" stepKey="selectResourceAccess" /> + <performOn stepKey="checkNeededResources" selector="{{AdminCreateRoleSection.resourceTree}}" function="function($I,$userRoles={{role.resources}}){foreach($userRoles as $userRole){$I->conditionalClick('//li[@data-id=\'' . $userRole . '\']//*[@class=\'jstree-checkbox\']','//li[@data-id=\'' . $userRole . '\' and contains(@class, \'jstree-checked\')]',false);}}" /> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/User/Test/Mftf/ActionGroup/AdminOpenCreateRolePageActionGroup.xml b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminOpenCreateRolePageActionGroup.xml new file mode 100644 index 0000000000000..4581449a321a4 --- /dev/null +++ b/app/code/Magento/User/Test/Mftf/ActionGroup/AdminOpenCreateRolePageActionGroup.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + /** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +--> +<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd"> + <actionGroup name="AdminOpenCreateRolePageActionGroup"> + <amOnPage url="{{AdminEditRolePage.url}}" stepKey="amOnNewAdminRolePage"/> + <waitForPageLoad stepKey="waitForNewAdminRolePageLoad"/> + </actionGroup> +</actionGroups> diff --git a/app/code/Magento/User/Test/Mftf/Data/UserRoleData.xml b/app/code/Magento/User/Test/Mftf/Data/UserRoleData.xml index 641b692adea5c..1c18ca13b9731 100644 --- a/app/code/Magento/User/Test/Mftf/Data/UserRoleData.xml +++ b/app/code/Magento/User/Test/Mftf/Data/UserRoleData.xml @@ -13,4 +13,15 @@ <data key="scope">1</data> <data key="access">1</data> </entity> + + <entity name="roleAdministrator" type="role"> + <data key="name" unique="suffix">Administrator </data> + <data key="resourceAccess">All</data> + <data key="resources">[]</data> + </entity> + <entity name="roleSales" type="role"> + <data key="name" unique="suffix">Role Sales </data> + <data key="resourceAccess">Custom</data> + <data key="resources">['Magento_Sales::sales','Magento_Sales::sales_operation','Magento_Sales::actions','Magento_Sales::sales_order','Magento_Sales::create','Magento_Sales::actions_view','Magento_Sales::email','Magento_Sales::reorder','Magento_Sales::actions_edit','Magento_Sales::cancel','Magento_Sales::review_payment','Magento_Sales::capture','Magento_Sales::invoice','Magento_Sales::creditmemo','Magento_Sales::hold','Magento_Sales::unhold','Magento_Sales::ship','Magento_Sales::comment','Magento_Sales::emails','Magento_Backend::system','Magento_Backend::system_other_settings','Magento_AdminNotification::adminnotification','Magento_AdminNotification::show_list']</data> + </entity> </entities> diff --git a/app/code/Magento/User/Test/Mftf/Section/AdminCreateRoleSection.xml b/app/code/Magento/User/Test/Mftf/Section/AdminCreateRoleSection.xml index 7dd313a2ba897..7c5268b1786b2 100644 --- a/app/code/Magento/User/Test/Mftf/Section/AdminCreateRoleSection.xml +++ b/app/code/Magento/User/Test/Mftf/Section/AdminCreateRoleSection.xml @@ -12,6 +12,9 @@ <element name="create" type="button" selector="#add"/> <element name="name" type="button" selector="#role_name"/> <element name="password" type="input" selector="#current_password"/> + <element name="resourceAccess" type="select" selector="[data-ui-id='adminhtml-user-editroles-tab-content-account'] [name='all']"/> + <element name="resourceTree" type="block" selector="[data-ui-id='adminhtml-user-editroles-tab-content-account'] [data-role='resource-tree']"/> + <element name="roleResources" type="button" selector="#role_info_tabs_account"/> <element name="roleResource" type="button" selector="#gws_is_all"/> <element name="resourceValue" type="button" selector="//*[text()='{{arg1}}']" parameterized="true"/> diff --git a/app/code/Magento/User/Test/Mftf/Test/AdminResetUserPasswordFailedTest.xml b/app/code/Magento/User/Test/Mftf/Test/AdminResetUserPasswordFailedTest.xml index 4b48c65a18994..e8cbaeaf6fee5 100644 --- a/app/code/Magento/User/Test/Mftf/Test/AdminResetUserPasswordFailedTest.xml +++ b/app/code/Magento/User/Test/Mftf/Test/AdminResetUserPasswordFailedTest.xml @@ -7,13 +7,15 @@ --> <tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> + xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd"> <test name="AdminResetUserPasswordFailedTest"> <annotations> <features value="User"/> + <stories value="Password Reset procedure for Admin Panel"/> <title value="Admin user should not be able to trigger the password reset procedure twice"/> <description value="Admin user should not be able to trigger the password reset procedure twice"/> - <testCaseId value="MC-14389" /> + <testCaseId value="MC-14389"/> + <severity value="CRITICAL"/> <group value="security"/> <group value="mtf_migrated"/> </annotations> diff --git a/app/code/Magento/User/ViewModel/JsonSerializer.php b/app/code/Magento/User/ViewModel/JsonSerializer.php new file mode 100644 index 0000000000000..3cd234d9c41e2 --- /dev/null +++ b/app/code/Magento/User/ViewModel/JsonSerializer.php @@ -0,0 +1,40 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\User\ViewModel; + +/** + * JsonSerializer + */ +class JsonSerializer implements \Magento\Framework\View\Element\Block\ArgumentInterface +{ + + /** + * @var \Magento\Framework\Serialize\Serializer\Json + */ + private $serializer; + + /** + * @param \Magento\Framework\Serialize\Serializer\Json $serializer + */ + public function __construct(\Magento\Framework\Serialize\Serializer\Json $serializer) + { + $this->serializer = $serializer; + } + + /** + * Returns serialized version of data + * + * @param array $data + * @return string + */ + public function serialize(array $data): string + { + return $this->serializer->serialize($data); + } +} diff --git a/app/code/Magento/User/view/adminhtml/layout/adminhtml_user_role_editrole.xml b/app/code/Magento/User/view/adminhtml/layout/adminhtml_user_role_editrole.xml index 5eb830cac5919..45718e1ab67f1 100644 --- a/app/code/Magento/User/view/adminhtml/layout/adminhtml_user_role_editrole.xml +++ b/app/code/Magento/User/view/adminhtml/layout/adminhtml_user_role_editrole.xml @@ -9,7 +9,11 @@ <body> <referenceContainer name="left"> <block class="Magento\User\Block\Role\Edit" name="adminhtml.user.editroles"> - <block class="Magento\User\Block\Role\Tab\Edit" name="adminhtml.user.tab.rolesedit"/> + <block class="Magento\User\Block\Role\Tab\Edit" name="adminhtml.user.tab.rolesedit"> + <arguments> + <argument name="json_serializer" xsi:type="object">Magento\User\ViewModel\JsonSerializer</argument> + </arguments> + </block> <action method="addTabAfter"> <argument name="name" xsi:type="string">account</argument> <argument name="block" xsi:type="string">adminhtml.user.tab.rolesedit</argument> diff --git a/app/code/Magento/User/view/adminhtml/templates/admin/forgotpassword.phtml b/app/code/Magento/User/view/adminhtml/templates/admin/forgotpassword.phtml index bc37eb57a12ab..263bc2bcf960a 100644 --- a/app/code/Magento/User/view/adminhtml/templates/admin/forgotpassword.phtml +++ b/app/code/Magento/User/view/adminhtml/templates/admin/forgotpassword.phtml @@ -3,29 +3,32 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <form method="post" action="" id="login-form" data-mage-init='{"form": {}, "validation": {}}'> <fieldset class="admin__fieldset"> - <legend class="admin__legend"><span><?= /* @escapeNotVerified */ __('Password Help') ?></span></legend><br/> - <input name="form_key" type="hidden" value="<?= /* @escapeNotVerified */ $block->getFormKey() ?>" /> - <p class="admin__field-info"><?= /* @escapeNotVerified */ __('Enter your email address. You will receive an email with a link to reset your password.') ?></p> + <legend class="admin__legend"><span><?= $block->escapeHtml(__('Password Help')) ?></span></legend><br/> + <input name="form_key" type="hidden" value="<?= $block->escapeHtmlAttr($block->getFormKey()) ?>" /> + <p class="admin__field-info"><?= $block->escapeHtml( + __('Enter your email address. You will receive an email with a link to reset your password.') + ) ?></p> <div class="admin__field _required field-email"> - <label for="email" class="admin__field-label"><span><?= /* @escapeNotVerified */ __('Email address') ?></span></label> + <label for="email" class="admin__field-label"><span><?= $block->escapeHtml(__('Email address')) ?></span></label> <div class="admin__field-control"> - <input type="text" id="email" name="email" value="" data-validate="{required:true, 'validate-email':true}" class="admin__control-text" /> + <input type="text" id="email" name="email" value="" + data-validate="{required:true, 'validate-email':true}" class="admin__control-text" /> </div> </div> <?= $block->getChildHtml('form.additional.info') ?> <div class="form-actions"> <div class="actions"> - <button class="action-retrieve action-primary" type="submit"><span><?= /* @escapeNotVerified */ __('Retrieve Password') ?></span></button> + <button class="action-retrieve action-primary" type="submit"> + <span><?= $block->escapeHtml(__('Retrieve Password')) ?></span> + </button> </div> <div class="links"> - <a class="action-back" href="<?= /* @escapeNotVerified */ $block->getUrl('adminhtml', ['_nosecret' => true]) ?>"> - <?= /* @escapeNotVerified */ __('Back to Sign in') ?> + <a class="action-back" + href="<?= $block->escapeUrl($block->getUrl('adminhtml', ['_nosecret' => true])) ?>"> + <?= $block->escapeHtml(__('Back to Sign in')) ?> </a> </div> </div> diff --git a/app/code/Magento/User/view/adminhtml/templates/admin/forgotpassword_url.phtml b/app/code/Magento/User/view/adminhtml/templates/admin/forgotpassword_url.phtml index 9015ccc75d5a5..a348a49b7e841 100644 --- a/app/code/Magento/User/view/adminhtml/templates/admin/forgotpassword_url.phtml +++ b/app/code/Magento/User/view/adminhtml/templates/admin/forgotpassword_url.phtml @@ -3,10 +3,10 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <div class="links"> -<a class="action-forgotpassword" href="<?= /* @escapeNotVerified */ $this->helper('Magento\Backend\Helper\Data')->getUrl('adminhtml/auth/forgotpassword', ['_nosecret' => true]) ?>"><?= /* @escapeNotVerified */ __('Forgot your password?') ?></a> +<a class="action-forgotpassword" + href="<?= $block->escapeUrl($block->getUrl('adminhtml/auth/forgotpassword', ['_nosecret' => true])) ?>"> + <?= $block->escapeHtml(__('Forgot your password?')) ?> +</a> </div> diff --git a/app/code/Magento/User/view/adminhtml/templates/admin/resetforgottenpassword.phtml b/app/code/Magento/User/view/adminhtml/templates/admin/resetforgottenpassword.phtml index cd5abcbc1fecf..b66ceaa0c707c 100644 --- a/app/code/Magento/User/view/adminhtml/templates/admin/resetforgottenpassword.phtml +++ b/app/code/Magento/User/view/adminhtml/templates/admin/resetforgottenpassword.phtml @@ -3,33 +3,40 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> -<form method="post" data-mage-init='{"form": {}, "validation": {}}' action="<?= /* @escapeNotVerified */ $block->getUrl('*/auth/resetpasswordpost', ['_query' => ['id' => $block->getUserId(), 'token' => $block->getResetPasswordLinkToken()]]) ?>" id="reset-password-form" autocomplete="off"> +<form method="post" data-mage-init='{"form": {}, "validation": {}}' + action="<?= $block->escapeUrl( + $block->getUrl( + '*/auth/resetpasswordpost', + ['_query' => ['id' => $block->getUserId(), 'token' => $block->getResetPasswordLinkToken()]] + ) + ) ?>" id="reset-password-form" autocomplete="off"> <fieldset class="admin__fieldset"> - <legend class="admin__legend"><span><?= /* @escapeNotVerified */ __('Reset a Password') ?></span></legend><br /> - <input name="form_key" type="hidden" value="<?= /* @escapeNotVerified */ $block->getFormKey() ?>" /> + <legend class="admin__legend"><span><?= $block->escapeHtml(__('Reset a Password')) ?></span></legend><br /> + <input name="form_key" type="hidden" value="<?= $block->escapeHtmlAttr($block->getFormKey()) ?>" /> <div class="admin__field _required field-password"> - <label class="admin__field-label" for="password"><span><?= /* @escapeNotVerified */ __('New Password') ?></span></label> + <label class="admin__field-label" for="password"><span><?= $block->escapeHtml(__('New Password')) ?></span></label> <div class="admin__field-control"> - <input type="password" class="admin__control-text" data-validate="{required:true, 'validate-admin-password':true}" name="password" id="password" placeholder="new password" autocomplete="off" /> + <input type="password" class="admin__control-text" + data-validate="{required:true, 'validate-admin-password':true}" name="password" id="password" + placeholder="new password" autocomplete="off" /> </div> </div> <div class="admin__field _required field-confirmation"> - <label class="admin__field-label" for="confirmation"><span><?= /* @escapeNotVerified */ __('Confirm New Password') ?></span></label> + <label class="admin__field-label" for="confirmation"><span><?= $block->escapeHtml(__('Confirm New Password')) ?></span></label> <div class="admin__field-control"> - <input type="password" class="admin__control-text" data-validate="{required:true, 'validate-cpassword':true}" name="confirmation" id="confirmation" placeholder="confirm new password" autocomplete="off" /> + <input type="password" class="admin__control-text" + data-validate="{required:true, 'validate-cpassword':true}" name="confirmation" id="confirmation" + placeholder="confirm new password" autocomplete="off" /> </div> </div> <div class="form-actions"> <div class="actions"> - <button type="submit" title="<?= /* @escapeNotVerified */ __('Reset Password') ?>" class="action-reset action-primary"><span><?= /* @escapeNotVerified */ __('Reset Password') ?></span></button> + <button type="submit" title="<?= $block->escapeHtml(__('Reset Password')) ?>" class="action-reset action-primary"><span><?= $block->escapeHtml(__('Reset Password')) ?></span></button> </div> <div class="links"> - <a class="action-back" href="<?= /* @escapeNotVerified */ $block->getUrl('adminhtml', ['_nosecret' => true]) ?>"><?= /* @escapeNotVerified */ __('Back to Sign in') ?></a> + <a class="action-back" href="<?= $block->escapeUrl($block->getUrl('adminhtml', ['_nosecret' => true])) ?>"><?= $block->escapeHtml(__('Back to Sign in')) ?></a> </div> </div> </fieldset> diff --git a/app/code/Magento/User/view/adminhtml/templates/role/edit.phtml b/app/code/Magento/User/view/adminhtml/templates/role/edit.phtml index 25d8e4331a30d..edee167dc1b8a 100644 --- a/app/code/Magento/User/view/adminhtml/templates/role/edit.phtml +++ b/app/code/Magento/User/view/adminhtml/templates/role/edit.phtml @@ -4,11 +4,6 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - -?> - -<?php /** @var $block \Magento\User\Block\Role\Tab\Edit */ ?> @@ -16,32 +11,41 @@ <fieldset class="fieldset form-inline entry-edit"> <legend class="legend"> - <span><?= /* @escapeNotVerified */ __('Roles Resources') ?></span> + <span><?= $block->escapeHtml(__('Roles Resources')) ?></span> </legend><br /> <div class="field"> - <label class="label" for="all"><span><?= /* @escapeNotVerified */ __('Resource Access') ?></span></label> + <label class="label" for="all"><span><?= $block->escapeHtml(__('Resource Access')) ?></span></label> <div class="control"> - <select id="all" name="all" onchange="jQuery('[data-role=tree-resources-container]').toggle()" class="select"> - <option value="0" <?= ($block->isEverythingAllowed() ? '' : 'selected="selected"') ?>><?= /* @escapeNotVerified */ __('Custom') ?></option> - <option value="1" <?= ($block->isEverythingAllowed() ? 'selected="selected"' : '') ?>><?= /* @escapeNotVerified */ __('All') ?></option> + <select id="all" name="all" + onchange="jQuery('[data-role=tree-resources-container]').toggle()" class="select"> + <option value="0" <?= ($block->isEverythingAllowed() ? '' : 'selected="selected"') ?>> + <?= $block->escapeHtml(__('Custom')) ?> + </option> + <option value="1" <?= ($block->isEverythingAllowed() ? 'selected="selected"' : '') ?>> + <?= $block->escapeHtml(__('All')) ?> + </option> </select> </div> </div> - <div class="field<?php if ($block->isEverythingAllowed()):?> no-display<?php endif?>" data-role="tree-resources-container"> - <label class="label"><span><?= /* @escapeNotVerified */ __('Resources') ?></span></label> + <div class="field + <?php if ($block->isEverythingAllowed()) :?> + no-display + <?php endif ?>" + data-role="tree-resources-container"> + <label class="label"><span><?= $block->escapeHtml(__('Resources')) ?></span></label> <div class="control"> - <div class="tree x-tree" data-role="resource-tree" data-mage-init='<?php - echo $block->escapeHtml($this->helper('Magento\Framework\Json\Helper\Data')->jsonEncode([ - 'rolesTree' => [ - "treeInitData" => $block->getTree(), - "treeInitSelectedData" => $block->getSelectedResources(), - ], - ])); - ?>'></div> + <div class="tree x-tree" data-role="resource-tree" data-mage-init='<?= /* @noEscape */ + $block->getJsonSerializer()->serialize([ + 'rolesTree' => [ + "treeInitData" => $block->getTree(), + "treeInitSelectedData" => $block->getSelectedResources(), + ], + ]); ?>'> + </div> </div> </div> </fieldset> diff --git a/app/code/Magento/User/view/adminhtml/templates/role/info.phtml b/app/code/Magento/User/view/adminhtml/templates/role/info.phtml index 69f2627cd563f..6cf1bb373541d 100644 --- a/app/code/Magento/User/view/adminhtml/templates/role/info.phtml +++ b/app/code/Magento/User/view/adminhtml/templates/role/info.phtml @@ -4,7 +4,7 @@ * See COPYING.txt for license details. */ ?> -<form action="<?= /* @escapeNotVerified */ $block->getUrl('*/*/saverole') ?>" method="post" id="role-edit-form"> +<form action="<?= $block->escapeUrl($block->getUrl('*/*/saverole')) ?>" method="post" id="role-edit-form"> <?= $block->getBlockHtml('formkey') ?> </form> <script> diff --git a/app/code/Magento/User/view/adminhtml/templates/role/users.phtml b/app/code/Magento/User/view/adminhtml/templates/role/users.phtml index d30e658512fc8..a62570d4e8cd7 100644 --- a/app/code/Magento/User/view/adminhtml/templates/role/users.phtml +++ b/app/code/Magento/User/view/adminhtml/templates/role/users.phtml @@ -5,7 +5,7 @@ */ ?> <fieldset class="fieldset"> - <legend class="legend"><span><?= /* @escapeNotVerified */ __('Role Users') ?></span></legend> + <legend class="legend"><span><?= $block->escapeHtml(__('Role Users')) ?></span></legend> <br /> <?= $block->getGridHtml() ?> </fieldset> diff --git a/app/code/Magento/User/view/adminhtml/templates/role/users_grid_js.phtml b/app/code/Magento/User/view/adminhtml/templates/role/users_grid_js.phtml index 964d925a5f2fd..5505d34322fda 100644 --- a/app/code/Magento/User/view/adminhtml/templates/role/users_grid_js.phtml +++ b/app/code/Magento/User/view/adminhtml/templates/role/users_grid_js.phtml @@ -3,8 +3,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile ?> <script> require([ @@ -16,8 +14,8 @@ require([ ], function(jQuery, confirm, _){ <!-- <?php $myBlock = $block->getLayout()->getBlock('roleUsersGrid'); ?> -<?php if (is_object($myBlock) && $myBlock->getJsObjectName()): ?> - var checkBoxes = $H(<?= /* @escapeNotVerified */ $myBlock->getUsers(true) ?>); +<?php if (is_object($myBlock) && $myBlock->getJsObjectName()) : ?> + var checkBoxes = $H(<?= /* @noEscape */ $myBlock->getUsers(true) ?>); var warning = false; if (checkBoxes.size() > 0) { warning = true; @@ -46,7 +44,7 @@ require([ if (checked) { confirm({ - content: "<?= /* @escapeNotVerified */ __('Warning!\r\nThis action will remove this user from already assigned role\r\nAre you sure?') ?>", + content: "<?= $myBlock->escapeHtml(__('Warning!\r\nThis action will remove this user from already assigned role\r\nAre you sure?')) ?>", actions: { confirm: function () { checkbox[0].checked = false; @@ -95,7 +93,7 @@ require([ if (!allCheckbox.checked && _.size(checkBoxes._object) > 0) { allCheckbox.checked = true; confirm({ - content: "<?= /* @escapeNotVerified */ __('Warning!\r\nThis action will remove those users from already assigned roles\r\nAre you sure?') ?>", + content: "<?= $myBlock->escapeHtml(__('Warning!\r\nThis action will remove those users from already assigned roles\r\nAre you sure?')) ?>", actions: { confirm: function () { allCheckbox.checked = false; @@ -108,25 +106,25 @@ require([ } } function markCheckboxes(value) { - <?= /* @escapeNotVerified */ $myBlock->getJsObjectName() ?>.rows.each(function(row) + <?= $myBlock->escapeJs($myBlock->getJsObjectName()) ?>.rows.each(function(row) { $(row).getElementsByClassName('checkbox')[0].checked = value; - roleUsersRowInit(<?= /* @escapeNotVerified */ $myBlock->getJsObjectName() ?>, row); + roleUsersRowInit(<?= $myBlock->escapeJs($myBlock->getJsObjectName()) ?>, row); }); } function onLoad() { - if (typeof <?= /* @escapeNotVerified */ $myBlock->getJsObjectName() ?> !== 'undefined') { - <?= /* @escapeNotVerified */ $myBlock->getJsObjectName() ?>. + if (typeof <?= $myBlock->escapeJs($myBlock->getJsObjectName()) ?> !== 'undefined') { + <?= $myBlock->escapeJs($myBlock->getJsObjectName()) ?>. rowClickCallback = roleUsersRowClick; - <?= /* @escapeNotVerified */ $myBlock->getJsObjectName() ?>. + <?= $myBlock->escapeJs($myBlock->getJsObjectName()) ?>. initRowCallback = roleUsersRowInit; - <?= /* @escapeNotVerified */ $myBlock->getJsObjectName() ?>. + <?= $myBlock->escapeJs($myBlock->getJsObjectName()) ?>. checkboxCheckCallback = registerUserRole; - <?= /* @escapeNotVerified */ $myBlock->getJsObjectName() ?>. + <?= $myBlock->escapeJs($myBlock->getJsObjectName()) ?>. checkCheckboxes = massSelectUsers; - <?= /* @escapeNotVerified */ $myBlock->getJsObjectName() ?>. + <?= $myBlock->escapeJs($myBlock->getJsObjectName()) ?>. rows.each(function (row) { - roleUsersRowInit(<?= /* @escapeNotVerified */ $myBlock->getJsObjectName() ?>, row) + roleUsersRowInit(<?= $myBlock->escapeJs($myBlock->getJsObjectName()) ?>, row) }); $('in_role_user_old').value = $('in_role_user').value; } else { diff --git a/app/code/Magento/User/view/adminhtml/templates/user/roles_grid_js.phtml b/app/code/Magento/User/view/adminhtml/templates/user/roles_grid_js.phtml index 11ac72e6e382c..92a97e825ea67 100644 --- a/app/code/Magento/User/view/adminhtml/templates/user/roles_grid_js.phtml +++ b/app/code/Magento/User/view/adminhtml/templates/user/roles_grid_js.phtml @@ -3,9 +3,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <script> require([ @@ -14,10 +11,10 @@ require([ ], function(){ <?php $myBlock = $block->getLayout()->getBlock('user.roles.grid'); ?> -<?php if (is_object($myBlock) && $myBlock->getJsObjectName()): ?> +<?php if (is_object($myBlock) && $myBlock->getJsObjectName()) : ?> var radioBoxes = $H({}); var warning = false; - var userRoles = $H(<?= /* @escapeNotVerified */ $myBlock->getSelectedRoles(true) ?>); + var userRoles = $H(<?= /* @noEscape */ $myBlock->getSelectedRoles(true) ?>); if (userRoles.size() > 0) warning = true; $('user_user_roles').value = userRoles.toQueryString(); @@ -40,7 +37,7 @@ require([ if(checkbox[0] && !checkbox[0].checked){ var checked = isInput ? checkbox[0].checked : !checkbox[0].checked; if (checked && warning && radioBoxes.size() > 0) { - if ( !confirm("<?= /* @escapeNotVerified */ __('Warning!\r\nThis action will remove this user from already assigned role\r\nAre you sure?') ?>") ) { + if ( !confirm("<?= $myBlock->escapeHtml(__('Warning!\r\nThis action will remove this user from already assigned role\r\nAre you sure?')) ?>") ) { checkbox[0].checked = false; for(i in radioBoxes) { if( radioBoxes[i].status == 1) { @@ -51,7 +48,7 @@ require([ } warning = false; } - <?= /* @escapeNotVerified */ $myBlock->getJsObjectName() ?>.setCheckboxChecked(checkbox[0], checked); + <?= $myBlock->escapeJs($myBlock->getJsObjectName()) ?>.setCheckboxChecked(checkbox[0], checked); } } } @@ -63,17 +60,19 @@ require([ } } -<?= /* @escapeNotVerified */ $myBlock->getJsObjectName() ?>.rowClickCallback = roleRowClick; -<?= /* @escapeNotVerified */ $myBlock->getJsObjectName() ?>.initRowCallback = rolesRowInit; -<?= /* @escapeNotVerified */ $myBlock->getJsObjectName() ?>.checkboxCheckCallback = registerUserRole; -<?= /* @escapeNotVerified */ $myBlock->getJsObjectName() ?>.rows.each(function(row){rolesRowInit(<?= /* @escapeNotVerified */ $myBlock->getJsObjectName() ?>, row)}); + <?= $myBlock->escapeJs($myBlock->getJsObjectName()) ?>.rowClickCallback = roleRowClick; + <?= $myBlock->escapeJs($myBlock->getJsObjectName()) ?>.initRowCallback = rolesRowInit; + <?= $myBlock->escapeJs($myBlock->getJsObjectName()) ?>.checkboxCheckCallback = registerUserRole; + <?= $myBlock->escapeJs($myBlock->getJsObjectName()) ?>.rows.each(function(row){ + rolesRowInit(<?= $myBlock->escapeJs($myBlock->getJsObjectName()) ?>, row) + }); <?php endif; ?> }); </script> <?php $editBlock = $block->getLayout()->getBlock('adminhtml.user.edit'); ?> -<?php if (is_object($editBlock)): ?> +<?php if (is_object($editBlock)) : ?> <script type="text/x-magento-init"> { "[data-role=delete-user]" : { diff --git a/app/code/Magento/Vault/Api/PaymentTokenManagementInterface.php b/app/code/Magento/Vault/Api/PaymentTokenManagementInterface.php index 045ba3efac12b..ef654ffa11fd4 100644 --- a/app/code/Magento/Vault/Api/PaymentTokenManagementInterface.php +++ b/app/code/Magento/Vault/Api/PaymentTokenManagementInterface.php @@ -20,7 +20,7 @@ interface PaymentTokenManagementInterface * Lists payment tokens that match specified search criteria. * * @param int $customerId Customer ID. - * @return \Magento\Vault\Api\Data\PaymentTokenSearchResultsInterface Payment token search result interface. + * @return \Magento\Vault\Api\Data\PaymentTokenSearchResultsInterface[] Payment token search result interface. * @since 100.1.0 */ public function getListByCustomerId($customerId); @@ -56,6 +56,8 @@ public function getByGatewayToken($token, $paymentMethodCode, $customerId); public function getByPublicHash($hash, $customerId); /** + * Save token with payment link + * * @param PaymentTokenInterface $token * @param OrderPaymentInterface $payment * @return bool diff --git a/app/code/Magento/Vault/Observer/AfterPaymentSaveObserver.php b/app/code/Magento/Vault/Observer/AfterPaymentSaveObserver.php index 49d34091b136a..14b7cc6c726bb 100644 --- a/app/code/Magento/Vault/Observer/AfterPaymentSaveObserver.php +++ b/app/code/Magento/Vault/Observer/AfterPaymentSaveObserver.php @@ -77,11 +77,9 @@ public function execute(Observer $observer) $paymentToken->setPaymentMethodCode($payment->getMethod()); $additionalInformation = $payment->getAdditionalInformation(); - if (isset($additionalInformation[VaultConfigProvider::IS_ACTIVE_CODE])) { - $paymentToken->setIsVisible( - (bool) (int) $additionalInformation[VaultConfigProvider::IS_ACTIVE_CODE] - ); - } + $paymentToken->setIsVisible( + (bool) (int) ($additionalInformation[VaultConfigProvider::IS_ACTIVE_CODE] ?? 0) + ); $paymentToken->setPublicHash($this->generatePublicHash($paymentToken)); @@ -115,7 +113,7 @@ protected function generatePublicHash(PaymentTokenInterface $paymentToken) /** * Reads Payment token from Order Payment * - * @param OrderPaymentExtensionInterface | null $extensionAttributes + * @param OrderPaymentExtensionInterface|null $extensionAttributes * @return PaymentTokenInterface | null */ protected function getPaymentToken(OrderPaymentExtensionInterface $extensionAttributes = null) diff --git a/app/code/Magento/Vault/Test/Unit/Observer/AfterPaymentSaveObserverTest.php b/app/code/Magento/Vault/Test/Unit/Observer/AfterPaymentSaveObserverTest.php index 09c17d1e58d98..2ae16e186030d 100644 --- a/app/code/Magento/Vault/Test/Unit/Observer/AfterPaymentSaveObserverTest.php +++ b/app/code/Magento/Vault/Test/Unit/Observer/AfterPaymentSaveObserverTest.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ + namespace Magento\Vault\Test\Unit\Observer; use Magento\Framework\App\DeploymentConfig; @@ -15,9 +16,13 @@ use Magento\Sales\Model\Order\Payment; use Magento\Vault\Model\PaymentToken; use Magento\Vault\Model\PaymentTokenManagement; +use Magento\Vault\Model\Ui\VaultConfigProvider; use Magento\Vault\Observer\AfterPaymentSaveObserver; use PHPUnit_Framework_MockObject_MockObject as MockObject; +/** + * Tests for AfterPaymentSaveObserver. + */ class AfterPaymentSaveObserverTest extends \PHPUnit\Framework\TestCase { /** @@ -93,7 +98,7 @@ protected function setUp() // Sales Order Payment Model $this->salesOrderPaymentMock = $this->getMockBuilder(Payment::class) - ->setMethods(null) + ->setMethods(['getAdditionalInformation']) ->disableOriginalConstructor() ->getMock(); $this->salesOrderPaymentMock->setOrder($this->salesOrderMock); @@ -122,9 +127,10 @@ protected function setUp() * @param string $token * @param bool $isActive * @param string $method + * @param array $additionalInfo * @dataProvider positiveCaseDataProvider */ - public function testPositiveCase($customerId, $createdAt, $token, $isActive, $method) + public function testPositiveCase($customerId, $createdAt, $token, $isActive, $method, $additionalInfo) { $this->paymentTokenMock->setGatewayToken($token); $this->paymentTokenMock->setCustomerId($customerId); @@ -136,6 +142,8 @@ public function testPositiveCase($customerId, $createdAt, $token, $isActive, $me ->method('getVaultPaymentToken') ->willReturn($this->paymentTokenMock); + $this->salesOrderPaymentMock->method('getAdditionalInformation')->willReturn($additionalInfo); + if (!empty($token)) { $this->paymentTokenManagementMock->expects($this->once()) ->method('saveTokenWithPaymentLink') @@ -158,6 +166,10 @@ public function testPositiveCase($customerId, $createdAt, $token, $isActive, $me static::assertEquals($token, $paymentToken->getGatewayToken()); static::assertEquals($isActive, $paymentToken->getIsActive()); static::assertEquals($createdAt, $paymentToken->getCreatedAt()); + static::assertEquals( + $additionalInfo[VaultConfigProvider::IS_ACTIVE_CODE] ?? false, + $paymentToken->getIsVisible() + ); } /** @@ -171,14 +183,32 @@ public function positiveCaseDataProvider() '10\20\2015', 'asdfg', true, - 'paypal' + 'paypal', + [], + ], + [ + 1, + '10\20\2015', + 'asdfg', + true, + 'paypal', + [VaultConfigProvider::IS_ACTIVE_CODE => true], + ], + [ + 1, + '10\20\2015', + 'asdfg', + true, + 'paypal', + [VaultConfigProvider::IS_ACTIVE_CODE => false], ], [ null, null, null, false, - null + null, + [], ], ]; } diff --git a/app/code/Magento/Vault/view/frontend/layout/customer_account.xml b/app/code/Magento/Vault/view/frontend/layout/customer_account.xml index 071bf39ca3254..73ce9247fef0a 100644 --- a/app/code/Magento/Vault/view/frontend/layout/customer_account.xml +++ b/app/code/Magento/Vault/view/frontend/layout/customer_account.xml @@ -5,10 +5,14 @@ * See COPYING.txt for license details. */ --> -<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> +<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> <body> <referenceBlock name="customer_account_navigation"> - <block class="Magento\Customer\Block\Account\SortLinkInterface" name="customer-account-navigation-my-credit-cards-link"> + <block class="Magento\Customer\Block\Account\SortLinkInterface" + name="customer-account-navigation-my-credit-cards-link" + ifconfig="payment/braintree/active" + > <arguments> <argument name="path" xsi:type="string">vault/cards/listaction</argument> <argument name="label" xsi:type="string" translate="true">Stored Payment Methods</argument> diff --git a/app/code/Magento/Webapi/Model/Rest/Swagger/Generator.php b/app/code/Magento/Webapi/Model/Rest/Swagger/Generator.php index 855f455120d89..847def6e8922c 100644 --- a/app/code/Magento/Webapi/Model/Rest/Swagger/Generator.php +++ b/app/code/Magento/Webapi/Model/Rest/Swagger/Generator.php @@ -41,6 +41,11 @@ class Generator extends AbstractSchemaGenerator /** Array signifier */ const ARRAY_SIGNIFIER = '[0]'; + /** + * Wrapper node for XML requests + */ + private const XML_SCHEMA_PARAMWRAPPER = 'request'; + /** * Swagger factory instance. * @@ -134,7 +139,7 @@ public function __construct( } /** - * {@inheritdoc} + * @inheritdoc */ protected function generateSchema($requestedServiceMetadata, $requestScheme, $requestHost, $endpointUrl) { @@ -193,6 +198,32 @@ protected function getGeneralInfo() ]; } + /** + * List out consumes data type + * + * @return array + */ + private function getConsumableDatatypes() + { + return [ + 'application/json', + 'application/xml', + ]; + } + + /** + * List out produces data type + * + * @return array + */ + private function getProducibleDatatypes() + { + return [ + 'application/json', + 'application/xml', + ]; + } + /** * Generate path info based on method data * @@ -212,6 +243,8 @@ protected function generatePathInfo($methodName, $httpMethodData, $tagName) 'tags' => [$tagName], 'description' => $methodData['documentation'], 'operationId' => $operationId, + 'consumes' => $this->getConsumableDatatypes(), + 'produces' => $this->getProducibleDatatypes(), ]; $parameters = $this->generateMethodParameters($httpMethodData, $operationId); @@ -602,7 +635,7 @@ protected function getDefinitionReference($typeName) /** * Get the CamelCased type name in 'hyphen-separated-lowercase-words' format * - * e.g. test-module5-v1-entity-all-soap-and-rest + * E.g. test-module5-v1-entity-all-soap-and-rest * * @param string $typeName * @return string @@ -842,6 +875,17 @@ private function generateBodySchema($parameterName, $parameterInfo, $description $description ); $bodySchema['type'] = 'object'; + + /* + * Make sure we have a proper XML wrapper for request parameters for the XML format. + */ + if (!isset($bodySchema['xml']) || !is_array($bodySchema['xml'])) { + $bodySchema['xml'] = []; + } + if (!isset($bodySchema['xml']['name']) || empty($bodySchema['xml']['name'])) { + $bodySchema['xml']['name'] = self::XML_SCHEMA_PARAMWRAPPER; + } + return $bodySchema; } diff --git a/app/code/Magento/Webapi/Test/Unit/Model/Rest/Swagger/GeneratorTest.php b/app/code/Magento/Webapi/Test/Unit/Model/Rest/Swagger/GeneratorTest.php index 66b59babb7189..172db875c6c49 100644 --- a/app/code/Magento/Webapi/Test/Unit/Model/Rest/Swagger/GeneratorTest.php +++ b/app/code/Magento/Webapi/Test/Unit/Model/Rest/Swagger/GeneratorTest.php @@ -223,7 +223,7 @@ public function generateDataProvider() ] ], // @codingStandardsIgnoreStart - '{"swagger":"2.0","info":{"version":"","title":""},"host":"magento.host","basePath":"/rest/default","schemes":["http://"],"tags":[{"name":"testModule5AllSoapAndRestV2","description":"AllSoapAndRestInterface"}],"paths":{"/V1/testModule5":{"post":{"tags":["testModule5AllSoapAndRestV2"],"description":"Add new item.","operationId":"' . self::OPERATION_NAME . 'Post","parameters":[{"name":"operationNamePostBody","in":"body","schema":{"required":["item"],"properties":{"item":{"$ref":"#/definitions/test-module5-v2-entity-all-soap-and-rest"}},"type":"object"}}],"responses":{"200":{"description":"200 Success.","schema":{"$ref":"#/definitions/test-module5-v2-entity-all-soap-and-rest"}},"401":{"description":"401 Unauthorized","schema":{"$ref":"#/definitions/error-response"}},"500":{"description":"Internal Server error","schema":{"$ref":"#/definitions/error-response"}},"default":{"description":"Unexpected error","schema":{"$ref":"#/definitions/error-response"}}}}}},"definitions":{"error-response":{"type":"object","properties":{"message":{"type":"string","description":"Error message"},"errors":{"$ref":"#/definitions/error-errors"},"code":{"type":"integer","description":"Error code"},"parameters":{"$ref":"#/definitions/error-parameters"},"trace":{"type":"string","description":"Stack trace"}},"required":["message"]},"error-errors":{"type":"array","description":"Errors list","items":{"$ref":"#/definitions/error-errors-item"}},"error-errors-item":{"type":"object","description":"Error details","properties":{"message":{"type":"string","description":"Error message"},"parameters":{"$ref":"#/definitions/error-parameters"}}},"error-parameters":{"type":"array","description":"Error parameters list","items":{"$ref":"#/definitions/error-parameters-item"}},"error-parameters-item":{"type":"object","description":"Error parameters item","properties":{"resources":{"type":"string","description":"ACL resource"},"fieldName":{"type":"string","description":"Missing or invalid field name"},"fieldValue":{"type":"string","description":"Incorrect field value"}}},"test-module5-v2-entity-all-soap-and-rest":{"type":"object","description":"Some Data Object","properties":{"price":{"type":"integer"}},"required":["price"]}}}' + '{"swagger":"2.0","info":{"version":"","title":""},"host":"magento.host","basePath":"/rest/default","schemes":["http://"],"tags":[{"name":"testModule5AllSoapAndRestV2","description":"AllSoapAndRestInterface"}],"paths":{"/V1/testModule5":{"post":{"tags":["testModule5AllSoapAndRestV2"],"description":"Add new item.","operationId":"operationNamePost","consumes":["application/json","application/xml"],"produces":["application/json","application/xml"],"parameters":[{"name":"operationNamePostBody","in":"body","schema":{"required":["item"],"properties":{"item":{"$ref":"#/definitions/test-module5-v2-entity-all-soap-and-rest"}},"type":"object","xml":{"name":"request"}}}],"responses":{"200":{"description":"200 Success.","schema":{"$ref":"#/definitions/test-module5-v2-entity-all-soap-and-rest"}},"401":{"description":"401 Unauthorized","schema":{"$ref":"#/definitions/error-response"}},"500":{"description":"Internal Server error","schema":{"$ref":"#/definitions/error-response"}},"default":{"description":"Unexpected error","schema":{"$ref":"#/definitions/error-response"}}}}}},"definitions":{"error-response":{"type":"object","properties":{"message":{"type":"string","description":"Error message"},"errors":{"$ref":"#/definitions/error-errors"},"code":{"type":"integer","description":"Error code"},"parameters":{"$ref":"#/definitions/error-parameters"},"trace":{"type":"string","description":"Stack trace"}},"required":["message"]},"error-errors":{"type":"array","description":"Errors list","items":{"$ref":"#/definitions/error-errors-item"}},"error-errors-item":{"type":"object","description":"Error details","properties":{"message":{"type":"string","description":"Error message"},"parameters":{"$ref":"#/definitions/error-parameters"}}},"error-parameters":{"type":"array","description":"Error parameters list","items":{"$ref":"#/definitions/error-parameters-item"}},"error-parameters-item":{"type":"object","description":"Error parameters item","properties":{"resources":{"type":"string","description":"ACL resource"},"fieldName":{"type":"string","description":"Missing or invalid field name"},"fieldValue":{"type":"string","description":"Incorrect field value"}}},"test-module5-v2-entity-all-soap-and-rest":{"type":"object","description":"Some Data Object","properties":{"price":{"type":"integer"}},"required":["price"]}}}' // @codingStandardsIgnoreEnd ], [ @@ -271,7 +271,7 @@ public function generateDataProvider() ] ], // @codingStandardsIgnoreStart - '{"swagger":"2.0","info":{"version":"","title":""},"host":"magento.host","basePath":"/rest/default","schemes":["http://"],"tags":[{"name":"testModule5AllSoapAndRestV2","description":"AllSoapAndRestInterface"}],"paths":{"/V1/testModule5":{"get":{"tags":["testModule5AllSoapAndRestV2"],"description":"Retrieve existing item.","operationId":"' . self::OPERATION_NAME . 'Get","responses":{"200":{"description":"200 Success.","schema":{"$ref":"#/definitions/test-module5-v2-entity-all-soap-and-rest"}},"401":{"description":"401 Unauthorized","schema":{"$ref":"#/definitions/error-response"}},"500":{"description":"Internal Server error","schema":{"$ref":"#/definitions/error-response"}},"default":{"description":"Unexpected error","schema":{"$ref":"#/definitions/error-response"}}}}}},"definitions":{"error-response":{"type":"object","properties":{"message":{"type":"string","description":"Error message"},"errors":{"$ref":"#/definitions/error-errors"},"code":{"type":"integer","description":"Error code"},"parameters":{"$ref":"#/definitions/error-parameters"},"trace":{"type":"string","description":"Stack trace"}},"required":["message"]},"error-errors":{"type":"array","description":"Errors list","items":{"$ref":"#/definitions/error-errors-item"}},"error-errors-item":{"type":"object","description":"Error details","properties":{"message":{"type":"string","description":"Error message"},"parameters":{"$ref":"#/definitions/error-parameters"}}},"error-parameters":{"type":"array","description":"Error parameters list","items":{"$ref":"#/definitions/error-parameters-item"}},"error-parameters-item":{"type":"object","description":"Error parameters item","properties":{"resources":{"type":"string","description":"ACL resource"},"fieldName":{"type":"string","description":"Missing or invalid field name"},"fieldValue":{"type":"string","description":"Incorrect field value"}}},"test-module5-v2-entity-all-soap-and-rest":{"type":"object","description":"Some Data Object","properties":{"price":{"type":"integer"}},"required":["price"]}}}' + '{"swagger":"2.0","info":{"version":"","title":""},"host":"magento.host","basePath":"/rest/default","schemes":["http://"],"tags":[{"name":"testModule5AllSoapAndRestV2","description":"AllSoapAndRestInterface"}],"paths":{"/V1/testModule5":{"get":{"tags":["testModule5AllSoapAndRestV2"],"description":"Retrieve existing item.","operationId":"operationNameGet","consumes":["application/json","application/xml"],"produces":["application/json","application/xml"],"responses":{"200":{"description":"200 Success.","schema":{"$ref":"#/definitions/test-module5-v2-entity-all-soap-and-rest"}},"401":{"description":"401 Unauthorized","schema":{"$ref":"#/definitions/error-response"}},"500":{"description":"Internal Server error","schema":{"$ref":"#/definitions/error-response"}},"default":{"description":"Unexpected error","schema":{"$ref":"#/definitions/error-response"}}}}}},"definitions":{"error-response":{"type":"object","properties":{"message":{"type":"string","description":"Error message"},"errors":{"$ref":"#/definitions/error-errors"},"code":{"type":"integer","description":"Error code"},"parameters":{"$ref":"#/definitions/error-parameters"},"trace":{"type":"string","description":"Stack trace"}},"required":["message"]},"error-errors":{"type":"array","description":"Errors list","items":{"$ref":"#/definitions/error-errors-item"}},"error-errors-item":{"type":"object","description":"Error details","properties":{"message":{"type":"string","description":"Error message"},"parameters":{"$ref":"#/definitions/error-parameters"}}},"error-parameters":{"type":"array","description":"Error parameters list","items":{"$ref":"#/definitions/error-parameters-item"}},"error-parameters-item":{"type":"object","description":"Error parameters item","properties":{"resources":{"type":"string","description":"ACL resource"},"fieldName":{"type":"string","description":"Missing or invalid field name"},"fieldValue":{"type":"string","description":"Incorrect field value"}}},"test-module5-v2-entity-all-soap-and-rest":{"type":"object","description":"Some Data Object","properties":{"price":{"type":"integer"}},"required":["price"]}}}' // @codingStandardsIgnoreEnd ], ]; diff --git a/app/code/Magento/Webapi/view/adminhtml/layout/adminhtml_integration_edit.xml b/app/code/Magento/Webapi/view/adminhtml/layout/adminhtml_integration_edit.xml index 31b3bbf13be6a..c9c69b4267ebd 100644 --- a/app/code/Magento/Webapi/view/adminhtml/layout/adminhtml_integration_edit.xml +++ b/app/code/Magento/Webapi/view/adminhtml/layout/adminhtml_integration_edit.xml @@ -9,7 +9,11 @@ <update handle="editor"/> <body> <referenceBlock name="integration_edit_tabs"> - <block class="Magento\Integration\Block\Adminhtml\Integration\Edit\Tab\Webapi" name="integration_edit_tab_webapi" template="Magento_Integration::resourcetree.phtml"/> + <block class="Magento\Integration\Block\Adminhtml\Integration\Edit\Tab\Webapi" name="integration_edit_tab_webapi" template="Magento_Integration::resourcetree.phtml"> + <arguments> + <argument name="json_serializer" xsi:type="object">Magento\Integration\ViewModel\JsonSerializer</argument> + </arguments> + </block> <action method="addTabAfter"> <argument name="name" xsi:type="string">api_section</argument> <argument name="block" xsi:type="string">integration_edit_tab_webapi</argument> diff --git a/app/code/Magento/Weee/view/adminhtml/templates/items/price/row.phtml b/app/code/Magento/Weee/view/adminhtml/templates/items/price/row.phtml index ea35eff438c0d..0d8365ff4bd85 100644 --- a/app/code/Magento/Weee/view/adminhtml/templates/items/price/row.phtml +++ b/app/code/Magento/Weee/view/adminhtml/templates/items/price/row.phtml @@ -4,64 +4,63 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - +// phpcs:disable Magento2.Templates.ThisInTemplate ?> <?php /** @var \Magento\Weee\Block\Adminhtml\Items\Price\Renderer $block */ /** @var $_weeeHelper \Magento\Weee\Helper\Data */ -$_weeeHelper = $this->helper('Magento\Weee\Helper\Data'); +$_weeeHelper = $this->helper(\Magento\Weee\Helper\Data::class); $_item = $block->getItem(); ?> -<?php if ($block->displayBothPrices() || $block->displayPriceExclTax()): ?> +<?php if ($block->displayBothPrices() || $block->displayPriceExclTax()) : ?> <div class="price-excl-tax"> - <?php if ($block->displayBothPrices()): ?> - <span class="label"><?= /* @escapeNotVerified */ __('Excl. Tax') ?>:</span> + <?php if ($block->displayBothPrices()) : ?> + <span class="label"><?= $block->escapeHtml(__('Excl. Tax')) ?>:</span> <?php endif; ?> <?= $block->getRowPriceExclTaxHtml() ?> - <?php if ($this->helper('Magento\Weee\Helper\Data')->getApplied($_item)): ?> - <?php if ($block->displayPriceWithWeeeDetails()): ?> + <?php if ($this->helper(\Magento\Weee\Helper\Data::class)->getApplied($_item)) : ?> + <?php if ($block->displayPriceWithWeeeDetails()) : ?> <small> - <?php foreach ($this->helper('Magento\Weee\Helper\Data')->getApplied($_item) as $tax): ?> - <span class="nobr"><?= /* @escapeNotVerified */ $tax['title'] ?>: <?= /* @escapeNotVerified */ $block->displayPrices($tax['base_row_amount'], $tax['row_amount']) ?></span> + <?php foreach ($this->helper(\Magento\Weee\Helper\Data::class)->getApplied($_item) as $tax) : ?> + <span class="nobr"><?= $block->escapeHtml($tax['title']) ?>: <?= /* @noEscape */ $block->displayPrices($tax['base_row_amount'], $tax['row_amount']) ?></span> <?php endforeach; ?> </small> <?php endif; ?> - <?php if ($block->displayFinalPrice()): ?> + <?php if ($block->displayFinalPrice()) : ?> <br /> - <span class="nobr"><?= /* @escapeNotVerified */ __('Total') ?>:<br /> + <span class="nobr"><?= $block->escapeHtml(__('Total')) ?>:<br /> <?= $block->getFinalRowPriceExclTaxHtml() ?> </span> <?php endif; ?> <?php endif; ?> </div> <?php endif; ?> -<?php if ($block->displayBothPrices() || $block->displayPriceInclTax()): ?> +<?php if ($block->displayBothPrices() || $block->displayPriceInclTax()) : ?> <div class="price-incl-tax"> - <?php if ($block->displayBothPrices()): ?> - <span class="label"><?= /* @escapeNotVerified */ __('Incl. Tax') ?>:</span> + <?php if ($block->displayBothPrices()) : ?> + <span class="label"><?= $block->escapeHtml(__('Incl. Tax')) ?>:</span> <?php endif; ?> <?= $block->getRowPriceInclTaxHtml() ?> - <?php if ($this->helper('Magento\Weee\Helper\Data')->getApplied($_item)): ?> + <?php if ($this->helper(\Magento\Weee\Helper\Data::class)->getApplied($_item)) : ?> <br /> - <?php if ($block->displayPriceWithWeeeDetails()): ?> + <?php if ($block->displayPriceWithWeeeDetails()) : ?> <small> - <?php foreach ($this->helper('Magento\Weee\Helper\Data')->getApplied($_item) as $tax): ?> - <span class="nobr"><?= /* @escapeNotVerified */ $tax['title'] ?>: <?= /* @escapeNotVerified */ $block->displayPrices($tax['base_row_amount_incl_tax'], $tax['row_amount_incl_tax']) ?></span> + <?php foreach ($this->helper(\Magento\Weee\Helper\Data::class)->getApplied($_item) as $tax) : ?> + <span class="nobr"><?= $block->escapeHtml($tax['title']) ?>: <?= /* @noEscape */ $block->displayPrices($tax['base_row_amount_incl_tax'], $tax['row_amount_incl_tax']) ?></span> <?php endforeach; ?> </small> <?php endif; ?> - <?php if ($block->displayFinalPrice()): ?> + <?php if ($block->displayFinalPrice()) : ?> <br /> - <span class="nobr"><?= /* @escapeNotVerified */ __('Total') ?>:<br /> + <span class="nobr"><?= $block->escapeHtml(__('Total')) ?>:<br /> <?= $block->getFinalRowPriceInclTaxHtml() ?> </span> <?php endif; ?> diff --git a/app/code/Magento/Weee/view/adminhtml/templates/items/price/total.phtml b/app/code/Magento/Weee/view/adminhtml/templates/items/price/total.phtml index fa55009fb5b6f..b0f29e2f03def 100644 --- a/app/code/Magento/Weee/view/adminhtml/templates/items/price/total.phtml +++ b/app/code/Magento/Weee/view/adminhtml/templates/items/price/total.phtml @@ -3,9 +3,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <?php /** @var \Magento\Weee\Block\Adminhtml\Items\Price\Renderer $block */ @@ -13,4 +10,4 @@ $_item = $block->getItem(); ?> -<?= /* @escapeNotVerified */ $block->displayPrices($block->getBaseTotalAmount($_item), $block->getTotalAmount($_item)) ?> +<?= /* @noEscape */ $block->displayPrices($block->getBaseTotalAmount($_item), $block->getTotalAmount($_item)) ?> diff --git a/app/code/Magento/Weee/view/adminhtml/templates/items/price/unit.phtml b/app/code/Magento/Weee/view/adminhtml/templates/items/price/unit.phtml index 13eca4afd2e8e..8dbaff2e2a6b1 100644 --- a/app/code/Magento/Weee/view/adminhtml/templates/items/price/unit.phtml +++ b/app/code/Magento/Weee/view/adminhtml/templates/items/price/unit.phtml @@ -4,65 +4,64 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - +// phpcs:disable Magento2.Templates.ThisInTemplate ?> <?php /** @var \Magento\Weee\Block\Adminhtml\Items\Price\Renderer $block */ /** @var $_weeeHelper \Magento\Weee\Helper\Data */ -$_weeeHelper = $this->helper('Magento\Weee\Helper\Data'); +$_weeeHelper = $this->helper(\Magento\Weee\Helper\Data::class); $_item = $block->getItem(); ?> -<?php if ($block->displayBothPrices() || $block->displayPriceExclTax()): ?> +<?php if ($block->displayBothPrices() || $block->displayPriceExclTax()) : ?> <div class="price-excl-tax"> - <?php if ($block->displayBothPrices()): ?> - <span class="label"><?= /* @escapeNotVerified */ __('Excl. Tax') ?>:</span> + <?php if ($block->displayBothPrices()) : ?> + <span class="label"><?= $block->escapeHtml(__('Excl. Tax')) ?>:</span> <?php endif; ?> <?= $block->getUnitPriceExclTaxHtml() ?> - <?php if ($this->helper('Magento\Weee\Helper\Data')->getApplied($_item)): ?> + <?php if ($this->helper(\Magento\Weee\Helper\Data::class)->getApplied($_item)) : ?> <br /> - <?php if ($block->displayPriceWithWeeeDetails()): ?> + <?php if ($block->displayPriceWithWeeeDetails()) : ?> <small> - <?php foreach ($this->helper('Magento\Weee\Helper\Data')->getApplied($_item) as $tax): ?> - <span class="nobr"><?= /* @escapeNotVerified */ $tax['title'] ?>: <?= /* @escapeNotVerified */ $block->displayPrices($tax['base_amount'], $tax['amount']) ?></span> + <?php foreach ($this->helper(\Magento\Weee\Helper\Data::class)->getApplied($_item) as $tax) : ?> + <span class="nobr"><?= $block->escapeHtml($tax['title']) ?>: <?= /* @noEscape */ $block->displayPrices($tax['base_amount'], $tax['amount']) ?></span> <?php endforeach; ?> </small> <?php endif; ?> - <?php if ($block->displayFinalPrice()): ?> + <?php if ($block->displayFinalPrice()) : ?> <br /> - <span class="nobr"><?= /* @escapeNotVerified */ __('Total') ?>:<br /> + <span class="nobr"><?= $block->escapeHtml(__('Total')) ?>:<br /> <?= $block->getFinalUnitPriceExclTaxHtml() ?> </span> <?php endif; ?> <?php endif; ?> </div> <?php endif; ?> -<?php if ($block->displayBothPrices() || $block->displayPriceInclTax()): ?> +<?php if ($block->displayBothPrices() || $block->displayPriceInclTax()) : ?> <div class="price-incl-tax"> - <?php if ($block->displayBothPrices()): ?> - <span class="label"><?= /* @escapeNotVerified */ __('Incl. Tax') ?>:</span> + <?php if ($block->displayBothPrices()) : ?> + <span class="label"><?= $block->escapeHtml(__('Incl. Tax')) ?>:</span> <?php endif; ?> <?= $block->getUnitPriceInclTaxHtml() ?> - <?php if ($this->helper('Magento\Weee\Helper\Data')->getApplied($_item)): ?> + <?php if ($this->helper(\Magento\Weee\Helper\Data::class)->getApplied($_item)) : ?> <br /> - <?php if ($block->displayPriceWithWeeeDetails()): ?> + <?php if ($block->displayPriceWithWeeeDetails()) : ?> <small> - <?php foreach ($this->helper('Magento\Weee\Helper\Data')->getApplied($_item) as $tax): ?> - <span class="nobr"><?= /* @escapeNotVerified */ $tax['title'] ?>: <?= /* @escapeNotVerified */ $block->displayPrices($tax['base_amount_incl_tax'], $tax['amount_incl_tax']) ?></span> + <?php foreach ($this->helper(\Magento\Weee\Helper\Data::class)->getApplied($_item) as $tax) : ?> + <span class="nobr"><?= $block->escapeHtml($tax['title']) ?>: <?= /* @noEscape */ $block->displayPrices($tax['base_amount_incl_tax'], $tax['amount_incl_tax']) ?></span> <?php endforeach; ?> </small> <?php endif; ?> - <?php if ($block->displayFinalPrice()): ?> + <?php if ($block->displayFinalPrice()) : ?> <br /> - <span class="nobr"><?= /* @escapeNotVerified */ __('Total') ?>:<br /> + <span class="nobr"><?= $block->escapeHtml(__('Total')) ?>:<br /> <?= $block->getFinalUnitPriceInclTaxHtml() ?> </span> <?php endif; ?> diff --git a/app/code/Magento/Weee/view/adminhtml/templates/order/create/items/price/row.phtml b/app/code/Magento/Weee/view/adminhtml/templates/order/create/items/price/row.phtml index d9bd79e65a153..769e9e68e3a46 100644 --- a/app/code/Magento/Weee/view/adminhtml/templates/order/create/items/price/row.phtml +++ b/app/code/Magento/Weee/view/adminhtml/templates/order/create/items/price/row.phtml @@ -4,8 +4,7 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - +// phpcs:disable Magento2.Templates.ThisInTemplate ?> <?php /** @var $block \Magento\Weee\Block\Item\Price\Renderer */ @@ -13,50 +12,50 @@ $_item = $block->getItem(); ?> -<?php if ($block->displayPriceExclTax() || $block->displayBothPrices()): ?> - <?php if ($block->displayBothPrices()): ?> - <span class="label"><?= /* @escapeNotVerified */ __('Excl. Tax') ?>:</span> +<?php if ($block->displayPriceExclTax() || $block->displayBothPrices()) : ?> + <?php if ($block->displayBothPrices()) : ?> + <span class="label"><?= $block->escapeHtml(__('Excl. Tax')) ?>:</span> <?php endif; ?> - <?= /* @escapeNotVerified */ $block->formatPrice($block->getRowDisplayPriceExclTax()) ?> + <?= /* @noEscape */ $block->formatPrice($block->getRowDisplayPriceExclTax()) ?> - <?php if ($this->helper('Magento\Weee\Helper\Data')->getApplied($_item)): ?> + <?php if ($this->helper(\Magento\Weee\Helper\Data::class)->getApplied($_item)) : ?> <br /> - <?php if ($block->displayPriceWithWeeeDetails()): ?> + <?php if ($block->displayPriceWithWeeeDetails()) : ?> <small> - <?php foreach ($this->helper('Magento\Weee\Helper\Data')->getApplied($_item) as $tax): ?> - <span class="nobr"><?= /* @escapeNotVerified */ $tax['title'] ?>: <?= /* @escapeNotVerified */ $block->formatPrice($tax['row_amount'], true, true) ?></span><br /> + <?php foreach ($this->helper(\Magento\Weee\Helper\Data::class)->getApplied($_item) as $tax) : ?> + <span class="nobr"><?= $block->escapeHtml($tax['title']) ?>: <?= /* @noEscape */ $block->formatPrice($tax['row_amount'], true, true) ?></span><br /> <?php endforeach; ?> </small> <?php endif; ?> - <?php if ($block->displayFinalPrice()): ?> + <?php if ($block->displayFinalPrice()) : ?> <br /> - <span class="nobr"><?= /* @escapeNotVerified */ __('Total') ?>:<br /> - <?= /* @escapeNotVerified */ $block->formatPrice($block->getFinalRowDisplayPriceExclTax()) ?> + <span class="nobr"><?= $block->escapeHtml(__('Total')) ?>:<br /> + <?= /* @noEscape */ $block->formatPrice($block->getFinalRowDisplayPriceExclTax()) ?> </span> <?php endif; ?> <?php endif; ?> <?php endif; ?> -<?php if ($block->displayPriceInclTax() || $block->displayBothPrices()): ?> - <?php if ($block->displayBothPrices()): ?> - <br /><span class="label"><?= /* @escapeNotVerified */ __('Incl. Tax') ?>:</span> +<?php if ($block->displayPriceInclTax() || $block->displayBothPrices()) : ?> + <?php if ($block->displayBothPrices()) : ?> + <br /><span class="label"><?= $block->escapeHtml(__('Incl. Tax')) ?>:</span> <?php endif; ?> - <?= /* @escapeNotVerified */ $block->formatPrice($block->getRowDisplayPriceInclTax()) ?> - <?php if ($this->helper('Magento\Weee\Helper\Data')->getApplied($_item)): ?> + <?= /* @noEscape */ $block->formatPrice($block->getRowDisplayPriceInclTax()) ?> + <?php if ($this->helper(\Magento\Weee\Helper\Data::class)->getApplied($_item)) : ?> <br /> - <?php if ($block->displayPriceWithWeeeDetails()): ?> + <?php if ($block->displayPriceWithWeeeDetails()) : ?> <small> - <?php foreach ($this->helper('Magento\Weee\Helper\Data')->getApplied($_item) as $tax): ?> - <span class="nobr"><?= /* @escapeNotVerified */ $tax['title'] ?>: <?= /* @escapeNotVerified */ $block->formatPrice($tax['row_amount_incl_tax'], true, true) ?></span><br /> + <?php foreach ($this->helper(\Magento\Weee\Helper\Data::class)->getApplied($_item) as $tax) : ?> + <span class="nobr"><?= $block->escapeHtml($tax['title']) ?>: <?= /* @noEscape */ $block->formatPrice($tax['row_amount_incl_tax'], true, true) ?></span><br /> <?php endforeach; ?> </small> <?php endif; ?> - <?php if ($block->displayFinalPrice()): ?> - <span class="nobr"><?= /* @escapeNotVerified */ __('Total Incl. Tax') ?>:<br /> - <?= /* @escapeNotVerified */ $block->formatPrice($block->getFinalRowDisplayPriceInclTax()) ?> + <?php if ($block->displayFinalPrice()) : ?> + <span class="nobr"><?= $block->escapeHtml(__('Total Incl. Tax')) ?>:<br /> + <?= /* @noEscape */ $block->formatPrice($block->getFinalRowDisplayPriceInclTax()) ?> </span> <?php endif; ?> <?php endif; ?> diff --git a/app/code/Magento/Weee/view/adminhtml/templates/order/create/items/price/total.phtml b/app/code/Magento/Weee/view/adminhtml/templates/order/create/items/price/total.phtml index bda7b807c6094..fceca17acf61e 100644 --- a/app/code/Magento/Weee/view/adminhtml/templates/order/create/items/price/total.phtml +++ b/app/code/Magento/Weee/view/adminhtml/templates/order/create/items/price/total.phtml @@ -4,63 +4,61 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - +// phpcs:disable Magento2.Templates.ThisInTemplate ?> <?php /** @var \Magento\Weee\Block\Item\Price\Renderer $block */ - -$_weeeHelper = $this->helper('Magento\Weee\Helper\Data'); +$_weeeHelper = $this->helper(\Magento\Weee\Helper\Data::class); $_item = $block->getItem(); ?> -<?php if ($block->displayPriceExclTax() || $block->displayBothPrices()): ?> +<?php if ($block->displayPriceExclTax() || $block->displayBothPrices()) : ?> <?php $_rowTotalWithoutDiscount = $block->getRowDisplayPriceExclTax() - $_item->getTotalDiscountAmount(); ?> - <?php if ($block->displayBothPrices()): ?> - <span class="label"><?= /* @escapeNotVerified */ __('Excl. Tax') ?>:</span> + <?php if ($block->displayBothPrices()) : ?> + <span class="label"><?= $block->escapeHtml(__('Excl. Tax')) ?>:</span> <?php endif; ?> - <?= /* @escapeNotVerified */ $block->formatPrice(max(0, $_rowTotalWithoutDiscount)) ?> + <?= /* @noEscape */ $block->formatPrice(max(0, $_rowTotalWithoutDiscount)) ?> - <?php if ($this->helper('Magento\Weee\Helper\Data')->getApplied($_item)): ?> + <?php if ($this->helper(\Magento\Weee\Helper\Data::class)->getApplied($_item)) : ?> <br /> - <?php if ($block->displayPriceWithWeeeDetails()): ?> + <?php if ($block->displayPriceWithWeeeDetails()) : ?> <small> - <?php foreach ($this->helper('Magento\Weee\Helper\Data')->getApplied($_item) as $tax): ?> - <span class="nobr"><?= /* @escapeNotVerified */ $tax['title'] ?>: <?= /* @escapeNotVerified */ $block->formatPrice($tax['row_amount'], true, true) ?></span><br /> + <?php foreach ($this->helper(\Magento\Weee\Helper\Data::class)->getApplied($_item) as $tax) : ?> + <span class="nobr"><?= $block->escapeHtml($tax['title']) ?>: <?= /* @noEscape */ $block->formatPrice($tax['row_amount'], true, true) ?></span><br /> <?php endforeach; ?> </small> <?php endif; ?> - <?php if ($block->displayFinalPrice()): ?> + <?php if ($block->displayFinalPrice()) : ?> <br /> - <span class="nobr"><?= /* @escapeNotVerified */ __('Total') ?>:<br /> - <?= /* @escapeNotVerified */ $block->formatPrice($block->getFinalRowDisplayPriceExclTax() - $_item->getTotalDiscountAmount()) ?> + <span class="nobr"><?= $block->escapeHtml(__('Total')) ?>:<br /> + <?= /* @noEscape */ $block->formatPrice($block->getFinalRowDisplayPriceExclTax() - $_item->getTotalDiscountAmount()) ?> </span> <?php endif; ?> <?php endif; ?> <?php endif; ?> -<?php if ($block->displayPriceInclTax() || $block->displayBothPrices()): ?> - <?php if ($block->displayBothPrices()): ?> - <br /><span class="label"><?= /* @escapeNotVerified */ __('Incl. Tax') ?>:</span> +<?php if ($block->displayPriceInclTax() || $block->displayBothPrices()) : ?> + <?php if ($block->displayBothPrices()) : ?> + <br /><span class="label"><?= $block->escapeHtml(__('Incl. Tax')) ?>:</span> <?php endif; ?> <?php $_incl = $block->getTotalAmount($_item); ?> - <?= /* @escapeNotVerified */ $block->formatPrice(max(0, $_incl)) ?> - <?php if ($this->helper('Magento\Weee\Helper\Data')->getApplied($_item)): ?> + <?= /* @noEscape */ $block->formatPrice(max(0, $_incl)) ?> + <?php if ($this->helper(\Magento\Weee\Helper\Data::class)->getApplied($_item)) : ?> <br /> - <?php if ($block->displayPriceWithWeeeDetails()): ?> + <?php if ($block->displayPriceWithWeeeDetails()) : ?> <small> - <?php foreach ($this->helper('Magento\Weee\Helper\Data')->getApplied($_item) as $tax): ?> - <span class="nobr"><?= /* @escapeNotVerified */ $tax['title'] ?>: <?= /* @escapeNotVerified */ $block->formatPrice($tax['row_amount_incl_tax'], true, true) ?></span><br /> + <?php foreach ($this->helper(\Magento\Weee\Helper\Data::class)->getApplied($_item) as $tax) : ?> + <span class="nobr"><?= $block->escapeHtml($tax['title']) ?>: <?= /* @noEscape */ $block->formatPrice($tax['row_amount_incl_tax'], true, true) ?></span><br /> <?php endforeach; ?> </small> <?php endif; ?> - <?php if ($block->displayFinalPrice()): ?> - <span class="nobr"><?= /* @escapeNotVerified */ __('Total Incl. Tax') ?>:<br /> - <?= /* @escapeNotVerified */ $block->formatPrice($block->getFinalRowDisplayPriceInclTax() - $_item->getTotalDiscountAmount()) ?> + <?php if ($block->displayFinalPrice()) : ?> + <span class="nobr"><?= $block->escapeHtml(__('Total Incl. Tax')) ?>:<br /> + <?= /* @noEscape */ $block->formatPrice($block->getFinalRowDisplayPriceInclTax() - $_item->getTotalDiscountAmount()) ?> </span> <?php endif; ?> <?php endif; ?> diff --git a/app/code/Magento/Weee/view/adminhtml/templates/order/create/items/price/unit.phtml b/app/code/Magento/Weee/view/adminhtml/templates/order/create/items/price/unit.phtml index d56e0fc3ebb3f..362c88ecedd4b 100644 --- a/app/code/Magento/Weee/view/adminhtml/templates/order/create/items/price/unit.phtml +++ b/app/code/Magento/Weee/view/adminhtml/templates/order/create/items/price/unit.phtml @@ -4,8 +4,7 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - +// phpcs:disable Magento2.Templates.ThisInTemplate ?> <?php /** @var \Magento\Weee\Block\Item\Price\Renderer $block */ @@ -13,51 +12,51 @@ $_item = $block->getItem(); ?> -<?php if ($block->displayPriceExclTax() || $block->displayBothPrices()): ?> - <?php if ($block->displayBothPrices()): ?> - <span class="label"><?= /* @escapeNotVerified */ __('Excl. Tax') ?>:</span> +<?php if ($block->displayPriceExclTax() || $block->displayBothPrices()) : ?> + <?php if ($block->displayBothPrices()) : ?> + <span class="label"><?= $block->escapeHtml(__('Excl. Tax')) ?>:</span> <?php endif; ?> - <?= /* @escapeNotVerified */ $block->formatPrice($block->getUnitDisplayPriceExclTax()) ?> + <?= /* @noEscape */ $block->formatPrice($block->getUnitDisplayPriceExclTax()) ?> - <?php if ($this->helper('Magento\Weee\Helper\Data')->getApplied($_item)): ?> + <?php if ($this->helper(\Magento\Weee\Helper\Data::class)->getApplied($_item)) : ?> <br /> - <?php if ($block->displayPriceWithWeeeDetails()): ?> + <?php if ($block->displayPriceWithWeeeDetails()) : ?> <small> - <?php foreach ($this->helper('Magento\Weee\Helper\Data')->getApplied($_item) as $tax): ?> - <span class="nobr"><?= /* @escapeNotVerified */ $tax['title'] ?>: <?= /* @escapeNotVerified */ $block->formatPrice($tax['amount'], true, true) ?></span><br /> + <?php foreach ($this->helper(\Magento\Weee\Helper\Data::class)->getApplied($_item) as $tax) : ?> + <span class="nobr"><?= $block->escapeHtml($tax['title']) ?>: <?= /* @noEscape */ $block->formatPrice($tax['amount'], true, true) ?></span><br /> <?php endforeach; ?> </small> <?php endif; ?> - <?php if ($block->displayFinalPrice()): ?> + <?php if ($block->displayFinalPrice()) : ?> <br /> - <span class="nobr"><?= /* @escapeNotVerified */ __('Total') ?>:<br /> - <?= /* @escapeNotVerified */ $block->formatPrice($block->getFinalUnitDisplayPriceExclTax()) ?> + <span class="nobr"><?= $block->escapeHtml(__('Total')) ?>:<br /> + <?= /* @noEscape */ $block->formatPrice($block->getFinalUnitDisplayPriceExclTax()) ?> </span> <?php endif; ?> <?php endif; ?> <?php endif; ?> -<?php if ($block->displayPriceInclTax() || $block->displayBothPrices()): ?> - <?php if ($block->displayBothPrices()): ?> - <br /><span class="label"><?= /* @escapeNotVerified */ __('Incl. Tax') ?>:</span> +<?php if ($block->displayPriceInclTax() || $block->displayBothPrices()) : ?> + <?php if ($block->displayBothPrices()) : ?> + <br /><span class="label"><?= $block->escapeHtml(__('Incl. Tax')) ?>:</span> <?php endif; ?> - <?= /* @escapeNotVerified */ $block->formatPrice($block->getUnitDisplayPriceInclTax()) ?> + <?= /* @noEscape */ $block->formatPrice($block->getUnitDisplayPriceInclTax()) ?> - <?php if ($this->helper('Magento\Weee\Helper\Data')->getApplied($_item)): ?> + <?php if ($this->helper(\Magento\Weee\Helper\Data::class)->getApplied($_item)) : ?> <br /> - <?php if ($block->displayPriceWithWeeeDetails()): ?> + <?php if ($block->displayPriceWithWeeeDetails()) : ?> <small> - <?php foreach ($this->helper('Magento\Weee\Helper\Data')->getApplied($_item) as $tax): ?> - <span class="nobr"><?= /* @escapeNotVerified */ $tax['title'] ?>: <?= /* @escapeNotVerified */ $block->formatPrice($tax['amount_incl_tax'], true, true) ?></span><br /> + <?php foreach ($this->helper(\Magento\Weee\Helper\Data::class)->getApplied($_item) as $tax) : ?> + <span class="nobr"><?= $block->escapeHtml($tax['title']) ?>: <?= /* @noEscape */ $block->formatPrice($tax['amount_incl_tax'], true, true) ?></span><br /> <?php endforeach; ?> </small> <?php endif; ?> - <?php if ($block->displayFinalPrice()): ?> - <span class="nobr"><?= /* @escapeNotVerified */ __('Total Incl. Tax') ?>:<br /> - <?= /* @escapeNotVerified */ $block->formatPrice($block->getFinalUnitDisplayPriceInclTax()) ?> + <?php if ($block->displayFinalPrice()) : ?> + <span class="nobr"><?= $block->escapeHtml(__('Total Incl. Tax')) ?>:<br /> + <?= /* @noEscape */ $block->formatPrice($block->getFinalUnitDisplayPriceInclTax()) ?> </span> <?php endif; ?> <?php endif; ?> diff --git a/app/code/Magento/Weee/view/adminhtml/templates/renderer/tax.phtml b/app/code/Magento/Weee/view/adminhtml/templates/renderer/tax.phtml index 01ecaca435d15..1b77231640868 100644 --- a/app/code/Magento/Weee/view/adminhtml/templates/renderer/tax.phtml +++ b/app/code/Magento/Weee/view/adminhtml/templates/renderer/tax.phtml @@ -4,32 +4,31 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - +// phpcs:disable Magento2.Templates.ThisInTemplate ?> <?php /** @var $block \Magento\Weee\Block\Renderer\Weee\Tax */ $data = ['fptAttribute' => [ - 'region' => $this->helper('Magento\Framework\Json\Helper\Data')->jsonDecode( - $this->helper('Magento\Directory\Helper\Data')->getRegionJson() + 'region' => $this->helper(\Magento\Framework\Json\Helper\Data::class)->jsonDecode( + $this->helper(\Magento\Directory\Helper\Data::class)->getRegionJson() ), 'itemsData' => $block->getValues(), 'bundlePriceType' => '#price_type', ]]; ?> -<div id="attribute-<?= $block->getElement()->getHtmlId() ?>-container" class="field" - data-attribute-code="<?= $block->getElement()->getHtmlId() ?>" - data-mage-init="<?= $block->escapeHtml($this->helper('Magento\Framework\Json\Helper\Data')->jsonEncode($data)) ?>"> - <label class="label"><span><?= /* @escapeNotVerified */ $block->getElement()->getLabel() ?></span></label> +<div id="attribute-<?= /* @noEscape */ $block->getElement()->getHtmlId() ?>-container" class="field" + data-attribute-code="<?= /* @noEscape */ $block->getElement()->getHtmlId() ?>" + data-mage-init="<?= /* @noEscape */ $this->helper(\Magento\Framework\Json\Helper\Data::class)->jsonEncode($data) ?>"> + <label class="label"><span><?= $block->escapeHtml($block->getElement()->getLabel()) ?></span></label> <div class="control"> <table class="data-table"> <thead> <tr> - <th class="col-website" <?php if (!$block->isMultiWebsites()): ?>style="display: none;"<?php endif; ?>><?= /* @escapeNotVerified */ __('Website') ?></th> - <th class="col-country required"><?= /* @escapeNotVerified */ __('Country/State') ?></th> - <th class="col-tax required"><?= /* @escapeNotVerified */ __('Tax') ?></th> - <th class="col-action"><?= /* @escapeNotVerified */ __('Action') ?></th> + <th class="col-website" <?php if (!$block->isMultiWebsites()) : ?>style="display: none;"<?php endif; ?>><?= $block->escapeHtml(__('Website')) ?></th> + <th class="col-country required"><?= $block->escapeHtml(__('Country/State')) ?></th> + <th class="col-tax required"><?= $block->escapeHtml(__('Tax')) ?></th> + <th class="col-action"><?= $block->escapeHtml(__('Action')) ?></th> </tr> </thead> <tfoot> @@ -44,41 +43,45 @@ $data = ['fptAttribute' => [ Hidden field below with attribute code id is necessary for jQuery validation plugin. Validation message will be displayed after this field. --> - <input type="hidden" name="<?= $block->getElement()->getHtmlId() ?>" id="<?= $block->getElement()->getHtmlId() ?>" disabled="disabled"> + <input type="hidden" name="<?= /* @noEscape */ $block->getElement()->getHtmlId() ?>" id="<?= /* @noEscape */ $block->getElement()->getHtmlId() ?>" disabled="disabled"> </div> <script data-role="row-template" type="text/x-magento-template"> - <tr id="<?= $block->getElement()->getHtmlId() ?>_weee_tax_row_<%- data.index %>" data-role="fpt-item-row"> - <td class="col-website" <?php if (!$block->isMultiWebsites()): ?>style="display: none"<?php endif; ?>> - <select id="<?= /* @escapeNotVerified */ $block->getElement()->getName() ?>_weee_tax_row_<%- data.index %>_website" - name="<?= /* @escapeNotVerified */ $block->getElement()->getName() ?>[<%- data.index %>][website_id]" - class="<?= /* @escapeNotVerified */ $block->getElement()->getClass() ?> website required-entry" data-role="select-website"> - <?php foreach ($block->getWebsites() as $_websiteId => $_info): ?> - <option value="<?= /* @escapeNotVerified */ $_websiteId ?>"><?= /* @escapeNotVerified */ $_info['name'] ?><?php if (!empty($_info['currency'])): ?>[<?= /* @escapeNotVerified */ $_info['currency'] ?>]<?php endif; ?></option> + <?php + $elementName = $block->escapeHtmlAttr($block->getElement()->getName()); + $elementClass = $block->escapeHtmlAttr($block->getElement()->getClass()); + ?> + <tr id="<?= /* @noEscape */ $block->getElement()->getHtmlId() ?>_weee_tax_row_<%- data.index %>" data-role="fpt-item-row"> + <td class="col-website" <?php if (!$block->isMultiWebsites()) : ?>style="display: none"<?php endif; ?>> + <select id="<?= /* @noEscape */ $elementName ?>_weee_tax_row_<%- data.index %>_website" + name="<?= /* @noEscape */ $elementName ?>[<%- data.index %>][website_id]" + class="<?= /* @noEscape */ $elementClass ?> website required-entry" data-role="select-website"> + <?php foreach ($block->getWebsites() as $_websiteId => $_info) : ?> + <option value="<?= /* @noEscape */ $_websiteId ?>"><?= $block->escapeHtml($_info['name']) ?><?php if (!empty($_info['currency'])) : ?>[<?= /* @noEscape */ $_info['currency'] ?>]<?php endif; ?></option> <?php endforeach ?> </select> </td> <td class="col-country"> - <select id="<?= /* @escapeNotVerified */ $block->getElement()->getName() ?>_weee_tax_row_<%- data.index %>_country" - name="<?= /* @escapeNotVerified */ $block->getElement()->getName() ?>[<%- data.index %>][country]" - class="<?= /* @escapeNotVerified */ $block->getElement()->getClass() ?> country required-entry" data-role="select-country"> - <?php foreach ($block->getCountries() as $_country): ?> - <option value="<?= /* @escapeNotVerified */ $_country['value'] ?>"><?= /* @escapeNotVerified */ htmlspecialchars($_country['label']) ?></option> + <select id="<?= /* @noEscape */ $elementName ?>_weee_tax_row_<%- data.index %>_country" + name="<?= /* @noEscape */ $elementName ?>[<%- data.index %>][country]" + class="<?= /* @noEscape */ $elementClass ?> country required-entry" data-role="select-country"> + <?php foreach ($block->getCountries() as $_country) : ?> + <option value="<?= $block->escapeHtmlAttr($_country['value']) ?>"><?= $block->escapeHtml($_country['label']) ?></option> <?php endforeach ?> </select> - <select id="<?= /* @escapeNotVerified */ $block->getElement()->getName() ?>_weee_tax_row_<%- data.index %>_state" - name="<?= /* @escapeNotVerified */ $block->getElement()->getName() ?>[<%- data.index %>][state]" - class="<?= /* @escapeNotVerified */ $block->getElement()->getClass() ?> state" disabled="" data-role="select-state"> + <select id="<?= /* @noEscape */ $elementName ?>_weee_tax_row_<%- data.index %>_state" + name="<?= /* @noEscape */ $elementName ?>[<%- data.index %>][state]" + class="<?= /* @noEscape */ $elementClass ?> state" disabled="" data-role="select-state"> <option value="0">*</option> </select> </td> <td class="col-tax"> - <input name="<?= /* @escapeNotVerified */ $block->getElement()->getName() ?>[<%- data.index %>][price]" - class="<?= /* @escapeNotVerified */ $block->getElement()->getClass() ?> required-entry validate-greater-than-zero" + <input name="<?= /* @noEscape */ $elementName ?>[<%- data.index %>][price]" + class="<?= /* @noEscape */ $elementClass ?> required-entry validate-greater-than-zero" type="text" value="<%- data.value %>"/> </td> <td class="col-action"> - <input name="<?= /* @escapeNotVerified */ $block->getElement()->getName() ?>[<%- data.index %>][delete]" class="delete" type="hidden" value="" data-role="delete-fpt-item"/> + <input name="<?= /* @noEscape */ $elementName ?>[<%- data.index %>][delete]" class="delete" type="hidden" value="" data-role="delete-fpt-item"/> <?= $block->getChildHtml('delete_button') ?> </td> </tr> diff --git a/app/code/Magento/Weee/view/base/templates/pricing/adjustment.phtml b/app/code/Magento/Weee/view/base/templates/pricing/adjustment.phtml index c05d48cab6a43..a2b5ad4f196e8 100644 --- a/app/code/Magento/Weee/view/base/templates/pricing/adjustment.phtml +++ b/app/code/Magento/Weee/view/base/templates/pricing/adjustment.phtml @@ -3,9 +3,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - -// @codingStandardsIgnoreFile - ?> <?php @@ -18,34 +15,37 @@ $taxDisplay = $block->getTaxDisplayConfig(); $priceDisplay = $block->isPriceIncludesTax(); ?> -<?php if ($block->showInclDescr() || $block->showExclDescrIncl()): // incl. + weee || excl. + weee + final ?> - <?php foreach ($block->getWeeeTaxAttributes() as $weeeTaxAttribute): ?> - <?php if ($taxDisplay == Magento\Tax\Model\Config::DISPLAY_TYPE_INCLUDING_TAX): ?> +<?php if ($block->showInclDescr() || $block->showExclDescrIncl()) : // incl. + weee || excl. + weee + final ?> + <?php foreach ($block->getWeeeTaxAttributes() as $weeeTaxAttribute) : ?> + <?php + $attributeName = $block->escapeHtmlAttr($block->renderWeeeTaxAttributeName($weeeTaxAttribute)); + ?> + <?php if ($taxDisplay == Magento\Tax\Model\Config::DISPLAY_TYPE_INCLUDING_TAX) : ?> <span class="weee" data-price-type="weee" - data-label="<?= /* @escapeNotVerified */ $block->renderWeeeTaxAttributeName($weeeTaxAttribute) ?>"> - <?= /* @escapeNotVerified */ $block->renderWeeeTaxAttributeWithTax($weeeTaxAttribute) ?></span> - <?php elseif ($taxDisplay == Magento\Tax\Model\Config::DISPLAY_TYPE_EXCLUDING_TAX): ?> + data-label="<?= /* @noEscape */ $attributeName ?>"> + <?= /* @noEscape */ $block->renderWeeeTaxAttributeWithTax($weeeTaxAttribute) ?></span> + <?php elseif ($taxDisplay == Magento\Tax\Model\Config::DISPLAY_TYPE_EXCLUDING_TAX) : ?> <span class="weee" data-price-type="weee" - data-label="<?= /* @escapeNotVerified */ $block->renderWeeeTaxAttributeName($weeeTaxAttribute) ?>"> - <?= /* @escapeNotVerified */ $block->renderWeeeTaxAttributeWithoutTax($weeeTaxAttribute) ?></span> - <?php elseif ($taxDisplay == Magento\Tax\Model\Config::DISPLAY_TYPE_BOTH): ?> + data-label="<?= /* @noEscape */ $attributeName ?>"> + <?= /* @noEscape */ $block->renderWeeeTaxAttributeWithoutTax($weeeTaxAttribute) ?></span> + <?php elseif ($taxDisplay == Magento\Tax\Model\Config::DISPLAY_TYPE_BOTH) : ?> <span class="weee" data-price-type="weee" - data-label="<?= /* @escapeNotVerified */ $block->renderWeeeTaxAttributeName($weeeTaxAttribute) . ' Incl. Tax' ?>"> - <?= /* @escapeNotVerified */ $block->renderWeeeTaxAttributeWithTax($weeeTaxAttribute) ?></span> + data-label="<?= /* @noEscape */ $attributeName . ' ' . $block->escapeHtmlAttr(__('Incl. Tax')) ?>"> + <?= /* @noEscape */ $block->renderWeeeTaxAttributeWithTax($weeeTaxAttribute) ?></span> <span class="weee" data-price-type="weee" - data-label="<?= /* @escapeNotVerified */ $block->renderWeeeTaxAttributeName($weeeTaxAttribute) . ' Excl. Tax' ?>"> - <?= /* @escapeNotVerified */ $block->renderWeeeTaxAttributeWithoutTax($weeeTaxAttribute) ?></span> + data-label="<?= /* @noEscape */ $attributeName . ' ' . $block->escapeHtmlAttr(__('Excl. Tax')) ?>"> + <?= /* @noEscape */ $block->renderWeeeTaxAttributeWithoutTax($weeeTaxAttribute) ?></span> <?php endif; ?> <?php endforeach; ?> <?php endif; ?> -<?php if ($block->showExclDescrIncl()): // excl. + weee + final ?> +<?php if ($block->showExclDescrIncl()) : // excl. + weee + final ?> <span class="price-final price-final_price" data-price-type="weeePrice" - data-price-amount="<?= /* @escapeNotVerified */ $block->getRawFinalAmount() ?>" - data-label="<?= /* @escapeNotVerified */ __('Final Price') ?>"><?= /* @escapeNotVerified */ $block->getFinalAmount() ?></span> + data-price-amount="<?= /* @noEscape */ $block->getRawFinalAmount() ?>" + data-label="<?= $block->escapeHtmlAttr(__('Final Price')) ?>"><?= /* @noEscape */ $block->getFinalAmount() ?></span> <?php endif; ?> diff --git a/app/code/Magento/Weee/view/frontend/templates/checkout/cart/item/price/sidebar.phtml b/app/code/Magento/Weee/view/frontend/templates/checkout/cart/item/price/sidebar.phtml index 9c7a77c54a373..0cd1ca8057984 100644 --- a/app/code/Magento/Weee/view/frontend/templates/checkout/cart/item/price/sidebar.phtml +++ b/app/code/Magento/Weee/view/frontend/templates/checkout/cart/item/price/sidebar.phtml @@ -4,7 +4,7 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile +// phpcs:disable Magento2.Templates.ThisInTemplate /** @var $block \Magento\Weee\Block\Item\Price\Renderer */ @@ -15,30 +15,30 @@ $originalZone = $block->getZone(); $block->setZone(\Magento\Framework\Pricing\Render::ZONE_CART); ?> -<?php if ($block->displayPriceInclTax() || $block->displayBothPrices()): ?> - <span class="price-including-tax" data-label="<?= $block->escapeHtml(__('Incl. Tax')) ?>"> - <?php if ($block->displayPriceWithWeeeDetails()): ?> +<?php if ($block->displayPriceInclTax() || $block->displayBothPrices()) : ?> + <span class="price-including-tax" data-label="<?= $block->escapeHtmlAttr(__('Incl. Tax')) ?>"> + <?php if ($block->displayPriceWithWeeeDetails()) : ?> <span class="minicart-tax-total"> - <?php else: ?> + <?php else : ?> <span class="minicart-price"> <?php endif; ?> - <?= /* @escapeNotVerified */ $block->formatPrice($block->getUnitDisplayPriceInclTax()) ?> + <?= /* @noEscape */ $block->formatPrice($block->getUnitDisplayPriceInclTax()) ?> </span> - <?php if ($block->displayPriceWithWeeeDetails()): ?> - <?php if ($this->helper('Magento\Weee\Helper\Data')->getApplied($item)): ?> + <?php if ($block->displayPriceWithWeeeDetails()) : ?> + <?php if ($this->helper(\Magento\Weee\Helper\Data::class)->getApplied($item)) : ?> <span class="minicart-tax-info"> - <?php foreach ($this->helper('Magento\Weee\Helper\Data')->getApplied($item) as $tax): ?> - <span class="weee" data-label="<?= /* @escapeNotVerified */ $tax['title'] ?>"> - <?= /* @escapeNotVerified */ $block->formatPrice($tax['amount_incl_tax'], true, true) ?> + <?php foreach ($this->helper(\Magento\Weee\Helper\Data::class)->getApplied($item) as $tax) : ?> + <span class="weee" data-label="<?= $block->escapeHtmlAttr($tax['title']) ?>"> + <?= /* @noEscape */ $block->formatPrice($tax['amount_incl_tax'], true, true) ?> </span> <?php endforeach; ?> </span> - <?php if ($block->displayFinalPrice()): ?> + <?php if ($block->displayFinalPrice()) : ?> <span class="minicart-tax-total"> - <span class="weee" data-label="<?= $block->escapeHtml(__('Total Incl. Tax')) ?>"> - <?= /* @escapeNotVerified */ $block->formatPrice($block->getFinalUnitDisplayPriceInclTax()) ?> + <span class="weee" data-label="<?= $block->escapeHtmlAttr(__('Total Incl. Tax')) ?>"> + <?= /* @noEscape */ $block->formatPrice($block->getFinalUnitDisplayPriceInclTax()) ?> </span> </span> <?php endif; ?> @@ -47,30 +47,30 @@ $block->setZone(\Magento\Framework\Pricing\Render::ZONE_CART); </span> <?php endif; ?> -<?php if ($block->displayPriceExclTax() || $block->displayBothPrices()): ?> - <span class="price-excluding-tax" data-label="<?= $block->escapeHtml(__('Excl. Tax')) ?>"> - <?php if ($block->displayPriceWithWeeeDetails()): ?> +<?php if ($block->displayPriceExclTax() || $block->displayBothPrices()) : ?> + <span class="price-excluding-tax" data-label="<?= $block->escapeHtmlAttr(__('Excl. Tax')) ?>"> + <?php if ($block->displayPriceWithWeeeDetails()) : ?> <span class="minicart-tax-total"> - <?php else: ?> + <?php else : ?> <span class="minicart-price"> <?php endif; ?> - <?= /* @escapeNotVerified */ $block->formatPrice($block->getUnitDisplayPriceExclTax()) ?> + <?= /* @noEscape */ $block->formatPrice($block->getUnitDisplayPriceExclTax()) ?> </span> - <?php if ($block->displayPriceWithWeeeDetails()): ?> - <?php if ($this->helper('Magento\Weee\Helper\Data')->getApplied($item)): ?> + <?php if ($block->displayPriceWithWeeeDetails()) : ?> + <?php if ($this->helper(\Magento\Weee\Helper\Data::class)->getApplied($item)) : ?> <span class="minicart-tax-info"> - <?php foreach ($this->helper('Magento\Weee\Helper\Data')->getApplied($item) as $tax): ?> - <span class="weee" data-label="<?= /* @escapeNotVerified */ $tax['title'] ?>"> - <?= /* @escapeNotVerified */ $block->formatPrice($tax['amount'], true, true) ?> + <?php foreach ($this->helper(\Magento\Weee\Helper\Data::class)->getApplied($item) as $tax) : ?> + <span class="weee" data-label="<?= $block->escapeHtmlAttr($tax['title']) ?>"> + <?= /* @noEscape */ $block->formatPrice($tax['amount'], true, true) ?> </span> <?php endforeach; ?> </span> - <?php if ($block->displayFinalPrice()): ?> + <?php if ($block->displayFinalPrice()) : ?> <span class="minicart-tax-total"> - <span class="weee" data-label="<?= $block->escapeHtml(__('Total')) ?>"> - <?= /* @escapeNotVerified */ $block->formatPrice($block->getFinalUnitDisplayPriceExclTax()) ?> + <span class="weee" data-label="<?= $block->escapeHtmlAttr(__('Total')) ?>"> + <?= /* @noEscape */ $block->formatPrice($block->getFinalUnitDisplayPriceExclTax()) ?> </span> </span> <?php endif; ?> diff --git a/app/code/Magento/Weee/view/frontend/templates/checkout/onepage/review/item/price/row_excl_tax.phtml b/app/code/Magento/Weee/view/frontend/templates/checkout/onepage/review/item/price/row_excl_tax.phtml index bc63a5eff642b..15abae5c889fe 100644 --- a/app/code/Magento/Weee/view/frontend/templates/checkout/onepage/review/item/price/row_excl_tax.phtml +++ b/app/code/Magento/Weee/view/frontend/templates/checkout/onepage/review/item/price/row_excl_tax.phtml @@ -4,33 +4,33 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile +// phpcs:disable Magento2.Templates.ThisInTemplate /** @var $block \Magento\Weee\Block\Item\Price\Renderer */ $_item = $block->getItem(); ?> -<?php if ($block->displayPriceWithWeeeDetails()): ?> - <span class="cart-tax-total" data-mage-init='{"taxToggle": {"itemTaxId" : "#esubtotal-item-tax-details<?= /* @escapeNotVerified */ $_item->getId() ?>"}}'> -<?php else: ?> +<?php if ($block->displayPriceWithWeeeDetails()) : ?> + <span class="cart-tax-total" data-mage-init='{"taxToggle": {"itemTaxId" : "#esubtotal-item-tax-details<?= (int) $_item->getId() ?>"}}'> +<?php else : ?> <span class="cart-price"> <?php endif; ?> -<?= /* @escapeNotVerified */ $block->formatPrice($block->getRowDisplayPriceExclTax()) ?> +<?= /* @noEscape */ $block->formatPrice($block->getRowDisplayPriceExclTax()) ?> </span> -<?php if ($this->helper('Magento\Weee\Helper\Data')->getApplied($_item)): ?> - <span class="cart-tax-info" id="esubtotal-item-tax-details<?= /* @escapeNotVerified */ $_item->getId() ?>" style="display: none;"> - <?php if ($block->displayPriceWithWeeeDetails()): ?> - <?php foreach ($this->helper('Magento\Weee\Helper\Data')->getApplied($_item) as $tax): ?> - <span class="weee" data-label="<?= /* @escapeNotVerified */ $tax['title'] ?>"><?= /* @escapeNotVerified */ $block->formatPrice($tax['row_amount'], true, true) ?></span> +<?php if ($this->helper(\Magento\Weee\Helper\Data::class)->getApplied($_item)) : ?> + <span class="cart-tax-info" id="esubtotal-item-tax-details<?= (int) $_item->getId() ?>" style="display: none;"> + <?php if ($block->displayPriceWithWeeeDetails()) : ?> + <?php foreach ($this->helper(\Magento\Weee\Helper\Data::class)->getApplied($_item) as $tax) : ?> + <span class="weee" data-label="<?= $block->escapeHtmlAttr($tax['title']) ?>"><?= /* @noEscape */ $block->formatPrice($tax['row_amount'], true, true) ?></span> <?php endforeach; ?> <?php endif; ?> </span> - <?php if ($block->displayFinalPrice()): ?> - <span class="cart-tax-total" data-mage-init='{"taxToggle": {"itemTaxId" : "#esubtotal-item-tax-details<?= /* @escapeNotVerified */ $_item->getId() ?>"}}'> - <span class="weee" data-label="<?= /* @escapeNotVerified */ __('Total') ?>"> - <?= /* @escapeNotVerified */ $block->formatPrice($block->getFinalRowDisplayPriceExclTax()) ?> + <?php if ($block->displayFinalPrice()) : ?> + <span class="cart-tax-total" data-mage-init='{"taxToggle": {"itemTaxId" : "#esubtotal-item-tax-details<?= (int) $_item->getId() ?>"}}'> + <span class="weee" data-label="<?= $block->escapeHtmlAttr(__('Total')) ?>"> + <?= /* @noEscape */ $block->formatPrice($block->getFinalRowDisplayPriceExclTax()) ?> </span> </span> <?php endif; ?> diff --git a/app/code/Magento/Weee/view/frontend/templates/checkout/onepage/review/item/price/row_incl_tax.phtml b/app/code/Magento/Weee/view/frontend/templates/checkout/onepage/review/item/price/row_incl_tax.phtml index a85904e197bfb..b848698b8b829 100644 --- a/app/code/Magento/Weee/view/frontend/templates/checkout/onepage/review/item/price/row_incl_tax.phtml +++ b/app/code/Magento/Weee/view/frontend/templates/checkout/onepage/review/item/price/row_incl_tax.phtml @@ -4,36 +4,36 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile +// phpcs:disable Magento2.Templates.ThisInTemplate /** @var $block \Magento\Weee\Block\Item\Price\Renderer */ $_item = $block->getItem(); /** @var $_weeeHelper \Magento\Weee\Helper\Data */ -$_weeeHelper = $this->helper('Magento\Weee\Helper\Data'); +$_weeeHelper = $this->helper(\Magento\Weee\Helper\Data::class); ?> <?php $_incl = $_item->getRowTotalInclTax(); ?> -<?php if ($block->displayPriceWithWeeeDetails()): ?> - <span class="cart-tax-total" data-mage-init='{"taxToggle": {"itemTaxId" : "#subtotal-item-tax-details<?= /* @escapeNotVerified */ $_item->getId() ?>"}}'> -<?php else: ?> +<?php if ($block->displayPriceWithWeeeDetails()) : ?> + <span class="cart-tax-total" data-mage-init='{"taxToggle": {"itemTaxId" : "#subtotal-item-tax-details<?= (int) $_item->getId() ?>"}}'> +<?php else : ?> <span class="cart-price"> <?php endif; ?> -<?= /* @escapeNotVerified */ $block->formatPrice($block->getRowDisplayPriceInclTax()) ?> +<?= /* @noEscape */ $block->formatPrice($block->getRowDisplayPriceInclTax()) ?> </span> -<?php if ($this->helper('Magento\Weee\Helper\Data')->getApplied($_item)): ?> - <span class="cart-tax-info" id="subtotal-item-tax-details<?= /* @escapeNotVerified */ $_item->getId() ?>" style="display: none;"> - <?php if ($block->displayPriceWithWeeeDetails()): ?> - <?php foreach ($this->helper('Magento\Weee\Helper\Data')->getApplied($_item) as $tax): ?> - <span class="weee" data-label="<?= /* @escapeNotVerified */ $tax['title'] ?>"><?= /* @escapeNotVerified */ $block->formatPrice($tax['row_amount_incl_tax'], true, true) ?></span> +<?php if ($this->helper(\Magento\Weee\Helper\Data::class)->getApplied($_item)) : ?> + <span class="cart-tax-info" id="subtotal-item-tax-details<?= (int) $_item->getId() ?>" style="display: none;"> + <?php if ($block->displayPriceWithWeeeDetails()) : ?> + <?php foreach ($this->helper(\Magento\Weee\Helper\Data::class)->getApplied($_item) as $tax) : ?> + <span class="weee" data-label="<?= $block->escapeHtmlAttr($tax['title']) ?>"><?= /* @noEscape */ $block->formatPrice($tax['row_amount_incl_tax'], true, true) ?></span> <?php endforeach; ?> <?php endif; ?> </span> - <?php if ($block->displayFinalPrice()): ?> - <span class="cart-tax-total" data-mage-init='{"taxToggle": {"itemTaxId" : "#subtotal-item-tax-details<?= /* @escapeNotVerified */ $_item->getId() ?>"}}'> - <span class="weee" data-label="<?= /* @escapeNotVerified */ __('Total Incl. Tax') ?>"> - <?= /* @escapeNotVerified */ $block->formatPrice($block->getFinalRowDisplayPriceInclTax()) ?> + <?php if ($block->displayFinalPrice()) : ?> + <span class="cart-tax-total" data-mage-init='{"taxToggle": {"itemTaxId" : "#subtotal-item-tax-details<?= (int) $_item->getId() ?>"}}'> + <span class="weee" data-label="<?= $block->escapeHtmlAttr(__('Total Incl. Tax')) ?>"> + <?= /* @noEscape */ $block->formatPrice($block->getFinalRowDisplayPriceInclTax()) ?> </span> </span> <?php endif; ?> diff --git a/app/code/Magento/Weee/view/frontend/templates/checkout/onepage/review/item/price/unit_excl_tax.phtml b/app/code/Magento/Weee/view/frontend/templates/checkout/onepage/review/item/price/unit_excl_tax.phtml index dc1d22ff3b0dd..a485de90c871d 100644 --- a/app/code/Magento/Weee/view/frontend/templates/checkout/onepage/review/item/price/unit_excl_tax.phtml +++ b/app/code/Magento/Weee/view/frontend/templates/checkout/onepage/review/item/price/unit_excl_tax.phtml @@ -4,34 +4,33 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile +// phpcs:disable Magento2.Templates.ThisInTemplate /** @var $block \Magento\Weee\Block\Item\Price\Renderer */ $_item = $block->getItem(); ?> -<?php if ($block->displayPriceWithWeeeDetails()): ?> - <span class="cart-tax-total" data-mage-init='{"taxToggle": {"itemTaxId" : "#eunit-item-tax-details<?= /* @escapeNotVerified */ $_item->getId() ?>"}}'> -<?php else: ?> +<?php if ($block->displayPriceWithWeeeDetails()) : ?> + <span class="cart-tax-total" data-mage-init='{"taxToggle": {"itemTaxId" : "#eunit-item-tax-details<?= (int) $_item->getId() ?>"}}'> +<?php else : ?> <span class="cart-price"> <?php endif; ?> -<?= /* @escapeNotVerified */ $block->formatPrice($block->getUnitDisplayPriceExclTax()) ?> +<?= /* @noEscape */ $block->formatPrice($block->getUnitDisplayPriceExclTax()) ?> </span> - -<?php if ($this->helper('Magento\Weee\Helper\Data')->getApplied($_item)): ?> - <span class="cart-tax-info" id="eunit-item-tax-details<?= /* @escapeNotVerified */ $_item->getId() ?>" style="display:none;"> - <?php if ($block->displayPriceWithWeeeDetails()): ?> - <?php foreach ($this->helper('Magento\Weee\Helper\Data')->getApplied($_item) as $tax): ?> - <span class="weee" data-label="<?= /* @escapeNotVerified */ $tax['title'] ?>"><?= /* @escapeNotVerified */ $block->formatPrice($tax['amount'], true, true) ?></span> +<?php if ($this->helper(\Magento\Weee\Helper\Data::class)->getApplied($_item)) : ?> + <span class="cart-tax-info" id="eunit-item-tax-details<?= (int) $_item->getId() ?>" style="display:none;"> + <?php if ($block->displayPriceWithWeeeDetails()) : ?> + <?php foreach ($this->helper(\Magento\Weee\Helper\Data::class)->getApplied($_item) as $tax) : ?> + <span class="weee" data-label="<?= $block->escapeHtmlAttr($tax['title']) ?>"><?= /* @noEscape */ $block->formatPrice($tax['amount'], true, true) ?></span> <?php endforeach; ?> <?php endif; ?> </span> - <?php if ($block->displayFinalPrice()): ?> - <span class="cart-tax-total" data-mage-init='{"taxToggle": {"itemTaxId" : "#eunit-item-tax-details<?= /* @escapeNotVerified */ $_item->getId() ?>"}}'> - <span class="weee" data-label="<?= /* @escapeNotVerified */ __('Total') ?>"> - <?= /* @escapeNotVerified */ $block->formatPrice($block->getFinalUnitDisplayPriceExclTax()) ?> + <?php if ($block->displayFinalPrice()) : ?> + <span class="cart-tax-total" data-mage-init='{"taxToggle": {"itemTaxId" : "#eunit-item-tax-details<?= (int) $_item->getId() ?>"}}'> + <span class="weee" data-label="<?= $block->escapeHtmlAttr(__('Total')) ?>"> + <?= /* @noEscape */ $block->formatPrice($block->getFinalUnitDisplayPriceExclTax()) ?> </span> </span> <?php endif; ?> diff --git a/app/code/Magento/Weee/view/frontend/templates/checkout/onepage/review/item/price/unit_incl_tax.phtml b/app/code/Magento/Weee/view/frontend/templates/checkout/onepage/review/item/price/unit_incl_tax.phtml index 3b7c2f530d3be..0dada610e181e 100644 --- a/app/code/Magento/Weee/view/frontend/templates/checkout/onepage/review/item/price/unit_incl_tax.phtml +++ b/app/code/Magento/Weee/view/frontend/templates/checkout/onepage/review/item/price/unit_incl_tax.phtml @@ -4,37 +4,37 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile +// phpcs:disable Magento2.Templates.ThisInTemplate /** @var $block \Magento\Weee\Block\Item\Price\Renderer */ $_item = $block->getItem(); /** @var $_weeeHelper \Magento\Weee\Helper\Data */ -$_weeeHelper = $this->helper('Magento\Weee\Helper\Data'); +$_weeeHelper = $this->helper(\Magento\Weee\Helper\Data::class); ?> <?php $_incl = $_item->getPriceInclTax(); ?> -<?php if ($block->displayPriceWithWeeeDetails()): ?> - <span class="cart-tax-total" data-mage-init='{"taxToggle": {"itemTaxId" : "#unit-item-tax-details<?= /* @escapeNotVerified */ $_item->getId() ?>"}}'> -<?php else: ?> +<?php if ($block->displayPriceWithWeeeDetails()) : ?> + <span class="cart-tax-total" data-mage-init='{"taxToggle": {"itemTaxId" : "#unit-item-tax-details<?= (int) $_item->getId() ?>"}}'> +<?php else : ?> <span class="cart-price"> <?php endif; ?> -<?= /* @escapeNotVerified */ $block->formatPrice($block->getUnitDisplayPriceInclTax()) ?> +<?= /* @noEscape */ $block->formatPrice($block->getUnitDisplayPriceInclTax()) ?> </span> -<?php if ($this->helper('Magento\Weee\Helper\Data')->getApplied($_item)): ?> - <span class="cart-tax-info" id="unit-item-tax-details<?= /* @escapeNotVerified */ $_item->getId() ?>" style="display: none;"> - <?php if ($block->displayPriceWithWeeeDetails()): ?> - <?php foreach ($this->helper('Magento\Weee\Helper\Data')->getApplied($_item) as $tax): ?> - <span class="weee" data-label="<?= /* @escapeNotVerified */ $tax['title'] ?>"><?= /* @escapeNotVerified */ $block->formatPrice($tax['amount_incl_tax'], true, true) ?></span> +<?php if ($this->helper(\Magento\Weee\Helper\Data::class)->getApplied($_item)) : ?> + <span class="cart-tax-info" id="unit-item-tax-details<?= (int) $_item->getId() ?>" style="display: none;"> + <?php if ($block->displayPriceWithWeeeDetails()) : ?> + <?php foreach ($this->helper(\Magento\Weee\Helper\Data::class)->getApplied($_item) as $tax) : ?> + <span class="weee" data-label="<?= $block->escapeHtmlAttr($tax['title']) ?>"><?= /* @noEscape */ $block->formatPrice($tax['amount_incl_tax'], true, true) ?></span> <?php endforeach; ?> <?php endif; ?> </span> - <?php if ($block->displayFinalPrice()): ?> - <span class="cart-tax-total" data-mage-init='{"taxToggle": {"itemTaxId" : "#unit-item-tax-details<?= /* @escapeNotVerified */ $_item->getId() ?>"}}'> - <span class="weee" data-label="<?= /* @escapeNotVerified */ __('Total Incl. Tax') ?>"> - <?= /* @escapeNotVerified */ $block->formatPrice($block->getFinalUnitDisplayPriceInclTax()) ?> + <?php if ($block->displayFinalPrice()) : ?> + <span class="cart-tax-total" data-mage-init='{"taxToggle": {"itemTaxId" : "#unit-item-tax-details<?= (int) $_item->getId() ?>"}}'> + <span class="weee" data-label="<?= $block->escapeHtmlAttr(__('Total Incl. Tax')) ?>"> + <?= /* @noEscape */ $block->formatPrice($block->getFinalUnitDisplayPriceInclTax()) ?> </span> </span> <?php endif; ?> diff --git a/app/code/Magento/Weee/view/frontend/templates/email/items/price/row.phtml b/app/code/Magento/Weee/view/frontend/templates/email/items/price/row.phtml index bd0eefd9d2b98..edcf73d2f9577 100644 --- a/app/code/Magento/Weee/view/frontend/templates/email/items/price/row.phtml +++ b/app/code/Magento/Weee/view/frontend/templates/email/items/price/row.phtml @@ -4,61 +4,60 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - +// phpcs:disable Magento2.Templates.ThisInTemplate ?> <?php /** @var \Magento\Weee\Block\Item\Price\Renderer $block */ /** @var $_weeeHelper \Magento\Weee\Helper\Data */ -$_weeeHelper = $this->helper('Magento\Weee\Helper\Data'); +$_weeeHelper = $this->helper(\Magento\Weee\Helper\Data::class); $_item = $block->getItem(); /** @var \Magento\Sales\Model\Order $_order */ $_order = $_item->getOrder(); ?> -<?php if ($block->displayPriceExclTax() || $block->displayBothPrices()): ?> - <?php if ($block->displayBothPrices()): ?> - <span class="label"><?= /* @escapeNotVerified */ __('Excl. Tax') ?>:</span> +<?php if ($block->displayPriceExclTax() || $block->displayBothPrices()) : ?> + <?php if ($block->displayBothPrices()) : ?> + <span class="label"><?= $block->escapeHtml(__('Excl. Tax')) ?>:</span> <?php endif; ?> - <?= /* @escapeNotVerified */ $_order->formatPrice($block->getRowDisplayPriceExclTax()) ?> + <?= /* @noEscape */ $_order->formatPrice($block->getRowDisplayPriceExclTax()) ?> - <?php if ($this->helper('Magento\Weee\Helper\Data')->getApplied($_item)): ?> + <?php if ($this->helper(\Magento\Weee\Helper\Data::class)->getApplied($_item)) : ?> <br /> - <?php if ($block->displayPriceWithWeeeDetails()): ?> + <?php if ($block->displayPriceWithWeeeDetails()) : ?> <small> - <?php foreach ($this->helper('Magento\Weee\Helper\Data')->getApplied($_item) as $tax): ?> - <span class="nobr"><?= /* @escapeNotVerified */ $tax['title'] ?>: <?= /* @escapeNotVerified */ $_order->formatPrice($tax['row_amount'], true, true) ?></span><br /> + <?php foreach ($this->helper(\Magento\Weee\Helper\Data::class)->getApplied($_item) as $tax) : ?> + <span class="nobr"><?= $block->escapeHtml($tax['title']) ?>: <?= /* @noEscape */ $_order->formatPrice($tax['row_amount'], true, true) ?></span><br /> <?php endforeach; ?> </small> <?php endif; ?> - <?php if ($block->displayFinalPrice()): ?> + <?php if ($block->displayFinalPrice()) : ?> <br /> - <span class="nobr"><?= /* @escapeNotVerified */ __('Total') ?>:<br /> <?= /* @escapeNotVerified */ $_order->formatPrice($block->getFinalRowDisplayPriceExclTax()) ?></span> + <span class="nobr"><?= $block->escapeHtml(__('Total')) ?>:<br /> <?= /* @noEscape */ $_order->formatPrice($block->getFinalRowDisplayPriceExclTax()) ?></span> <?php endif; ?> <?php endif; ?> <?php endif; ?> -<?php if ($block->displayPriceInclTax() || $block->displayBothPrices()): ?> - <?php if ($block->displayBothPrices()): ?> - <br /><span class="label"><?= /* @escapeNotVerified */ __('Incl. Tax') ?>:</span> +<?php if ($block->displayPriceInclTax() || $block->displayBothPrices()) : ?> + <?php if ($block->displayBothPrices()) : ?> + <br /><span class="label"><?= $block->escapeHtml(__('Incl. Tax')) ?>:</span> <?php endif; ?> - <?= /* @escapeNotVerified */ $_order->formatPrice($block->getRowDisplayPriceInclTax()) ?> - <?php if ($this->helper('Magento\Weee\Helper\Data')->getApplied($_item)): ?> + <?= /* @noEscape */ $_order->formatPrice($block->getRowDisplayPriceInclTax()) ?> + <?php if ($this->helper(\Magento\Weee\Helper\Data::class)->getApplied($_item)) : ?> <br /> - <?php if ($block->displayPriceWithWeeeDetails()): ?> + <?php if ($block->displayPriceWithWeeeDetails()) : ?> <small> - <?php foreach ($this->helper('Magento\Weee\Helper\Data')->getApplied($_item) as $tax): ?> - <span class="nobr"><?= /* @escapeNotVerified */ $tax['title'] ?>: <?= /* @escapeNotVerified */ $_order->formatPrice($tax['row_amount_incl_tax'], true, true) ?></span><br /> + <?php foreach ($this->helper(\Magento\Weee\Helper\Data::class)->getApplied($_item) as $tax) : ?> + <span class="nobr"><?= $block->escapeHtml($tax['title']) ?>: <?= /* @noEscape */ $_order->formatPrice($tax['row_amount_incl_tax'], true, true) ?></span><br /> <?php endforeach; ?> </small> <?php endif; ?> - <?php if ($block->displayFinalPrice()): ?> - <span class="nobr"><?= /* @escapeNotVerified */ __('Total Incl. Tax') ?>:<br /> <?= /* @escapeNotVerified */ $_order->formatPrice($block->getFinalRowDisplayPriceInclTax()) ?></span> + <?php if ($block->displayFinalPrice()) : ?> + <span class="nobr"><?= $block->escapeHtml(__('Total Incl. Tax')) ?>:<br /> <?= /* @noEscape */ $_order->formatPrice($block->getFinalRowDisplayPriceInclTax()) ?></span> <?php endif; ?> <?php endif; ?> <?php endif; ?> diff --git a/app/code/Magento/Weee/view/frontend/templates/item/price/row.phtml b/app/code/Magento/Weee/view/frontend/templates/item/price/row.phtml index dc128902ef4d2..37aa852871408 100644 --- a/app/code/Magento/Weee/view/frontend/templates/item/price/row.phtml +++ b/app/code/Magento/Weee/view/frontend/templates/item/price/row.phtml @@ -4,37 +4,37 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile +// phpcs:disable Magento2.Templates.ThisInTemplate /** @var $block \Magento\Weee\Block\Item\Price\Renderer */ $item = $block->getItem(); ?> -<?php if (($block->displayPriceInclTax() || $block->displayBothPrices()) && !$item->getNoSubtotal()): ?> - <span class="price-including-tax" data-label="<?= $block->escapeHtml(__('Incl. Tax')) ?>"> - <?php if ($block->displayPriceWithWeeeDetails()): ?> +<?php if (($block->displayPriceInclTax() || $block->displayBothPrices()) && !$item->getNoSubtotal()) : ?> + <span class="price-including-tax" data-label="<?= $block->escapeHtmlAttr(__('Incl. Tax')) ?>"> + <?php if ($block->displayPriceWithWeeeDetails()) : ?> <span class="cart-tax-total" - data-mage-init='{"taxToggle": {"itemTaxId" : "#subtotal-item-tax-details<?= /* @escapeNotVerified */ $item->getId() ?>"}}'> - <?php else: ?> + data-mage-init='{"taxToggle": {"itemTaxId" : "#subtotal-item-tax-details<?= (int) $item->getId() ?>"}}'> + <?php else : ?> <span class="cart-price"> <?php endif; ?> - <?= /* @escapeNotVerified */ $block->formatPrice($block->getRowDisplayPriceInclTax()) ?> + <?= /* @noEscape */ $block->formatPrice($block->getRowDisplayPriceInclTax()) ?> </span> - <?php if ($this->helper('Magento\Weee\Helper\Data')->getApplied($item)): ?> - <div class="cart-tax-info" id="subtotal-item-tax-details<?= /* @escapeNotVerified */ $item->getId() ?>" style="display: none;"> - <?php foreach ($this->helper('Magento\Weee\Helper\Data')->getApplied($item) as $tax): ?> - <span class="weee" data-label="<?= /* @escapeNotVerified */ $tax['title'] ?>"> - <?= /* @escapeNotVerified */ $block->formatPrice($tax['row_amount_incl_tax'], true, true) ?> + <?php if ($this->helper(\Magento\Weee\Helper\Data::class)->getApplied($item)) : ?> + <div class="cart-tax-info" id="subtotal-item-tax-details<?= (int) $item->getId() ?>" style="display: none;"> + <?php foreach ($this->helper(\Magento\Weee\Helper\Data::class)->getApplied($item) as $tax) : ?> + <span class="weee" data-label="<?= $block->escapeHtmlAttr($tax['title']) ?>"> + <?= /* @noEscape */ $block->formatPrice($tax['row_amount_incl_tax'], true, true) ?> </span> <?php endforeach; ?> </div> - <?php if ($block->displayFinalPrice()): ?> + <?php if ($block->displayFinalPrice()) : ?> <span class="cart-tax-total" - data-mage-init='{"taxToggle": {"itemTaxId" : "#subtotal-item-tax-details<?= /* @escapeNotVerified */ $item->getId() ?>"}}'> - <span class="weee" data-label="<?= $block->escapeHtml(__('Total Incl. Tax')) ?>"> - <?= /* @escapeNotVerified */ $block->formatPrice($block->getFinalRowDisplayPriceInclTax()) ?> + data-mage-init='{"taxToggle": {"itemTaxId" : "#subtotal-item-tax-details<?= (int) $item->getId() ?>"}}'> + <span class="weee" data-label="<?= $block->escapeHtmlAttr(__('Total Incl. Tax')) ?>"> + <?= /* @noEscape */ $block->formatPrice($block->getFinalRowDisplayPriceInclTax()) ?> </span> </span> <?php endif; ?> @@ -42,32 +42,32 @@ $item = $block->getItem(); </span> <?php endif; ?> -<?php if ($block->displayPriceExclTax() || $block->displayBothPrices()): ?> - <span class="price-excluding-tax" data-label="<?= $block->escapeHtml(__('Excl. Tax')) ?>"> - <?php if ($block->displayPriceWithWeeeDetails()): ?> +<?php if ($block->displayPriceExclTax() || $block->displayBothPrices()) : ?> + <span class="price-excluding-tax" data-label="<?= $block->escapeHtmlAttr(__('Excl. Tax')) ?>"> + <?php if ($block->displayPriceWithWeeeDetails()) : ?> <span class="cart-tax-total" - data-mage-init='{"taxToggle": {"itemTaxId" : "#esubtotal-item-tax-details<?= /* @escapeNotVerified */ $item->getId() ?>"}}'> - <?php else: ?> + data-mage-init='{"taxToggle": {"itemTaxId" : "#esubtotal-item-tax-details<?= (int) $item->getId() ?>"}}'> + <?php else : ?> <span class="cart-price"> <?php endif; ?> - <?= /* @escapeNotVerified */ $block->formatPrice($block->getRowDisplayPriceExclTax()) ?> + <?= /* @noEscape */ $block->formatPrice($block->getRowDisplayPriceExclTax()) ?> </span> - <?php if ($this->helper('Magento\Weee\Helper\Data')->getApplied($item)): ?> - <span class="cart-tax-info" id="esubtotal-item-tax-details<?= /* @escapeNotVerified */ $item->getId() ?>" + <?php if ($this->helper(\Magento\Weee\Helper\Data::class)->getApplied($item)) : ?> + <span class="cart-tax-info" id="esubtotal-item-tax-details<?= (int) $item->getId() ?>" style="display: none;"> - <?php foreach ($this->helper('Magento\Weee\Helper\Data')->getApplied($item) as $tax): ?> - <span class="weee" data-label="<?= /* @escapeNotVerified */ $tax['title'] ?>"> - <?= /* @escapeNotVerified */ $block->formatPrice($tax['row_amount'], true, true) ?> + <?php foreach ($this->helper(\Magento\Weee\Helper\Data::class)->getApplied($item) as $tax) : ?> + <span class="weee" data-label="<?= $block->escapeHtmlAttr($tax['title']) ?>"> + <?= /* @noEscape */ $block->formatPrice($tax['row_amount'], true, true) ?> </span> <?php endforeach; ?> </span> - <?php if ($block->displayFinalPrice()): ?> + <?php if ($block->displayFinalPrice()) : ?> <span class="cart-tax-total" - data-mage-init='{"taxToggle": {"itemTaxId" : "#esubtotal-item-tax-details<?= /* @escapeNotVerified */ $item->getId() ?>"}}'> - <span class="weee" data-label="<?= $block->escapeHtml(__('Total')) ?>"> - <?= /* @escapeNotVerified */ $block->formatPrice($block->getFinalRowDisplayPriceExclTax()) ?> + data-mage-init='{"taxToggle": {"itemTaxId" : "#esubtotal-item-tax-details<?= (int) $item->getId() ?>"}}'> + <span class="weee" data-label="<?= $block->escapeHtmlAttr(__('Total')) ?>"> + <?= /* @noEscape */ $block->formatPrice($block->getFinalRowDisplayPriceExclTax()) ?> </span> </span> <?php endif; ?> diff --git a/app/code/Magento/Weee/view/frontend/templates/item/price/total_after_discount.phtml b/app/code/Magento/Weee/view/frontend/templates/item/price/total_after_discount.phtml index 03c050e0a0b04..46d16ad13ea6e 100644 --- a/app/code/Magento/Weee/view/frontend/templates/item/price/total_after_discount.phtml +++ b/app/code/Magento/Weee/view/frontend/templates/item/price/total_after_discount.phtml @@ -4,10 +4,8 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - /** @var \Magento\Weee\Block\Item\Price\Renderer $block */ $_item = $block->getItem(); ?> <?php $_order = $block->getItem()->getOrderItem()->getOrder() ?> -<?= /* @escapeNotVerified */ $_order->formatPrice($block->getTotalAmount($_item)) ?> +<?= /* @noEscape */ $_order->formatPrice($block->getTotalAmount($_item)) ?> diff --git a/app/code/Magento/Weee/view/frontend/templates/item/price/unit.phtml b/app/code/Magento/Weee/view/frontend/templates/item/price/unit.phtml index 575a0dd6c9aa8..4e62409ad00f4 100644 --- a/app/code/Magento/Weee/view/frontend/templates/item/price/unit.phtml +++ b/app/code/Magento/Weee/view/frontend/templates/item/price/unit.phtml @@ -4,37 +4,37 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile +// phpcs:disable Magento2.Templates.ThisInTemplate /** @var $block \Magento\Weee\Block\Item\Price\Renderer */ $item = $block->getItem(); ?> -<?php if ($block->displayPriceInclTax() || $block->displayBothPrices()): ?> - <span class="price-including-tax" data-label="<?= $block->escapeHtml(__('Incl. Tax')) ?>"> - <?php if ($block->displayPriceWithWeeeDetails()): ?> +<?php if ($block->displayPriceInclTax() || $block->displayBothPrices()) : ?> + <span class="price-including-tax" data-label="<?= $block->escapeHtmlAttr(__('Incl. Tax')) ?>"> + <?php if ($block->displayPriceWithWeeeDetails()) : ?> <span class="cart-tax-total" - data-mage-init='{"taxToggle": {"itemTaxId" : "#unit-item-tax-details<?= /* @escapeNotVerified */ $item->getId() ?>"}}'> - <?php else: ?> + data-mage-init='{"taxToggle": {"itemTaxId" : "#unit-item-tax-details<?= (int) $item->getId() ?>"}}'> + <?php else : ?> <span class="cart-price"> <?php endif; ?> - <?= /* @escapeNotVerified */ $block->formatPrice($block->getUnitDisplayPriceInclTax()) ?> + <?= /* @noEscape */ $block->formatPrice($block->getUnitDisplayPriceInclTax()) ?> </span> - <?php if ($this->helper('Magento\Weee\Helper\Data')->getApplied($item)): ?> - <span class="cart-tax-info" id="unit-item-tax-details<?= /* @escapeNotVerified */ $item->getId() ?>" style="display: none;"> - <?php foreach ($this->helper('Magento\Weee\Helper\Data')->getApplied($item) as $tax): ?> - <span class="weee" data-label="<?= /* @escapeNotVerified */ $tax['title'] ?>"> - <?= /* @escapeNotVerified */ $block->formatPrice($tax['amount_incl_tax'], true, true) ?> + <?php if ($this->helper(\Magento\Weee\Helper\Data::class)->getApplied($item)) : ?> + <span class="cart-tax-info" id="unit-item-tax-details<?= (int) $item->getId() ?>" style="display: none;"> + <?php foreach ($this->helper(\Magento\Weee\Helper\Data::class)->getApplied($item) as $tax) : ?> + <span class="weee" data-label="<?= $block->escapeHtmlAttr($tax['title']) ?>"> + <?= /* @noEscape */ $block->formatPrice($tax['amount_incl_tax'], true, true) ?> </span> <?php endforeach; ?> </span> - <?php if ($block->displayFinalPrice()): ?> + <?php if ($block->displayFinalPrice()) : ?> <span class="cart-tax-total" - data-mage-init='{"taxToggle": {"itemTaxId" : "#unit-item-tax-details<?= /* @escapeNotVerified */ $item->getId() ?>"}}'> - <span class="weee" data-label="<?= $block->escapeHtml(__('Total Incl. Tax')) ?>"> - <?= /* @escapeNotVerified */ $block->formatPrice($block->getFinalUnitDisplayPriceInclTax()) ?> + data-mage-init='{"taxToggle": {"itemTaxId" : "#unit-item-tax-details<?= (int) $item->getId() ?>"}}'> + <span class="weee" data-label="<?= $block->escapeHtmlAttr(__('Total Incl. Tax')) ?>"> + <?= /* @noEscape */ $block->formatPrice($block->getFinalUnitDisplayPriceInclTax()) ?> </span> </span> <?php endif; ?> @@ -42,32 +42,32 @@ $item = $block->getItem(); </span> <?php endif; ?> -<?php if ($block->displayPriceExclTax() || $block->displayBothPrices()): ?> - <span class="price-excluding-tax" data-label="<?= $block->escapeHtml(__('Excl. Tax')) ?>"> - <?php if ($block->displayPriceWithWeeeDetails()): ?> +<?php if ($block->displayPriceExclTax() || $block->displayBothPrices()) : ?> + <span class="price-excluding-tax" data-label="<?= $block->escapeHtmlAttr(__('Excl. Tax')) ?>"> + <?php if ($block->displayPriceWithWeeeDetails()) : ?> <span class="cart-tax-total" - data-mage-init='{"taxToggle": {"itemTaxId" : "#eunit-item-tax-details<?= /* @escapeNotVerified */ $item->getId() ?>"}}'> - <?php else: ?> + data-mage-init='{"taxToggle": {"itemTaxId" : "#eunit-item-tax-details<?= (int) $item->getId() ?>"}}'> + <?php else : ?> <span class="cart-price"> <?php endif; ?> - <?= /* @escapeNotVerified */ $block->formatPrice($block->getUnitDisplayPriceExclTax()) ?> + <?= /* @noEscape */ $block->formatPrice($block->getUnitDisplayPriceExclTax()) ?> </span> - <?php if ($this->helper('Magento\Weee\Helper\Data')->getApplied($item)): ?> - <span class="cart-tax-info" id="eunit-item-tax-details<?= /* @escapeNotVerified */ $item->getId() ?>" + <?php if ($this->helper(\Magento\Weee\Helper\Data::class)->getApplied($item)) : ?> + <span class="cart-tax-info" id="eunit-item-tax-details<?= (int) $item->getId() ?>" style="display: none;"> - <?php foreach ($this->helper('Magento\Weee\Helper\Data')->getApplied($item) as $tax): ?> - <span class="weee" data-label="<?= /* @escapeNotVerified */ $tax['title'] ?>"> - <?= /* @escapeNotVerified */ $block->formatPrice($tax['amount'], true, true) ?> + <?php foreach ($this->helper(\Magento\Weee\Helper\Data::class)->getApplied($item) as $tax) : ?> + <span class="weee" data-label="<?= $block->escapeHtmlAttr($tax['title']) ?>"> + <?= /* @noEscape */ $block->formatPrice($tax['amount'], true, true) ?> </span> <?php endforeach; ?> </span> - <?php if ($block->displayFinalPrice()): ?> + <?php if ($block->displayFinalPrice()) : ?> <span class="cart-tax-total" - data-mage-init='{"taxToggle": {"itemTaxId" : "#eunit-item-tax-details<?= /* @escapeNotVerified */ $item->getId() ?>"}}'> - <span class="weee" data-label="<?= $block->escapeHtml(__('Total')) ?>"> - <?= /* @escapeNotVerified */ $block->formatPrice($block->getFinalUnitDisplayPriceExclTax()) ?> + data-mage-init='{"taxToggle": {"itemTaxId" : "#eunit-item-tax-details<?= (int) $item->getId() ?>"}}'> + <span class="weee" data-label="<?= $block->escapeHtmlAttr(__('Total')) ?>"> + <?= /* @noEscape */ $block->formatPrice($block->getFinalUnitDisplayPriceExclTax()) ?> </span> </span> <?php endif; ?> diff --git a/app/code/Magento/Wishlist/Helper/Data.php b/app/code/Magento/Wishlist/Helper/Data.php index 3d25e16294fcd..6c1ebd87b4e8d 100644 --- a/app/code/Magento/Wishlist/Helper/Data.php +++ b/app/code/Magento/Wishlist/Helper/Data.php @@ -6,6 +6,8 @@ namespace Magento\Wishlist\Helper; use Magento\Framework\App\ActionInterface; +use Magento\Framework\App\ObjectManager; +use Magento\Framework\Escaper; use Magento\Wishlist\Controller\WishlistProviderInterface; /** @@ -100,6 +102,11 @@ class Data extends \Magento\Framework\App\Helper\AbstractHelper */ protected $productRepository; + /** + * @var Escaper + */ + private $escaper; + /** * @param \Magento\Framework\App\Helper\Context $context * @param \Magento\Framework\Registry $coreRegistry @@ -130,6 +137,7 @@ public function __construct( $this->_customerViewHelper = $customerViewHelper; $this->wishlistProvider = $wishlistProvider; $this->productRepository = $productRepository; + $this->escaper = ObjectManager::getInstance()->get(Escaper::class); parent::__construct($context); } @@ -323,10 +331,10 @@ public function getAddParams($item, array $params = []) { $productId = null; if ($item instanceof \Magento\Catalog\Model\Product) { - $productId = $item->getEntityId(); + $productId = (int) $item->getEntityId(); } if ($item instanceof \Magento\Wishlist\Model\Item) { - $productId = $item->getProductId(); + $productId = (int) $item->getProductId(); } $url = $this->_getUrlStore($item)->getUrl('wishlist/index/add'); @@ -334,7 +342,10 @@ public function getAddParams($item, array $params = []) $params['product'] = $productId; } - return $this->_postDataHelper->getPostData($url, $params); + return $this->_postDataHelper->getPostData( + $this->escaper->escapeUrl($url), + $params + ); } /** @@ -490,14 +501,14 @@ public function getListUrl($wishlistId = null) */ public function isAllow() { - if ($this->_moduleManager->isOutputEnabled($this->_getModuleName()) && $this->scopeConfig->getValue( + $isOutputEnabled = $this->_moduleManager->isOutputEnabled($this->_getModuleName()); + + $isWishlistActive = $this->scopeConfig->getValue( 'wishlist/general/active', \Magento\Store\Model\ScopeInterface::SCOPE_STORE - ) - ) { - return true; - } - return false; + ); + + return $isOutputEnabled && $isWishlistActive; } /** diff --git a/app/code/Magento/Wishlist/Test/Mftf/Data/WishlistData.xml b/app/code/Magento/Wishlist/Test/Mftf/Data/WishlistData.xml index c6a9704698b05..a8220ad0cfca3 100644 --- a/app/code/Magento/Wishlist/Test/Mftf/Data/WishlistData.xml +++ b/app/code/Magento/Wishlist/Test/Mftf/Data/WishlistData.xml @@ -12,7 +12,7 @@ <var key="product" entityType="product" entityKey="id"/> <var key="customer_email" entityType="customer" entityKey="email"/> <var key="customer_password" entityType="customer" entityKey="password"/> - <data key="shareInfo_emails" entityType="customer" >JohnDoe123456789@example.com,JohnDoe987654321@example.com,JohnDoe123456abc@example.com</data> - <data key="shareInfo_message" entityType="customer">Sharing message.</data> + <data key="shareInfo_emails">JohnDoe123456789@example.com,JohnDoe987654321@example.com,JohnDoe123456abc@example.com</data> + <data key="shareInfo_message">Sharing message.</data> </entity> </entities> diff --git a/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/header/actions-group/_search.less b/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/header/actions-group/_search.less index ddc3cb455402b..2d3c6140d588f 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/header/actions-group/_search.less +++ b/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/header/actions-group/_search.less @@ -86,6 +86,8 @@ position: absolute; right: 0; top: 100%; + word-break: break-word; + word-wrap: break-word; z-index: 2; &:after { diff --git a/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/main/_collapsible-blocks.less b/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/main/_collapsible-blocks.less index dec35d1364836..a43f9acbaa099 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/main/_collapsible-blocks.less +++ b/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/main/_collapsible-blocks.less @@ -105,11 +105,16 @@ // .admin__collapsible-block-wrapper { + .admin__collapsible-title[aria-expanded='true'] { + &:before { + content: @icon-expand-close__content; + } + } + .__collapsible-block-wrapper-pattern(); .admin__collapsible-title { .__collapsible-title-pattern(); } - &.opened, &._show { > .fieldset-wrapper-title { diff --git a/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/main/_store-scope.less b/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/main/_store-scope.less index 05e6350c88d8e..cd73d4955d176 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/main/_store-scope.less +++ b/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/main/_store-scope.less @@ -37,7 +37,7 @@ .store-scope { .admin__legend { .admin__field-tooltip { - margin-left: -@indent__base; + margin-left: 0; margin-top: .5rem; } } diff --git a/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/main/actions-bar/_store-switcher.less b/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/main/actions-bar/_store-switcher.less index 22a584f1c8b80..c4bb1a027e4b9 100644 --- a/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/main/actions-bar/_store-switcher.less +++ b/app/design/adminhtml/Magento/backend/Magento_Backend/web/css/source/module/main/actions-bar/_store-switcher.less @@ -235,7 +235,7 @@ .store-view { &:not(.store-switcher) { float: left; - margin-top: 13px; + margin-top: 1.10rem; } .store-switcher-label { diff --git a/app/design/adminhtml/Magento/backend/web/css/source/forms/_controls.less b/app/design/adminhtml/Magento/backend/web/css/source/forms/_controls.less index 6c3756370d9ce..4c32405f2c995 100644 --- a/app/design/adminhtml/Magento/backend/web/css/source/forms/_controls.less +++ b/app/design/adminhtml/Magento/backend/web/css/source/forms/_controls.less @@ -202,7 +202,6 @@ option:empty { .admin__control-textarea { &:extend(.abs-form-control-pattern all); - height: 8.48rem; line-height: 1.18; padding-top: .8rem; resize: vertical; diff --git a/app/design/adminhtml/Magento/backend/web/css/styles-old.less b/app/design/adminhtml/Magento/backend/web/css/styles-old.less index c9e56abbba8d4..a1fe748ad5f51 100644 --- a/app/design/adminhtml/Magento/backend/web/css/styles-old.less +++ b/app/design/adminhtml/Magento/backend/web/css/styles-old.less @@ -1366,7 +1366,7 @@ font-size: 14px; font-weight: 600; line-height: 3.2rem; - padding: 0 30px 0 0; + padding: 0 30px 0 30px; white-space: nowrap; word-wrap: break-word; diff --git a/app/design/frontend/Magento/blank/Magento_Catalog/web/css/source/module/_listings.less b/app/design/frontend/Magento/blank/Magento_Catalog/web/css/source/module/_listings.less index f96538b59a70f..50a5e6351533d 100644 --- a/app/design/frontend/Magento/blank/Magento_Catalog/web/css/source/module/_listings.less +++ b/app/design/frontend/Magento/blank/Magento_Catalog/web/css/source/module/_listings.less @@ -120,7 +120,6 @@ .reviews-actions { font-size: @font-size__s; margin-top: 5px; - text-transform: lowercase; } } diff --git a/app/design/frontend/Magento/blank/Magento_Swatches/web/css/source/_module.less b/app/design/frontend/Magento/blank/Magento_Swatches/web/css/source/_module.less index b7271e3c1e248..c67b9c7d751e3 100644 --- a/app/design/frontend/Magento/blank/Magento_Swatches/web/css/source/_module.less +++ b/app/design/frontend/Magento/blank/Magento_Swatches/web/css/source/_module.less @@ -89,7 +89,7 @@ } &-options { - margin-top: @indent__s; + margin: @indent__s 0; &:focus { box-shadow: none; diff --git a/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/module/_listings.less b/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/module/_listings.less index eeec441c74aca..92945d61e4368 100644 --- a/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/module/_listings.less +++ b/app/design/frontend/Magento/luma/Magento_Catalog/web/css/source/module/_listings.less @@ -123,7 +123,6 @@ .reviews-actions { font-size: @font-size__s; margin-top: 5px; - text-transform: lowercase; } } diff --git a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_cart.less b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_cart.less index 71814cd0f0422..460a961830b43 100644 --- a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_cart.less +++ b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_cart.less @@ -210,7 +210,7 @@ height: 36px; margin-top: -7px; text-align: center; - width: 45px; + width: 60px; } } diff --git a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_minicart.less b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_minicart.less index 5ca3322403102..af94dd7b97bbb 100644 --- a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_minicart.less +++ b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/_minicart.less @@ -371,7 +371,7 @@ .item-qty { margin-right: @indent__s; text-align: center; - width: 45px; + width: 60px; } .update-cart-item { diff --git a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/_checkout.less b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/_checkout.less index ac5ab0d87bf62..a2daf0da247d1 100644 --- a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/_checkout.less +++ b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/_checkout.less @@ -76,6 +76,36 @@ display: none; } } + + .abs-discount-code { + .actions-toolbar { + display: table-cell; + vertical-align: top; + width: 1%; + + .primary { + float: left; + .action { + &:extend(.abs-revert-to-action-secondary all); + border-bottom-left-radius: 0; + border-top-left-radius: 0; + margin: 0 0 0 -2px; + white-space: nowrap; + width: auto; + } + } + } + .form-discount { + display: table; + width: 100%; + + > .field { + > .label { + display: none; + } + } + } + } } // diff --git a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/_payment-options.less b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/_payment-options.less index 23bb15e6fb4fe..8490deb8a3830 100644 --- a/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/_payment-options.less +++ b/app/design/frontend/Magento/luma/Magento_Checkout/web/css/source/module/checkout/_payment-options.less @@ -69,6 +69,9 @@ .payment-option-content { .lib-css(padding, 0 0 @indent__base @checkout-payment-option-content__padding__xl); + + &:extend(.abs-discount-code all); + .primary { .action { &.action-apply { @@ -76,6 +79,7 @@ } } } + } .payment-option-inner { diff --git a/app/design/frontend/Magento/luma/Magento_Customer/layout/customer_account.xml b/app/design/frontend/Magento/luma/Magento_Customer/layout/customer_account.xml index 5ef5dcac1131d..21f22459e1a98 100644 --- a/app/design/frontend/Magento/luma/Magento_Customer/layout/customer_account.xml +++ b/app/design/frontend/Magento/luma/Magento_Customer/layout/customer_account.xml @@ -7,52 +7,12 @@ --> <page layout="2columns-left" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> <body> - <referenceContainer name="sidebar.main"> - <block class="Magento\Framework\View\Element\Template" name="customer_account_navigation_block" template="Magento_Theme::html/collapsible.phtml" before="-"> - <arguments> - <argument name="block_title" translate="true" xsi:type="string">My Account</argument> - <argument name="block_css" xsi:type="string">block-collapsible-nav</argument> - </arguments> - <block class="Magento\Customer\Block\Account\Navigation" name="customer_account_navigation" before="-"> - <arguments> - <argument name="css_class" xsi:type="string">nav items</argument> - </arguments> - <block class="Magento\Customer\Block\Account\SortLinkInterface" name="customer-account-navigation-account-link"> - <arguments> - <argument name="label" xsi:type="string" translate="true">My Account</argument> - <argument name="path" xsi:type="string">customer/account</argument> - <argument name="sortOrder" xsi:type="number">250</argument> - </arguments> - </block> - <block class="Magento\Customer\Block\Account\Delimiter" name="customer-account-navigation-delimiter-1" - template="Magento_Customer::account/navigation-delimiter.phtml"> - <arguments> - <argument name="sortOrder" xsi:type="number">200</argument> - </arguments> - </block> - <block class="Magento\Customer\Block\Account\SortLinkInterface" name="customer-account-navigation-address-link"> - <arguments> - <argument name="label" xsi:type="string" translate="true">Address Book</argument> - <argument name="path" xsi:type="string">customer/address</argument> - <argument name="sortOrder" xsi:type="number">190</argument> - </arguments> - </block> - <block class="Magento\Customer\Block\Account\SortLinkInterface" name="customer-account-navigation-account-edit-link"> - <arguments> - <argument name="label" xsi:type="string" translate="true">Account Information</argument> - <argument name="path" xsi:type="string">customer/account/edit</argument> - <argument name="sortOrder" xsi:type="number">180</argument> - </arguments> - </block> - <block class="Magento\Customer\Block\Account\Delimiter" name="customer-account-navigation-delimiter-2" - template="Magento_Customer::account/navigation-delimiter.phtml"> - <arguments> - <argument name="sortOrder" xsi:type="number">130</argument> - </arguments> - </block> - </block> - </block> - </referenceContainer> + <referenceBlock name="sidebar.main.account_nav"> + <arguments> + <argument name="block_title" translate="true" xsi:type="string">My Account</argument> + <argument name="block_css" xsi:type="string">block-collapsible-nav</argument> + </arguments> + </referenceBlock> <move element="page.main.title" destination="content.top" before="-"/> </body> </page> diff --git a/app/design/frontend/Magento/luma/Magento_LayeredNavigation/templates/layer/state.phtml b/app/design/frontend/Magento/luma/Magento_LayeredNavigation/templates/layer/state.phtml index 6e73b6706f193..7ebecb4c99f2f 100644 --- a/app/design/frontend/Magento/luma/Magento_LayeredNavigation/templates/layer/state.phtml +++ b/app/design/frontend/Magento/luma/Magento_LayeredNavigation/templates/layer/state.phtml @@ -20,30 +20,30 @@ role="heading" aria-level="2" data-role="title" - data-count="<?= count($_filters) ?>"><?= /* @escapeNotVerified */ __('Now Shopping by') ?></strong> + data-count="<?= /* @noEscape */ count($_filters) ?>"><?= $block->escapeHtml(__('Now Shopping by')) ?></strong> <ol class="items"> <?php foreach ($_filters as $_filter): ?> <li class="item"> <span class="filter-label"><?= $block->escapeHtml(__($_filter->getName())) ?></span> - <span class="filter-value"><?= /* @escapeNotVerified */ $block->stripTags($_filter->getLabel()) ?></span> + <span class="filter-value"><?= $block->escapeHtml($block->stripTags($_filter->getLabel())) ?></span> <?php $clearLinkUrl = $_filter->getClearLinkUrl(); - $currentFilterName = $block->escapeHtml(__($_filter->getName())) . " " . $block->stripTags($_filter->getLabel()); + $currentFilterName = $block->escapeHtmlAttr(__($_filter->getName()) . " " . $block->stripTags($_filter->getLabel())); if ($clearLinkUrl): ?> - <a class="action previous" href="<?= /* @escapeNotVerified */ $_filter->getRemoveUrl() ?>" - title="<?= /* @escapeNotVerified */ __('Previous') ?>"> - <span><?= /* @escapeNotVerified */ __('Previous') ?></span> + <a class="action previous" href="<?= $block->escapeUrl($_filter->getRemoveUrl()) ?>" + title="<?= $block->escapeHtmlAttr(__('Previous')) ?>"> + <span><?= $block->escapeHtml(__('Previous')) ?></span> </a> <a class="action remove" - title="<?= $block->escapeHtml($_filter->getFilter()->getClearLinkText()) ?>" - href="<?= /* @escapeNotVerified */ $clearLinkUrl ?>"> + title="<?= $block->escapeHtmlAttr($_filter->getFilter()->getClearLinkText()) ?>" + href="<?= $block->escapeUrl($clearLinkUrl) ?>"> <span><?= $block->escapeHtml($_filter->getFilter()->getClearLinkText()) ?></span> </a> <?php else: ?> - <a class="action remove" href="<?= /* @escapeNotVerified */ $_filter->getRemoveUrl() ?>" - title="<?= /* @escapeNotVerified */ $block->escapeHtml(__('Remove')) . " " . $currentFilterName ?>"> - <span><?= /* @escapeNotVerified */ __('Remove This Item') ?></span> + <a class="action remove" href="<?= $block->escapeUrl($_filter->getRemoveUrl()) ?>" + title="<?= /* @noEscape */ $block->escapeHtmlAttr(__('Remove')) . " " . $currentFilterName ?>"> + <span><?= $block->escapeHtml(__('Remove This Item')) ?></span> </a> <?php endif; ?> </li> diff --git a/app/design/frontend/Magento/luma/Magento_Theme/web/css/source/_module.less b/app/design/frontend/Magento/luma/Magento_Theme/web/css/source/_module.less index dfcc51e0a0a26..b841f2206e1d9 100644 --- a/app/design/frontend/Magento/luma/Magento_Theme/web/css/source/_module.less +++ b/app/design/frontend/Magento/luma/Magento_Theme/web/css/source/_module.less @@ -80,7 +80,7 @@ .page-main { > .page-title-wrapper { .page-title { - word-break: break-all; + hyphens: auto; } } } diff --git a/app/design/frontend/Magento/luma/web/css/source/_extends.less b/app/design/frontend/Magento/luma/web/css/source/_extends.less index a88ecbf5057bb..66732d7f68eec 100644 --- a/app/design/frontend/Magento/luma/web/css/source/_extends.less +++ b/app/design/frontend/Magento/luma/web/css/source/_extends.less @@ -1532,6 +1532,7 @@ .abs-block-items-counter { .lib-css(color, @block-items__counter__color); .lib-font-size(12px); + vertical-align: middle; white-space: nowrap; &:before { @@ -1555,6 +1556,7 @@ strong { font-size: @font-size__l; font-weight: @font-weight__light; + vertical-align: middle; } } } @@ -1913,6 +1915,7 @@ display: table-cell; } } + } .media-width(@extremum, @break) when (@extremum = 'min') and (@break = @screen__m) { diff --git a/composer.json b/composer.json index cff2d676038d7..a1323796ed4df 100644 --- a/composer.json +++ b/composer.json @@ -86,7 +86,7 @@ "friendsofphp/php-cs-fixer": "~2.13.0", "lusitanian/oauth": "~0.8.10", "magento/magento-coding-standard": "~1.0.0", - "magento/magento2-functional-testing-framework": "~2.3.14", + "magento/magento2-functional-testing-framework": "~2.4.0", "pdepend/pdepend": "2.5.2", "phpmd/phpmd": "@stable", "phpunit/phpunit": "~6.5.0", @@ -101,11 +101,13 @@ "magento/module-admin-notification": "*", "magento/module-advanced-pricing-import-export": "*", "magento/module-amqp": "*", + "magento/module-amqp-store": "*", "magento/module-analytics": "*", "magento/module-asynchronous-operations": "*", "magento/module-authorization": "*", "magento/module-authorizenet": "*", "magento/module-authorizenet-acceptjs": "*", + "magento/module-authorizenet-graph-ql": "*", "magento/module-advanced-search": "*", "magento/module-backend": "*", "magento/module-backup": "*", @@ -127,6 +129,7 @@ "magento/module-catalog-widget": "*", "magento/module-checkout": "*", "magento/module-checkout-agreements": "*", + "magento/module-checkout-agreements-graph-ql": "*", "magento/module-cms": "*", "magento/module-cms-url-rewrite": "*", "magento/module-config": "*", @@ -195,12 +198,14 @@ "magento/module-payment": "*", "magento/module-paypal": "*", "magento/module-paypal-captcha": "*", + "magento/module-paypal-graph-ql": "*", "magento/module-persistent": "*", "magento/module-product-alert": "*", "magento/module-product-video": "*", "magento/module-quote": "*", "magento/module-quote-analytics": "*", "magento/module-quote-graph-ql": "*", + "magento/module-related-product-graph-ql": "*", "magento/module-release-notification": "*", "magento/module-reports": "*", "magento/module-require-js": "*", diff --git a/composer.lock b/composer.lock index 5d9f7fbdf695c..5454994ca82db 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "597fe6a47b695221292482fead498d83", + "content-hash": "912e04a44c38c8918799bd01b828fba0", "packages": [ { "name": "braintree/braintree_php", @@ -2169,7 +2169,7 @@ }, { "name": "Gert de Pagter", - "email": "backendtea@gmail.com" + "email": "BackEndTea@gmail.com" } ], "description": "Symfony polyfill for ctype functions", @@ -6699,16 +6699,16 @@ }, { "name": "magento/magento2-functional-testing-framework", - "version": "2.3.14", + "version": "2.4.0", "source": { "type": "git", "url": "https://github.com/magento/magento2-functional-testing-framework.git", - "reference": "b4002b3fe53884895921b44cf519d42918e3c7c6" + "reference": "ef534dbcb3aeea68f9254dfd018165c546ad2edb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/magento/magento2-functional-testing-framework/zipball/b4002b3fe53884895921b44cf519d42918e3c7c6", - "reference": "b4002b3fe53884895921b44cf519d42918e3c7c6", + "url": "https://api.github.com/repos/magento/magento2-functional-testing-framework/zipball/ef534dbcb3aeea68f9254dfd018165c546ad2edb", + "reference": "ef534dbcb3aeea68f9254dfd018165c546ad2edb", "shasum": "" }, "require": { @@ -6721,12 +6721,12 @@ "fzaninotto/faker": "^1.6", "monolog/monolog": "^1.0", "mustache/mustache": "~2.5", - "php": "7.0.2|7.0.4|~7.0.6|~7.1.0|~7.2.0", + "php": "7.0.2||7.0.4||~7.0.6||~7.1.0||~7.2.0||~7.3.0", "symfony/process": "^2.8 || ^3.1 || ^4.0", "vlucas/phpdotenv": "^2.4" }, "require-dev": { - "brainmaestro/composer-git-hooks": "^2.3", + "brainmaestro/composer-git-hooks": "^2.3.1", "codacy/coverage": "^1.4", "codeception/aspect-mock": "^3.0", "doctrine/cache": "<1.7.0", @@ -6768,19 +6768,19 @@ "magento", "testing" ], - "time": "2019-02-19T16:03:22+00:00" + "time": "2019-04-29T20:56:26+00:00" }, { "name": "mikey179/vfsStream", "version": "v1.6.5", "source": { "type": "git", - "url": "https://github.com/mikey179/vfsStream.git", + "url": "https://github.com/bovigo/vfsStream.git", "reference": "d5fec95f541d4d71c4823bb5e30cf9b9e5b96145" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/mikey179/vfsStream/zipball/d5fec95f541d4d71c4823bb5e30cf9b9e5b96145", + "url": "https://api.github.com/repos/bovigo/vfsStream/zipball/d5fec95f541d4d71c4823bb5e30cf9b9e5b96145", "reference": "d5fec95f541d4d71c4823bb5e30cf9b9e5b96145", "shasum": "" }, diff --git a/dev/tests/api-functional/framework/Magento/TestFramework/TestCase/GraphQl/Client.php b/dev/tests/api-functional/framework/Magento/TestFramework/TestCase/GraphQl/Client.php index cdc9d5e8a4c9c..5af6413840c27 100644 --- a/dev/tests/api-functional/framework/Magento/TestFramework/TestCase/GraphQl/Client.php +++ b/dev/tests/api-functional/framework/Magento/TestFramework/TestCase/GraphQl/Client.php @@ -61,8 +61,13 @@ public function post(string $query, array $variables = [], string $operationName 'operationName' => !empty($operationName) ? $operationName : null ]; $postData = $this->json->jsonEncode($requestArray); + try { + $responseBody = $this->curlClient->post($url, $postData, $headers); + } catch (\Exception $e) { + // if response code > 400 then response is the exception message + $responseBody = $e->getMessage(); + } - $responseBody = $this->curlClient->post($url, $postData, $headers); return $this->processResponse($responseBody); } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Bundle/BundleProductViewTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Bundle/BundleProductViewTest.php index 0621370972763..d5d34e48d77c3 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Bundle/BundleProductViewTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Bundle/BundleProductViewTest.php @@ -15,6 +15,9 @@ use Magento\TestFramework\ObjectManager; use Magento\TestFramework\TestCase\GraphQlAbstract; +/** + * Bundle product view test + */ class BundleProductViewTest extends GraphQlAbstract { const KEY_PRICE_TYPE_FIXED = 'FIXED'; @@ -108,7 +111,7 @@ public function testAllFieldsBundleProducts() /** * @magentoApiDataFixture Magento/Bundle/_files/bundle_product_with_not_visible_children.php */ - public function testBundleProdutWithNotVisibleChildren() + public function testBundleProductWithNotVisibleChildren() { $productSku = 'bundle-product-1'; $query diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/StoreConfigTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/StoreConfigTest.php new file mode 100644 index 0000000000000..2672431dbb56c --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Catalog/StoreConfigTest.php @@ -0,0 +1,48 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQl\Catalog; + +use Magento\TestFramework\TestCase\GraphQlAbstract; + +/** + * Test the GraphQL endpoint's StoreConfigs query for Catalog Configs + */ +class StoreConfigTest extends GraphQlAbstract +{ + protected function setUp() + { + $this->markTestIncomplete('https://github.com/magento/graphql-ce/issues/167'); + } + + /** + * @magentoApiDataFixture Magento/Store/_files/store.php + */ + public function testGetStoreConfig() + { + $query + = <<<QUERY +{ + storeConfig{ + product_url_suffix, + category_url_suffix, + title_separator, + list_mode, + grid_per_page_values, + list_per_page_values, + grid_per_page, + list_per_page, + catalog_default_sort_by + } +} +QUERY; + $response = $this->graphQlQuery($query); + $this->assertArrayHasKey('storeConfig', $response); + + //TODO: provide assertions after unmarking test as incomplete + } +} diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/CheckoutAgreements/GetCheckoutAgreementsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/CheckoutAgreements/GetCheckoutAgreementsTest.php new file mode 100644 index 0000000000000..3aa01d7994222 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/CheckoutAgreements/GetCheckoutAgreementsTest.php @@ -0,0 +1,235 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQl\CheckoutAgreements; + +use Magento\CheckoutAgreements\Api\Data\AgreementInterface; +use Magento\CheckoutAgreements\Model\Agreement as AgreementModel; +use Magento\CheckoutAgreements\Model\AgreementFactory; +use Magento\CheckoutAgreements\Model\ResourceModel\Agreement; +use Magento\Config\Model\ResourceModel\Config; +use Magento\Framework\App\Config\ReinitableConfigInterface; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\ObjectManagerInterface; +use Magento\Store\Api\Data\StoreInterface; +use Magento\Store\Model\StoreManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\TestCase\GraphQlAbstract; + +/** + * Get checkout agreements test + */ +class GetCheckoutAgreementsTest extends GraphQlAbstract +{ + /** + * @var string + */ + private $agreementsXmlConfigPath = 'checkout/options/enable_agreements'; + + /** + * @var ObjectManagerInterface + */ + private $objectManager; + + /** + * @var Config + */ + private $config; + + protected function setUp() + { + parent::setUp(); + + $this->objectManager = Bootstrap::getObjectManager(); + + // TODO: remove usage of the Config, use ConfigFixture instead https://github.com/magento/graphql-ce/issues/167 + $this->config = $this->objectManager->get(Config::class); + $this->saveAgreementConfig(1); + } + + /** + * @magentoApiDataFixture Magento/CheckoutAgreements/_files/agreement_active_with_html_content.php + * @magentoApiDataFixture Magento/CheckoutAgreements/_files/agreement_inactive_with_text_content.php + */ + public function testGetActiveAgreement() + { + $query = $this->getQuery(); + + $response = $this->graphQlQuery($query); + + self::assertArrayHasKey('checkoutAgreements', $response); + $agreements = $response['checkoutAgreements']; + self::assertCount(1, $agreements); + self::assertEquals('Checkout Agreement (active)', $agreements[0]['name']); + self::assertEquals('Checkout agreement content: <b>HTML</b>', $agreements[0]['content']); + self::assertEquals('200px', $agreements[0]['content_height']); + self::assertEquals('Checkout agreement checkbox text.', $agreements[0]['checkbox_text']); + self::assertTrue($agreements[0]['is_html']); + self::assertEquals('AUTO', $agreements[0]['mode']); + } + + /** + * @magentoApiDataFixture Magento/CheckoutAgreements/_files/agreement_active_with_html_content.php + * @magentoApiDataFixture Magento/CheckoutAgreements/_files/agreement_inactive_with_text_content.php + * @magentoApiDataFixture Magento/Store/_files/second_store.php + */ + public function testGetActiveAgreementOnSecondStore() + { + $secondStoreCode = 'fixture_second_store'; + $agreementsName = 'Checkout Agreement (active)'; + + $query = $this->getQuery(); + $this->assignAgreementsToStore($secondStoreCode, $agreementsName); + + $headerMap['Store'] = $secondStoreCode; + $response = $this->graphQlQuery($query, [], '', $headerMap); + + self::assertArrayHasKey('checkoutAgreements', $response); + $agreements = $response['checkoutAgreements']; + self::assertCount(1, $agreements); + self::assertEquals($agreementsName, $agreements[0]['name']); + self::assertEquals('Checkout agreement content: <b>HTML</b>', $agreements[0]['content']); + self::assertEquals('200px', $agreements[0]['content_height']); + self::assertEquals('Checkout agreement checkbox text.', $agreements[0]['checkbox_text']); + self::assertTrue($agreements[0]['is_html']); + self::assertEquals('AUTO', $agreements[0]['mode']); + } + + /** + * @magentoApiDataFixture Magento/CheckoutAgreements/_files/agreement_active_with_html_content.php + * @magentoApiDataFixture Magento/CheckoutAgreements/_files/agreement_inactive_with_text_content.php + * @magentoApiDataFixture Magento/Store/_files/second_store.php + */ + public function testGetActiveAgreementFromSecondStoreOnDefaultStore() + { + $secondStoreCode = 'fixture_second_store'; + $agreementsName = 'Checkout Agreement (active)'; + + $query = $this->getQuery(); + $this->assignAgreementsToStore($secondStoreCode, $agreementsName); + + $response = $this->graphQlQuery($query); + + self::assertArrayHasKey('checkoutAgreements', $response); + $agreements = $response['checkoutAgreements']; + self::assertEmpty($agreements); + } + + public function testGetAgreementNotSet() + { + $query = $this->getQuery(); + + $response = $this->graphQlQuery($query); + + self::assertArrayHasKey('checkoutAgreements', $response); + $agreements = $response['checkoutAgreements']; + self::assertEmpty($agreements); + } + + /** + * @magentoApiDataFixture Magento/CheckoutAgreements/_files/agreement_active_with_html_content.php + * @magentoApiDataFixture Magento/CheckoutAgreements/_files/agreement_inactive_with_text_content.php + */ + public function testDisabledAgreements() + { + $query = $this->getQuery(); + $this->saveAgreementConfig(0); + + $response = $this->graphQlQuery($query); + + self::assertArrayHasKey('checkoutAgreements', $response); + $agreements = $response['checkoutAgreements']; + self::assertEmpty($agreements); + } + + /** + * @return string + */ + private function getQuery(): string + { + return + <<<QUERY +{ + checkoutAgreements { + agreement_id + name + content + content_height + checkbox_text + is_html + mode + } +} +QUERY; + } + + /** + * @param string $storeCode + * @param string $agreementsName + * @return void + */ + private function assignAgreementsToStore(string $storeCode, string $agreementsName): void + { + $agreementsFactory = $this->objectManager->get(AgreementFactory::class); + /** @var Agreement $agreementsResource */ + $agreementsResource = $this->objectManager->get(Agreement::class); + /** @var StoreManagerInterface $storeManager */ + $storeManager = $this->objectManager->get(StoreManagerInterface::class); + $store = $storeManager->getStore($storeCode); + /** @var AgreementModel $agreements */ + $agreements = $agreementsFactory->create(); + $agreementsResource->load($agreements, $agreementsName, AgreementInterface::NAME); + $agreements->setData('stores', [$store->getId()]); + $agreementsResource->save($agreements); + } + + protected function tearDown() + { + parent::tearDown(); + + $this->deleteAgreementConfig(); + } + + /** + * @param int $value + * @param StoreInterface $store + */ + private function saveAgreementConfig(int $value): void + { + $this->config->saveConfig( + $this->agreementsXmlConfigPath, + $value, + ScopeConfigInterface::SCOPE_TYPE_DEFAULT, + 0 + ); + + $this->reinitConfig(); + } + + /** + * Delete config + * + * @return void + */ + private function deleteAgreementConfig(): void + { + $this->config->deleteConfig( + $this->agreementsXmlConfigPath, + ScopeConfigInterface::SCOPE_TYPE_DEFAULT, + 0 + ); + + $this->reinitConfig(); + } + + private function reinitConfig(): void + { + /** @var ReinitableConfigInterface $config */ + $config = $this->objectManager->get(ReinitableConfigInterface::class); + $config->reinit(); + } +} diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Cms/CmsBlockTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Cms/CmsBlockTest.php index 57f526b1cb2f7..d598a463a48a7 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Cms/CmsBlockTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Cms/CmsBlockTest.php @@ -13,6 +13,9 @@ use Magento\TestFramework\TestCase\GraphQlAbstract; use Magento\Widget\Model\Template\FilterEmulate; +/** + * Get CMS Block test + */ class CmsBlockTest extends GraphQlAbstract { /** @@ -64,6 +67,39 @@ public function testGetCmsBlock() self::assertEquals($renderedContent, $response['cmsBlocks']['items'][0]['content']); } + /** + * Verify the fields of CMS Block selected by block_id + * + * @magentoApiDataFixture Magento/Cms/_files/blocks.php + */ + public function testGetCmsBlockByBlockId() + { + $cmsBlock = $this->blockRepository->getById('enabled_block'); + $cmsBlockData = $cmsBlock->getData(); + $blockId = $cmsBlockData['block_id']; + $renderedContent = $this->filterEmulate->setUseSessionInUrl(false)->filter($cmsBlock->getContent()); + + $query = + <<<QUERY +{ + cmsBlocks(identifiers: "$blockId") { + items { + identifier + title + content + } + } +} +QUERY; + $response = $this->graphQlQuery($query); + + self::assertArrayHasKey('cmsBlocks', $response); + self::assertArrayHasKey('items', $response['cmsBlocks']); + self::assertEquals($cmsBlockData['identifier'], $response['cmsBlocks']['items'][0]['identifier']); + self::assertEquals($cmsBlockData['title'], $response['cmsBlocks']['items'][0]['title']); + self::assertEquals($renderedContent, $response['cmsBlocks']['items'][0]['content']); + } + /** * Verify the message when CMS Block is disabled * diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Cms/CmsPageTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Cms/CmsPageTest.php index 86145fafa62f1..afbb3d40bc921 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Cms/CmsPageTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Cms/CmsPageTest.php @@ -11,6 +11,9 @@ use Magento\TestFramework\ObjectManager; use Magento\TestFramework\TestCase\GraphQlAbstract; +/** + * Get CMS Page test + */ class CmsPageTest extends GraphQlAbstract { /** @@ -50,6 +53,28 @@ public function testGetCmsPageById() $this->assertEquals($cmsPageData['meta_keywords'], $response['cmsPage']['meta_keywords']); } + /** + * Verify the fields of CMS Page selected by page_id + * + * @magentoApiDataFixture Magento/Cms/_files/pages.php + */ + public function testGetCmsPageByIdentifier() + { + $cmsPageIdentifier = 'page100'; + + $query = + <<<QUERY +{ + cmsPage(identifier: "$cmsPageIdentifier") { + identifier + } +} +QUERY; + + $response = $this->graphQlQuery($query); + $this->assertEquals($cmsPageIdentifier, $response['cmsPage']['identifier']); + } + /** * Verify the message when page_id is not specified. */ @@ -72,7 +97,7 @@ public function testGetCmsPageWithoutId() QUERY; $this->expectException(\Exception::class); - $this->expectExceptionMessage('Page id should be specified'); + $this->expectExceptionMessage('Page id/identifier should be specified'); $this->graphQlQuery($query); } @@ -102,6 +127,32 @@ public function testGetCmsPageByNonExistentId() $this->graphQlQuery($query); } + /** + * Verify the message when identifier does not exist. + * + * @expectedException \Exception + * @expectedExceptionMessage The CMS page with the "" ID doesn't exist. + */ + public function testGetCmsPageByNonExistentIdentifier() + { + $query = + <<<QUERY +{ + cmsPage(identifier: "") { + url_key + title + content + content_heading + page_layout + meta_title + meta_description + meta_keywords + } +} +QUERY; + $this->graphQlQuery($query); + } + /** * Verify the message when CMS Page selected by page_id is disabled * @@ -130,4 +181,32 @@ public function testGetDisabledCmsPageById() $this->expectExceptionMessage('No such entity.'); $this->graphQlQuery($query); } + + /** + * Verify the message when CMS Page selected by identifier is disabled + * + * @magentoApiDataFixture Magento/Cms/_files/noroute.php + * @expectedException \Exception + * @expectedExceptionMessage The CMS page with the "no-route" ID doesn't exist. + */ + public function testGetDisabledCmsPageByIdentifier() + { + $cmsPageIdentifier = 'no-route'; + $query = + <<<QUERY +{ + cmsPage(identifier: "$cmsPageIdentifier") { + url_key + title + content + content_heading + page_layout + meta_title + meta_description + meta_keywords + } +} +QUERY; + $this->graphQlQuery($query); + } } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/ApplyCouponToCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/ApplyCouponToCartTest.php index 5a2221a184294..eea00590a235f 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/ApplyCouponToCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/ApplyCouponToCartTest.php @@ -174,7 +174,6 @@ public function testApplyCouponToNonExistentCart() */ public function testApplyExpiredCoupon() { - $this->markTestIncomplete('https://github.com/magento/graphql-ce/issues/574'); $couponCode = '2?ds5!2d'; $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); $query = $this->getQuery($maskedQuoteId, $couponCode); diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetSelectedPaymentMethodTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetSelectedPaymentMethodTest.php index d876d74de661b..4432a233e96e7 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetSelectedPaymentMethodTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetSelectedPaymentMethodTest.php @@ -49,6 +49,27 @@ public function testGetSelectedPaymentMethod() $this->assertEquals('checkmo', $response['cart']['selected_payment_method']['code']); } + /** + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/enable_offline_payment_methods.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php + */ + public function testGetSelectedPaymentMethodBeforeSet() + { + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + + $query = $this->getQuery($maskedQuoteId); + $response = $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + + $this->assertArrayHasKey('cart', $response); + $this->assertArrayHasKey('selected_payment_method', $response['cart']); + $this->assertArrayHasKey('code', $response['cart']['selected_payment_method']); + $this->assertEquals('', $response['cart']['selected_payment_method']['code']); + } + /** * @magentoApiDataFixture Magento/Customer/_files/customer.php * @expectedException \Exception diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetSelectedShippingMethodTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetSelectedShippingMethodTest.php index 5575830ea51cd..9bb36bf8f0929 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetSelectedShippingMethodTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Customer/GetSelectedShippingMethodTest.php @@ -88,6 +88,40 @@ public function testGetSelectedShippingMethod() self::assertEquals('USD', $baseAmount['currency']); } + /** + * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php + */ + public function testGetSelectedShippingMethodBeforeSet() + { + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + + $query = $this->getQuery($maskedQuoteId); + $response = $this->graphQlQuery($query, [], '', $this->getHeaderMap()); + + self::assertArrayHasKey('cart', $response); + self::assertArrayHasKey('shipping_addresses', $response['cart']); + self::assertCount(1, $response['cart']['shipping_addresses']); + + $shippingAddress = current($response['cart']['shipping_addresses']); + self::assertArrayHasKey('selected_shipping_method', $shippingAddress); + + self::assertArrayHasKey('carrier_code', $shippingAddress['selected_shipping_method']); + self::assertNull($shippingAddress['selected_shipping_method']['carrier_code']); + + self::assertArrayHasKey('method_code', $shippingAddress['selected_shipping_method']); + self::assertNull($shippingAddress['selected_shipping_method']['method_code']); + + self::assertArrayHasKey('carrier_title', $shippingAddress['selected_shipping_method']); + self::assertNull($shippingAddress['selected_shipping_method']['carrier_title']); + + self::assertArrayHasKey('method_title', $shippingAddress['selected_shipping_method']); + self::assertNull($shippingAddress['selected_shipping_method']['method_title']); + } + /** * _security * @magentoApiDataFixture Magento/Customer/_files/customer.php @@ -193,6 +227,10 @@ private function getQuery(string $maskedQuoteId): string { cart(cart_id: "$maskedQuoteId") { shipping_addresses { + available_shipping_methods { + carrier_code + method_code + } selected_shipping_method { carrier_code method_code diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/ApplyCouponToCartTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/ApplyCouponToCartTest.php index affe36ea8617d..94b2c296001e6 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/ApplyCouponToCartTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/ApplyCouponToCartTest.php @@ -139,7 +139,6 @@ public function testApplyCouponToNonExistentCart() */ public function testApplyExpiredCoupon() { - $this->markTestIncomplete('https://github.com/magento/graphql-ce/issues/574'); $couponCode = '2?ds5!2d'; $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); $query = $this->getQuery($maskedQuoteId, $couponCode); diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetSelectedPaymentMethodTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetSelectedPaymentMethodTest.php index ef04da88ea67a..a918279bada65 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetSelectedPaymentMethodTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetSelectedPaymentMethodTest.php @@ -49,6 +49,26 @@ public function testGetSelectedPaymentMethod() $this->assertEquals('checkmo', $response['cart']['selected_payment_method']['code']); } + /** + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/enable_offline_payment_methods.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php + */ + public function testGetSelectedPaymentMethodBeforeSet() + { + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + + $query = $this->getQuery($maskedQuoteId); + $response = $this->graphQlQuery($query); + + $this->assertArrayHasKey('cart', $response); + $this->assertArrayHasKey('selected_payment_method', $response['cart']); + $this->assertArrayHasKey('code', $response['cart']['selected_payment_method']); + $this->assertEquals('', $response['cart']['selected_payment_method']['code']); + } + /** * @expectedException \Exception */ diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetSelectedShippingMethodTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetSelectedShippingMethodTest.php index bd684a950b590..5d1033b39819e 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetSelectedShippingMethodTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/Quote/Guest/GetSelectedShippingMethodTest.php @@ -80,6 +80,39 @@ public function testGetSelectedShippingMethod() self::assertEquals('USD', $baseAmount['currency']); } + /** + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * @magentoApiDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php + */ + public function testGetSelectedShippingMethodBeforeSet() + { + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + + $query = $this->getQuery($maskedQuoteId); + $response = $this->graphQlQuery($query); + + self::assertArrayHasKey('cart', $response); + self::assertArrayHasKey('shipping_addresses', $response['cart']); + self::assertCount(1, $response['cart']['shipping_addresses']); + + $shippingAddress = current($response['cart']['shipping_addresses']); + self::assertArrayHasKey('selected_shipping_method', $shippingAddress); + + self::assertArrayHasKey('carrier_code', $shippingAddress['selected_shipping_method']); + self::assertNull($shippingAddress['selected_shipping_method']['carrier_code']); + + self::assertArrayHasKey('method_code', $shippingAddress['selected_shipping_method']); + self::assertNull($shippingAddress['selected_shipping_method']['method_code']); + + self::assertArrayHasKey('carrier_title', $shippingAddress['selected_shipping_method']); + self::assertNull($shippingAddress['selected_shipping_method']['carrier_title']); + + self::assertArrayHasKey('method_title', $shippingAddress['selected_shipping_method']); + self::assertNull($shippingAddress['selected_shipping_method']['method_title']); + } + /** * _security * @magentoApiDataFixture Magento/Customer/_files/customer.php @@ -149,6 +182,10 @@ private function getQuery(string $maskedQuoteId): string { cart(cart_id: "$maskedQuoteId") { shipping_addresses { + available_shipping_methods { + carrier_code + method_code + } selected_shipping_method { carrier_code method_code diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/RelatedProduct/GetRelatedProductsTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/RelatedProduct/GetRelatedProductsTest.php new file mode 100644 index 0000000000000..dff1301e12f11 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/RelatedProduct/GetRelatedProductsTest.php @@ -0,0 +1,169 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQl\RelatedProduct; + +use Magento\TestFramework\TestCase\GraphQlAbstract; + +/** + * Get related products test + */ +class GetRelatedProductsTest extends GraphQlAbstract +{ + /** + * @magentoApiDataFixture Magento/Catalog/_files/products_related_multiple.php + */ + public function testQueryRelatedProducts() + { + $productSku = 'simple_with_cross'; + + $query = <<<QUERY +{ + products(filter: {sku: {eq: "{$productSku}"}}) + { + items { + related_products + { + sku + name + url_key + created_at + } + } + } +} +QUERY; + $response = $this->graphQlQuery($query); + + self::assertArrayHasKey('products', $response); + self::assertArrayHasKey('items', $response['products']); + self::assertEquals(1, count($response['products']['items'])); + self::assertArrayHasKey(0, $response['products']['items']); + self::assertArrayHasKey('related_products', $response['products']['items'][0]); + $relatedProducts = $response['products']['items'][0]['related_products']; + self::assertCount(2, $relatedProducts); + self::assertRelatedProducts($relatedProducts); + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/products_crosssell.php + */ + public function testQueryCrossSellProducts() + { + $productSku = 'simple_with_cross'; + + $query = <<<QUERY +{ + products(filter: {sku: {eq: "{$productSku}"}}) + { + items { + crosssell_products + { + sku + name + url_key + created_at + } + } + } +} +QUERY; + $response = $this->graphQlQuery($query); + + self::assertArrayHasKey('products', $response); + self::assertArrayHasKey('items', $response['products']); + self::assertEquals(1, count($response['products']['items'])); + self::assertArrayHasKey(0, $response['products']['items']); + self::assertArrayHasKey('crosssell_products', $response['products']['items'][0]); + $crossSellProducts = $response['products']['items'][0]['crosssell_products']; + self::assertCount(1, $crossSellProducts); + $crossSellProduct = $crossSellProducts[0]; + self::assertArrayHasKey('sku', $crossSellProduct); + self::assertArrayHasKey('name', $crossSellProduct); + self::assertArrayHasKey('url_key', $crossSellProduct); + self::assertArrayHasKey('created_at', $crossSellProduct); + self::assertEquals($crossSellProduct['sku'], 'simple'); + self::assertEquals($crossSellProduct['name'], 'Simple Cross Sell'); + self::assertEquals($crossSellProduct['url_key'], 'simple-cross-sell'); + self::assertNotEmpty($crossSellProduct['created_at']); + } + + /** + * @magentoApiDataFixture Magento/Catalog/_files/products_upsell.php + */ + public function testQueryUpSellProducts() + { + $productSku = 'simple_with_upsell'; + + $query = <<<QUERY +{ + products(filter: {sku: {eq: "{$productSku}"}}) + { + items { + upsell_products + { + sku + name + url_key + created_at + } + } + } +} +QUERY; + $response = $this->graphQlQuery($query); + + self::assertArrayHasKey('products', $response); + self::assertArrayHasKey('items', $response['products']); + self::assertEquals(1, count($response['products']['items'])); + self::assertArrayHasKey(0, $response['products']['items']); + self::assertArrayHasKey('upsell_products', $response['products']['items'][0]); + $upSellProducts = $response['products']['items'][0]['upsell_products']; + self::assertCount(1, $upSellProducts); + $upSellProduct = $upSellProducts[0]; + self::assertArrayHasKey('sku', $upSellProduct); + self::assertArrayHasKey('name', $upSellProduct); + self::assertArrayHasKey('url_key', $upSellProduct); + self::assertArrayHasKey('created_at', $upSellProduct); + self::assertEquals($upSellProduct['sku'], 'simple'); + self::assertEquals($upSellProduct['name'], 'Simple Up Sell'); + self::assertEquals($upSellProduct['url_key'], 'simple-up-sell'); + self::assertNotEmpty($upSellProduct['created_at']); + } + + /** + * @param array $relatedProducts + */ + private function assertRelatedProducts(array $relatedProducts): void + { + $expectedData = [ + 'simple' => [ + 'name' => 'Simple Related Product', + 'url_key' => 'simple-related-product', + + ], + 'simple_with_cross_two' => [ + 'name' => 'Simple Product With Related Product Two', + 'url_key' => 'simple-product-with-related-product-two', + ] + ]; + + foreach ($relatedProducts as $product) { + self::assertArrayHasKey('sku', $product); + self::assertArrayHasKey('name', $product); + self::assertArrayHasKey('url_key', $product); + self::assertArrayHasKey('created_at', $product); + + self::assertArrayHasKey($product['sku'], $expectedData); + $productExpectedData = $expectedData[$product['sku']]; + + self::assertEquals($product['name'], $productExpectedData['name']); + self::assertEquals($product['url_key'], $productExpectedData['url_key']); + self::assertNotEmpty($product['created_at']); + } + } +} diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/SendFriend/SendFriendTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/SendFriend/SendFriendTest.php index ae6faae7650b9..e7401100b7860 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/SendFriend/SendFriendTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/SendFriend/SendFriendTest.php @@ -7,6 +7,7 @@ namespace Magento\GraphQl\SendFriend; +use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\SendFriend\Model\SendFriend; use Magento\SendFriend\Model\SendFriendFactory; use Magento\TestFramework\Helper\Bootstrap; @@ -22,52 +23,32 @@ class SendFriendTest extends GraphQlAbstract * @var SendFriendFactory */ private $sendFriendFactory; + /** + * @var ProductRepositoryInterface + */ + private $productRepository; protected function setUp() { $this->sendFriendFactory = Bootstrap::getObjectManager()->get(SendFriendFactory::class); + $this->productRepository = Bootstrap::getObjectManager()->get(ProductRepositoryInterface::class); } /** - * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php */ public function testSendFriend() { - $query = - <<<QUERY -mutation { - sendEmailToFriend( - input: { - product_id: 1 - sender: { - name: "Name" - email: "e@mail.com" - message: "Lorem Ipsum" - } - recipients: [ - { + $productId = (int)$this->productRepository->get('simple_product')->getId(); + $recipients = '{ name: "Recipient Name 1" email:"recipient1@mail.com" }, { name: "Recipient Name 2" email:"recipient2@mail.com" - } - ] - } - ) { - sender { - name - email - message - } - recipients { - name - email - } - } -} -QUERY; + }'; + $query = $this->getQuery($productId, $recipients); $response = $this->graphQlMutation($query); self::assertEquals('Name', $response['sendEmailToFriend']['sender']['name']); @@ -81,41 +62,17 @@ public function testSendFriend() public function testSendWithoutExistProduct() { - $query = - <<<QUERY -mutation { - sendEmailToFriend( - input: { - product_id: 2018 - sender: { - name: "Name" - email: "e@mail.com" - message: "Lorem Ipsum" - } - recipients: [ - { + $productId = 2018; + $recipients = '{ name: "Recipient Name 1" email:"recipient1@mail.com" }, { name: "Recipient Name 2" email:"recipient2@mail.com" - } - ] - } - ) { - sender { - name - email - message - } - recipients { - name - email - } - } -} -QUERY; + }'; + $query = $this->getQuery($productId, $recipients); + $this->expectException(\Exception::class); $this->expectExceptionMessage( 'The product that was requested doesn\'t exist. Verify the product and try again.' @@ -124,26 +81,15 @@ public function testSendWithoutExistProduct() } /** - * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php */ public function testMaxSendEmailToFriend() { /** @var SendFriend $sendFriend */ $sendFriend = $this->sendFriendFactory->create(); - $query = - <<<QUERY -mutation { - sendEmailToFriend( - input: { - product_id: 1 - sender: { - name: "Name" - email: "e@mail.com" - message: "Lorem Ipsum" - } - recipients: [ - { + $productId = (int)$this->productRepository->get('simple_product')->getId(); + $recipients = '{ name: "Recipient Name 1" email:"recipient1@mail.com" }, @@ -166,22 +112,10 @@ public function testMaxSendEmailToFriend() { name: "Recipient Name 1" email:"recipient1@mail.com" - } - ] - } - ) { - sender { - name - email - message - } - recipients { - name - email - } - } -} -QUERY; + }'; + + $query = $this->getQuery($productId, $recipients); + $this->expectException(\Exception::class); $this->expectExceptionMessage("No more than {$sendFriend->getMaxRecipients()} emails can be sent at a time."); $this->graphQlMutation($query); @@ -221,7 +155,7 @@ public function testErrors(string $input, string $errorMessage) } /** - * @magentoApiDataFixture Magento/Catalog/_files/product_simple.php + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php * TODO: use magentoApiConfigFixture (to be merged https://github.com/magento/graphql-ce/pull/351) * @magentoApiDataFixture Magento/SendFriend/Fixtures/sendfriend_configuration.php */ @@ -231,42 +165,17 @@ public function testLimitMessagesPerHour() /** @var SendFriend $sendFriend */ $sendFriend = $this->sendFriendFactory->create(); - $query = - <<<QUERY -mutation { - sendEmailToFriend( - input: { - product_id: 1 - sender: { - name: "Name" - email: "e@mail.com" - message: "Lorem Ipsum" - } - recipients: [ - { + $productId = (int)$this->productRepository->get('simple_product')->getId(); + $recipients = '{ name: "Recipient Name 1" email:"recipient1@mail.com" }, - { + { name: "Recipient Name 2" email:"recipient2@mail.com" - } + }'; + $query = $this->getQuery($productId, $recipients); - ] - } - ) { - sender { - name - email - message - } - recipients { - name - email - } - } -} -QUERY; $this->expectException(\Exception::class); $this->expectExceptionMessage( "You can't send messages more than {$sendFriend->getMaxSendsToFriend()} times an hour." @@ -278,6 +187,49 @@ public function testLimitMessagesPerHour() } } + /** + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + */ + public function testSendProductWithoutSenderEmail() + { + $productId = (int)$this->productRepository->get('simple_product')->getId(); + $recipients = '{ + name: "Recipient Name 1" + email:"" + }'; + $query = $this->getQuery($productId, $recipients); + + $this->expectException(\Exception::class); + $this->expectExceptionMessage('GraphQL response contains errors: Please provide Email for all of recipients.'); + $this->graphQlMutation($query); + } + + /** + * @magentoApiDataFixture Magento/GraphQl/Catalog/_files/simple_product_without_visibility.php + */ + public function testSendProductWithoutVisibility() + { + $productId = (int)$this->productRepository->get('simple_product_without_visibility')->getId(); + $recipients = '{ + name: "Recipient Name 1" + email:"recipient1@mail.com" + }, + { + name: "Recipient Name 2" + email:"recipient2@mail.com" + }'; + $query = $this->getQuery($productId, $recipients); + + $response = $this->graphQlMutation($query); + self::assertEquals('Name', $response['sendEmailToFriend']['sender']['name']); + self::assertEquals('e@mail.com', $response['sendEmailToFriend']['sender']['email']); + self::assertEquals('Lorem Ipsum', $response['sendEmailToFriend']['sender']['message']); + self::assertEquals('Recipient Name 1', $response['sendEmailToFriend']['recipients'][0]['name']); + self::assertEquals('recipient1@mail.com', $response['sendEmailToFriend']['recipients'][0]['email']); + self::assertEquals('Recipient Name 2', $response['sendEmailToFriend']['recipients'][1]['name']); + self::assertEquals('recipient2@mail.com', $response['sendEmailToFriend']['recipients'][1]['email']); + } + /** * @return array */ @@ -358,4 +310,38 @@ public function sendFriendsErrorsDataProvider() ] ]; } + + /** + * @param int $productId + * @param string $recipients + * @return string + */ + private function getQuery(int $productId, string $recipients): string + { + return <<<QUERY +mutation { + sendEmailToFriend( + input: { + product_id: {$productId} + sender: { + name: "Name" + email: "e@mail.com" + message: "Lorem Ipsum" + } + recipients: [{$recipients}] + } + ) { + sender { + name + email + message + } + recipients { + name + email + } + } +} +QUERY; + } } diff --git a/dev/tests/api-functional/testsuite/Magento/GraphQl/UrlRewrite/UrlResolverTest.php b/dev/tests/api-functional/testsuite/Magento/GraphQl/UrlRewrite/UrlResolverTest.php index 370121a1dad78..8eaf33483531d 100644 --- a/dev/tests/api-functional/testsuite/Magento/GraphQl/UrlRewrite/UrlResolverTest.php +++ b/dev/tests/api-functional/testsuite/Magento/GraphQl/UrlRewrite/UrlResolverTest.php @@ -15,6 +15,7 @@ use Magento\Cms\Helper\Page as PageHelper; use Magento\Store\Model\ScopeInterface; use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\UrlRewrite\Model\UrlRewrite; /** * Test the GraphQL endpoint's URLResolver query to verify canonical URL's are correctly returned. @@ -355,4 +356,68 @@ public function testResolveSlash() $this->assertEquals($targetPath, $response['urlResolver']['relative_url']); $this->assertEquals('CMS_PAGE', $response['urlResolver']['type']); } + + /** + * Test for custom type which point to the valid product/category/cms page. + * + * @magentoApiDataFixture Magento/CatalogUrlRewrite/_files/product_with_category.php + */ + public function testGetNonExistentUrlRewrite() + { + $urlPath = 'non-exist-product.html'; + /** @var UrlRewrite $urlRewrite */ + $urlRewrite = $this->objectManager->create(UrlRewrite::class); + $urlRewrite->load($urlPath, 'request_path'); + + /** @var UrlFinderInterface $urlFinder */ + $urlFinder = $this->objectManager->get(UrlFinderInterface::class); + $actualUrls = $urlFinder->findOneByData( + [ + 'request_path' => $urlPath, + 'store_id' => 1 + ] + ); + $targetPath = $actualUrls->getTargetPath(); + + $query = <<<QUERY +{ + urlResolver(url:"{$urlPath}") + { + id + relative_url + type + } +} +QUERY; + $response = $this->graphQlQuery($query); + $this->assertArrayHasKey('urlResolver', $response); + $this->assertEquals('PRODUCT', $response['urlResolver']['type']); + $this->assertEquals($targetPath, $response['urlResolver']['relative_url']); + } + + /** + * Test for custom type which point to the invalid product/category/cms page. + * + * @magentoApiDataFixture Magento/UrlRewrite/_files/url_rewrite_not_existing_entity.php + */ + public function testNonExistentEntityUrlRewrite() + { + $urlPath = 'non-exist-entity.html'; + + $query = <<<QUERY +{ + urlResolver(url:"{$urlPath}") + { + id + relative_url + type + } +} +QUERY; + + $this->expectExceptionMessage( + "No such entity found with matching URL key: " . $urlPath + ); + $this->graphQlQuery($query); + } } diff --git a/dev/tests/api-functional/testsuite/Magento/Quote/Api/CartAddingItemsTest.php b/dev/tests/api-functional/testsuite/Magento/Quote/Api/CartAddingItemsTest.php new file mode 100644 index 0000000000000..b52c4fb6f0b78 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/Quote/Api/CartAddingItemsTest.php @@ -0,0 +1,95 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Quote\Api; + +use Magento\TestFramework\TestCase\WebapiAbstract; + +/** + * Class for payment info in quote for registered customer. + */ +class CartAddingItemsTest extends WebapiAbstract +{ + /** + * @var \Magento\TestFramework\ObjectManager + */ + protected $objectManager; + + protected function setUp() + { + $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + } + + /** + * Test price for cart after adding product to. + * + * @magentoApiDataFixture Magento/Catalog/_files/product_without_options_with_stock_data.php + * @magentoApiDataFixture Magento/Customer/_files/customer_one_address.php + * @return void + */ + public function testPriceForCreatingQuoteFromEmptyCart() + { + $this->_markTestAsRestOnly(); + + // Get customer ID token + /** @var \Magento\Integration\Api\CustomerTokenServiceInterface $customerTokenService */ + $customerTokenService = $this->objectManager->create( + \Magento\Integration\Api\CustomerTokenServiceInterface::class + ); + $token = $customerTokenService->createCustomerAccessToken( + 'customer_one_address@test.com', + 'password' + ); + + // Creating empty cart for registered customer. + $serviceInfo = [ + 'rest' => [ + 'resourcePath' => '/V1/carts/mine', + 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_POST, + 'token' => $token + ] + ]; + + $quoteId = $this->_webApiCall($serviceInfo, ['customerId' => 999]); // customerId 999 will get overridden + $this->assertGreaterThan(0, $quoteId); + + // Adding item to the cart + $serviceInfoForAddingProduct = [ + 'rest' => [ + 'resourcePath' => '/V1/carts/mine/items', + 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_POST, + 'token' => $token + ] + ]; + $requestData = [ + 'cartItem' => [ + 'quote_id' => $quoteId, + 'sku' => 'simple', + 'qty' => 1 + ] + ]; + $item = $this->_webApiCall($serviceInfoForAddingProduct, $requestData); + $this->assertNotEmpty($item); + $this->assertEquals(10, $item['price']); + + // Get payment information + $serviceInfoForGettingPaymentInfo = [ + 'rest' => [ + 'resourcePath' => '/V1/carts/mine/payment-information', + 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_GET, + 'token' => $token + ] + ]; + $paymentInfo = $this->_webApiCall($serviceInfoForGettingPaymentInfo); + $this->assertEquals($paymentInfo['totals']['grand_total'], 10); + + /** @var \Magento\Quote\Model\Quote $quote */ + $quote = $this->objectManager->create(\Magento\Quote\Model\Quote::class); + $quote->load($quoteId); + $quote->delete(); + } +} diff --git a/dev/tests/api-functional/testsuite/Magento/Quote/Api/CartManagementTest.php b/dev/tests/api-functional/testsuite/Magento/Quote/Api/CartManagementTest.php index 80a4acbc563d6..6d585561ae3a9 100644 --- a/dev/tests/api-functional/testsuite/Magento/Quote/Api/CartManagementTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Quote/Api/CartManagementTest.php @@ -3,6 +3,7 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Quote\Api; @@ -66,7 +67,7 @@ public function testCreateEmptyCartForGuest() } /** - * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/Customer/_files/customer_one_address.php */ public function testCreateEmptyCartForCustomer() { @@ -94,7 +95,7 @@ public function testCreateEmptyCartForCustomer() } /** - * @magentoApiDataFixture Magento/Customer/_files/customer.php + * @magentoApiDataFixture Magento/Customer/_files/customer_one_address.php */ public function testCreateEmptyCartAndGetCartForCustomer() { @@ -105,7 +106,10 @@ public function testCreateEmptyCartAndGetCartForCustomer() $customerTokenService = $this->objectManager->create( \Magento\Integration\Api\CustomerTokenServiceInterface::class ); - $token = $customerTokenService->createCustomerAccessToken('customer@example.com', 'password'); + $token = $customerTokenService->createCustomerAccessToken( + 'customer_one_address@test.com', + 'password' + ); $serviceInfo = [ 'rest' => [ diff --git a/dev/tests/api-functional/testsuite/Magento/Quote/Api/GuestCartAddingItemsTest.php b/dev/tests/api-functional/testsuite/Magento/Quote/Api/GuestCartAddingItemsTest.php new file mode 100644 index 0000000000000..2067393b0bc2e --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/Quote/Api/GuestCartAddingItemsTest.php @@ -0,0 +1,120 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Quote\Api; + +use Magento\TestFramework\TestCase\WebapiAbstract; + +/** + * Class for testing adding and deleting items flow. + */ +class GuestCartAddingItemsTest extends WebapiAbstract +{ + const SERVICE_VERSION = 'V1'; + const SERVICE_NAME = 'quoteGuestCartManagementV1'; + const RESOURCE_PATH = '/V1/guest-carts/'; + + /** + * @var \Magento\TestFramework\ObjectManager + */ + protected $objectManager; + + protected function setUp() + { + $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + } + + /** + * Test price for cart after deleting and adding product to. + * + * @magentoApiDataFixture Magento/Catalog/_files/product_without_options_with_stock_data.php + * @return void + */ + public function testPriceForCreatingQuoteFromEmptyCart() + { + // Creating empty cart + $serviceInfoForCreatingEmptyCart = [ + 'rest' => [ + 'resourcePath' => self::RESOURCE_PATH, + 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_POST, + ], + 'soap' => [ + 'service' => self::SERVICE_NAME, + 'serviceVersion' => self::SERVICE_VERSION, + 'operation' => self::SERVICE_NAME . 'CreateEmptyCart', + ], + ]; + $quoteId = $this->_webApiCall($serviceInfoForCreatingEmptyCart); + + // Adding item to the cart + $serviceInfoForAddingProduct = [ + 'rest' => [ + 'resourcePath' => self::RESOURCE_PATH . $quoteId . '/items', + 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_POST, + ], + 'soap' => [ + 'service' => GuestCartItemRepositoryTest::SERVICE_NAME, + 'serviceVersion' => self::SERVICE_VERSION, + 'operation' => GuestCartItemRepositoryTest::SERVICE_NAME . 'Save', + ], + ]; + $requestData = [ + 'cartItem' => [ + 'quote_id' => $quoteId, + 'sku' => 'simple', + 'qty' => 1 + ] + ]; + $item = $this->_webApiCall($serviceInfoForAddingProduct, $requestData); + $this->assertNotEmpty($item); + + // Delete the item for the cart + $serviceInfoForDeleteProduct = [ + 'rest' => [ + 'resourcePath' => self::RESOURCE_PATH . $quoteId . '/items/' . $item['item_id'], + 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_DELETE, + ], + 'soap' => [ + 'service' => GuestCartItemRepositoryTest::SERVICE_NAME, + 'serviceVersion' => self::SERVICE_VERSION, + 'operation' => GuestCartItemRepositoryTest::SERVICE_NAME . 'deleteById', + ], + ]; + $response = (TESTS_WEB_API_ADAPTER == self::ADAPTER_SOAP) ? + $this->_webApiCall($serviceInfoForDeleteProduct, ['cartId' => $quoteId, 'itemId' => $item['item_id']]) + : $this->_webApiCall($serviceInfoForDeleteProduct); + $this->assertTrue($response); + + // Add one more item and check price for this item + $serviceInfoForAddingProduct = [ + 'rest' => [ + 'resourcePath' => self::RESOURCE_PATH . $quoteId . '/items', + 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_POST, + ], + 'soap' => [ + 'service' => GuestCartItemRepositoryTest::SERVICE_NAME, + 'serviceVersion' => self::SERVICE_VERSION, + 'operation' => GuestCartItemRepositoryTest::SERVICE_NAME . 'Save', + ], + ]; + $requestData = [ + 'cartItem' => [ + 'quote_id' => $quoteId, + 'sku' => 'simple', + 'qty' => 1 + ] + ]; + $item = $this->_webApiCall($serviceInfoForAddingProduct, $requestData); + $this->assertNotEmpty($item); + $this->assertEquals($item['price'], 10); + + /** @var \Magento\Quote\Model\Quote $quote */ + $quote = $this->objectManager->create(\Magento\Quote\Model\Quote::class); + $quote->load($quoteId); + $quote->delete(); + } +} diff --git a/dev/tests/api-functional/testsuite/Magento/WebapiAsync/Model/AsyncScheduleMultiStoreTest.php b/dev/tests/api-functional/testsuite/Magento/WebapiAsync/Model/AsyncScheduleMultiStoreTest.php new file mode 100644 index 0000000000000..a58bb6b14d069 --- /dev/null +++ b/dev/tests/api-functional/testsuite/Magento/WebapiAsync/Model/AsyncScheduleMultiStoreTest.php @@ -0,0 +1,373 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\WebapiAsync\Model; + +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\TestFramework\MessageQueue\PreconditionFailedException; +use Magento\TestFramework\MessageQueue\PublisherConsumerController; +use Magento\TestFramework\MessageQueue\EnvironmentPreconditionException; +use Magento\TestFramework\TestCase\WebapiAbstract; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\Catalog\Model\ResourceModel\Product\Collection; +use Magento\Framework\Phrase; +use Magento\Framework\Registry; +use Magento\Framework\Webapi\Exception; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Api\Data\ProductInterface as Product; +use Magento\Framework\ObjectManagerInterface; +use Magento\Store\Model\Store; +use Magento\Framework\Webapi\Rest\Request; + +/** + * Check async request for multistore product creation service, scheduling bulk + * to rabbitmq running consumers and check async.operation.add consumer check + * if product was created by async requests + * + * @magentoAppIsolation enabled + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class AsyncScheduleMultiStoreTest extends WebapiAbstract +{ + const SERVICE_NAME = 'catalogProductRepositoryV1'; + const SERVICE_VERSION = 'V1'; + const REST_RESOURCE_PATH = '/V1/products'; + const ASYNC_RESOURCE_PATH = '/async/V1/products'; + const ASYNC_CONSUMER_NAME = 'async.operations.all'; + + const STORE_CODE_FROM_FIXTURE = 'fixturestore'; + const STORE_NAME_FROM_FIXTURE = 'Fixture Store'; + + const STORE_CODE_ALL = 'all'; + const STORE_CODE_DEFAULT = 'default'; + + private $stores = [ + self::STORE_CODE_DEFAULT, + self::STORE_CODE_ALL, + self::STORE_CODE_FROM_FIXTURE, + ]; + + const KEY_TIER_PRICES = 'tier_prices'; + const KEY_SPECIAL_PRICE = 'special_price'; + const KEY_CATEGORY_LINKS = 'category_links'; + + const BULK_UUID_KEY = 'bulk_uuid'; + + protected $consumers = [ + self::ASYNC_CONSUMER_NAME, + ]; + + /** + * @var string[] + */ + private $skus = []; + + /** + * @var PublisherConsumerController + */ + private $publisherConsumerController; + + /** + * @var ProductRepositoryInterface + */ + private $productRepository; + + /** + * @var ObjectManagerInterface + */ + private $objectManager; + + /** + * @var Registry + */ + private $registry; + + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->logFilePath = TESTS_TEMP_DIR . "/MessageQueueTestLog.txt"; + $this->registry = $this->objectManager->get(Registry::class); + + $params = array_merge_recursive( + Bootstrap::getInstance()->getAppInitParams(), + ['MAGE_DIRS' => ['cache' => ['path' => TESTS_TEMP_DIR . '/cache']]] + ); + + /** @var PublisherConsumerController publisherConsumerController */ + $this->publisherConsumerController = $this->objectManager->create( + PublisherConsumerController::class, + [ + 'consumers' => $this->consumers, + 'logFilePath' => $this->logFilePath, + 'appInitParams' => $params, + ] + ); + $this->productRepository = $this->objectManager->create(ProductRepositoryInterface::class); + + try { + $this->publisherConsumerController->initialize(); + } catch (EnvironmentPreconditionException $e) { + $this->markTestSkipped($e->getMessage()); + } catch (PreconditionFailedException $e) { + $this->fail( + $e->getMessage() + ); + } + + parent::setUp(); + } + + /** + * @dataProvider storeProvider + * @magentoApiDataFixture Magento/Store/_files/core_fixturestore.php + */ + public function testAsyncScheduleBulkMultistore($storeCode) + { + $product = $this->getProductData(); + $this->_markTestAsRestOnly(); + + /** @var Store $store */ + $store = $this->objectManager->create(Store::class); + $store->load(self::STORE_CODE_FROM_FIXTURE); + $this->assertEquals( + self::STORE_NAME_FROM_FIXTURE, + $store->getName(), + 'Precondition failed: fixture store was not created.' + ); + + try { + /** @var Product $productModel */ + $productModel = $this->objectManager->create( + Product::class, + ['data' => $product['product']] + ); + $this->productRepository->save($productModel); + } catch (\Exception $e) { + $this->fail("Precondition failed: product was not created."); + } + + $this->asyncScheduleAndTest($product, $storeCode); + $this->clearProducts(); + } + + private function asyncScheduleAndTest($product, $storeCode = null) + { + $sku = $product['product'][Product::SKU]; + $productName = $product['product'][Product::NAME]; + $newProductName = $product['product'][Product::NAME] . $storeCode; + + $this->skus[] = $sku; + + $product['product'][Product::NAME] = $newProductName; + $product['product'][Product::TYPE_ID] = 'virtual'; + + $response = $this->updateProductAsync($product, $sku, $storeCode); + + $this->assertArrayHasKey(self::BULK_UUID_KEY, $response); + $this->assertNotNull($response[self::BULK_UUID_KEY]); + + $this->assertCount(1, $response['request_items']); + $this->assertEquals('accepted', $response['request_items'][0]['status']); + $this->assertFalse($response['errors']); + + //assert one products is created + try { + $this->publisherConsumerController->waitForAsynchronousResult( + [$this, 'assertProductCreation'], + [$product] + ); + } catch (PreconditionFailedException $e) { + $this->fail("Not all products were created"); + } + + $requestData = ['id' => $sku, 'sku' => $sku]; + + foreach ($this->stores as $checkingStore) { + $serviceInfo = [ + 'rest' => [ + 'resourcePath' => self::REST_RESOURCE_PATH . '/' . $sku, + 'httpMethod' => Request::HTTP_METHOD_GET + ] + ]; + $storeResponse = $this->_webApiCall($serviceInfo, $requestData, null, $checkingStore); + if ($checkingStore == $storeCode || $storeCode == self::STORE_CODE_ALL) { + $this->assertEquals( + $newProductName, + $storeResponse[Product::NAME], + sprintf( + 'Product name in %s store is invalid after updating in store %s.', + $checkingStore, + $storeCode + ) + ); + } else { + $this->assertEquals( + $productName, + $storeResponse[Product::NAME], + sprintf( + 'Product name in %s store is invalid after updating in store %s.', + $checkingStore, + $storeCode + ) + ); + } + } + } + + public function tearDown() + { + $this->clearProducts(); + $this->publisherConsumerController->stopConsumers(); + parent::tearDown(); + } + + private function clearProducts() + { + $size = $this->objectManager->create(Collection::class) + ->addAttributeToFilter('sku', ['in' => $this->skus]) + ->load() + ->getSize(); + + if ($size == 0) { + return; + } + + $this->registry->unregister('isSecureArea'); + $this->registry->register('isSecureArea', true); + try { + foreach ($this->skus as $sku) { + $this->productRepository->deleteById($sku); + } + // phpcs:ignore Magento2.Exceptions.ThrowCatch + } catch (\Exception $e) { + throw $e; + //nothing to delete + } + $this->registry->unregister('isSecureArea'); + + $size = $this->objectManager->create(Collection::class) + ->addAttributeToFilter('sku', ['in' => $this->skus]) + ->load() + ->getSize(); + + if ($size > 0) { + //phpcs:ignore Magento2.Exceptions.DirectThrow + throw new Exception(new Phrase("Collection size after clearing the products: %size", ['size' => $size])); + } + $this->skus = []; + } + + /** + * @return array + */ + public function getProductData() + { + $productBuilder = function ($data) { + return array_replace_recursive( + $this->getSimpleProductData(), + $data + ); + }; + + return [ + 'product' => + $productBuilder( + [ + ProductInterface::TYPE_ID => 'simple', + ProductInterface::SKU => 'multistore-sku-test-1', + ProductInterface::NAME => 'Test Name ', + ] + ), + ]; + } + + public function storeProvider() + { + $dataSets = []; + foreach ($this->stores as $store) { + $dataSets[$store] = [$store]; + } + return $dataSets; + } + + /** + * Get Simple Product Data + * + * @param array $productData + * @return array + */ + private function getSimpleProductData($productData = []) + { + return [ + ProductInterface::SKU => isset($productData[ProductInterface::SKU]) + ? $productData[ProductInterface::SKU] : uniqid('sku-', true), + ProductInterface::NAME => isset($productData[ProductInterface::NAME]) + ? $productData[ProductInterface::NAME] : uniqid('sku-', true), + ProductInterface::VISIBILITY => 4, + ProductInterface::TYPE_ID => 'simple', + ProductInterface::PRICE => 3.62, + ProductInterface::STATUS => 1, + ProductInterface::TYPE_ID => 'simple', + ProductInterface::ATTRIBUTE_SET_ID => 4, + ]; + } + + /** + * @param $requestData + * @param string|null $storeCode + * @return mixed + */ + private function updateProductAsync($requestData, $sku, $storeCode = null) + { + $serviceInfo = [ + 'rest' => [ + 'resourcePath' => self::ASYNC_RESOURCE_PATH . '/' . $sku, + 'httpMethod' => Request::HTTP_METHOD_PUT, + ], + ]; + + return $this->_webApiCall($serviceInfo, $requestData, null, $storeCode); + } + + public function assertProductCreation($product) + { + $sku = $product['product'][Product::SKU]; + $collection = $this->objectManager->create(Collection::class) + ->addAttributeToFilter(Product::SKU, ['eq' => $sku]) + ->addAttributeToFilter(Product::TYPE_ID, ['eq' => 'virtual']) + ->load(); + $size = $collection->getSize(); + + return $size > 0; + } + + /** + * Remove test store + * //phpcs:disable + */ + public static function tearDownAfterClass() + { + parent::tearDownAfterClass(); + //phpcs:enable + /** @var Registry $registry */ + $registry = Bootstrap::getObjectManager()->get(Registry::class); + + $registry->unregister('isSecureArea'); + $registry->register('isSecureArea', true); + + /** @var Store $store*/ + $store = Bootstrap::getObjectManager()->create(Store::class); + $store->load('fixturestore'); + if ($store->getId()) { + $store->delete(); + } + + $registry->unregister('isSecureArea'); + $registry->register('isSecureArea', false); + } +} diff --git a/dev/tests/functional/lib/Magento/Mtf/Util/Command/Cli.php b/dev/tests/functional/lib/Magento/Mtf/Util/Command/Cli.php index 8fa22122cce89..f0abd280f3ebc 100644 --- a/dev/tests/functional/lib/Magento/Mtf/Util/Command/Cli.php +++ b/dev/tests/functional/lib/Magento/Mtf/Util/Command/Cli.php @@ -8,6 +8,7 @@ use Magento\Mtf\Util\Protocol\CurlInterface; use Magento\Mtf\Util\Protocol\CurlTransport; +use Magento\Mtf\Util\Protocol\CurlTransport\WebapiDecorator; /** * Perform bin/magento commands from command line for functional tests executions. @@ -17,7 +18,7 @@ class Cli /** * Url to command.php. */ - const URL = 'dev/tests/functional/utils/command.php'; + const URL = '/dev/tests/functional/utils/command.php'; /** * Curl transport protocol. @@ -26,12 +27,21 @@ class Cli */ private $transport; + /** + * Webapi handler. + * + * @var WebapiDecorator + */ + private $webapiHandler; + /** * @param CurlTransport $transport + * @param WebapiDecorator $webapiHandler */ - public function __construct(CurlTransport $transport) + public function __construct(CurlTransport $transport, WebapiDecorator $webapiHandler) { $this->transport = $transport; + $this->webapiHandler = $webapiHandler; } /** @@ -43,22 +53,31 @@ public function __construct(CurlTransport $transport) */ public function execute($command, $options = []) { - $curl = $this->transport; - $curl->write($this->prepareUrl($command, $options), [], CurlInterface::GET); - $curl->read(); - $curl->close(); + $this->transport->write( + rtrim(str_replace('index.php', '', $_ENV['app_frontend_url']), '/') . self::URL, + $this->prepareParamArray($command, $options), + CurlInterface::POST, + [] + ); + $this->transport->read(); + $this->transport->close(); } /** - * Prepare url. + * Prepare parameter array. * * @param string $command * @param array $options [optional] - * @return string + * @return array */ - private function prepareUrl($command, $options = []) + private function prepareParamArray($command, $options = []) { - $command .= ' ' . implode(' ', $options); - return $_ENV['app_frontend_url'] . self::URL . '?command=' . urlencode($command); + if (!empty($options)) { + $command .= ' ' . implode(' ', $options); + } + return [ + 'token' => urlencode($this->webapiHandler->getWebapiToken()), + 'command' => urlencode($command) + ]; } } diff --git a/dev/tests/functional/lib/Magento/Mtf/Util/Command/File/Export/Reader.php b/dev/tests/functional/lib/Magento/Mtf/Util/Command/File/Export/Reader.php index d7336b51a18e2..f5b6d681e4f6c 100644 --- a/dev/tests/functional/lib/Magento/Mtf/Util/Command/File/Export/Reader.php +++ b/dev/tests/functional/lib/Magento/Mtf/Util/Command/File/Export/Reader.php @@ -3,12 +3,12 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - namespace Magento\Mtf\Util\Command\File\Export; use Magento\Mtf\ObjectManagerInterface; use Magento\Mtf\Util\Protocol\CurlTransport; use Magento\Mtf\Util\Protocol\CurlInterface; +use Magento\Mtf\Util\Protocol\CurlTransport\WebapiDecorator; /** * File reader for Magento export files. @@ -36,16 +36,29 @@ class Reader implements ReaderInterface */ private $transport; + /** + * Webapi handler. + * + * @var WebapiDecorator + */ + private $webapiHandler; + /** * @param ObjectManagerInterface $objectManager * @param CurlTransport $transport + * @param WebapiDecorator $webapiHandler * @param string $template */ - public function __construct(ObjectManagerInterface $objectManager, CurlTransport $transport, $template) - { + public function __construct( + ObjectManagerInterface $objectManager, + CurlTransport $transport, + WebapiDecorator $webapiHandler, + $template + ) { $this->objectManager = $objectManager; $this->template = $template; $this->transport = $transport; + $this->webapiHandler = $webapiHandler; } /** @@ -70,20 +83,28 @@ public function getData() */ private function getFiles() { - $this->transport->write($this->prepareUrl(), [], CurlInterface::GET); + $this->transport->write( + rtrim(str_replace('index.php', '', $_ENV['app_frontend_url']), '/') . self::URL, + $this->prepareParamArray(), + CurlInterface::POST, + [] + ); $serializedFiles = $this->transport->read(); $this->transport->close(); // phpcs:ignore Magento2.Security.InsecureFunction - return unserialize($serializedFiles, ['allowed_classes' => false]); + return unserialize($serializedFiles); } /** - * Prepare url. + * Prepare parameter array. * - * @return string + * @return array */ - private function prepareUrl() + private function prepareParamArray() { - return $_ENV['app_frontend_url'] . self::URL . '?template=' . urlencode($this->template); + return [ + 'token' => urlencode($this->webapiHandler->getWebapiToken()), + 'template' => urlencode($this->template) + ]; } } diff --git a/dev/tests/functional/lib/Magento/Mtf/Util/Command/File/Export/ReaderInterface.php b/dev/tests/functional/lib/Magento/Mtf/Util/Command/File/Export/ReaderInterface.php index 93f7cf1ce9764..3666e8643efa3 100644 --- a/dev/tests/functional/lib/Magento/Mtf/Util/Command/File/Export/ReaderInterface.php +++ b/dev/tests/functional/lib/Magento/Mtf/Util/Command/File/Export/ReaderInterface.php @@ -14,7 +14,7 @@ interface ReaderInterface /** * Url to export.php. */ - const URL = 'dev/tests/functional/utils/export.php'; + const URL = '/dev/tests/functional/utils/export.php'; /** * Exporting files as Data object from Magento. diff --git a/dev/tests/functional/lib/Magento/Mtf/Util/Command/File/Log.php b/dev/tests/functional/lib/Magento/Mtf/Util/Command/File/Log.php index f4e55682857a2..2539be593a713 100644 --- a/dev/tests/functional/lib/Magento/Mtf/Util/Command/File/Log.php +++ b/dev/tests/functional/lib/Magento/Mtf/Util/Command/File/Log.php @@ -7,6 +7,7 @@ namespace Magento\Mtf\Util\Command\File; use Magento\Mtf\Util\Protocol\CurlTransport; +use Magento\Mtf\Util\Protocol\CurlTransport\WebapiDecorator; /** * Get content of log file in var/log folder. @@ -16,7 +17,7 @@ class Log /** * Url to log.php. */ - const URL = 'dev/tests/functional/utils/log.php'; + const URL = '/dev/tests/functional/utils/log.php'; /** * Curl transport protocol. @@ -25,12 +26,21 @@ class Log */ private $transport; + /** + * Webapi handler. + * + * @var WebapiDecorator + */ + private $webapiHandler; + /** * @param CurlTransport $transport + * @param WebapiDecorator $webapiHandler */ - public function __construct(CurlTransport $transport) + public function __construct(CurlTransport $transport, WebapiDecorator $webapiHandler) { $this->transport = $transport; + $this->webapiHandler = $webapiHandler; } /** @@ -41,22 +51,29 @@ public function __construct(CurlTransport $transport) */ public function getFileContent($name) { - $curl = $this->transport; - $curl->write($this->prepareUrl($name), [], CurlTransport::GET); - $data = $curl->read(); - $curl->close(); + $this->transport->write( + rtrim(str_replace('index.php', '', $_ENV['app_frontend_url']), '/') . self::URL, + $this->prepareParamArray($name), + CurlInterface::POST, + [] + ); + $data = $this->transport->read(); + $this->transport->close(); // phpcs:ignore Magento2.Security.InsecureFunction return unserialize($data); } /** - * Prepare url. + * Prepare parameter array. * * @param string $name - * @return string + * @return array */ - private function prepareUrl($name) + private function prepareParamArray($name) { - return $_ENV['app_frontend_url'] . self::URL . '?name=' . urlencode($name); + return [ + 'token' => urlencode($this->webapiHandler->getWebapiToken()), + 'name' => urlencode($name) + ]; } } diff --git a/dev/tests/functional/lib/Magento/Mtf/Util/Command/GeneratedCode.php b/dev/tests/functional/lib/Magento/Mtf/Util/Command/GeneratedCode.php index dde3409ed1562..a9fefa25ffa24 100644 --- a/dev/tests/functional/lib/Magento/Mtf/Util/Command/GeneratedCode.php +++ b/dev/tests/functional/lib/Magento/Mtf/Util/Command/GeneratedCode.php @@ -7,6 +7,7 @@ use Magento\Mtf\Util\Protocol\CurlInterface; use Magento\Mtf\Util\Protocol\CurlTransport; +use Magento\Mtf\Util\Protocol\CurlTransport\WebapiDecorator; /** * GeneratedCode removes generated code of Magento (like generated/code and generated/metadata). @@ -16,7 +17,7 @@ class GeneratedCode /** * Url to deleteMagentoGeneratedCode.php. */ - const URL = 'dev/tests/functional/utils/deleteMagentoGeneratedCode.php'; + const URL = '/dev/tests/functional/utils/deleteMagentoGeneratedCode.php'; /** * Curl transport protocol. @@ -25,12 +26,21 @@ class GeneratedCode */ private $transport; + /** + * Webapi handler. + * + * @var WebapiDecorator + */ + private $webapiHandler; + /** * @param CurlTransport $transport + * @param WebapiDecorator $webapiHandler */ - public function __construct(CurlTransport $transport) + public function __construct(CurlTransport $transport, WebapiDecorator $webapiHandler) { $this->transport = $transport; + $this->webapiHandler = $webapiHandler; } /** @@ -40,10 +50,25 @@ public function __construct(CurlTransport $transport) */ public function delete() { - $url = $_ENV['app_frontend_url'] . self::URL; - $curl = $this->transport; - $curl->write($url, [], CurlInterface::GET); - $curl->read(); - $curl->close(); + $this->transport->write( + rtrim(str_replace('index.php', '', $_ENV['app_frontend_url']), '/') . self::URL, + $this->prepareParamArray(), + CurlInterface::POST, + [] + ); + $this->transport->read(); + $this->transport->close(); + } + + /** + * Prepare parameter array. + * + * @return array + */ + private function prepareParamArray() + { + return [ + 'token' => urlencode($this->webapiHandler->getWebapiToken()) + ]; } } diff --git a/dev/tests/functional/lib/Magento/Mtf/Util/Command/Locales.php b/dev/tests/functional/lib/Magento/Mtf/Util/Command/Locales.php index f669d91f2f2e5..a55d803f43087 100644 --- a/dev/tests/functional/lib/Magento/Mtf/Util/Command/Locales.php +++ b/dev/tests/functional/lib/Magento/Mtf/Util/Command/Locales.php @@ -7,6 +7,7 @@ use Magento\Mtf\Util\Protocol\CurlInterface; use Magento\Mtf\Util\Protocol\CurlTransport; +use Magento\Mtf\Util\Protocol\CurlTransport\WebapiDecorator; /** * Returns array of locales depends on fetching type. @@ -26,7 +27,7 @@ class Locales /** * Url to locales.php. */ - const URL = 'dev/tests/functional/utils/locales.php'; + const URL = '/dev/tests/functional/utils/locales.php'; /** * Curl transport protocol. @@ -35,12 +36,21 @@ class Locales */ private $transport; + /** + * Webapi handler. + * + * @var WebapiDecorator + */ + private $webapiHandler; + /** * @param CurlTransport $transport Curl transport protocol + * @param WebapiDecorator $webapiHandler */ - public function __construct(CurlTransport $transport) + public function __construct(CurlTransport $transport, WebapiDecorator $webapiHandler) { $this->transport = $transport; + $this->webapiHandler = $webapiHandler; } /** @@ -51,12 +61,28 @@ public function __construct(CurlTransport $transport) */ public function getList($type = self::TYPE_ALL) { - $url = $_ENV['app_frontend_url'] . self::URL . '?type=' . $type; - $curl = $this->transport; - $curl->write($url, [], CurlInterface::GET); - $result = $curl->read(); - $curl->close(); - + $this->transport->write( + rtrim(str_replace('index.php', '', $_ENV['app_frontend_url']), '/') . self::URL, + $this->prepareParamArray($type), + CurlInterface::POST, + [] + ); + $result = $this->transport->read(); + $this->transport->close(); return explode('|', $result); } + + /** + * Prepare parameter array. + * + * @param string $type + * @return array + */ + private function prepareParamArray($type) + { + return [ + 'token' => urlencode($this->webapiHandler->getWebapiToken()), + 'type' => urlencode($type) + ]; + } } diff --git a/dev/tests/functional/lib/Magento/Mtf/Util/Command/PathChecker.php b/dev/tests/functional/lib/Magento/Mtf/Util/Command/PathChecker.php index fd1f746a6f09c..4b12f6eec87aa 100644 --- a/dev/tests/functional/lib/Magento/Mtf/Util/Command/PathChecker.php +++ b/dev/tests/functional/lib/Magento/Mtf/Util/Command/PathChecker.php @@ -7,6 +7,7 @@ use Magento\Mtf\Util\Protocol\CurlInterface; use Magento\Mtf\Util\Protocol\CurlTransport; +use Magento\Mtf\Util\Protocol\CurlTransport\WebapiDecorator; /** * PathChecker checks that path to file or directory exists. @@ -16,7 +17,7 @@ class PathChecker /** * Url to checkPath.php. */ - const URL = 'dev/tests/functional/utils/pathChecker.php'; + const URL = '/dev/tests/functional/utils/pathChecker.php'; /** * Curl transport protocol. @@ -26,11 +27,21 @@ class PathChecker private $transport; /** + * Webapi handler. + * + * @var WebapiDecorator + */ + private $webapiHandler; + + /** + * @constructor * @param CurlTransport $transport + * @param WebapiDecorator $webapiHandler */ - public function __construct(CurlTransport $transport) + public function __construct(CurlTransport $transport, WebapiDecorator $webapiHandler) { $this->transport = $transport; + $this->webapiHandler = $webapiHandler; } /** @@ -41,12 +52,28 @@ public function __construct(CurlTransport $transport) */ public function pathExists($path) { - $url = $_ENV['app_frontend_url'] . self::URL . '?path=' . urlencode($path); - $curl = $this->transport; - $curl->write($url, [], CurlInterface::GET); - $result = $curl->read(); - $curl->close(); - + $this->transport->write( + rtrim(str_replace('index.php', '', $_ENV['app_frontend_url']), '/') . self::URL, + $this->prepareParamArray($path), + CurlInterface::POST, + [] + ); + $result = $this->transport->read(); + $this->transport->close(); return strpos($result, 'path exists: true') !== false; } + + /** + * Prepare parameter array. + * + * @param string $path + * @return array + */ + private function prepareParamArray($path) + { + return [ + 'token' => urlencode($this->webapiHandler->getWebapiToken()), + 'path' => urlencode($path) + ]; + } } diff --git a/dev/tests/functional/lib/Magento/Mtf/Util/Command/Website.php b/dev/tests/functional/lib/Magento/Mtf/Util/Command/Website.php index 7d73634c0360d..fec20bb2a8715 100644 --- a/dev/tests/functional/lib/Magento/Mtf/Util/Command/Website.php +++ b/dev/tests/functional/lib/Magento/Mtf/Util/Command/Website.php @@ -3,11 +3,11 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - namespace Magento\Mtf\Util\Command; use Magento\Mtf\Util\Protocol\CurlInterface; use Magento\Mtf\Util\Protocol\CurlTransport; +use Magento\Mtf\Util\Protocol\CurlTransport\WebapiDecorator; /** * Perform Website folder creation for functional tests executions. @@ -17,7 +17,7 @@ class Website /** * Url to website.php. */ - const URL = 'dev/tests/functional/utils/website.php'; + const URL = '/dev/tests/functional/utils/website.php'; /** * Curl transport protocol. @@ -26,13 +26,22 @@ class Website */ private $transport; + /** + * Webapi handler. + * + * @var WebapiDecorator + */ + private $webapiHandler; + /** * @constructor * @param CurlTransport $transport + * @param WebapiDecorator $webapiHandler */ - public function __construct(CurlTransport $transport) + public function __construct(CurlTransport $transport, WebapiDecorator $webapiHandler) { $this->transport = $transport; + $this->webapiHandler = $webapiHandler; } /** @@ -43,21 +52,28 @@ public function __construct(CurlTransport $transport) */ public function create($websiteCode) { - $curl = $this->transport; - $curl->addOption(CURLOPT_HEADER, 1); - $curl->write($this->prepareUrl($websiteCode), [], CurlInterface::GET); - $curl->read(); - $curl->close(); + $this->transport->addOption(CURLOPT_HEADER, 1); + $this->transport->write( + rtrim(str_replace('index.php', '', $_ENV['app_frontend_url']), '/') . self::URL, + $this->prepareParamArray($websiteCode), + CurlInterface::POST, + [] + ); + $this->transport->read(); + $this->transport->close(); } /** - * Prepare url. + * Prepare parameter array. * * @param string $websiteCode - * @return string + * @return array */ - private function prepareUrl($websiteCode) + private function prepareParamArray($websiteCode) { - return $_ENV['app_frontend_url'] . self::URL . '?website_code=' . urlencode($websiteCode); + return [ + 'token' => urlencode($this->webapiHandler->getWebapiToken()), + 'website_code' => urlencode($websiteCode) + ]; } } diff --git a/dev/tests/functional/lib/Magento/Mtf/Util/Protocol/CurlTransport/BackendDecorator.php b/dev/tests/functional/lib/Magento/Mtf/Util/Protocol/CurlTransport/BackendDecorator.php index b1c552370835c..a9a082e2c0027 100644 --- a/dev/tests/functional/lib/Magento/Mtf/Util/Protocol/CurlTransport/BackendDecorator.php +++ b/dev/tests/functional/lib/Magento/Mtf/Util/Protocol/CurlTransport/BackendDecorator.php @@ -63,24 +63,60 @@ public function __construct(CurlTransport $transport, DataInterface $configurati */ protected function authorize() { - // Perform GET to backend url so form_key is set - $url = $_ENV['app_backend_url']; - $this->transport->write($url, [], CurlInterface::GET); - $this->read(); - - $url = $_ENV['app_backend_url'] . $this->configuration->get('application/0/backendLoginUrl/0/value'); - $data = [ - 'login[username]' => $this->configuration->get('application/0/backendLogin/0/value'), - 'login[password]' => $this->configuration->get('application/0/backendPassword/0/value'), - 'form_key' => $this->formKey, - ]; - $this->transport->write($url, $data, CurlInterface::POST); - $response = $this->read(); - if (strpos($response, 'login-form') !== false) { + // There are situations where magento application backend url could be slightly different from the environment + // variable we know. It could be intentionally (e.g. InstallTest) or unintentionally. We would still want tests + // to run in this case. + // When the original app_backend_url does not work, we will try 4 variants of the it. i.e. with and without + // url rewrite, http and https. + $urls = []; + $originalUrl = rtrim($_ENV['app_backend_url'], '/') . '/'; + $urls[] = $originalUrl; + // It could be the case that the page needs a refresh, so we will try the original one twice. + $urls[] = $originalUrl; + if (strpos($originalUrl, '/index.php') !== false) { + $url2 = str_replace('/index.php', '', $originalUrl); + } else { + $url2 = $originalUrl . 'index.php/'; + } + $urls[] = $url2; + if (strpos($originalUrl, 'https') !== false) { + $urls[] = str_replace('https', 'http', $originalUrl); + $urls[] = str_replace('https', 'http', $url2); + } else { + $urls[] = str_replace('http', 'https', $originalUrl); + $urls[] = str_replace('http', 'https', $url2); + } + + $isAuthorized = false; + foreach ($urls as $url) { + try { + // Perform GET to backend url so form_key is set + $this->transport->write($url, [], CurlInterface::GET); + $this->read(); + + $authUrl = $url . $this->configuration->get('application/0/backendLoginUrl/0/value'); + $data = [ + 'login[username]' => $this->configuration->get('application/0/backendLogin/0/value'), + 'login[password]' => $this->configuration->get('application/0/backendPassword/0/value'), + 'form_key' => $this->formKey, + ]; + + $this->transport->write($authUrl, $data, CurlInterface::POST); + $response = $this->read(); + if (strpos($response, 'login-form') !== false) { + continue; + } + $isAuthorized = true; + $_ENV['app_backend_url'] = $url; + break; + // phpcs:ignore Magento2.Exceptions.ThrowCatch + } catch (\Exception $e) { + continue; + } + } + if ($isAuthorized == false) { // phpcs:ignore Magento2.Exceptions.DirectThrow - throw new \Exception( - 'Admin user cannot be logged in by curl handler!' - ); + throw new \Exception('Admin user cannot be logged in by curl handler!'); } } diff --git a/dev/tests/functional/lib/Magento/Mtf/Util/Protocol/CurlTransport/WebapiDecorator.php b/dev/tests/functional/lib/Magento/Mtf/Util/Protocol/CurlTransport/WebapiDecorator.php index 3aa756904ab00..df5ab45a3f96d 100644 --- a/dev/tests/functional/lib/Magento/Mtf/Util/Protocol/CurlTransport/WebapiDecorator.php +++ b/dev/tests/functional/lib/Magento/Mtf/Util/Protocol/CurlTransport/WebapiDecorator.php @@ -70,6 +70,13 @@ class WebapiDecorator implements CurlInterface */ protected $response; + /** + * Webapi token. + * + * @var string + */ + protected $webapiToken; + /** * @construct * @param ObjectManager $objectManager @@ -110,6 +117,9 @@ protected function init() $integration->persist(); $this->setConfiguration($integration); + $this->webapiToken = $integration->getToken(); + } else { + $this->webapiToken = $integrationToken; } } @@ -161,7 +171,13 @@ protected function setConfiguration(Integration $integration) */ protected function isValidIntegration() { - $this->write($_ENV['app_frontend_url'] . 'rest/V1/modules', [], CurlInterface::GET); + $url = rtrim($_ENV['app_frontend_url'], '/'); + if (strpos($url, 'index.php') === false) { + $url .= '/index.php/rest/V1/modules'; + } else { + $url .= '/rest/V1/modules'; + } + $this->write($url, [], CurlInterface::GET); $response = json_decode($this->read(), true); return (null !== $response) && !isset($response['message']); @@ -219,4 +235,18 @@ public function close() { $this->transport->close(); } + + /** + * Return webapiToken. + * + * @return string + */ + public function getWebapiToken() + { + // Request token if integration is no longer valid + if (!$this->isValidIntegration()) { + $this->init(); + } + return $this->webapiToken; + } } diff --git a/dev/tests/functional/tests/app/Magento/Backend/Test/TestCase/LoginAfterJSMinificationTest.xml b/dev/tests/functional/tests/app/Magento/Backend/Test/TestCase/LoginAfterJSMinificationTest.xml index 4519655395ff9..e63efd4db9900 100644 --- a/dev/tests/functional/tests/app/Magento/Backend/Test/TestCase/LoginAfterJSMinificationTest.xml +++ b/dev/tests/functional/tests/app/Magento/Backend/Test/TestCase/LoginAfterJSMinificationTest.xml @@ -8,7 +8,7 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\Backend\Test\TestCase\LoginAfterJSMinificationTest" summary="Verify login is successful after JS minification is enabled"> <variation name="LoginAfterJSMinificationEnabledVariation1" summary="Verify login is successful after JS minification" ticketId="MAGETWO-71416"> - <data name="tag" xsi:type="string">severity:S1</data> + <data name="tag" xsi:type="string">severity:S1,mftf_migrated:yes</data> <data name="configData" xsi:type="string">minify_js_files</data> <data name="menuItem" xsi:type="string">Dashboard</data> <data name="pageTitle" xsi:type="string">Dashboard</data> diff --git a/dev/tests/functional/tests/app/Magento/Braintree/Test/Block/Form/BraintreeCc.php b/dev/tests/functional/tests/app/Magento/Braintree/Test/Block/Form/BraintreeCc.php index b4569e63e5886..5f02a29a0f5ff 100644 --- a/dev/tests/functional/tests/app/Magento/Braintree/Test/Block/Form/BraintreeCc.php +++ b/dev/tests/functional/tests/app/Magento/Braintree/Test/Block/Form/BraintreeCc.php @@ -64,12 +64,6 @@ function () use ($element, $iframe) { ); $this->browser->switchToFrame($iframeLocator); $element = $this->browser->find('body'); - $this->browser->waitUntil( - function () use ($element) { - $fieldElement = $element->find('input'); - return $fieldElement->isVisible() ? true : null; - } - ); $this->_fill([$mapping[$field]], $element); $this->browser->switchToFrame(); } diff --git a/dev/tests/functional/tests/app/Magento/Bundle/Test/TestCase/DeleteProductFromMiniShoppingCartTest.xml b/dev/tests/functional/tests/app/Magento/Bundle/Test/TestCase/DeleteProductFromMiniShoppingCartTest.xml index cc93c82a6312a..3f16c27b6ec79 100644 --- a/dev/tests/functional/tests/app/Magento/Bundle/Test/TestCase/DeleteProductFromMiniShoppingCartTest.xml +++ b/dev/tests/functional/tests/app/Magento/Bundle/Test/TestCase/DeleteProductFromMiniShoppingCartTest.xml @@ -10,6 +10,7 @@ <variation name="DeleteBundleProductFromMiniShoppingCartTestVariation"> <data name="products/0" xsi:type="string">bundleProduct::default</data> <data name="deletedProductIndex" xsi:type="string">0</data> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <constraint name="Magento\Checkout\Test\Constraint\AssertCartIsEmpty" /> </variation> </testCase> diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Product/View.php b/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Product/View.php index 990906b7e302a..c4ac7f32a94bf 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Product/View.php +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Block/Product/View.php @@ -220,6 +220,13 @@ class View extends AbstractConfigureBlock */ private $thresholdMessage = '.availability.only'; + /** + * Qty field error message selector. + * + * @var string + */ + private $qtyErrorMessage = '#qty-error'; + /** * Checks if threshold message is displayed. * @@ -677,4 +684,16 @@ public function checkVideoDataPresence($videoData) $dataVideoSelector = $this->productVideo . '[data-code="' . $videoData. '"]'; return $this->_rootElement->find($dataVideoSelector)->isPresent(); } + + /** + * Resolve qty field error message. + * + * @return string + */ + public function getQtyErrorMessage() + { + $this->waitForElementVisible($this->qtyErrorMessage); + + return $this->_rootElement->find($this->qtyErrorMessage)->getText(); + } } diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertProductInventoryMaxAllowedQty.php b/dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertProductInventoryMaxAllowedQty.php index cb05b289105ff..fadd9e1476334 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertProductInventoryMaxAllowedQty.php +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertProductInventoryMaxAllowedQty.php @@ -22,7 +22,7 @@ class AssertProductInventoryMaxAllowedQty extends AbstractConstraint * * @var string */ - private $errorMessage = 'The most you may purchase is %s.'; + private $errorMessage = 'The maximum you may purchase is %s.'; /** * Check if max qty setting is working correctly. @@ -48,8 +48,8 @@ public function processAssert( $catalogProductView->getViewBlock()->waitLoader(); $catalogProductView->getViewBlock()->setQtyAndClickAddToCart($maxQty * 2); \PHPUnit\Framework\Assert::assertEquals( - $catalogProductView->getMessagesBlock()->getErrorMessage(), sprintf($this->errorMessage, $maxQty), + $catalogProductView->getViewBlock()->getQtyErrorMessage(), 'The maximum purchase warning message is not appears.' ); diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertProductInventoryMinAllowedQty.php b/dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertProductInventoryMinAllowedQty.php index b950fb216b1c5..8c0831578473b 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertProductInventoryMinAllowedQty.php +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/Constraint/AssertProductInventoryMinAllowedQty.php @@ -48,8 +48,8 @@ public function processAssert( $catalogProductView->getViewBlock()->waitLoader(); $catalogProductView->getViewBlock()->setQtyAndClickAddToCart(1); \PHPUnit\Framework\Assert::assertEquals( - $catalogProductView->getMessagesBlock()->getErrorMessage(), sprintf($this->errorMessage, $minQty), + $catalogProductView->getViewBlock()->getQtyErrorMessage(), 'The minimum purchase warning message is not appears.' ); diff --git a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/CreateSimpleProductEntityPartOneTest.xml b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/CreateSimpleProductEntityPartOneTest.xml index f97735304baa5..ffaefdd945565 100644 --- a/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/CreateSimpleProductEntityPartOneTest.xml +++ b/dev/tests/functional/tests/app/Magento/Catalog/Test/TestCase/Product/CreateSimpleProductEntityPartOneTest.xml @@ -8,7 +8,6 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\Catalog\Test\TestCase\Product\CreateSimpleProductEntityPartOneTest" summary="Create Simple Product" ticketId="MAGETWO-23414"> <variation name="CreateSimpleProductEntityTestVariation12" summary="Create product that visible only in search and check min/max qty allowed in the shopping cart" ticketId="MAGETWO-43376, MAGETWO-43359"> - <data name="issue" xsi:type="string">MAGETWO-63217: Error message doesn't appear on add in the cart less products than configured</data> <data name="configData" xsi:type="string">inventory_min_qty_3, inventory_max_qty_5</data> <data name="product/data/url_key" xsi:type="string">simple-product-%isolation%</data> <data name="product/data/name" xsi:type="string">Simple Product %isolation%</data> diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Onepage/Registration.php b/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Onepage/Registration.php index c1bab0ae68897..eda93a53a4866 100644 --- a/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Onepage/Registration.php +++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/Block/Onepage/Registration.php @@ -19,7 +19,7 @@ class Registration extends Block * * @var string */ - protected $createAccountButton = 'input[data-bind*="Create an Account"]'; + protected $createAccountButton = '[data-bind*="i18n: \'Create an Account\'"]'; /** * Click 'Create an Account' button and wait until button will be not visible. diff --git a/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/DeleteProductFromMiniShoppingCartTest.xml b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/DeleteProductFromMiniShoppingCartTest.xml index d29d1674e2b15..43ad90fd45be3 100644 --- a/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/DeleteProductFromMiniShoppingCartTest.xml +++ b/dev/tests/functional/tests/app/Magento/Checkout/Test/TestCase/DeleteProductFromMiniShoppingCartTest.xml @@ -8,7 +8,7 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\Checkout\Test\TestCase\DeleteProductFromMiniShoppingCartTest" summary="Delete Product from Mini Shopping Cart" ticketId="MAGETWO-29104"> <variation name="DeleteProductFromMiniShoppingCartTestVariation1"> - <data name="tag" xsi:type="string">severity:S0</data> + <data name="tag" xsi:type="string">severity:S0, mftf_migrated:yes</data> <data name="description" xsi:type="string">delete Simple</data> <data name="products/0" xsi:type="string">catalogProductSimple::default</data> <data name="products/1" xsi:type="string">catalogProductVirtual::default</data> @@ -17,7 +17,7 @@ <constraint name="Magento\Checkout\Test\Constraint\AssertProductPresentInMiniShoppingCart" /> </variation> <variation name="DeleteProductFromMiniShoppingCartTestVariation2"> - <data name="tag" xsi:type="string">severity:S1</data> + <data name="tag" xsi:type="string">severity:S1, mftf_migrated:yes</data> <data name="description" xsi:type="string">delete Simple</data> <data name="products/0" xsi:type="string">catalogProductSimple::default</data> <data name="deletedProductIndex" xsi:type="string">0</data> diff --git a/dev/tests/functional/tests/app/Magento/Cms/Test/TestCase/CmsPageMassActionTest.xml b/dev/tests/functional/tests/app/Magento/Cms/Test/TestCase/CmsPageMassActionTest.xml index d032ef47823e8..68cdf5ba7b4c2 100644 --- a/dev/tests/functional/tests/app/Magento/Cms/Test/TestCase/CmsPageMassActionTest.xml +++ b/dev/tests/functional/tests/app/Magento/Cms/Test/TestCase/CmsPageMassActionTest.xml @@ -12,6 +12,7 @@ <data name="cmsPages/1" xsi:type="string">3_column_template</data> <data name="action" xsi:type="string">Disable</data> <data name="expectedStatus" xsi:type="string">Disabled</data> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <constraint name="Magento\Cms\Test\Constraint\AssertCmsPagesInGrid" /> <constraint name="Magento\Cms\Test\Constraint\AssertCmsPagesDisabledOnFrontend" /> </variation> diff --git a/dev/tests/functional/tests/app/Magento/Config/Test/Handler/ConfigData/Curl.php b/dev/tests/functional/tests/app/Magento/Config/Test/Handler/ConfigData/Curl.php index 66587879848a3..0d89a1d4eba6e 100644 --- a/dev/tests/functional/tests/app/Magento/Config/Test/Handler/ConfigData/Curl.php +++ b/dev/tests/functional/tests/app/Magento/Config/Test/Handler/ConfigData/Curl.php @@ -123,9 +123,9 @@ protected function prepareConfigPath(array $input) */ protected function applyConfigSettings(array $data, $section) { - $url = $this->getUrl($section); $curl = new BackendDecorator(new CurlTransport(), $this->_configuration); $curl->addOption(CURLOPT_HEADER, 1); + $url = $this->getUrl($section); $curl->write($url, $data); $response = $curl->read(); $curl->close(); diff --git a/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/TestCase/DeleteProductFromMiniShoppingCartTest.xml b/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/TestCase/DeleteProductFromMiniShoppingCartTest.xml index 2d00317b61bad..042c4d45cac19 100644 --- a/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/TestCase/DeleteProductFromMiniShoppingCartTest.xml +++ b/dev/tests/functional/tests/app/Magento/ConfigurableProduct/Test/TestCase/DeleteProductFromMiniShoppingCartTest.xml @@ -10,6 +10,7 @@ <variation name="DeleteConfigurableProductFromMiniShoppingCartTestVariation"> <data name="products/0" xsi:type="string">configurableProduct::default</data> <data name="deletedProductIndex" xsi:type="string">0</data> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <constraint name="Magento\Checkout\Test\Constraint\AssertCartIsEmpty" /> </variation> </testCase> diff --git a/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/CreateExistingCustomerFrontendEntity.xml b/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/CreateExistingCustomerFrontendEntity.xml index 92f8d7c32eaeb..3865b6acd5b5a 100644 --- a/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/CreateExistingCustomerFrontendEntity.xml +++ b/dev/tests/functional/tests/app/Magento/Customer/Test/TestCase/CreateExistingCustomerFrontendEntity.xml @@ -14,6 +14,7 @@ <data name="customer/data/password" xsi:type="string">123123q#</data> <data name="customer/data/password_confirmation" xsi:type="string">123123q#</data> <data name="customer/data/website_id/dataset" xsi:type="string">default</data> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <constraint name="Magento\Customer\Test\Constraint\AssertCustomerFailRegisterMessage" /> </variation> </testCase> diff --git a/dev/tests/functional/tests/app/Magento/Downloadable/Test/TestCase/DeleteProductFromMiniShoppingCartTest.xml b/dev/tests/functional/tests/app/Magento/Downloadable/Test/TestCase/DeleteProductFromMiniShoppingCartTest.xml index bb6a917152503..980fdb2998c08 100644 --- a/dev/tests/functional/tests/app/Magento/Downloadable/Test/TestCase/DeleteProductFromMiniShoppingCartTest.xml +++ b/dev/tests/functional/tests/app/Magento/Downloadable/Test/TestCase/DeleteProductFromMiniShoppingCartTest.xml @@ -8,6 +8,7 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\Checkout\Test\TestCase\DeleteProductFromMiniShoppingCartTest" summary="Delete Downloadable Product from Mini Shopping Cart" ticketId="MAGETWO-29104"> <variation name="DeleteDownloadableProductFromMiniShoppingCartTestVariation"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="products/0" xsi:type="string">downloadableProduct::default</data> <data name="deletedProductIndex" xsi:type="string">0</data> <constraint name="Magento\Checkout\Test\Constraint\AssertCartIsEmpty" /> diff --git a/dev/tests/functional/tests/app/Magento/Reports/Test/Constraint/AssertReportStatisticsNoticeMessage.php b/dev/tests/functional/tests/app/Magento/Reports/Test/Constraint/AssertReportStatisticsNoticeMessage.php index f68666366a663..4abb2a6ead8a8 100644 --- a/dev/tests/functional/tests/app/Magento/Reports/Test/Constraint/AssertReportStatisticsNoticeMessage.php +++ b/dev/tests/functional/tests/app/Magento/Reports/Test/Constraint/AssertReportStatisticsNoticeMessage.php @@ -7,8 +7,6 @@ namespace Magento\Reports\Test\Constraint; use Magento\Reports\Test\Page\Adminhtml\SalesReport; -use Magento\Sales\Test\Fixture\OrderInjectable; -use DateTime; /** * Assert that message in Sales Reports Pages displays correct date/time. @@ -27,22 +25,22 @@ class AssertReportStatisticsNoticeMessage extends AbstractAssertSalesReportResul * * @param array $salesReport * @param SalesReport $salesReportPage - * @param DateTime $currentDate * @return void */ public function processAssert( array $salesReport, - SalesReport $salesReportPage, - DateTime $currentDate + SalesReport $salesReportPage ) { + $timezone = new \DateTimeZone($_ENV['magento_timezone']); + $initialDate = new \DateTime('now', $timezone); $this->salesReportPage = $salesReportPage; $this->searchInSalesReportGrid($salesReport); - $date = $this->getLastUpdatedDate(); - $currentDateTime = $currentDate->format('M j, Y, g'); - $displayedDateTime = date('M j, Y, g', strtotime($date)); - \PHPUnit\Framework\Assert::assertEquals( - $currentDateTime, - $displayedDateTime, + $displayedDate = new \DateTime($this->getLastUpdatedDate(), $timezone); + $currentDate = new \DateTime('now', $timezone); + + \PHPUnit\Framework\Assert::assertTrue( + $displayedDate->getTimestamp() > $initialDate->getTimestamp() + && $displayedDate->getTimestamp() < $currentDate->getTimestamp(), "Message in Sales Reports Page is displayed in an incorrect timezone." ); } diff --git a/dev/tests/functional/tests/app/Magento/Reports/Test/TestCase/SalesOrderReportEntityTest.xml b/dev/tests/functional/tests/app/Magento/Reports/Test/TestCase/SalesOrderReportEntityTest.xml index 998102bcfbc45..20501b73813fd 100644 --- a/dev/tests/functional/tests/app/Magento/Reports/Test/TestCase/SalesOrderReportEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/Reports/Test/TestCase/SalesOrderReportEntityTest.xml @@ -22,7 +22,6 @@ <constraint name="Magento\Reports\Test\Constraint\AssertReportStatisticsNoticeMessage" /> </variation> <variation name="SalesOrderReportEntityTestVariation2"> - <data name="tag" xsi:type="string">stable:no</data> <data name="order/dataset" xsi:type="string">default</data> <data name="order/data/price/dataset" xsi:type="string">full_invoice</data> <data name="salesReport/report_type" xsi:type="string">Order Created</data> @@ -36,7 +35,6 @@ <constraint name="Magento\Reports\Test\Constraint\AssertSalesReportTotalResult" /> </variation> <variation name="SalesOrderReportEntityTestVariation3"> - <data name="tag" xsi:type="string">stable:no</data> <data name="order/dataset" xsi:type="string">default</data> <data name="order/data/price/dataset" xsi:type="string">full_invoice</data> <data name="salesReport/report_type" xsi:type="string">Order Updated</data> @@ -50,5 +48,19 @@ <constraint name="Magento\Reports\Test\Constraint\AssertSalesReportIntervalResult" /> <constraint name="Magento\Reports\Test\Constraint\AssertSalesReportTotalResult" /> </variation> + <variation name="SalesOrderReportEntityTestVariation4"> + <data name="order/dataset" xsi:type="string">default</data> + <data name="order/data/price/dataset" xsi:type="string">full_invoice</data> + <data name="salesReport/report_type" xsi:type="string">Order Created</data> + <data name="salesReport/period_type" xsi:type="string">Year</data> + <data name="salesReport/from" xsi:type="string">12/31/Y 12:00 a-1 year</data> + <data name="salesReport/to" xsi:type="string">m/d/Y 12:00</data> + <data name="salesReport/show_order_statuses" xsi:type="string">Any</data> + <data name="salesReport/show_empty_rows" xsi:type="string">Yes</data> + <data name="salesReport/show_actual_columns" xsi:type="string">No</data> + <constraint name="Magento\Reports\Test\Constraint\AssertSalesReportIntervalResult" /> + <constraint name="Magento\Reports\Test\Constraint\AssertSalesReportTotalResult" /> + <constraint name="Magento\Reports\Test\Constraint\AssertReportStatisticsNoticeMessage" /> + </variation> </testCase> </config> diff --git a/dev/tests/functional/tests/app/Magento/Reports/Test/TestCase/SalesRefundsReportEntityTest.xml b/dev/tests/functional/tests/app/Magento/Reports/Test/TestCase/SalesRefundsReportEntityTest.xml index 27014c0afb375..58fad5925e5bf 100644 --- a/dev/tests/functional/tests/app/Magento/Reports/Test/TestCase/SalesRefundsReportEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/Reports/Test/TestCase/SalesRefundsReportEntityTest.xml @@ -20,7 +20,6 @@ <constraint name="Magento\Reports\Test\Constraint\AssertRefundReportIntervalResult" /> </variation> <variation name="SalesRefundsReportEntityTestVariation2"> - <data name="tag" xsi:type="string">stable:no</data> <data name="description" xsi:type="string">assert refunds month report</data> <data name="order/dataset" xsi:type="string">default</data> <data name="order/data/price/dataset" xsi:type="string">full_invoice</data> @@ -33,7 +32,6 @@ <constraint name="Magento\Reports\Test\Constraint\AssertRefundReportIntervalResult" /> </variation> <variation name="SalesRefundsReportEntityTestVariation3"> - <data name="tag" xsi:type="string">stable:no</data> <data name="description" xsi:type="string">assert refund Daily report</data> <data name="order/dataset" xsi:type="string">default</data> <data name="order/data/price/dataset" xsi:type="string">full_invoice</data> diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/AssignCustomOrderStatusTest.xml b/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/AssignCustomOrderStatusTest.xml index 6ac008dc4e98a..808201b8e6acc 100644 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/AssignCustomOrderStatusTest.xml +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/AssignCustomOrderStatusTest.xml @@ -8,6 +8,7 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\Sales\Test\TestCase\AssignCustomOrderStatusTest" summary="Assign Custom Order Status" ticketId="MAGETWO-29382"> <variation name="AssignCustomOrderStatusTestVariation1"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="orderStatusState/state" xsi:type="string">Pending</data> <data name="orderStatusState/is_default" xsi:type="string">Yes</data> <data name="orderStatusState/visible_on_front" xsi:type="string">No</data> @@ -16,6 +17,7 @@ <constraint name="Magento\Sales\Test\Constraint\AssertOrderNotVisibleOnMyAccount" /> </variation> <variation name="AssignCustomOrderStatusTestVariation2"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="orderStatusState/state" xsi:type="string">Pending</data> <data name="orderStatusState/is_default" xsi:type="string">Yes</data> <data name="orderStatusState/visible_on_front" xsi:type="string">Yes</data> diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CancelCreatedOrderTest.xml b/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CancelCreatedOrderTest.xml index fdb396bbbd052..6cf6a58e6d8ff 100644 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CancelCreatedOrderTest.xml +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CancelCreatedOrderTest.xml @@ -12,6 +12,7 @@ <data name="order/data/entity_id/products" xsi:type="string">catalogProductSimple::low_stock_product,catalogProductVirtual::virtual_low_stock,bundleProduct::bundle_low_stock,configurableProduct::configurable_low_stock</data> <data name="status" xsi:type="string">Canceled</data> <data name="configData" xsi:type="string">checkmo</data> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <constraint name="Magento\Sales\Test\Constraint\AssertOrderCancelSuccessMessage" /> <constraint name="Magento\Sales\Test\Constraint\AssertOrderInOrdersGrid" /> <constraint name="Magento\Sales\Test\Constraint\AssertProductsQtyAfterOrderCancel" /> @@ -25,6 +26,7 @@ <data name="order/data/entity_id/products" xsi:type="string">catalogProductSimple::product_10_dollar</data> <data name="status" xsi:type="string">Canceled</data> <data name="configData" xsi:type="string">zero_subtotal_checkout, freeshipping</data> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <constraint name="Magento\Sales\Test\Constraint\AssertOrderCancelSuccessMessage" /> <constraint name="Magento\Sales\Test\Constraint\AssertOrderInOrdersGridOnFrontend" /> </variation> @@ -33,6 +35,7 @@ <data name="order/data/payment_auth_expiration/method" xsi:type="string">banktransfer</data> <data name="status" xsi:type="string">Canceled</data> <data name="configData" xsi:type="string">banktransfer</data> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <constraint name="Magento\Sales\Test\Constraint\AssertOrderCancelSuccessMessage" /> <constraint name="Magento\Sales\Test\Constraint\AssertOrderInOrdersGridOnFrontend" /> </variation> @@ -41,6 +44,7 @@ <data name="order/data/payment_auth_expiration/method" xsi:type="string">cashondelivery</data> <data name="status" xsi:type="string">Canceled</data> <data name="configData" xsi:type="string">cashondelivery</data> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <constraint name="Magento\Sales\Test\Constraint\AssertOrderCancelSuccessMessage" /> <constraint name="Magento\Sales\Test\Constraint\AssertOrderInOrdersGridOnFrontend" /> </variation> @@ -50,6 +54,7 @@ <data name="order/data/payment_auth_expiration/po_number" xsi:type="string">po_number</data> <data name="status" xsi:type="string">Canceled</data> <data name="configData" xsi:type="string">purchaseorder</data> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <constraint name="Magento\Sales\Test\Constraint\AssertOrderCancelSuccessMessage" /> <constraint name="Magento\Sales\Test\Constraint\AssertOrderInOrdersGridOnFrontend" /> </variation> @@ -57,6 +62,7 @@ <data name="order/dataset" xsi:type="string">default</data> <data name="order/data/entity_id/products" xsi:type="string">catalogProductSimple::low_stock_product</data> <data name="configData" xsi:type="string">decrease_stock_after_order_no</data> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <constraint name="Magento\Sales\Test\Constraint\AssertOrderCancelSuccessMessage" /> <constraint name="Magento\ConfigurableProduct\Test\Constraint\AssertConfigurableProductsQtyAfterReorder" /> </variation> diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CreateOrderFromEditCustomerPageTest.xml b/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CreateOrderFromEditCustomerPageTest.xml index e96bcc0abc1bc..ca506adf39edc 100644 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CreateOrderFromEditCustomerPageTest.xml +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/CreateOrderFromEditCustomerPageTest.xml @@ -21,6 +21,7 @@ <item name="grandTotal" xsi:type="string">19.00</item> </data> <data name="issue" xsi:type="string">MAGETWO-65165: Unable to add product to wish list using 'create order' interface</data> + <data name="tag" xsi:type="string">to_maintain:yes, mftf_migrated:yes</data> <constraint name="Magento\Sales\Test\Constraint\AssertOrderSuccessCreateMessage" /> <constraint name="Magento\Sales\Test\Constraint\AssertOrderGrandTotal" /> <constraint name="Magento\Sales\Test\Constraint\AssertProductInCustomerShoppingCartOnBackendGrid" /> diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/FrontendOrderPagerTest.xml b/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/FrontendOrderPagerTest.xml index 871f6adfb5186..77aedc21b362b 100644 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/FrontendOrderPagerTest.xml +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/FrontendOrderPagerTest.xml @@ -8,10 +8,12 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\Sales\Test\TestCase\FrontendOrderPagerTest" summary="Pager is enabled for orders with more than 20 items" ticketId="MAGETWO-63457"> <variation name="FrontendOrderPagerTestVariation1" summary="Pager is absent for 20 order items" ticketId="MAGETWO-63457"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="order/dataset" xsi:type="string">twenty_products</data> <constraint name="Magento\Sales\Test\Constraint\AssertOrderItemsPagerHiddenOnFrontend" /> </variation> <variation name="FrontendOrderPagerTestVariation2" summary="Pager is displayed for 21 order items" ticketId="MAGETWO-63459"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="order/dataset" xsi:type="string">twenty_one_products</data> <constraint name="Magento\Sales\Test\Constraint\AssertOrderItemsPagerDisplayedOnFrontend" /> </variation> diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/HoldCreatedOrderTest.xml b/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/HoldCreatedOrderTest.xml index 8fc6c40f578f4..43eaa07fd69fb 100644 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/HoldCreatedOrderTest.xml +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/HoldCreatedOrderTest.xml @@ -13,6 +13,7 @@ <data name="order/data/entity_id/products" xsi:type="string">catalogProductSimple::default,catalogProductSimple::default</data> <data name="orderButtonsUnavailable" xsi:type="string">Invoice,Cancel,Reorder,Ship,Edit</data> <data name="status" xsi:type="string">On Hold</data> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <constraint name="Magento\Sales\Test\Constraint\AssertOrderOnHoldSuccessMessage" /> <constraint name="Magento\Sales\Test\Constraint\AssertOrderButtonsUnavailable" /> <constraint name="Magento\Sales\Test\Constraint\AssertUnholdButton" /> diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/MassOrdersUpdateTest.xml b/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/MassOrdersUpdateTest.xml index 1f75b07c8ca1e..90a75bd1bda5e 100644 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/MassOrdersUpdateTest.xml +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/MassOrdersUpdateTest.xml @@ -8,6 +8,7 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\Sales\Test\TestCase\MassOrdersUpdateTest" summary="Mass Update Orders" ticketId="MAGETWO-27897"> <variation name="MassOrdersUpdateTestVariation1"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="description" xsi:type="string">cancel orders in status Pending and Processing</data> <data name="steps" xsi:type="string">-</data> <data name="action" xsi:type="string">Cancel</data> @@ -17,6 +18,7 @@ <constraint name="Magento\Sales\Test\Constraint\AssertOrdersInOrdersGrid" /> </variation> <variation name="MassOrdersUpdateTestVariation2"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="description" xsi:type="string">try to cancel orders in status Complete, Closed</data> <data name="steps" xsi:type="string">invoice, shipment|invoice, credit memo</data> <data name="action" xsi:type="string">Cancel</data> @@ -26,6 +28,7 @@ <constraint name="Magento\Sales\Test\Constraint\AssertOrdersInOrdersGrid" /> </variation> <variation name="MassOrdersUpdateTestVariation3"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="description" xsi:type="string">try to cancel orders in status Processing, Closed</data> <data name="steps" xsi:type="string">invoice|invoice, credit memo</data> <data name="action" xsi:type="string">Cancel</data> @@ -35,6 +38,7 @@ <constraint name="Magento\Sales\Test\Constraint\AssertOrdersInOrdersGrid" /> </variation> <variation name="MassOrdersUpdateTestVariation4"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="description" xsi:type="string">Put orders in statuses Pending, Processing on Hold</data> <data name="steps" xsi:type="string">-|invoice</data> <data name="action" xsi:type="string">Hold</data> @@ -44,6 +48,7 @@ <constraint name="Magento\Sales\Test\Constraint\AssertOrdersInOrdersGrid" /> </variation> <variation name="MassOrdersUpdateTestVariation5"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="description" xsi:type="string">Try to put order in status Complete on Hold</data> <data name="steps" xsi:type="string">invoice, shipment</data> <data name="action" xsi:type="string">Hold</data> @@ -53,6 +58,7 @@ <constraint name="Magento\Sales\Test\Constraint\AssertOrdersInOrdersGrid" /> </variation> <variation name="MassOrdersUpdateTestVariation6"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="description" xsi:type="string">Release order in status On Hold</data> <data name="steps" xsi:type="string">on hold</data> <data name="action" xsi:type="string">Unhold</data> @@ -62,6 +68,7 @@ <constraint name="Magento\Sales\Test\Constraint\AssertOrdersInOrdersGrid" /> </variation> <variation name="MassOrdersUpdateTestVariation7"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="description" xsi:type="string">try to Release order in status Pending</data> <data name="steps" xsi:type="string">-</data> <data name="action" xsi:type="string">Unhold</data> diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/MoveLastOrderedProductsOnOrderPageTest.xml b/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/MoveLastOrderedProductsOnOrderPageTest.xml index 8bb4ef56361fb..ddb66fe921e8f 100644 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/MoveLastOrderedProductsOnOrderPageTest.xml +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/MoveLastOrderedProductsOnOrderPageTest.xml @@ -8,11 +8,13 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\Sales\Test\TestCase\MoveLastOrderedProductsOnOrderPageTest" summary="Add Products to Order from Last Ordered Products Section" ticketId="MAGETWO-27640"> <variation name="MoveLastOrderedProductsOnOrderPageTestVariation1"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="order/dataset" xsi:type="string">default</data> <data name="order/data/entity_id/products" xsi:type="string">catalogProductSimple::default</data> <constraint name="Magento\Sales\Test\Constraint\AssertProductInItemsOrderedGrid" /> </variation> <variation name="MoveLastOrderedProductsOnOrderPageTestVariation2"> + <data name="tag" xsi:type="string">mftf_migrated:yes</data> <data name="order/dataset" xsi:type="string">default</data> <data name="order/data/entity_id/products" xsi:type="string">configurableProduct::configurable_with_qty_1</data> <constraint name="Magento\Sales\Test\Constraint\AssertProductInItemsOrderedGrid" /> diff --git a/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/MoveRecentlyViewedProductsOnOrderPageTest.xml b/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/MoveRecentlyViewedProductsOnOrderPageTest.xml index 579e8543d8c75..e32f5c2d18ee2 100644 --- a/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/MoveRecentlyViewedProductsOnOrderPageTest.xml +++ b/dev/tests/functional/tests/app/Magento/Sales/Test/TestCase/MoveRecentlyViewedProductsOnOrderPageTest.xml @@ -8,12 +8,12 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\Sales\Test\TestCase\MoveRecentlyViewedProductsOnOrderPageTest" summary="Add Products to Order from Recently Viewed Products Section" ticketId="MAGETWO-29723"> <variation name="MoveRecentlyViewedProductsOnOrderPageTestVariation1"> - <data name="tag" xsi:type="string">to_maintain:yes</data> + <data name="tag" xsi:type="string">to_maintain:yes, mftf_migrated:yes</data> <data name="products/0" xsi:type="string">configurableProduct::default</data> <constraint name="Magento\Sales\Test\Constraint\AssertProductInItemsOrderedGrid" /> </variation> <variation name="MoveRecentlyViewedProductsOnOrderPageTestVariation2"> - <data name="tag" xsi:type="string">to_maintain:yes</data> + <data name="tag" xsi:type="string">to_maintain:yes, mftf_migrated:yes</data> <data name="products/0" xsi:type="string">bundleProduct::bundle_fixed_product</data> <constraint name="Magento\Sales\Test\Constraint\AssertProductInItemsOrderedGrid" /> </variation> diff --git a/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/LockAdminUserWhenCreatingNewRoleTest.xml b/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/LockAdminUserWhenCreatingNewRoleTest.xml index 73a2a71b7c342..0e4c33f7371e6 100644 --- a/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/LockAdminUserWhenCreatingNewRoleTest.xml +++ b/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/LockAdminUserWhenCreatingNewRoleTest.xml @@ -8,7 +8,7 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\Security\Test\TestCase\LockAdminUserWhenCreatingNewRoleTest" summary="Lock admin user after entering incorrect password while creating new role"> <variation name="LockAdminUserWhenCreatingNewRoleTestVariation1"> - <data name="tag" xsi:type="string">severity:S2</data> + <data name="tag" xsi:type="string">severity:S2, mftf_migrated:yes</data> <data name="configData" xsi:type="string">user_lockout_failures</data> <data name="customAdmin/dataset" xsi:type="string">custom_admin_with_default_role</data> <data name="role/data/rolename" xsi:type="string">AdminRole%isolation%</data> diff --git a/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/LockCustomerOnLoginPageTest.xml b/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/LockCustomerOnLoginPageTest.xml index 7c043a36708b6..07976ad01bd96 100644 --- a/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/LockCustomerOnLoginPageTest.xml +++ b/dev/tests/functional/tests/app/Magento/Security/Test/TestCase/LockCustomerOnLoginPageTest.xml @@ -8,7 +8,7 @@ <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../vendor/magento/mtf/etc/variations.xsd"> <testCase name="Magento\Security\Test\TestCase\LockCustomerOnLoginPageTest" summary="Lock customer on login page"> <variation name="LockCustomerOnLoginPageTestVariation1"> - <data name="tag" xsi:type="string">severity:S1</data> + <data name="tag" xsi:type="string">severity:S1,mftf_migrated:yes</data> <data name="configData" xsi:type="string">customer_max_login_failures_number,captcha_storefront_disable</data> <data name="initialCustomer/dataset" xsi:type="string">default</data> <data name="incorrectPassword" xsi:type="string">incorrect password</data> diff --git a/dev/tests/functional/tests/app/Magento/Tax/Test/TestCase/UpdateTaxRuleEntityTest.xml b/dev/tests/functional/tests/app/Magento/Tax/Test/TestCase/UpdateTaxRuleEntityTest.xml index 277bdbb7f5466..8b864b0eaba9b 100644 --- a/dev/tests/functional/tests/app/Magento/Tax/Test/TestCase/UpdateTaxRuleEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/Tax/Test/TestCase/UpdateTaxRuleEntityTest.xml @@ -48,7 +48,6 @@ <constraint name="Magento\Tax\Test\Constraint\AssertTaxRuleIsApplied" /> </variation> <variation name="UpdateTaxRuleEntityTestVariation4"> - <data name="tag" xsi:type="string">stable:no</data> <data name="initialTaxRule/dataset" xsi:type="string">tax_rule_with_custom_tax_classes</data> <data name="address/data/country_id" xsi:type="string">United States</data> <data name="address/data/region_id" xsi:type="string">Idaho</data> diff --git a/dev/tests/functional/utils/authenticate.php b/dev/tests/functional/utils/authenticate.php new file mode 100644 index 0000000000000..958b692cbd385 --- /dev/null +++ b/dev/tests/functional/utils/authenticate.php @@ -0,0 +1,34 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +/** + * Check if token passed in is a valid auth token. + * + * @param string $token + * @return bool + * + * phpcs:disable Squiz.Functions.GlobalFunction + */ +function authenticate($token) +{ + // phpcs:ignore Magento2.Security.IncludeFile + require_once __DIR__ . '/../../../../app/bootstrap.php'; + + // phpcs:ignore Magento2.Security.Superglobal + $magentoObjectManagerFactory = \Magento\Framework\App\Bootstrap::createObjectManagerFactory(BP, $_SERVER); + // phpcs:ignore Magento2.Security.Superglobal + $magentoObjectManager = $magentoObjectManagerFactory->create($_SERVER); + $tokenModel = $magentoObjectManager->get(\Magento\Integration\Model\Oauth\Token::class); + + $tokenPassedIn = $token; + // Token returned will be null if the token we passed in is invalid + $tokenFromMagento = $tokenModel->loadByToken($tokenPassedIn)->getToken(); + if (!empty($tokenFromMagento) && ($tokenFromMagento == $tokenPassedIn)) { + return true; + } else { + return false; + } +} diff --git a/dev/tests/functional/utils/command.php b/dev/tests/functional/utils/command.php index 99025dd1cffcc..9405f4ff7c4ca 100644 --- a/dev/tests/functional/utils/command.php +++ b/dev/tests/functional/utils/command.php @@ -3,7 +3,8 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - +// phpcs:ignore Magento2.Security.IncludeFile +include __DIR__ . '/authenticate.php'; // phpcs:ignore Magento2.Security.IncludeFile require_once __DIR__ . '/../../../../app/bootstrap.php'; @@ -11,18 +12,26 @@ use Symfony\Component\Console\Output\NullOutput; // phpcs:ignore Magento2.Security.Superglobal -if (isset($_GET['command'])) { - // phpcs:ignore Magento2.Security.Superglobal - $command = urldecode($_GET['command']); - // phpcs:ignore Magento2.Security.Superglobal - $magentoObjectManagerFactory = \Magento\Framework\App\Bootstrap::createObjectManagerFactory(BP, $_SERVER); +if (!empty($_POST['token']) && !empty($_POST['command'])) { // phpcs:ignore Magento2.Security.Superglobal - $magentoObjectManager = $magentoObjectManagerFactory->create($_SERVER); - $cli = $magentoObjectManager->create(\Magento\Framework\Console\Cli::class); - $input = new StringInput($command); - $input->setInteractive(false); - $output = new NullOutput(); - $cli->doRun($input, $output); + if (authenticate(urldecode($_POST['token']))) { + // phpcs:ignore Magento2.Security.Superglobal + $command = urldecode($_POST['command']); + // phpcs:ignore Magento2.Security.Superglobal + $magentoObjectManagerFactory = \Magento\Framework\App\Bootstrap::createObjectManagerFactory(BP, $_SERVER); + // phpcs:ignore Magento2.Security.Superglobal + $magentoObjectManager = $magentoObjectManagerFactory->create($_SERVER); + $cli = $magentoObjectManager->create(\Magento\Framework\Console\Cli::class); + // phpcs:ignore Magento2.Functions.DiscouragedFunction + $input = new StringInput(escapeshellcmd($command)); + $input->setInteractive(false); + $output = new NullOutput(); + $cli->doRun($input, $output); + } else { + // phpcs:ignore Magento2.Security.LanguageConstruct + echo "Command not unauthorized."; + } } else { - throw new \InvalidArgumentException("Command GET parameter is not set."); + // phpcs:ignore Magento2.Security.LanguageConstruct + echo "'token' or 'command' parameter is not set."; } diff --git a/dev/tests/functional/utils/deleteMagentoGeneratedCode.php b/dev/tests/functional/utils/deleteMagentoGeneratedCode.php index 17260bd1da635..bd4ed828202e1 100644 --- a/dev/tests/functional/utils/deleteMagentoGeneratedCode.php +++ b/dev/tests/functional/utils/deleteMagentoGeneratedCode.php @@ -3,6 +3,20 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +// phpcs:ignore Magento2.Security.IncludeFile +include __DIR__ . '/authenticate.php'; -// phpcs:ignore Magento2.Security.InsecureFunction -exec('rm -rf ../../../../generated/*'); +// phpcs:ignore Magento2.Security.Superglobal +if (!empty($_POST['token']) && !empty($_POST['path'])) { + // phpcs:ignore Magento2.Security.Superglobal + if (authenticate(urldecode($_POST['token']))) { + // phpcs:ignore Magento2.Security.InsecureFunction + exec('rm -rf ../../../../generated/*'); + } else { + // phpcs:ignore Magento2.Security.LanguageConstruct + echo "Command not unauthorized."; + } +} else { + // phpcs:ignore Magento2.Security.LanguageConstruct + echo "'token' parameter is not set."; +} diff --git a/dev/tests/functional/utils/export.php b/dev/tests/functional/utils/export.php index fa50bc729d0f6..df97c8db48406 100644 --- a/dev/tests/functional/utils/export.php +++ b/dev/tests/functional/utils/export.php @@ -3,32 +3,40 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +// phpcs:ignore Magento2.Security.IncludeFile +include __DIR__ . '/authenticate.php'; // phpcs:ignore Magento2.Security.Superglobal -if (!isset($_GET['template'])) { - // phpcs:ignore Magento2.Exceptions.DirectThrow - throw new \InvalidArgumentException('Argument "template" must be set.'); -} +if (!empty($_POST['token']) && !empty($_POST['template'])) { + // phpcs:ignore Magento2.Security.Superglobal + if (authenticate(urldecode($_POST['token']))) { + $varDir = '../../../../var/export/'; + // phpcs:ignore Magento2.Security.Superglobal + $template = urldecode($_POST['template']); + // phpcs:ignore Magento2.Functions.DiscouragedFunction + $fileList = scandir($varDir, SCANDIR_SORT_NONE); + $files = []; -$varDir = '../../../../var/export/'; -// phpcs:ignore Magento2.Security.Superglobal -$template = urldecode($_GET['template']); -// phpcs:ignore Magento2.Functions.DiscouragedFunction -$fileList = scandir($varDir, SCANDIR_SORT_NONE); -$files = []; + foreach ($fileList as $fileName) { + if (preg_match("`$template`", $fileName) === 1) { + $filePath = $varDir . $fileName; + $files[] = [ + // phpcs:ignore Magento2.Functions.DiscouragedFunction + 'content' => file_get_contents($filePath), + 'name' => $fileName, + // phpcs:ignore Magento2.Functions.DiscouragedFunction + 'date' => filectime($filePath), + ]; + } + } -foreach ($fileList as $fileName) { - if (preg_match("`$template`", $fileName) === 1) { - $filePath = $varDir . $fileName; - $files[] = [ - // phpcs:ignore Magento2.Functions.DiscouragedFunction - 'content' => file_get_contents($filePath), - 'name' => $fileName, - // phpcs:ignore Magento2.Functions.DiscouragedFunction - 'date' => filectime($filePath), - ]; + // phpcs:ignore Magento2.Security.LanguageConstruct, Magento2.Security.InsecureFunction + echo serialize($files); + } else { + // phpcs:ignore Magento2.Security.LanguageConstruct + echo "Command not unauthorized."; } +} else { + // phpcs:ignore Magento2.Security.LanguageConstruct + echo "'token' or 'template' parameter is not set."; } - -// phpcs:ignore Magento2.Security.LanguageConstruct, Magento2.Security.InsecureFunction -echo serialize($files); diff --git a/dev/tests/functional/utils/locales.php b/dev/tests/functional/utils/locales.php index 11e1e2b70fa50..40781ba8b68ec 100644 --- a/dev/tests/functional/utils/locales.php +++ b/dev/tests/functional/utils/locales.php @@ -3,20 +3,33 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +// phpcs:ignore Magento2.Security.IncludeFile +include __DIR__ . '/authenticate.php'; // phpcs:ignore Magento2.Security.Superglobal -if (isset($_GET['type']) && $_GET['type'] == 'deployed') { +if (!empty($_POST['token'])) { // phpcs:ignore Magento2.Security.Superglobal - $themePath = isset($_GET['theme_path']) ? $_GET['theme_path'] : 'adminhtml/Magento/backend'; - $directory = __DIR__ . '/../../../../pub/static/' . $themePath; - // phpcs:ignore Magento2.Functions.DiscouragedFunction - $locales = array_diff(scandir($directory), ['..', '.']); + if (authenticate(urldecode($_POST['token']))) { + // phpcs:ignore Magento2.Security.Superglobal + if ($_POST['type'] == 'deployed') { + // phpcs:ignore Magento2.Security.Superglobal + $themePath = isset($_POST['theme_path']) ? $_POST['theme_path'] : 'adminhtml/Magento/backend'; + $directory = __DIR__ . '/../../../../pub/static/' . $themePath; + // phpcs:ignore Magento2.Functions.DiscouragedFunction + $locales = array_diff(scandir($directory), ['..', '.']); + } else { + // phpcs:ignore Magento2.Security.IncludeFile + require_once __DIR__ . DIRECTORY_SEPARATOR . 'bootstrap.php'; + $localeConfig = $magentoObjectManager->create(\Magento\Framework\Locale\Config::class); + $locales = $localeConfig->getAllowedLocales(); + } + // phpcs:ignore Magento2.Security.LanguageConstruct + echo implode('|', $locales); + } else { + // phpcs:ignore Magento2.Security.LanguageConstruct + echo "Command not unauthorized."; + } } else { - // phpcs:ignore Magento2.Security.IncludeFile - require_once __DIR__ . DIRECTORY_SEPARATOR . 'bootstrap.php'; - $localeConfig = $magentoObjectManager->create(\Magento\Framework\Locale\Config::class); - $locales = $localeConfig->getAllowedLocales(); + // phpcs:ignore Magento2.Security.LanguageConstruct + echo "'token' parameter is not set."; } - -// phpcs:ignore Magento2.Security.LanguageConstruct -echo implode('|', $locales); diff --git a/dev/tests/functional/utils/log.php b/dev/tests/functional/utils/log.php index 30783ae8e1d28..c07f52575504f 100644 --- a/dev/tests/functional/utils/log.php +++ b/dev/tests/functional/utils/log.php @@ -3,21 +3,28 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - declare(strict_types=1); -// phpcs:ignore Magento2.Security.Superglobal -if (!isset($_GET['name'])) { - // phpcs:ignore Magento2.Exceptions.DirectThrow - throw new \InvalidArgumentException( - 'The name of log file is required for getting logs.' - ); -} +// phpcs:ignore Magento2.Security.IncludeFile +include __DIR__ . '/authenticate.php'; // phpcs:ignore Magento2.Security.Superglobal -$name = urldecode($_GET['name']); -if (preg_match('/\.\.(\\\|\/)/', $name)) { - throw new \InvalidArgumentException('Invalid log file name'); -} +if (!empty($_POST['token']) && !empty($_POST['name'])) { + // phpcs:ignore Magento2.Security.Superglobal + if (authenticate(urldecode($_POST['token']))) { + // phpcs:ignore Magento2.Security.Superglobal + $name = urldecode($_POST['name']); + if (preg_match('/\.\.(\\\|\/)/', $name)) { + // phpcs:ignore Magento2.Exceptions.DirectThrow + throw new \InvalidArgumentException('Invalid log file name'); + } -// phpcs:ignore Magento2.Security.InsecureFunction, Magento2.Functions.DiscouragedFunction, Magento2.Security.LanguageConstruct -echo serialize(file_get_contents('../../../../var/log' .'/' .$name)); + // phpcs:ignore Magento2.Security.InsecureFunction, Magento2.Functions.DiscouragedFunction, Magento2.Security.LanguageConstruct + echo serialize(file_get_contents('../../../../var/log' . '/' . $name)); + } else { + // phpcs:ignore Magento2.Security.LanguageConstruct + echo "Command not unauthorized."; + } +} else { + // phpcs:ignore Magento2.Security.LanguageConstruct + echo "'token' or 'name' parameter is not set."; +} diff --git a/dev/tests/functional/utils/pathChecker.php b/dev/tests/functional/utils/pathChecker.php index 217cf90af0a56..d4a59529fac44 100644 --- a/dev/tests/functional/utils/pathChecker.php +++ b/dev/tests/functional/utils/pathChecker.php @@ -3,20 +3,28 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +// phpcs:ignore Magento2.Security.IncludeFile +include __DIR__ . '/authenticate.php'; // phpcs:ignore Magento2.Security.Superglobal -if (isset($_GET['path'])) { +if (!empty($_POST['token']) && !empty($_POST['path'])) { // phpcs:ignore Magento2.Security.Superglobal - $path = urldecode($_GET['path']); - // phpcs:ignore Magento2.Functions.DiscouragedFunction - if (file_exists('../../../../' . $path)) { - // phpcs:ignore Magento2.Security.LanguageConstruct - echo 'path exists: true'; + if (authenticate(urldecode($_POST['token']))) { + // phpcs:ignore Magento2.Security.Superglobal + $path = urldecode($_POST['path']); + // phpcs:ignore Magento2.Functions.DiscouragedFunction + if (file_exists('../../../../' . $path)) { + // phpcs:ignore Magento2.Security.LanguageConstruct + echo 'path exists: true'; + } else { + // phpcs:ignore Magento2.Security.LanguageConstruct + echo 'path exists: false'; + } } else { // phpcs:ignore Magento2.Security.LanguageConstruct - echo 'path exists: false'; + echo "Command not unauthorized."; } } else { - // phpcs:ignore Magento2.Exceptions.DirectThrow - throw new \InvalidArgumentException("GET parameter 'path' is not set."); + // phpcs:ignore Magento2.Security.LanguageConstruct + echo "'token' or 'path' parameter is not set."; } diff --git a/dev/tests/functional/utils/website.php b/dev/tests/functional/utils/website.php index 720b4962aedd4..859b60785e49d 100644 --- a/dev/tests/functional/utils/website.php +++ b/dev/tests/functional/utils/website.php @@ -3,36 +3,45 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +// phpcs:ignore Magento2.Security.IncludeFile +include __DIR__ . '/authenticate.php'; // phpcs:ignore Magento2.Security.Superglobal -if (!isset($_GET['website_code'])) { - // phpcs:ignore Magento2.Exceptions.DirectThrow - throw new \Exception("website_code GET parameter is not set."); -} - -// phpcs:ignore Magento2.Security.Superglobal -$websiteCode = urldecode($_GET['website_code']); -$rootDir = '../../../../'; -$websiteDir = $rootDir . 'websites/' . $websiteCode . '/'; -// phpcs:ignore Magento2.Functions.DiscouragedFunction -$contents = file_get_contents($rootDir . 'index.php'); +if (!empty($_POST['token']) && !empty($_POST['website_code'])) { + // phpcs:ignore Magento2.Security.Superglobal + if (authenticate(urldecode($_POST['token']))) { + // phpcs:ignore Magento2.Security.Superglobal + $websiteCode = urldecode($_POST['website_code']); + $rootDir = '../../../../'; + $websiteDir = $rootDir . 'websites/' . $websiteCode . '/'; + // phpcs:ignore Magento2.Functions.DiscouragedFunction + $contents = file_get_contents($rootDir . 'index.php'); -$websiteParam = <<<EOD + $websiteParam = <<<EOD \$params = \$_SERVER; \$params[\Magento\Store\Model\StoreManager::PARAM_RUN_CODE] = '$websiteCode'; \$params[\Magento\Store\Model\StoreManager::PARAM_RUN_TYPE] = 'website'; EOD; -$pattern = '`(try {.*?)(\/app\/bootstrap.*?}\n)(.*?)\$_SERVER`mis'; -$replacement = "$1/../..$2\n$websiteParam$3\$params"; + $pattern = '`(try {.*?)(\/app\/bootstrap.*?}\n)(.*?)\$_SERVER`mis'; + $replacement = "$1/../..$2\n$websiteParam$3\$params"; -$contents = preg_replace($pattern, $replacement, $contents); + $contents = preg_replace($pattern, $replacement, $contents); -$old = umask(0); -// phpcs:ignore Magento2.Functions.DiscouragedFunction -mkdir($websiteDir, 0760, true); -umask($old); -// phpcs:ignore Magento2.Functions.DiscouragedFunction -copy($rootDir . '.htaccess', $websiteDir . '.htaccess'); -// phpcs:ignore Magento2.Functions.DiscouragedFunction -file_put_contents($websiteDir . 'index.php', $contents); + $old = umask(0); + // phpcs:ignore Magento2.Functions.DiscouragedFunction + mkdir($websiteDir, 0760, true); + umask($old); + + // phpcs:ignore Magento2.Functions.DiscouragedFunction + copy($rootDir . '.htaccess', $websiteDir . '.htaccess'); + // phpcs:ignore Magento2.Functions.DiscouragedFunction + file_put_contents($websiteDir . 'index.php', $contents); + } else { + // phpcs:ignore Magento2.Security.LanguageConstruct + echo "Command not unauthorized."; + } +} else { + // phpcs:ignore Magento2.Security.LanguageConstruct + echo "'token' or 'website_code' parameter is not set."; +} diff --git a/dev/tests/integration/.gitignore b/dev/tests/integration/.gitignore index 7f8540b3c7710..c4d6c1a77a9cc 100644 --- a/dev/tests/integration/.gitignore +++ b/dev/tests/integration/.gitignore @@ -2,3 +2,4 @@ !/etc/integration-tests-config.xml /var/ /etc/*.php +/framework/tests/unit/var/allure-results/ diff --git a/dev/tests/integration/framework/bootstrap.php b/dev/tests/integration/framework/bootstrap.php index 1cae393dc01c3..59fb1535d1884 100644 --- a/dev/tests/integration/framework/bootstrap.php +++ b/dev/tests/integration/framework/bootstrap.php @@ -5,9 +5,15 @@ */ use Magento\Framework\Autoload\AutoloaderRegistry; +/** + * phpcs:disable PSR1.Files.SideEffects + * phpcs:disable Squiz.Functions.GlobalFunction + * phpcs:disable Magento2.Security.IncludeFile + */ require_once __DIR__ . '/../../../../app/bootstrap.php'; require_once __DIR__ . '/autoload.php'; +// phpcs:ignore Magento2.Functions.DiscouragedFunction $testsBaseDir = dirname(__DIR__); $fixtureBaseDir = $testsBaseDir. '/testsuite'; @@ -19,15 +25,15 @@ define('INTEGRATION_TESTS_DIR', $testsBaseDir); } -$testFrameworkDir = __DIR__; -require_once 'deployTestModules.php'; - try { setCustomErrorHandler(); /* Bootstrap the application */ $settings = new \Magento\TestFramework\Bootstrap\Settings($testsBaseDir, get_defined_constants()); + $testFrameworkDir = __DIR__; + require_once 'deployTestModules.php'; + if ($settings->get('TESTS_EXTRA_VERBOSE_LOG')) { $filesystem = new \Magento\Framework\Filesystem\Driver\File(); $exceptionHandler = new \Magento\Framework\Logger\Handler\Exception($filesystem); @@ -44,14 +50,16 @@ } $installConfigFile = $settings->getAsConfigFile('TESTS_INSTALL_CONFIG_FILE'); + // phpcs:ignore Magento2.Functions.DiscouragedFunction if (!file_exists($installConfigFile)) { $installConfigFile .= '.dist'; } $globalConfigFile = $settings->getAsConfigFile('TESTS_GLOBAL_CONFIG_FILE'); + // phpcs:ignore Magento2.Functions.DiscouragedFunction if (!file_exists($globalConfigFile)) { $globalConfigFile .= '.dist'; } - $sandboxUniqueId = md5(sha1_file($installConfigFile)); + $sandboxUniqueId = hash('sha256', sha1_file($installConfigFile)); $installDir = TESTS_TEMP_DIR . "/sandbox-{$settings->get('TESTS_PARALLEL_THREAD', 0)}-{$sandboxUniqueId}"; $application = new \Magento\TestFramework\Application( $shell, @@ -99,7 +107,9 @@ /* Unset declared global variables to release the PHPUnit from maintaining their values between tests */ unset($testsBaseDir, $logWriter, $settings, $shell, $application, $bootstrap); } catch (\Exception $e) { + // phpcs:ignore Magento2.Security.LanguageConstruct.DirectOutput echo $e . PHP_EOL; + // phpcs:ignore Magento2.Security.LanguageConstruct.ExitUsage exit(1); } diff --git a/dev/tests/integration/framework/deployTestModules.php b/dev/tests/integration/framework/deployTestModules.php index 4c894d80f9800..34e1aa9b4e23f 100644 --- a/dev/tests/integration/framework/deployTestModules.php +++ b/dev/tests/integration/framework/deployTestModules.php @@ -5,7 +5,10 @@ */ /** - * @var $testFrameworkDir string - Must be defined in parent script. + * phpcs:disable PSR1.Files.SideEffects + * phpcs:disable Squiz.Functions.GlobalFunction + * @var string $testFrameworkDir - Must be defined in parent script. + * @var \Magento\TestFramework\Bootstrap\Settings $settings - Must be defined in parent script. */ /** Copy test modules to app/code/Magento to make them visible for Magento instance */ @@ -20,10 +23,14 @@ $source = $file->getPathname(); $relativePath = substr($source, strlen($pathToCommittedTestModules)); $destination = $pathToInstalledMagentoInstanceModules . $relativePath; + // phpcs:ignore Magento2.Functions.DiscouragedFunction $targetDir = dirname($destination); + // phpcs:ignore Magento2.Functions.DiscouragedFunction if (!is_dir($targetDir)) { + // phpcs:ignore Magento2.Functions.DiscouragedFunction mkdir($targetDir, 0755, true); } + // phpcs:ignore Magento2.Functions.DiscouragedFunction copy($source, $destination); } } @@ -31,10 +38,42 @@ // Register the modules under '_files/' $pathPattern = $pathToInstalledMagentoInstanceModules . '/TestModule*/registration.php'; +// phpcs:ignore Magento2.Functions.DiscouragedFunction $files = glob($pathPattern, GLOB_NOSORT); if ($files === false) { throw new \RuntimeException('glob() returned error while searching in \'' . $pathPattern . '\''); } foreach ($files as $file) { + // phpcs:ignore Magento2.Security.IncludeFile include $file; } + +if ((int)$settings->get('TESTS_PARALLEL_RUN') !== 1) { + // Only delete modules if we are not using parallel executions + // phpcs:ignore Magento2.Functions.DiscouragedFunction + register_shutdown_function( + 'deleteTestModules', + $pathToCommittedTestModules, + $pathToInstalledMagentoInstanceModules + ); +} + +/** + * Delete all test module directories which have been created before + * + * @param string $pathToCommittedTestModules + * @param string $pathToInstalledMagentoInstanceModules + */ +function deleteTestModules($pathToCommittedTestModules, $pathToInstalledMagentoInstanceModules) +{ + $filesystem = new \Symfony\Component\Filesystem\Filesystem(); + $iterator = new DirectoryIterator($pathToCommittedTestModules); + /** @var SplFileInfo $file */ + foreach ($iterator as $file) { + if ($file->isDir() && !in_array($file->getFilename(), ['.', '..'])) { + $targetDirPath = $pathToInstalledMagentoInstanceModules . '/' . $file->getFilename(); + $filesystem->remove($targetDirPath); + } + } + unset($iterator, $file); +} diff --git a/dev/tests/integration/phpunit.xml.dist b/dev/tests/integration/phpunit.xml.dist index 815abde6ac26b..858de7a9873e7 100644 --- a/dev/tests/integration/phpunit.xml.dist +++ b/dev/tests/integration/phpunit.xml.dist @@ -74,6 +74,7 @@ <!--<const name="MONGODB_DATABASE_NAME" value="magento_integration_tests"/>--> <!-- Connection parameters for RabbitMQ tests --> <!--<const name="RABBITMQ_MANAGEMENT_PORT" value="15672"/>--> + <!--<const name="TESTS_PARALLEL_RUN" value="1"/>--> </php> <!-- Test listeners --> <listeners> diff --git a/dev/tests/integration/testsuite/Magento/AdvancedPricingImportExport/Model/Export/AdvancedPricingTest.php b/dev/tests/integration/testsuite/Magento/AdvancedPricingImportExport/Model/Export/AdvancedPricingTest.php index 481cc629c6777..b4f38d207c1f4 100644 --- a/dev/tests/integration/testsuite/Magento/AdvancedPricingImportExport/Model/Export/AdvancedPricingTest.php +++ b/dev/tests/integration/testsuite/Magento/AdvancedPricingImportExport/Model/Export/AdvancedPricingTest.php @@ -6,11 +6,24 @@ namespace Magento\AdvancedPricingImportExport\Model\Export; use Magento\Framework\App\Filesystem\DirectoryList; +use Magento\TestFramework\Indexer\TestCase; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\Framework\Filesystem; +use Magento\AdvancedPricingImportExport\Model\Export\AdvancedPricing as ExportAdvancedPricing; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product; +use Magento\ImportExport\Model\Export\Adapter\Csv as ExportAdapterCsv; +use Magento\AdvancedPricingImportExport\Model\Import\AdvancedPricing as ImportAdvancedPricing; +use Magento\ImportExport\Model\Import\Source\Csv as ImportSourceCsv; +use Magento\ImportExport\Model\Import; -class AdvancedPricingTest extends \Magento\TestFramework\Indexer\TestCase +/** + * Advanced pricing test + */ +class AdvancedPricingTest extends TestCase { /** - * @var \Magento\AdvancedPricingImportExport\Model\Export\AdvancedPricing + * @var ExportAdvancedPricing */ protected $model; @@ -20,13 +33,15 @@ class AdvancedPricingTest extends \Magento\TestFramework\Indexer\TestCase protected $objectManager; /** - * @var \Magento\Framework\Filesystem + * @var Filesystem */ protected $fileSystem; + // @codingStandardsIgnoreStart public static function setUpBeforeClass() { - $db = \Magento\TestFramework\Helper\Bootstrap::getInstance()->getBootstrap() + $db = Bootstrap::getInstance() + ->getBootstrap() ->getApplication() ->getDbInstance(); if (!$db->isDbDumpExists()) { @@ -36,16 +51,15 @@ public static function setUpBeforeClass() parent::setUpBeforeClass(); } + // @codingStandardsIgnoreEnd protected function setUp() { parent::setUp(); - $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - $this->fileSystem = $this->objectManager->get(\Magento\Framework\Filesystem::class); - $this->model = $this->objectManager->create( - \Magento\AdvancedPricingImportExport\Model\Export\AdvancedPricing::class - ); + $this->objectManager = Bootstrap::getObjectManager(); + $this->fileSystem = $this->objectManager->get(Filesystem::class); + $this->model = $this->objectManager->create(ExportAdvancedPricing::class); } /** @@ -56,16 +70,15 @@ protected function setUp() */ public function testExport() { - $productRepository = $this->objectManager->create( - \Magento\Catalog\Api\ProductRepositoryInterface::class - ); + $productRepository = $this->objectManager->create(ProductRepositoryInterface::class); $index = 0; $ids = []; $origPricingData = []; $skus = ['simple']; while (isset($skus[$index])) { - $ids[$index] = $productRepository->get($skus[$index])->getId(); - $origPricingData[$index] = $this->objectManager->create(\Magento\Catalog\Model\Product::class) + $ids[$index] = $productRepository->get($skus[$index]) + ->getId(); + $origPricingData[$index] = $this->objectManager->create(Product::class) ->load($ids[$index]) ->getTierPrices(); $index++; @@ -80,7 +93,7 @@ public function testExport() while ($index > 0) { $index--; - $newPricingData = $this->objectManager->create(\Magento\Catalog\Model\Product::class) + $newPricingData = $this->objectManager->create(Product::class) ->load($ids[$index]) ->getTierPrices(); $this->assertEquals(count($origPricingData[$index]), count($newPricingData)); @@ -97,7 +110,7 @@ public function testExport() private function assertDiscountTypes($exportContent) { $this->assertContains( - '2.0000,8.0000,Fixed', + '2.0000,8.000000,Fixed', $exportContent ); $this->assertContains( @@ -115,16 +128,15 @@ private function assertDiscountTypes($exportContent) */ public function testExportMultipleWebsites() { - $productRepository = $this->objectManager->create( - \Magento\Catalog\Api\ProductRepositoryInterface::class - ); + $productRepository = $this->objectManager->create(ProductRepositoryInterface::class); $index = 0; $ids = []; $origPricingData = []; $skus = ['AdvancedPricingSimple 1', 'AdvancedPricingSimple 2']; while (isset($skus[$index])) { - $ids[$index] = $productRepository->get($skus[$index])->getId(); - $origPricingData[$index] = $this->objectManager->create(\Magento\Catalog\Model\Product::class) + $ids[$index] = $productRepository->get($skus[$index]) + ->getId(); + $origPricingData[$index] = $this->objectManager->create(Product::class) ->load($ids[$index]) ->getTierPrices(); $index++; @@ -141,7 +153,7 @@ public function testExportMultipleWebsites() while ($index > 0) { $index--; - $newPricingData = $this->objectManager->create(\Magento\Catalog\Model\Product::class) + $newPricingData = $this->objectManager->create(Product::class) ->load($ids[$index]) ->getTierPrices(); $this->assertEquals(count($origPricingData[$index]), count($newPricingData)); @@ -156,10 +168,11 @@ public function testExportMultipleWebsites() private function exportData($csvFile) { $this->model->setWriter( - \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( - \Magento\ImportExport\Model\Export\Adapter\Csv::class, - ['fileSystem' => $this->fileSystem, 'destination' => $csvFile] - ) + Bootstrap::getObjectManager() + ->create( + ExportAdapterCsv::class, + ['fileSystem' => $this->fileSystem, 'destination' => $csvFile] + ) ); $exportContent = $this->model->export(); $this->assertNotEmpty($exportContent); @@ -172,13 +185,11 @@ private function exportData($csvFile) */ private function importData($csvFile) { - /** @var \Magento\AdvancedPricingImportExport\Model\Import\AdvancedPricing $importModel */ - $importModel = $this->objectManager->create( - \Magento\AdvancedPricingImportExport\Model\Import\AdvancedPricing::class - ); + /** @var ImportAdvancedPricing $importModel */ + $importModel = $this->objectManager->create(ImportAdvancedPricing::class); $directory = $this->fileSystem->getDirectoryWrite(DirectoryList::VAR_DIR); $source = $this->objectManager->create( - \Magento\ImportExport\Model\Import\Source\Csv::class, + ImportSourceCsv::class, [ 'file' => $csvFile, 'directory' => $directory @@ -186,7 +197,7 @@ private function importData($csvFile) ); $errors = $importModel->setParameters( [ - 'behavior' => \Magento\ImportExport\Model\Import::BEHAVIOR_APPEND, + 'behavior' => Import::BEHAVIOR_APPEND, 'entity' => 'advanced_pricing' ] )->setSource( diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/Model/Resolver/Customer/PlaceOrderWithAuthorizeNetTest.php b/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/Model/Resolver/Customer/PlaceOrderWithAuthorizeNetTest.php new file mode 100644 index 0000000000000..1f9d5574ad3c3 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/Model/Resolver/Customer/PlaceOrderWithAuthorizeNetTest.php @@ -0,0 +1,183 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\AuthorizenetGraphQl\Model\Resolver\Customer; + +use Magento\Framework\App\Request\Http; +use Magento\Framework\Serialize\SerializerInterface; +use Magento\GraphQl\Controller\GraphQl; +use Magento\GraphQl\Quote\GetMaskedQuoteIdByReservedOrderId; +use Magento\Integration\Api\CustomerTokenServiceInterface; +use Magento\Framework\Webapi\Request; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\Framework\HTTP\ZendClient; +use Magento\Framework\HTTP\ZendClientFactory; +use Magento\TestFramework\ObjectManager; +use PHPUnit\Framework\MockObject\Builder\InvocationMocker; +use Magento\Payment\Gateway\Data\PaymentDataObjectFactory; +use PHPUnit\Framework\MockObject\MockObject; +use Magento\Quote\Model\Quote\PaymentFactory; +use PHPUnit\Framework\TestCase; +use Zend_Http_Response; + +/** + * Tests end to end Place Order process for customer via authorizeNet + * + * @magentoAppArea graphql + * @magentoDbIsolation disabled + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class PlaceOrderWithAuthorizeNetTest extends TestCase +{ + const CONTENT_TYPE = 'application/json'; + + /** @var ObjectManager */ + private $objectManager; + + /** @var GetMaskedQuoteIdByReservedOrderId */ + private $getMaskedQuoteIdByReservedOrderId; + + /** @var SerializerInterface */ + private $jsonSerializer; + + /** @var Http */ + private $request; + + /** @var ZendClient|MockObject|InvocationMocker */ + private $clientMock; + + /** @var CustomerTokenServiceInterface */ + private $customerTokenService; + + /** @var Zend_Http_Response */ + protected $responseMock; + + /** @var PaymentFactory */ + private $paymentFactory; + + protected function setUp() : void + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->jsonSerializer = $this->objectManager->get(SerializerInterface::class); + $this->request = $this->objectManager->get(Http::class); + $this->getMaskedQuoteIdByReservedOrderId = $this->objectManager->get(GetMaskedQuoteIdByReservedOrderId::class); + $this->customerTokenService = $this->objectManager->get(CustomerTokenServiceInterface::class); + $this->clientMock = $this->createMock(ZendClient::class); + $this->responseMock = $this->createMock(Zend_Http_Response::class); + $this->clientMock->method('request') + ->willReturn($this->responseMock); + $this->clientMock->method('setUri') + ->with('https://apitest.authorize.net/xml/v1/request.api'); + $clientFactoryMock = $this->createMock(ZendClientFactory::class); + $clientFactoryMock->method('create') + ->willReturn($this->clientMock); + /** @var PaymentDataObjectFactory $paymentFactory */ + $this->paymentFactory = $this->objectManager->get(PaymentDataObjectFactory::class); + $this->objectManager->addSharedInstance($clientFactoryMock, ZendClientFactory::class); + } + + /** + * @magentoConfigFixture default_store payment/authorizenet_acceptjs/active 1 + * @magentoConfigFixture default_store payment/authorizenet_acceptjs/environment sandbox + * @magentoConfigFixture default_store payment/authorizenet_acceptjs/login someusername + * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_key somepassword + * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_signature_key abc + * @magentoDataFixture Magento/Sales/_files/default_rollback.php + * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoDataFixture Magento/AuthorizenetGraphQl/_files/simple_product_authorizenet.php + * @magentoDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoDataFixture Magento/AuthorizenetGraphQl/_files/set_new_shipping_address_authorizenet.php + * @magentoDataFixture Magento/AuthorizenetGraphQl/_files/set_new_billing_address_authorizenet.php + * @magentoDataFixture Magento/AuthorizenetGraphQl/_files/add_simple_products_authorizenet.php + * @magentoDataFixture Magento/GraphQl/Quote/_files/set_flatrate_shipping_method.php + */ + public function testDispatchToPlaceOrderWithRegisteredCustomer(): void + { + $paymentMethod = 'authorizenet_acceptjs'; + $cartId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + $query + = <<<QUERY + mutation { + setPaymentMethodOnCart(input: { + cart_id: "$cartId" + payment_method: { + code: "$paymentMethod" + additional_data: + {authorizenet_acceptjs: + {opaque_data_descriptor: "mydescriptor", + opaque_data_value: "myvalue", + cc_last_4: 1111}} + } + }) { + cart { + selected_payment_method { + code + } + } + } + placeOrder(input: {cart_id: "$cartId"}) { + order { + order_id + } + } +} +QUERY; + $postData = [ + 'query' => $query, + 'variables' => null, + 'operationName' => null + ]; + $this->request->setPathInfo('/graphql'); + $this->request->setMethod('POST'); + $this->request->setContent($this->jsonSerializer->serialize($postData)); + $customerToken = $this->customerTokenService->createCustomerAccessToken('customer@example.com', 'password'); + $bearerCustomerToken = 'Bearer ' . $customerToken; + $webApiRequest = $this->objectManager->get(Request::class); + $webApiRequest->getHeaders()->addHeaderLine('Content-Type', 'application/json') + ->addHeaderLine('Accept', 'application/json') + ->addHeaderLine('Authorization', $bearerCustomerToken); + $this->request->setHeaders($webApiRequest->getHeaders()); + $graphql = $this->objectManager->get(\Magento\GraphQl\Controller\GraphQl::class); + + // phpcs:ignore Magento2.Security.IncludeFile + $expectedRequest = include __DIR__ . '/../../../_files/request_authorize_customer.php'; + // phpcs:ignore Magento2.Security.IncludeFile + $authorizeResponse = include __DIR__ . '/../../../_files/response_authorize.php'; + + $this->clientMock->method('setRawData') + ->with(json_encode($expectedRequest), 'application/json'); + + $this->responseMock->method('getBody')->willReturn(json_encode($authorizeResponse)); + + $response = $graphql->dispatch($this->request); + $responseData = $this->jsonSerializer->unserialize($response->getContent()); + + $this->assertArrayNotHasKey('errors', $responseData, 'Response has errors'); + $this->assertTrue( + isset($responseData['data']['setPaymentMethodOnCart']['cart']['selected_payment_method']['code']) + ); + $this->assertEquals( + $paymentMethod, + $responseData['data']['setPaymentMethodOnCart']['cart']['selected_payment_method']['code'] + ); + + $this->assertTrue( + isset($responseData['data']['placeOrder']['order']['order_id']) + ); + + $this->assertEquals( + 'test_quote', + $responseData['data']['placeOrder']['order']['order_id'] + ); + } + + protected function tearDown() + { + $this->objectManager->removeSharedInstance(ZendClientFactory::class); + parent::tearDown(); + } +} diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/Model/Resolver/Customer/SetAuthorizeNetPaymentMethodOnCartTest.php b/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/Model/Resolver/Customer/SetAuthorizeNetPaymentMethodOnCartTest.php new file mode 100644 index 0000000000000..4c84bbf0b58aa --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/Model/Resolver/Customer/SetAuthorizeNetPaymentMethodOnCartTest.php @@ -0,0 +1,115 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\AuthorizenetGraphQl\Model\Resolver\Customer; + +use Magento\Framework\App\Request\Http; +use Magento\Framework\Serialize\SerializerInterface; +use Magento\Framework\Webapi\Request; +use Magento\GraphQl\Quote\GetMaskedQuoteIdByReservedOrderId; +use Magento\Integration\Api\CustomerTokenServiceInterface; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Tests SetPaymentMethod mutation for customer via authorizeNet payment + * + * @magentoAppArea graphql + * @magentoDbIsolation disabled + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class SetAuthorizeNetPaymentMethodOnCartTest extends TestCase +{ + const CONTENT_TYPE = 'application/json'; + + /** @var \Magento\Framework\ObjectManagerInterface */ + private $objectManager; + + /** @var GetMaskedQuoteIdByReservedOrderId */ + private $getMaskedQuoteIdByReservedOrderId; + + /** @var SerializerInterface */ + private $jsonSerializer; + + /** @var CustomerTokenServiceInterface */ + private $customerTokenService; + + /** @var Http */ + private $request; + + protected function setUp() : void + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->jsonSerializer = $this->objectManager->get(SerializerInterface::class); + $this->request = $this->objectManager->get(Http::class); + $this->getMaskedQuoteIdByReservedOrderId = $this->objectManager->get(GetMaskedQuoteIdByReservedOrderId::class); + $this->customerTokenService = $this->objectManager->get(CustomerTokenServiceInterface::class); + } + + /** + * @magentoConfigFixture default_store payment/authorizenet_acceptjs/active 1 + * @magentoConfigFixture default_store payment/authorizenet_acceptjs/environment sandbox + * @magentoConfigFixture default_store payment/authorizenet_acceptjs/login someusername + * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_key somepassword + * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_signature_key abc + * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * @magentoDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php + */ + public function testDispatchToSetPaymentMethodWithAuthorizenet(): void + { + $methodCode = 'authorizenet_acceptjs'; + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + $query + = <<<QUERY + mutation { + setPaymentMethodOnCart(input: { + cart_id: "$maskedQuoteId" + payment_method: { + code: "$methodCode" + additional_data: + {authorizenet_acceptjs: + {opaque_data_descriptor: "COMMON.ACCEPT.INAPP.PAYMENT", + opaque_data_value: "abx", + cc_last_4: 1111}} + } + }) { + cart { + selected_payment_method { + code + } + } + } +} +QUERY; + $postData = [ + 'query' => $query, + 'variables' => null, + 'operationName' => null + ]; + $this->request->setPathInfo('/graphql'); + $this->request->setMethod('POST'); + $this->request->setContent($this->jsonSerializer->serialize($postData)); + $customerToken = $this->customerTokenService->createCustomerAccessToken('customer@example.com', 'password'); + $bearerCustomerToken = 'Bearer ' . $customerToken; + $contentType ='application/json'; + $webApiRequest = $this->objectManager->get(Request::class); + $webApiRequest->getHeaders()->addHeaderLine('Content-Type', $contentType) + ->addHeaderLine('Accept', $contentType) + ->addHeaderLine('Authorization', $bearerCustomerToken); + $this->request->setHeaders($webApiRequest->getHeaders()); + $graphql = $this->objectManager->get(\Magento\GraphQl\Controller\GraphQl::class); + $response = $graphql->dispatch($this->request); + $output = $this->jsonSerializer->unserialize($response->getContent()); + $this->assertArrayNotHasKey('errors', $output, 'Response has errors'); + $this->assertArrayHasKey('setPaymentMethodOnCart', $output['data']); + $selectedPaymentMethod = $output['data']['setPaymentMethodOnCart']['cart']['selected_payment_method']; + $this->assertEquals($methodCode, $selectedPaymentMethod['code']); + } +} diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/Model/Resolver/Guest/PlaceOrderWithAuthorizeNetTest.php b/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/Model/Resolver/Guest/PlaceOrderWithAuthorizeNetTest.php new file mode 100644 index 0000000000000..1be7069b90cda --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/Model/Resolver/Guest/PlaceOrderWithAuthorizeNetTest.php @@ -0,0 +1,175 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\AuthorizenetGraphQl\Model\Resolver\Guest; + +use Magento\Framework\App\Request\Http; +use Magento\Framework\Serialize\SerializerInterface; +use Magento\GraphQl\Controller\GraphQl; +use Magento\GraphQl\Quote\GetMaskedQuoteIdByReservedOrderId; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\Framework\HTTP\ZendClient; +use Magento\Framework\HTTP\ZendClientFactory; +use Magento\TestFramework\ObjectManager; +use PHPUnit\Framework\MockObject\Builder\InvocationMocker; +use Magento\Payment\Gateway\Data\PaymentDataObjectFactory; +use PHPUnit\Framework\MockObject\MockObject; +use Magento\Quote\Model\Quote\PaymentFactory; +use PHPUnit\Framework\TestCase; +use Zend_Http_Response; + +/** + * Tests end to end Place Order process for non logged in customer using authorizeNet payment + * + * @magentoAppArea graphql + * @magentoDbIsolation disabled + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class PlaceOrderWithAuthorizeNetTest extends TestCase +{ + const CONTENT_TYPE = 'application/json'; + + /** @var ObjectManager */ + private $objectManager; + + /** @var GetMaskedQuoteIdByReservedOrderId */ + private $getMaskedQuoteIdByReservedOrderId; + + /** @var GraphQl */ + private $graphql; + + /** @var SerializerInterface */ + private $jsonSerializer; + + /** @var Http */ + private $request; + + /** @var ZendClient|MockObject|InvocationMocker */ + private $clientMock; + + /** @var Zend_Http_Response */ + protected $responseMock; + + /** @var PaymentFactory */ + private $paymentFactory; + + protected function setUp() : void + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->graphql = $this->objectManager->get(\Magento\GraphQl\Controller\GraphQl::class); + $this->jsonSerializer = $this->objectManager->get(SerializerInterface::class); + $this->request = $this->objectManager->get(Http::class); + $this->getMaskedQuoteIdByReservedOrderId = $this->objectManager->get(GetMaskedQuoteIdByReservedOrderId::class); + $this->clientMock = $this->createMock(ZendClient::class); + $this->responseMock = $this->createMock(Zend_Http_Response::class); + $this->clientMock->method('request') + ->willReturn($this->responseMock); + $this->clientMock->method('setUri') + ->with('https://apitest.authorize.net/xml/v1/request.api'); + $clientFactoryMock = $this->createMock(ZendClientFactory::class); + $clientFactoryMock->method('create') + ->willReturn($this->clientMock); + /** @var PaymentDataObjectFactory $paymentFactory */ + $this->paymentFactory = $this->objectManager->get(PaymentDataObjectFactory::class); + $this->objectManager->addSharedInstance($clientFactoryMock, ZendClientFactory::class); + } + + /** + * @magentoConfigFixture default_store payment/authorizenet_acceptjs/active 1 + * @magentoConfigFixture default_store payment/authorizenet_acceptjs/environment sandbox + * @magentoConfigFixture default_store payment/authorizenet_acceptjs/login someusername + * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_key somepassword + * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_signature_key abc + * @magentoDataFixture Magento/Sales/_files/default_rollback.php + * @magentoDataFixture Magento/AuthorizenetGraphQl/_files/simple_product_authorizenet.php + * @magentoDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php + * @magentoDataFixture Magento/GraphQl/Quote/_files/guest/set_guest_email.php + * @magentoDataFixture Magento/AuthorizenetGraphQl/_files/set_new_shipping_address_authorizenet.php + * @magentoDataFixture Magento/AuthorizenetGraphQl/_files/set_new_billing_address_authorizenet.php + * @magentoDataFixture Magento/AuthorizenetGraphQl/_files/add_simple_products_authorizenet.php + * @magentoDataFixture Magento/GraphQl/Quote/_files/set_flatrate_shipping_method.php + */ + public function testDispatchToPlaceAnOrderWithAuthorizenet(): void + { + $paymentMethod = 'authorizenet_acceptjs'; + $cartId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + $query + = <<<QUERY + mutation { + setPaymentMethodOnCart(input: { + cart_id: "$cartId" + payment_method: { + code: "$paymentMethod" + additional_data: + {authorizenet_acceptjs: + {opaque_data_descriptor: "mydescriptor", + opaque_data_value: "myvalue", + cc_last_4: 1111}} + } + }) { + cart { + selected_payment_method { + code + } + } + } + placeOrder(input: {cart_id: "$cartId"}) { + order { + order_id + } + } +} +QUERY; + $postData = [ + 'query' => $query, + 'variables' => null, + 'operationName' => null + ]; + $this->request->setPathInfo('/graphql'); + $this->request->setMethod('POST'); + $this->request->setContent(json_encode($postData)); + $headers = $this->objectManager->create(\Zend\Http\Headers::class) + ->addHeaders(['Content-Type' => 'application/json']); + $this->request->setHeaders($headers); + // phpcs:ignore Magento2.Security.IncludeFile + $expectedRequest = include __DIR__ . '/../../../_files/request_authorize.php'; + // phpcs:ignore Magento2.Security.IncludeFile + $authorizeResponse = include __DIR__ . '/../../../_files/response_authorize.php'; + + $this->clientMock->method('setRawData') + ->with(json_encode($expectedRequest), 'application/json'); + + $this->responseMock->method('getBody')->willReturn(json_encode($authorizeResponse)); + + $response = $this->graphql->dispatch($this->request); + $responseData = $this->jsonSerializer->unserialize($response->getContent()); + + $this->assertArrayNotHasKey('errors', $responseData, 'Response has errors'); + $this->assertTrue( + isset($responseData['data']['setPaymentMethodOnCart']['cart']['selected_payment_method']['code']) + ); + $this->assertEquals( + $paymentMethod, + $responseData['data']['setPaymentMethodOnCart']['cart']['selected_payment_method']['code'] + ); + + $this->assertTrue( + isset($responseData['data']['placeOrder']['order']['order_id']) + ); + + $this->assertEquals( + 'test_quote', + $responseData['data']['placeOrder']['order']['order_id'] + ); + } + + protected function tearDown() + { + $this->objectManager->removeSharedInstance(ZendClientFactory::class); + parent::tearDown(); + } +} diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/Model/Resolver/Guest/SetAuthorizeNetPaymentMethodOnCartTest.php b/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/Model/Resolver/Guest/SetAuthorizeNetPaymentMethodOnCartTest.php new file mode 100644 index 0000000000000..ba63861086b1d --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/Model/Resolver/Guest/SetAuthorizeNetPaymentMethodOnCartTest.php @@ -0,0 +1,107 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\AuthorizenetGraphQl\Model\Resolver\Guest; + +use Magento\Framework\App\Request\Http; +use Magento\Framework\Serialize\SerializerInterface; +use Magento\GraphQl\Controller\GraphQl; +use Magento\GraphQl\Quote\GetMaskedQuoteIdByReservedOrderId; +use Magento\TestFramework\Helper\Bootstrap; +use PHPUnit\Framework\TestCase; + +/** + * Tests SetPaymentMethod mutation for guest via authorizeNet payment + * + * @magentoAppArea graphql + * @magentoDbIsolation disabled + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class SetAuthorizeNetPaymentMethodOnCartTest extends TestCase +{ + const CONTENT_TYPE = 'application/json'; + + /** @var \Magento\Framework\ObjectManagerInterface */ + private $objectManager; + + /** @var GetMaskedQuoteIdByReservedOrderId */ + private $getMaskedQuoteIdByReservedOrderId; + + /** @var GraphQl */ + private $graphql; + + /** @var SerializerInterface */ + private $jsonSerializer; + + /** @var Http */ + private $request; + + protected function setUp() : void + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->graphql = $this->objectManager->get(\Magento\GraphQl\Controller\GraphQl::class); + $this->jsonSerializer = $this->objectManager->get(SerializerInterface::class); + $this->request = $this->objectManager->get(Http::class); + $this->getMaskedQuoteIdByReservedOrderId = $this->objectManager->get(GetMaskedQuoteIdByReservedOrderId::class); + } + + /** + * @magentoConfigFixture default_store payment/authorizenet_acceptjs/active 1 + * @magentoConfigFixture default_store payment/authorizenet_acceptjs/environment sandbox + * @magentoConfigFixture default_store payment/authorizenet_acceptjs/login someusername + * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_key somepassword + * @magentoConfigFixture default_store payment/authorizenet_acceptjs/trans_signature_key abc + * @magentoDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php + * @magentoDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * @magentoDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php + */ + public function testDispatchToSetPaymentMethodWithAuthorizenet(): void + { + $methodCode = 'authorizenet_acceptjs'; + $maskedQuoteId = $this->getMaskedQuoteIdByReservedOrderId->execute('test_quote'); + $query + = <<<QUERY + mutation { + setPaymentMethodOnCart(input: { + cart_id: "$maskedQuoteId" + payment_method: { + code: "$methodCode" + additional_data: + {authorizenet_acceptjs: + {opaque_data_descriptor: "COMMON.ACCEPT.INAPP.PAYMENT", + opaque_data_value: "abx", + cc_last_4: 1111}} + } + }) { + cart { + selected_payment_method { + code + } + } + } +} +QUERY; + $postData = [ + 'query' => $query, + 'variables' => null, + 'operationName' => null + ]; + $this->request->setPathInfo('/graphql'); + $this->request->setMethod('POST'); + $this->request->setContent(json_encode($postData)); + $headers = $this->objectManager->create(\Zend\Http\Headers::class) + ->addHeaders(['Content-Type' => 'application/json']); + $this->request->setHeaders($headers); + $response = $this->graphql->dispatch($this->request); + $output = $this->jsonSerializer->unserialize($response->getContent()); + $this->assertArrayNotHasKey('errors', $output, 'Response has errors'); + $this->assertArrayHasKey('setPaymentMethodOnCart', $output['data']); + $selectedPaymentMethod = $output['data']['setPaymentMethodOnCart']['cart']['selected_payment_method']; + $this->assertEquals($methodCode, $selectedPaymentMethod['code']); + } +} diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/add_simple_products_authorizenet.php b/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/add_simple_products_authorizenet.php new file mode 100644 index 0000000000000..3646e864ab49e --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/add_simple_products_authorizenet.php @@ -0,0 +1,28 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\Quote\Model\QuoteFactory; +use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; +use Magento\TestFramework\Helper\Bootstrap; + +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = Bootstrap::getObjectManager()->get(ProductRepositoryInterface::class); +/** @var QuoteFactory $quoteFactory */ +$quoteFactory = Bootstrap::getObjectManager()->get(QuoteFactory::class); +/** @var QuoteResource $quoteResource */ +$quoteResource = Bootstrap::getObjectManager()->get(QuoteResource::class); +/** @var CartRepositoryInterface $cartRepository */ +$cartRepository = Bootstrap::getObjectManager()->get(CartRepositoryInterface::class); + +$product = $productRepository->get('simple_product'); + +$quote = $quoteFactory->create(); +$quoteResource->load($quote, 'test_quote', 'reserved_order_id'); +$quote->addProduct($product, 4); +$cartRepository->save($quote); diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/request_authorize.php b/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/request_authorize.php new file mode 100644 index 0000000000000..c91c8081736c4 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/request_authorize.php @@ -0,0 +1,65 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +return [ + 'createTransactionRequest' => [ + 'merchantAuthentication' =>[ + 'name' => 'someusername', + 'transactionKey' => 'somepassword', + ], + 'transactionRequest' => [ + 'transactionType' => 'authOnlyTransaction', + 'amount' => '100.00', + 'payment' => [ + 'opaqueData' => [ + 'dataDescriptor' => 'mydescriptor', + 'dataValue' => 'myvalue', + ], + ], + 'solution' => [ + 'id' => 'AAA102993', + ], + 'order' => [ + 'invoiceNumber' => 'test_quote', + ], + 'poNumber' => null, + 'customer' => [ + 'id' => null, + 'email' => 'guest@example.com', + ], + 'billTo' => [ + 'firstName' => 'firstname', + 'lastName' => 'lastname', + 'company' => '', + 'address' => 'street', + 'city' => 'Los Angeles', + 'state' => 'CA', + 'zip' => '11111', + 'country' => 'US', + ], + 'shipTo' => [ + 'firstName' => 'John', + 'lastName' => 'Doe', + 'company' => '', + 'address' => '6161 West Centinela Avenue', + 'city' => 'Los Angeles', + 'state' => 'CA', + 'zip' => '11111', + 'country' => 'US', + ], + 'userFields' => [ + 'userField' => [ + [ + 'name' => 'transactionType', + 'value' => 'authOnlyTransaction', + ], + ], + ], + ], + ] +]; diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/request_authorize_customer.php b/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/request_authorize_customer.php new file mode 100644 index 0000000000000..0ef173009bd6c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/request_authorize_customer.php @@ -0,0 +1,65 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +return [ + 'createTransactionRequest' => [ + 'merchantAuthentication' =>[ + 'name' => 'someusername', + 'transactionKey' => 'somepassword', + ], + 'transactionRequest' => [ + 'transactionType' => 'authOnlyTransaction', + 'amount' => '100.00', + 'payment' => [ + 'opaqueData' => [ + 'dataDescriptor' => 'mydescriptor', + 'dataValue' => 'myvalue', + ], + ], + 'solution' => [ + 'id' => 'AAA102993', + ], + 'order' => [ + 'invoiceNumber' => 'test_quote', + ], + 'poNumber' => null, + 'customer' => [ + 'id' => '1', + 'email' => 'customer@example.com', + ], + 'billTo' => [ + 'firstName' => 'firstname', + 'lastName' => 'lastname', + 'company' => '', + 'address' => 'street', + 'city' => 'Los Angeles', + 'state' => 'CA', + 'zip' => '11111', + 'country' => 'US', + ], + 'shipTo' => [ + 'firstName' => 'John', + 'lastName' => 'Doe', + 'company' => '', + 'address' => '6161 West Centinela Avenue', + 'city' => 'Los Angeles', + 'state' => 'CA', + 'zip' => '11111', + 'country' => 'US', + ], + 'userFields' => [ + 'userField' => [ + [ + 'name' => 'transactionType', + 'value' => 'authOnlyTransaction', + ], + ], + ], + ], + ] +]; diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/response_authorize.php b/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/response_authorize.php new file mode 100644 index 0000000000000..f80495137ca29 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/response_authorize.php @@ -0,0 +1,47 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +return [ + 'transactionResponse' => [ + 'responseCode' => '1', + 'authCode' => 'abc123', + 'avsResultCode' => 'Y', + 'cvvResultCode' => 'P', + 'cavvResultCode' => '2', + 'transId' => '123456', + 'refTransID' => '', + 'transHash' => 'foobar', + 'testRequest' => '0', + 'accountNumber' => 'XXXX1111', + 'accountType' => 'Visa', + 'messages' => [ + [ + 'code' => '1', + 'description' => 'This transaction has been approved.' + ] + ], + 'userFields' => [ + [ + 'name' => 'transactionType', + 'value' => 'authOnlyTransaction' + ] + ], + 'transHashSha2' => 'CD1E57FB1B5C876FDBD536CB16F8BBBA687580EDD78DD881C7F14DC4467C32BF6C' + . '808620FBD59E5977DF19460B98CCFC0DA0D90755992C0D611CABB8E2BA52B0', + 'SupplementalDataQualificationIndicator' => 0 + ], + 'messages' => [ + 'resultCode' => 'Ok', + 'message' => [ + [ + 'code' => 'I00001', + 'text' => 'Successful.' + ] + ] + ] +]; diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/set_new_billing_address_authorizenet.php b/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/set_new_billing_address_authorizenet.php new file mode 100644 index 0000000000000..4f045c550cd37 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/set_new_billing_address_authorizenet.php @@ -0,0 +1,44 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Framework\Api\DataObjectHelper; +use Magento\Quote\Api\Data\AddressInterface; +use Magento\Quote\Api\Data\AddressInterfaceFactory; +use Magento\Quote\Api\BillingAddressManagementInterface; +use Magento\Quote\Model\QuoteFactory; +use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; + +use Magento\TestFramework\Helper\Bootstrap; + +/** @var QuoteFactory $quoteFactory */ +$quoteFactory = Bootstrap::getObjectManager()->get(QuoteFactory::class); +/** @var QuoteResource $quoteResource */ +$quoteResource = Bootstrap::getObjectManager()->get(QuoteResource::class); +/** @var AddressInterfaceFactory $quoteAddressFactory */ +$quoteAddressFactory = Bootstrap::getObjectManager()->get(AddressInterfaceFactory::class); +/** @var DataObjectHelper $dataObjectHelper */ +$dataObjectHelper = Bootstrap::getObjectManager()->get(DataObjectHelper::class); +/** @var BillingAddressManagementInterface $billingAddressManagement */ +$billingAddressManagement = Bootstrap::getObjectManager()->get(BillingAddressManagementInterface::class); + +$quoteAddressData = [ + AddressInterface::KEY_TELEPHONE => 11111111, + AddressInterface::KEY_POSTCODE => 11111, + AddressInterface::KEY_COUNTRY_ID => 'US', + AddressInterface::KEY_CITY => 'Los Angeles', + AddressInterface::KEY_COMPANY => '', + AddressInterface::KEY_STREET => 'street', + AddressInterface::KEY_LASTNAME => 'lastname', + AddressInterface::KEY_FIRSTNAME => 'firstname', + AddressInterface::KEY_REGION_ID => 12, +]; +$quoteAddress = $quoteAddressFactory->create(); +$dataObjectHelper->populateWithArray($quoteAddress, $quoteAddressData, AddressInterfaceFactory::class); + +$quote = $quoteFactory->create(); +$quoteResource->load($quote, 'test_quote', 'reserved_order_id'); +$billingAddressManagement->assign($quote->getId(), $quoteAddress); diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/set_new_shipping_address_authorizenet.php b/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/set_new_shipping_address_authorizenet.php new file mode 100644 index 0000000000000..8837a3cb2397c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/set_new_shipping_address_authorizenet.php @@ -0,0 +1,43 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Framework\Api\DataObjectHelper; +use Magento\Quote\Api\Data\AddressInterface; +use Magento\Quote\Api\Data\AddressInterfaceFactory; +use Magento\Quote\Model\QuoteFactory; +use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; +use Magento\Quote\Model\ShippingAddressManagementInterface; +use Magento\TestFramework\Helper\Bootstrap; + +/** @var QuoteFactory $quoteFactory */ +$quoteFactory = Bootstrap::getObjectManager()->get(QuoteFactory::class); +/** @var QuoteResource $quoteResource */ +$quoteResource = Bootstrap::getObjectManager()->get(QuoteResource::class); +/** @var AddressInterfaceFactory $quoteAddressFactory */ +$quoteAddressFactory = Bootstrap::getObjectManager()->get(AddressInterfaceFactory::class); +/** @var DataObjectHelper $dataObjectHelper */ +$dataObjectHelper = Bootstrap::getObjectManager()->get(DataObjectHelper::class); +/** @var ShippingAddressManagementInterface $shippingAddressManagement */ +$shippingAddressManagement = Bootstrap::getObjectManager()->get(ShippingAddressManagementInterface::class); + +$quoteAddressData = [ + AddressInterface::KEY_TELEPHONE => 3468676, + AddressInterface::KEY_POSTCODE => '11111', + AddressInterface::KEY_COUNTRY_ID => 'US', + AddressInterface::KEY_CITY => 'Los Angeles', + AddressInterface::KEY_COMPANY => '', + AddressInterface::KEY_STREET => '6161 West Centinela Avenue', + AddressInterface::KEY_LASTNAME => 'Doe', + AddressInterface::KEY_FIRSTNAME => 'John', + AddressInterface::KEY_REGION_ID => 12, +]; +$quoteAddress = $quoteAddressFactory->create(); +$dataObjectHelper->populateWithArray($quoteAddress, $quoteAddressData, AddressInterfaceFactory::class); + +$quote = $quoteFactory->create(); +$quoteResource->load($quote, 'test_quote', 'reserved_order_id'); +$shippingAddressManagement->assign($quote->getId(), $quoteAddress); diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/simple_product_authorizenet.php b/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/simple_product_authorizenet.php new file mode 100644 index 0000000000000..50ec950853711 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/simple_product_authorizenet.php @@ -0,0 +1,47 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Api\Data\ProductInterfaceFactory; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Type; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Framework\Api\DataObjectHelper; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +/** @var ProductInterfaceFactory $productFactory */ +$productFactory = $objectManager->get(ProductInterfaceFactory::class); +/** @var DataObjectHelper $dataObjectHelper */ +$dataObjectHelper = Bootstrap::getObjectManager()->get(DataObjectHelper::class); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); + +$product = $productFactory->create(); +$productData = [ + ProductInterface::TYPE_ID => Type::TYPE_SIMPLE, + ProductInterface::ATTRIBUTE_SET_ID => 4, + ProductInterface::SKU => 'simple_product', + ProductInterface::NAME => 'Simple Product', + ProductInterface::PRICE => 20, + ProductInterface::VISIBILITY => Visibility::VISIBILITY_BOTH, + ProductInterface::STATUS => Status::STATUS_ENABLED, +]; +$dataObjectHelper->populateWithArray($product, $productData, ProductInterface::class); +/** Out of interface */ +$product + ->setWebsiteIds([1]) + ->setStockData( + [ + 'qty' => 85.5, + 'is_in_stock' => true, + 'manage_stock' => true, + 'is_qty_decimal' => true + ] + ); +$productRepository->save($product); diff --git a/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/simple_product_authorizenet_rollback.php b/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/simple_product_authorizenet_rollback.php new file mode 100644 index 0000000000000..6b37585f8224f --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/AuthorizenetGraphQl/_files/simple_product_authorizenet_rollback.php @@ -0,0 +1,8 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +// phpcs:ignore Magento2.Security.IncludeFile +require __DIR__ . '/../../GraphQl/Catalog/_files/simple_product_rollback.php'; diff --git a/dev/tests/integration/testsuite/Magento/Backend/_files/allowed_countries_fr.php b/dev/tests/integration/testsuite/Magento/Backend/_files/allowed_countries_fr.php new file mode 100644 index 0000000000000..4bce8d95dafa6 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Backend/_files/allowed_countries_fr.php @@ -0,0 +1,16 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types = 1); + +use Magento\Framework\App\Config\ConfigResource\ConfigInterface; +use Magento\Framework\App\Config\ReinitableConfigInterface; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +/** @var ConfigInterface $config */ +$config = $objectManager->get(ConfigInterface::class); +$config->saveConfig('general/country/allow', 'FR'); +$objectManager->get(ReinitableConfigInterface::class)->reinit(); diff --git a/dev/tests/integration/testsuite/Magento/Backend/_files/allowed_countries_fr_rollback.php b/dev/tests/integration/testsuite/Magento/Backend/_files/allowed_countries_fr_rollback.php new file mode 100644 index 0000000000000..711d985786329 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Backend/_files/allowed_countries_fr_rollback.php @@ -0,0 +1,16 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types = 1); + +use Magento\Framework\App\Config\ConfigResource\ConfigInterface; +use Magento\Framework\App\Config\ReinitableConfigInterface; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +/** @var ConfigInterface $config */ +$config = $objectManager->get(ConfigInterface::class); +$config->deleteConfig('general/country/allow'); +$objectManager->get(ReinitableConfigInterface::class)->reinit(); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/OptionTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/OptionTest.php index c9d5ed732df2e..bfb49686447c0 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/OptionTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/OptionTest.php @@ -8,6 +8,8 @@ use Magento\Catalog\Api\Data\ProductCustomOptionInterfaceFactory; use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Store\Model\Store; +use Magento\Store\Model\StoreManagerInterface; use Magento\TestFramework\Helper\Bootstrap; /** @@ -31,6 +33,11 @@ class OptionTest extends \PHPUnit\Framework\TestCase */ private $customOptionFactory; + /** + * @var StoreManagerInterface + */ + private $storeManager; + /** * @inheritdoc */ @@ -38,6 +45,7 @@ protected function setUp() { $this->productRepository = Bootstrap::getObjectManager()->create(ProductRepositoryInterface::class); $this->customOptionFactory = Bootstrap::getObjectManager()->create(ProductCustomOptionInterfaceFactory::class); + $this->storeManager = Bootstrap::getObjectManager()->get(StoreManagerInterface::class); } /** @@ -103,4 +111,39 @@ private function createFileOption(string $rawExtensions) return $this->customOptionFactory->create(['data' => $data]); } + + /** + * Test to save option price by store + * + * @magentoDataFixture Magento/Catalog/_files/product_with_options.php + * @magentoDataFixture Magento/Store/_files/core_second_third_fixturestore.php + * @magentoConfigFixture default_store catalog/price/scope 1 + * @magentoConfigFixture secondstore_store catalog/price/scope 1 + */ + public function testSaveOptionPriceByStore() + { + $secondWebsitePrice = 22.0; + $defaultStoreId = $this->storeManager->getStore()->getId(); + $secondStoreId = $this->storeManager->getStore('secondstore')->getId(); + + /** @var \Magento\Catalog\Model\Product $product */ + $product = $this->productRepository->get('simple'); + $option = $product->getOptions()[0]; + $defaultPrice = $option->getPrice(); + + $option->setPrice($secondWebsitePrice); + $product->setStoreId($secondStoreId); + // set Current store='secondstore' to correctly save product options for 'secondstore' + $this->storeManager->setCurrentStore($secondStoreId); + $this->productRepository->save($product); + $this->storeManager->setCurrentStore($defaultStoreId); + + $product = $this->productRepository->get('simple', false, Store::DEFAULT_STORE_ID, true); + $option = $product->getOptions()[0]; + $this->assertEquals($defaultPrice, $option->getPrice(), 'Price value by default store is wrong'); + + $product = $this->productRepository->get('simple', false, $secondStoreId, true); + $option = $product->getOptions()[0]; + $this->assertEquals($secondWebsitePrice, $option->getPrice(), 'Price value by store_id=1 is wrong'); + } } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/UrlTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/UrlTest.php index f9075a58c39ef..4cf059d4bf692 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/UrlTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/Product/UrlTest.php @@ -42,6 +42,46 @@ public function testGetUrlInStore() $this->assertStringEndsWith('simple-product.html', $this->_model->getUrlInStore($product)); } + /** + * @magentoDataFixture Magento/Store/_files/second_store.php + * @magentoConfigFixture default_store web/unsecure/base_url http://sample.com/ + * @magentoConfigFixture default_store web/unsecure/base_link_url http://sample.com/ + * @magentoConfigFixture fixturestore_store web/unsecure/base_url http://sample-second.com/ + * @magentoConfigFixture fixturestore_store web/unsecure/base_link_url http://sample-second.com/ + * @magentoDataFixture Magento/Catalog/_files/product_simple_multistore.php + * @dataProvider getUrlsWithSecondStoreProvider + * @magentoAppArea adminhtml + */ + public function testGetUrlInStoreWithSecondStore($storeCode, $expectedProductUrl) + { + $repository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + \Magento\Catalog\Model\ProductRepository::class + ); + /** @var \Magento\Store\Model\Store $store */ + $store = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() + ->create(\Magento\Store\Model\Store::class); + $store->load($storeCode, 'code'); + /** @var \Magento\Store\Model\Store $store */ + + $product = $repository->get('simple'); + + $this->assertEquals( + $expectedProductUrl, + $this->_model->getUrlInStore($product, ['_scope' => $store->getId(), '_nosid' => true]) + ); + } + + /** + * @return array + */ + public function getUrlsWithSecondStoreProvider() + { + return [ + 'case1' => ['fixturestore', 'http://sample-second.com/index.php/simple-product-one.html'], + 'case2' => ['default', 'http://sample.com/index.php/simple-product-one.html'] + ]; + } + public function testGetProductUrl() { $repository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( diff --git a/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/CollectionTest.php b/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/CollectionTest.php index 904af7f334080..4cc6265a992fa 100644 --- a/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/CollectionTest.php +++ b/dev/tests/integration/testsuite/Magento/Catalog/Model/ResourceModel/Product/CollectionTest.php @@ -5,6 +5,9 @@ */ namespace Magento\Catalog\Model\ResourceModel\Product; +/** + * Collection test + */ class CollectionTest extends \PHPUnit\Framework\TestCase { /** @@ -181,6 +184,7 @@ public function testJoinTable() $productTable = $this->collection->getTable('catalog_product_entity'); $urlRewriteTable = $this->collection->getTable('url_rewrite'); + // phpcs:ignore $expected = 'SELECT `e`.*, `alias`.`request_path` FROM `' . $productTable . '` AS `e`' . ' LEFT JOIN `' . $urlRewriteTable . '` AS `alias` ON (alias.entity_id =e.entity_id)' . ' AND (alias.entity_type = \'product\')'; @@ -198,4 +202,17 @@ public function testAddAttributeToFilterAffectsGetSize(): void $this->collection->addAttributeToFilter('sku', 'Product1'); $this->assertEquals(1, $this->collection->getSize()); } + + /** + * Add tier price attribute filter to collection + * + * @magentoDataFixture Magento/Catalog/Model/ResourceModel/_files/few_simple_products.php + * @magentoDataFixture Magento/Catalog/Model/ResourceModel/_files/product_simple.php + */ + public function testAddAttributeTierPriceToFilter(): void + { + $this->assertEquals(11, $this->collection->getSize()); + $this->collection->addAttributeToFilter('tier_price', ['gt' => 0]); + $this->assertEquals(1, $this->collection->getSize()); + } } diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_non_latin_url_key.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_non_latin_url_key.php new file mode 100644 index 0000000000000..23fd8d7fe324e --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_non_latin_url_key.php @@ -0,0 +1,63 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Type; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Framework\ObjectManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; + +$stockDataConfig = [ + 'use_config_manage_stock' => 1, + 'qty' => 100, + 'is_qty_decimal' => 0, + 'is_in_stock' => 1 +]; + +/** @var ObjectManagerInterface $objectManager */ +$objectManager = Bootstrap::getObjectManager(); + +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->create(ProductRepositoryInterface::class); + +/** @var ProductInterface $product */ +$product = $objectManager->create(ProductInterface::class); +$product->setTypeId(Type::TYPE_SIMPLE) + ->setAttributeSetId(4) + ->setWebsiteIds([1]) + ->setName('Чудовий продукт без Url Key') + ->setSku('ukrainian-without-url-key') + ->setPrice(10) + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setCategoryIds([2]) + ->setStockData($stockDataConfig); +try { + $productRepository->save($product); +} catch (\Exception $e) { + // problems during save +}; + +/** @var ProductInterface $product */ +$product = $objectManager->create(ProductInterface::class); +$product->setTypeId(Type::TYPE_SIMPLE) + ->setAttributeSetId(4) + ->setWebsiteIds([1]) + ->setName('Надзвичайний продукт з Url Key') + ->setSku('ukrainian-with-url-key') + ->setPrice(10) + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setCategoryIds([2]) + ->setStockData($stockDataConfig) + ->setUrlKey('надзвичайний продукт на кожен день'); +try { + $productRepository->save($product); +} catch (\Exception $e) { + // problems during save +}; diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_non_latin_url_key_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_non_latin_url_key_rollback.php new file mode 100644 index 0000000000000..d4592430c0e94 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple_with_non_latin_url_key_rollback.php @@ -0,0 +1,37 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Registry; +use Magento\TestFramework\Helper\Bootstrap; + +Bootstrap::getInstance()->getInstance()->reinitialize(); + +/** @var Registry $registry */ +$registry = Bootstrap::getObjectManager()->get(Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = Bootstrap::getObjectManager() + ->get(ProductRepositoryInterface::class); + +$productSkus = [ + 'ukrainian-with-url-key', + 'ukrainian-without-url-key', +]; +try { + foreach ($productSkus as $sku) { + $product = $productRepository->get($sku, false, null, true); + $productRepository->delete($product); + } +} catch (NoSuchEntityException $e) { + // nothing to delete +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_without_options_with_stock_data.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_without_options_with_stock_data.php new file mode 100644 index 0000000000000..268d5bf792de4 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_without_options_with_stock_data.php @@ -0,0 +1,30 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +/** @var $product \Magento\Catalog\Model\Product */ +$product = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Catalog\Model\Product::class); +$product->setTypeId('simple') + ->setId(1) + ->setAttributeSetId(4) + ->setWebsiteIds([1]) + ->setName('Simple Product Without Custom Options') + ->setSku('simple') + ->setPrice(10) + ->setMetaTitle('meta title') + ->setMetaKeyword('meta keyword') + ->setMetaDescription('meta description') + ->setVisibility(\Magento\Catalog\Model\Product\Visibility::VISIBILITY_BOTH) + ->setStatus(\Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED) + ->setQty(100) + ->setStockData( + [ + 'use_config_manage_stock' => 1, + 'qty' => 100, + 'is_qty_decimal' => 0, + 'is_in_stock' => 1, + ] + )->save(); diff --git a/dev/tests/integration/testsuite/Magento/Catalog/_files/product_without_options_with_stock_data_rollback.php b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_without_options_with_stock_data_rollback.php new file mode 100644 index 0000000000000..41d06e5f25526 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Catalog/_files/product_without_options_with_stock_data_rollback.php @@ -0,0 +1,26 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +/** @var \Magento\Framework\Registry $registry */ +$registry = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(\Magento\Framework\Registry::class); + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +$repository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + \Magento\Catalog\Model\ProductRepository::class +); +try { + $product = $repository->get('simple', false, null, true); + $product->delete(); +// phpcs:ignore Magento2.CodeAnalysis.EmptyBlock +} catch (\Magento\Framework\Exception\NoSuchEntityException $e) { + //Entity already deleted +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php index 67446960e15dc..41cd85e6ec2f6 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/ProductTest.php @@ -25,11 +25,13 @@ use Magento\ImportExport\Model\Import; use Magento\Store\Model\Store; use Psr\Log\LoggerInterface; +use Magento\Framework\Exception\NoSuchEntityException; /** * Class ProductTest * @magentoAppIsolation enabled * @magentoDbIsolation enabled + * @magentoAppArea adminhtml * @magentoDataFixtureBeforeTransaction Magento/Catalog/_files/enable_reindex_schedule.php * @magentoDataFixtureBeforeTransaction Magento/Catalog/_files/enable_catalog_product_reindex_schedule.php * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -78,9 +80,32 @@ protected function setUp() \Magento\CatalogImportExport\Model\Import\Product::class, ['logger' => $this->logger] ); + $this->importedProducts = []; + parent::setUp(); } + protected function tearDown() + { + /* We rollback here the products created during the Import because they were + created during test execution and we do not have the rollback for them */ + /** @var ProductRepositoryInterface $productRepository */ + $productRepository = $this->objectManager->create(ProductRepositoryInterface::class); + foreach ($this->importedProducts as $productSku) { + try { + $product = $productRepository->get($productSku, false, null, true); + $productRepository->delete($product); + } catch (NoSuchEntityException $e) { + // nothing to delete + } + } + } + + /** + * @var array + */ + private $importedProducts; + /** * Options for assertion * @@ -279,7 +304,10 @@ public function testStockState() * @param string $importFile * @param string $sku * @param int $expectedOptionsQty + * @throws \Magento\Framework\Exception\LocalizedException + * @throws \Magento\Framework\Exception\NoSuchEntityException * @magentoAppIsolation enabled + * * @return void */ @@ -1359,6 +1387,7 @@ protected function loadCategoryByName($categoryName) * @dataProvider validateUrlKeysDataProvider * @param $importFile string * @param $expectedErrors array + * @throws \Magento\Framework\Exception\LocalizedException */ public function testValidateUrlKeys($importFile, $expectedErrors) { @@ -1560,9 +1589,9 @@ public function testExistingProductWithUrlKeys() public function testAddUpdateProductWithInvalidUrlKeys() : void { $products = [ - 'simple1' => 'cuvee-merlot-cabernet-igp-pays-d-oc-frankrijk', + 'simple1' => 'cuvée merlot-cabernet igp pays d\'oc frankrijk', 'simple2' => 'normal-url', - 'simple3' => 'some-wrong-url' + 'simple3' => 'some!wrong\'url' ]; $filesystem = \Magento\TestFramework\Helper\Bootstrap::getObjectManager() ->create(\Magento\Framework\Filesystem::class); @@ -1651,6 +1680,52 @@ public function testImportWithoutUrlKeys() } } + /** + * @magentoDataFixture Magento/Catalog/_files/product_simple_with_non_latin_url_key.php + * @magentoDbIsolation disabled + * @magentoAppIsolation enabled + * @return void + * @throws \Magento\Framework\Exception\LocalizedException + */ + public function testImportWithNonLatinUrlKeys() + { + $productsCreatedByFixture = [ + 'ukrainian-with-url-key' => 'nove-im-ja-pislja-importu-scho-stane-url-key', + 'ukrainian-without-url-key' => 'новий url key після імпорту', + ]; + $productsImportedByCsv = [ + 'imported-ukrainian-with-url-key' => 'імпортований продукт', + 'imported-ukrainian-without-url-key' => 'importovanij-produkt-bez-url-key', + ]; + $productSkuMap = array_merge($productsCreatedByFixture, $productsImportedByCsv); + $this->importedProducts = array_keys($productsImportedByCsv); + + $filesystem = $this->objectManager->create(\Magento\Framework\Filesystem::class); + $directory = $filesystem->getDirectoryWrite(DirectoryList::ROOT); + $source = $this->objectManager->create( + \Magento\ImportExport\Model\Import\Source\Csv::class, + [ + 'file' => __DIR__ . '/_files/products_to_import_with_non_latin_url_keys.csv', + 'directory' => $directory, + ] + ); + + $errors = $this->_model->setParameters( + ['behavior' => \Magento\ImportExport\Model\Import::BEHAVIOR_ADD_UPDATE, 'entity' => 'catalog_product'] + ) + ->setSource($source) + ->validateData(); + + $this->assertEquals($errors->getErrorsCount(), 0); + $this->_model->importData(); + + /** @var ProductRepositoryInterface $productRepository */ + $productRepository = $this->objectManager->create(ProductRepositoryInterface::class); + foreach ($productSkuMap as $productSku => $productUrlKey) { + $this->assertEquals($productUrlKey, $productRepository->get($productSku)->getUrlKey()); + } + } + /** * Make sure the absence of a url_key column in the csv file won't erase the url key of the existing products. * To reach the goal we need to not send the name column, as the url key is generated from it. diff --git a/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_with_non_latin_url_keys.csv b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_with_non_latin_url_keys.csv new file mode 100644 index 0000000000000..41c6eaa254e19 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogImportExport/Model/Import/_files/products_to_import_with_non_latin_url_keys.csv @@ -0,0 +1,5 @@ +sku,product_type,store_view_code,name,price,attribute_set_code,url_key +imported-ukrainian-with-url-key,simple,,"Імпортований продукт з Url Key",50,Default,"імпортований продукт" +imported-ukrainian-without-url-key,simple,,"Імпортований продукт без Url Key",55,Default, +ukrainian-without-url-key,simple,,"Чудовий продукт без Url Key",55,Default,"новий url key після імпорту" +ukrainian-with-url-key,simple,,"Нове ім'я після імпорту що стане url key",55,Default, diff --git a/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Search/AttributeSearchWeightTest.php b/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Search/AttributeSearchWeightTest.php new file mode 100644 index 0000000000000..775210669abd8 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogSearch/Model/Search/AttributeSearchWeightTest.php @@ -0,0 +1,179 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +declare(strict_types=1); + +namespace Magento\CatalogSearch\Model\Search; + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\ResourceModel\Eav\Attribute; +use Magento\Eav\Api\AttributeRepositoryInterface; +use Magento\Elasticsearch\SearchAdapter\ConnectionManager; +use Magento\Elasticsearch6\Model\Client\Elasticsearch as ElasticsearchClient; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Exception\StateException; +use Magento\Framework\Search\Request\Builder; +use Magento\Framework\Search\Request\Config as RequestConfig; +use Magento\Framework\Search\Response\QueryResponse; +use Magento\Framework\Search\SearchEngineInterface; +use Magento\Indexer\Model\Indexer; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Helper\CacheCleaner; +use Magento\TestFramework\ObjectManager; +use PHPUnit\Framework\TestCase; + +/** + * Test for name over sku search weight of product attributes + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @magentoAppIsolation enabled + * @magentoDbIsolation enabled + */ +class AttributeSearchWeightTest extends TestCase +{ + + /** @var $objectManager ObjectManager */ + private $objectManager; + + /** + * @var ConnectionManager + */ + private $connectionManager; + + /** + * @var ElasticsearchClient + */ + private $client; + + /** + * @var ProductRepositoryInterface + */ + private $productRepository; + + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + $this->connectionManager = $this->objectManager->create(ConnectionManager::class); + $this->client = $this->connectionManager->getConnection(); + $this->productRepository = $this->objectManager->create(ProductRepositoryInterface::class); + } + + /** + * @param string $attributeName + * @param int $searchWeight + * @throws NoSuchEntityException + * @throws StateException + */ + private function setAttributeSearchWeight(string $attributeName, int $searchWeight) + { + /** @var AttributeRepositoryInterface $attributeRepository */ + $attributeRepository = $this->objectManager->create(AttributeRepositoryInterface::class); + + /** @var Attribute $attribute */ + $attribute = $attributeRepository->get('catalog_product', $attributeName); + + if ($attribute) { + $attribute->setSearchWeight($searchWeight); + $attributeRepository->save($attribute); + } + } + + /** + * @throws \Throwable + */ + private function reindex() + { + CacheCleaner::cleanAll(); + + /** @var Indexer $indexer */ + $indexer = $this->objectManager->create(Indexer::class); + $indexer->load('catalogsearch_fulltext'); + $indexer->reindexAll(); + } + + /** + * @param string $query + * @return array + * @throws NoSuchEntityException + */ + private function findProducts(string $query): array + { + $config = $this->objectManager->create(RequestConfig::class); + + /** @var Builder $requestBuilder */ + $requestBuilder = $this->objectManager->create( + Builder::class, + ['config' => $config] + ); + $requestBuilder->bind('search_term', $query); + $requestBuilder->setRequestName('quick_search_container'); + + /** @var QueryResponse $searchResult */ + $searchResults = $this->objectManager->create(SearchEngineInterface::class) + ->search($requestBuilder->create()); + + $products = []; + foreach ($searchResults as $searchResult) { + $products [] = $this->productRepository->getById($searchResult->getId()); + } + + return $products; + } + + /** + * @dataProvider skuOverNameAttributeSearchWeightDataProvider + * @magentoConfigFixture default/catalog/search/engine elasticsearch6 + * @magentoConfigFixture current_store catalog/search/elasticsearch_index_prefix composite_product_search + * @magentoDataFixture Magento/CatalogSearch/_files/products_for_sku_search_weight_score.php + * @param string $searchQuery + * @param int $skuSearchWeight + * @param int $nameSearchWeight + * @param string $firstMatchProductName + * @param string $secondMatchProductName + * @throws NoSuchEntityException + * @throws \Throwable + */ + public function testSkuOverNameAttributeSearchWeight( + string $searchQuery, + int $skuSearchWeight, + int $nameSearchWeight, + string $firstMatchProductName, + string $secondMatchProductName + ) { + $this->setAttributeSearchWeight('sku', $skuSearchWeight); + $this->setAttributeSearchWeight('name', $nameSearchWeight); + $this->reindex(); + + /** @var Product $products [] */ + $products = $this->findProducts($searchQuery); + + $this->assertCount( + 2, + $products, + 'Expected to find 2 products, found ' . count($products) . '.' + ); + + $this->assertEquals( + $firstMatchProductName, + $products[0]->getData('name'), + 'Products order is not as expected.' + ); + $this->assertEquals( + $secondMatchProductName, + $products[1]->getData('name'), + 'Products order is not as expected.' + ); + } + + public function skuOverNameAttributeSearchWeightDataProvider(): array + { + return [ + ['1-2-3-4', 10, 5, 'test', '1-2-3-4'], + ['1-2-3-4', 5, 10, '1-2-3-4', 'test'], + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/CatalogSearch/_files/products_for_sku_search_weight_score.php b/dev/tests/integration/testsuite/Magento/CatalogSearch/_files/products_for_sku_search_weight_score.php new file mode 100644 index 0000000000000..96d5c256dc727 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/CatalogSearch/_files/products_for_sku_search_weight_score.php @@ -0,0 +1,54 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Type; +use Magento\Catalog\Model\Product\Visibility; +use Magento\TestFramework\Helper\Bootstrap; + +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = Bootstrap::getObjectManager()->create(ProductRepositoryInterface::class); +$product = Bootstrap::getObjectManager()->create(Product::class); +$product->setTypeId(Type::TYPE_SIMPLE) + ->setAttributeSetId(4) + ->setWebsiteIds([1]) + ->setName('1-2-3-4') + ->setSku('testsku') + ->setPrice(10) + ->setTaxClassId(0) + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setStockData( + [ + 'use_config_manage_stock' => 1, + 'qty' => 100, + 'is_qty_decimal' => 0, + 'is_in_stock' => 1, + ] + ); +$productRepository->save($product); + +$product = Bootstrap::getObjectManager()->create(Product::class); +$product->setTypeId(Type::TYPE_SIMPLE) + ->setAttributeSetId(4) + ->setWebsiteIds([1]) + ->setName('test') + ->setSku('1-2-3-4') + ->setPrice(10) + ->setTaxClassId(0) + ->setVisibility(Visibility::VISIBILITY_BOTH) + ->setStatus(Status::STATUS_ENABLED) + ->setStockData( + [ + 'use_config_manage_stock' => 1, + 'qty' => 100, + 'is_qty_decimal' => 0, + 'is_in_stock' => 1, + ] + ); +$productRepository->save($product); diff --git a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/CategoryUrlRewriteGeneratorTest.php b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/CategoryUrlRewriteGeneratorTest.php index a0cac124bea07..cfa82b90e2f6a 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/CategoryUrlRewriteGeneratorTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/CategoryUrlRewriteGeneratorTest.php @@ -6,32 +6,44 @@ namespace Magento\CatalogUrlRewrite\Model; +use Magento\Catalog\Api\CategoryRepositoryInterface; +use Magento\Catalog\Model\Category; +use Magento\Catalog\Model\ProductRepository; +use Magento\Framework\Exception\CouldNotSaveException; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\ObjectManagerInterface; +use Magento\TestFramework\Helper\Bootstrap; use Magento\UrlRewrite\Model\OptionProvider; +use Magento\UrlRewrite\Model\UrlFinderInterface; use Magento\UrlRewrite\Service\V1\Data\UrlRewrite; -use Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator; +use PHPUnit\Framework\Exception; +use PHPUnit\Framework\TestCase; /** * @magentoAppArea adminhtml + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ -class CategoryUrlRewriteGeneratorTest extends \PHPUnit\Framework\TestCase +class CategoryUrlRewriteGeneratorTest extends TestCase { - /** @var \Magento\Framework\ObjectManagerInterface */ + /** @var ObjectManagerInterface */ protected $objectManager; protected function setUp() { - $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + $this->objectManager = Bootstrap::getObjectManager(); } /** * @magentoDataFixture Magento/CatalogUrlRewrite/_files/categories_with_products.php + * @magentoConfigFixture default/catalog/seo/generate_category_product_rewrites 1 * @magentoDbIsolation enabled * @magentoAppIsolation enabled */ public function testGenerateUrlRewritesWithoutSaveHistory() { - /** @var \Magento\Catalog\Model\Category $category */ - $category = $this->objectManager->create(\Magento\Catalog\Model\Category::class); + /** @var Category $category */ + $category = $this->objectManager->create(Category::class); $category->load(3); $category->setData('save_rewrites_history', false); $category->setUrlKey('new-url'); @@ -50,8 +62,8 @@ public function testGenerateUrlRewritesWithoutSaveHistory() $this->assertResults($categoryExpectedResult, $actualResults); - /** @var \Magento\Catalog\Model\ProductRepository $productRepository */ - $productRepository = $this->objectManager->create(\Magento\Catalog\Model\ProductRepository::class); + /** @var ProductRepository $productRepository */ + $productRepository = $this->objectManager->create(ProductRepository::class); $product = $productRepository->get('12345'); $productForTest = $product->getId(); @@ -98,13 +110,14 @@ public function testGenerateUrlRewritesWithoutSaveHistory() /** * @magentoDbIsolation enabled + * @magentoConfigFixture default/catalog/seo/generate_category_product_rewrites 1 * @magentoDataFixture Magento/CatalogUrlRewrite/_files/categories_with_products.php * @magentoAppIsolation enabled */ public function testGenerateUrlRewritesWithSaveHistory() { - /** @var \Magento\Catalog\Model\Category $category */ - $category = $this->objectManager->create(\Magento\Catalog\Model\Category::class); + /** @var Category $category */ + $category = $this->objectManager->create(Category::class); $category->load(3); $category->setData('save_rewrites_history', true); $category->setUrlKey('new-url'); @@ -131,8 +144,8 @@ public function testGenerateUrlRewritesWithSaveHistory() $this->assertResults($categoryExpectedResult, $actualResults); - /** @var \Magento\Catalog\Model\ProductRepository $productRepository */ - $productRepository = $this->objectManager->create(\Magento\Catalog\Model\ProductRepository::class); + /** @var ProductRepository $productRepository */ + $productRepository = $this->objectManager->create(ProductRepository::class); $product = $productRepository->get('12345'); $productForTest = $product->getId(); @@ -200,14 +213,17 @@ public function testGenerateUrlRewritesWithSaveHistory() * @magentoDbIsolation enabled * @magentoAppIsolation enabled * @param string $urlKey + * @throws CouldNotSaveException + * @throws NoSuchEntityException + * @throws Exception * @dataProvider incorrectUrlRewritesDataProvider */ public function testGenerateUrlRewritesWithIncorrectUrlKey($urlKey) { - $this->expectException(\Magento\Framework\Exception\LocalizedException::class); + $this->expectException(LocalizedException::class); $this->expectExceptionMessage('Invalid URL key'); - /** @var \Magento\Catalog\Api\CategoryRepositoryInterface $repository */ - $repository = $this->objectManager->get(\Magento\Catalog\Api\CategoryRepositoryInterface::class); + /** @var CategoryRepositoryInterface $repository */ + $repository = $this->objectManager->get(CategoryRepositoryInterface::class); $category = $repository->get(3); $category->setUrlKey($urlKey); $repository->save($category); @@ -230,8 +246,8 @@ public function incorrectUrlRewritesDataProvider() */ protected function getActualResults(array $filter) { - /** @var \Magento\UrlRewrite\Model\UrlFinderInterface $urlFinder */ - $urlFinder = $this->objectManager->get(\Magento\UrlRewrite\Model\UrlFinderInterface::class); + /** @var UrlFinderInterface $urlFinder */ + $urlFinder = $this->objectManager->get(UrlFinderInterface::class); $actualResults = []; foreach ($urlFinder->findAllByData($filter) as $url) { $actualResults[] = [ @@ -244,9 +260,68 @@ protected function getActualResults(array $filter) return $actualResults; } + /** + * @magentoConfigFixture default/catalog/seo/generate_category_product_rewrites 0 + * @magentoDataFixture Magento/CatalogUrlRewrite/_files/categories_with_products.php + * @magentoDbIsolation enabled + * @magentoAppIsolation enabled + */ + public function testGenerateUrlRewritesWithoutGenerateCategoryRewrites() + { + /** @var Category $category */ + $category = $this->objectManager->create(Category::class); + $category->load(3); + $category->setData('save_rewrites_history', false); + $category->setUrlKey('new-url'); + $category->save(); + + $categoryFilter = [ + UrlRewrite::ENTITY_TYPE => CategoryUrlRewriteGenerator::ENTITY_TYPE, + UrlRewrite::ENTITY_ID => [3, 4, 5] + ]; + $actualResults = $this->getActualResults($categoryFilter); + $categoryExpectedResult = [ + ['new-url.html', 'catalog/category/view/id/3', 1, 0], + ['new-url/category-1-1.html', 'catalog/category/view/id/4', 1, 0], + ['new-url/category-1-1/category-1-1-1.html', 'catalog/category/view/id/5', 1, 0], + ]; + + $this->assertResults($categoryExpectedResult, $actualResults); + } + + /** + * @magentoConfigFixture default/catalog/seo/generate_category_product_rewrites 0 + * @magentoDataFixture Magento/CatalogUrlRewrite/_files/categories_with_products.php + * @magentoAppIsolation enabled + */ + public function testGenerateUrlRewritesWithoutGenerateProductRewrites() + { + /** @var ProductRepository $productRepository */ + $productRepository = $this->objectManager->create(ProductRepository::class); + $product = $productRepository->get('12345'); + $productForTest = $product->getId(); + + $productFilter = [ + UrlRewrite::ENTITY_TYPE => ProductUrlRewriteGenerator::ENTITY_TYPE, + UrlRewrite::ENTITY_ID => [$productForTest] + ]; + $actualResults = $this->getActualResults($productFilter); + $productExpectedResult = [ + [ + 'simple-product-two.html', + 'catalog/product/view/id/' . $productForTest, + 1, + 0 + ] + ]; + + $this->assertResults($productExpectedResult, $actualResults); + } + /** * @param array $expected * @param array $actual + * @throws Exception */ protected function assertResults($expected, $actual) { diff --git a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/ProductUrlRewriteGeneratorTest.php b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/ProductUrlRewriteGeneratorTest.php index 1203ca191b459..091e5318fa924 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/ProductUrlRewriteGeneratorTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Model/ProductUrlRewriteGeneratorTest.php @@ -30,6 +30,7 @@ protected function setUp() /** * @magentoDataFixture Magento/CatalogUrlRewrite/_files/product_with_category.php + * @magentoConfigFixture default/catalog/seo/generate_category_product_rewrites 1 * @magentoDbIsolation disabled * @magentoAppIsolation enabled */ @@ -45,10 +46,13 @@ public function testGenerateWithSpecificCategoryUrlKey() $generator = $this->objectManager->get(ProductUrlRewriteGenerator::class); $urls = $generator->generate($product); - $actualUrls = array_map(function ($url) { - /** @var \Magento\UrlRewrite\Service\V1\Data\UrlRewrite $url */ - return $url->getRequestPath(); - }, $urls); + $actualUrls = array_map( + function ($url) { + /** @var \Magento\UrlRewrite\Service\V1\Data\UrlRewrite $url */ + return $url->getRequestPath(); + }, + $urls + ); self::assertTrue(in_array('p002.html', $actualUrls)); self::assertTrue(in_array('cat-1/p002.html', $actualUrls)); diff --git a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Observer/UrlRewriteHandlerTest.php b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Observer/UrlRewriteHandlerTest.php index 402db06a0bbc9..87786a051966a 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Observer/UrlRewriteHandlerTest.php +++ b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/Observer/UrlRewriteHandlerTest.php @@ -48,6 +48,7 @@ protected function setUp() * @magentoDbIsolation disabled * @magentoDataFixture Magento/CatalogUrlRewrite/Fixtures/product_custom_url_key.php * @magentoConfigFixture admin_store catalog/seo/product_use_categories 1 + * @magentoConfigFixture default/catalog/seo/generate_category_product_rewrites 1 */ public function testGenerateProductUrlRewrites() { @@ -60,9 +61,14 @@ public function testGenerateProductUrlRewrites() ->setAnchorsAbove(false); $generatedUrls = $this->handler->generateProductUrlRewrites($category); - $actual = array_values(array_map(function (UrlRewrite $urlRewrite) { - return $urlRewrite->getRequestPath(); - }, $generatedUrls)); + $actual = array_values( + array_map( + function (UrlRewrite $urlRewrite) { + return $urlRewrite->getRequestPath(); + }, + $generatedUrls + ) + ); $expected = [ 'store-1-key.html', // the Default store diff --git a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/product_with_category.php b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/product_with_category.php index 2f3d4ea4c3e7f..3c03318942167 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/product_with_category.php +++ b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/product_with_category.php @@ -14,6 +14,7 @@ use Magento\Framework\Indexer\IndexerRegistry; use Magento\Store\Model\StoreManagerInterface; use Magento\TestFramework\Helper\Bootstrap; +use Magento\UrlRewrite\Model\UrlRewrite; /** * @var \Magento\Store\Model\Store $store @@ -80,3 +81,15 @@ /** @var CategoryLinkManagementInterface $linkManagement */ $linkManagement = $objectManager->get(CategoryLinkManagementInterface::class); $linkManagement->assignProductToCategories($product->getSku(), [Category::TREE_ROOT_ID, $category->getEntityId()]); + +/** @var UrlRewrite $urlRewrite */ +$urlRewrite = $objectManager->create(UrlRewrite::class); +$urlRewrite->setEntityType('custom') + ->setRequestPath('non-exist-product.html') + ->setTargetPath('catalog/product/view/id/' . $product->getId()) + ->setRedirectType(0) + ->setStoreId(1) + ->setDescription(null) + ->setIsAutogenerated(0); + +$urlRewrite->save(); diff --git a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/product_with_category_rollback.php b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/product_with_category_rollback.php index 2598dd6693500..61e2d84513f15 100644 --- a/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/product_with_category_rollback.php +++ b/dev/tests/integration/testsuite/Magento/CatalogUrlRewrite/_files/product_with_category_rollback.php @@ -9,6 +9,7 @@ use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Framework\Api\SearchCriteriaBuilder; use Magento\TestFramework\Helper\Bootstrap; +use Magento\UrlRewrite\Model\UrlRewrite; $objectManager = Bootstrap::getObjectManager(); /** @var \Magento\Framework\Registry $registry */ @@ -20,7 +21,6 @@ /** @var ProductRepositoryInterface $productRepository */ $productRepository = $objectManager->get(ProductRepositoryInterface::class); -//$productRepository->deleteById('p002'); $product = $productRepository->get('p002', false, null, true); $productRepository->delete($product); @@ -41,6 +41,11 @@ $categoryRepository->delete($category); } +/** @var UrlRewrite $urlRewrite */ +$urlRewrite = $objectManager->create(UrlRewrite::class); +$urlRewrite->load('non-exist-product.html', 'request_path'); +$urlRewrite->delete(); + $registry->unregister('isSecureArea'); $registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/Checkout/Model/ShippingInformationManagementTest.php b/dev/tests/integration/testsuite/Magento/Checkout/Model/ShippingInformationManagementTest.php new file mode 100644 index 0000000000000..369919437526c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Checkout/Model/ShippingInformationManagementTest.php @@ -0,0 +1,142 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Checkout\Model; + +use Magento\TestFramework\Helper\Bootstrap; +use Magento\Checkout\Api\Data\ShippingInformationInterface; +use Magento\Checkout\Api\PaymentInformationManagementInterface; +use Magento\Checkout\Api\ShippingInformationManagementInterface; +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Quote\Api\CartItemRepositoryInterface; +use Magento\Quote\Api\CartManagementInterface; +use Magento\Quote\Api\Data\AddressInterfaceFactory; +use Magento\Quote\Api\Data\CartItemInterface; +use Magento\Quote\Api\Data\PaymentInterface; +use Magento\Quote\Api\ShipmentEstimationInterface; +use Magento\Sales\Api\InvoiceOrderInterface; + +/** + * Shipping information managment test. + */ +class ShippingInformationManagementTest extends \PHPUnit\Framework\TestCase +{ + /** @var CartManagementInterface */ + private $cartManagement; + + /** @var CartItemRepositoryInterface */ + private $cartItemRepository; + + /** @var CartItemInterface */ + private $cartItem; + + /** @var ShippingInformationManagementInterface */ + private $shippingInformationManagement; + + /** @var ShippingInformationInterface */ + private $shippingInformation; + + /** @var CustomerRepositoryInterface */ + private $customerRepository; + + /** @var AddressInterfaceFactory */ + private $apiAddressFactory; + + /** @var ShipmentEstimationInterface */ + private $shipmentEstimation; + + /** @var PaymentInformationManagementInterface */ + private $paymentInformationManagement; + + /** @var PaymentInterface */ + private $payment; + + /** @var InvoiceOrderInterface */ + private $invoiceOrder; + + public function setUp() + { + $objectManager = Bootstrap::getObjectManager(); + + $this->cartManagement = $objectManager->create(CartManagementInterface::class); + $this->cartItemRepository = $objectManager->create(CartItemRepositoryInterface::class); + $this->cartItem = $objectManager->create(CartItemInterface::class); + $this->shippingInformationManagement = $objectManager->create(ShippingInformationManagementInterface::class); + $this->shippingInformation = $objectManager->create(ShippingInformationInterface::class); + $this->customerRepository = $objectManager->create(CustomerRepositoryInterface::class); + $this->apiAddressFactory = $objectManager->create(AddressInterfaceFactory::class); + $this->shipmentEstimation = $objectManager->create(ShipmentEstimationInterface::class); + $this->paymentInformationManagement = $objectManager->create(PaymentInformationManagementInterface::class); + $this->payment = $objectManager->create(PaymentInterface::class); + $this->invoiceOrder = $objectManager->create(InvoiceOrderInterface::class); + } + + /** + * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoDataFixture Magento/Customer/_files/customer_address.php + * @magentoDataFixture Magento/Catalog/_files/product_virtual_in_stock.php + */ + public function testQuoteApiWithOnlyVirtualProducts() + { + $customer = $this->customerRepository->getById(1); + + // Create empty quote + $quoteId = $this->cartManagement->createEmptyCartForCustomer($customer->getId()); + + $cartItem = $this->cartItem + ->setSku('virtual-product') + ->setQty(1) + ->setQuoteId($quoteId); + + // Add item to cart + $this->cartItemRepository->save($cartItem); + + $billingAddress = $shippingAddress = null; + foreach ($customer->getAddresses() as $address) { + $billingAddress = $address; + $shippingAddress = $address; + break; + } + + /** @var \Magento\Quote\Model\Quote\Address $apiBillingAddress */ + $apiBillingAddress = $this->apiAddressFactory->create(); + $apiBillingAddress->setRegion($billingAddress->getRegion()) + ->setRegionId($billingAddress->getRegionId()) + ->setCountryId($billingAddress->getCountryId()) + ->setStreet($billingAddress->getStreet()) + ->setPostcode($billingAddress->getPostcode()) + ->setCity($billingAddress->getCity()) + ->setFirstname($billingAddress->getFirstname()) + ->setLastname($billingAddress->getLastname()) + ->setEmail($customer->getEmail()) + ->setTelephone($billingAddress->getTelephone()); + + /** @var \Magento\Quote\Model\Quote\Address $apiShippingAddress */ + $apiShippingAddress = $this->apiAddressFactory->create(); + $apiShippingAddress->setRegion($shippingAddress->getRegion()) + ->setRegionId($shippingAddress->getRegionId()) + ->setCountryId($shippingAddress->getCountryId()) + ->setStreet($shippingAddress->getStreet()) + ->setPostcode($shippingAddress->getPostcode()) + ->setCity($shippingAddress->getCity()) + ->setFirstname($shippingAddress->getFirstname()) + ->setLastname($shippingAddress->getLastname()) + ->setEmail($customer->getEmail()) + ->setTelephone($shippingAddress->getTelephone()); + + // Estimate shipping + $this->shipmentEstimation->estimateByExtendedAddress($quoteId, $apiShippingAddress); + + $addressInformation = $this->shippingInformation + ->setBillingAddress($apiBillingAddress) + ->setShippingAddress($apiShippingAddress) + ->setShippingCarrierCode('flatrate') + ->setShippingMethodCode('flatrate'); + + // Set address information on quote + $this->shippingInformationManagement->saveAddressInformation($quoteId, $addressInformation); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php b/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php index 10b632c002475..898d3ff400b38 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Controller/AccountTest.php @@ -15,17 +15,17 @@ use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\App\Config\Value; use Magento\Framework\App\Http; +use Magento\Framework\App\Request\Http as HttpRequest; use Magento\Framework\Data\Form\FormKey; use Magento\Framework\Message\MessageInterface; +use Magento\Framework\Serialize\Serializer\Json; +use Magento\Framework\Stdlib\CookieManagerInterface; use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Mail\Template\TransportBuilderMock; use Magento\TestFramework\Request; use Magento\TestFramework\Response; -use Zend\Stdlib\Parameters; -use Magento\Framework\App\Request\Http as HttpRequest; -use Magento\TestFramework\Mail\Template\TransportBuilderMock; -use Magento\Framework\Serialize\Serializer\Json; -use Magento\Framework\Stdlib\CookieManagerInterface; use Magento\Theme\Controller\Result\MessagePlugin; +use Zend\Stdlib\Parameters; /** * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -169,6 +169,7 @@ public function testCreatepasswordActionWithDirectLink() $token = Bootstrap::getObjectManager()->get(\Magento\Framework\Math\Random::class) ->getUniqueHash(); $customer->changeResetPasswordLinkToken($token); + $customer->setData('confirmation', 'confirmation'); $customer->save(); $this->getRequest()->setParam('token', $token); @@ -187,6 +188,7 @@ public function testCreatepasswordActionWithDirectLink() $session = Bootstrap::getObjectManager()->get(Session::class); $this->assertEquals($token, $session->getRpToken()); $this->assertNotContains($token, $response->getHeader('Location')->getFieldValue()); + $this->assertCustomerConfirmationEquals(1, null); } /** @@ -201,6 +203,7 @@ public function testCreatepasswordActionWithSession() $token = Bootstrap::getObjectManager()->get(\Magento\Framework\Math\Random::class) ->getUniqueHash(); $customer->changeResetPasswordLinkToken($token); + $customer->setData('confirmation', 'confirmation'); $customer->save(); /** @var \Magento\Customer\Model\Session $customer */ @@ -213,6 +216,7 @@ public function testCreatepasswordActionWithSession() $response = $this->getResponse(); $text = $response->getBody(); $this->assertTrue((bool)preg_match('/' . $token . '/m', $text)); + $this->assertCustomerConfirmationEquals(1, null); } /** @@ -227,6 +231,7 @@ public function testCreatepasswordActionInvalidToken() $token = Bootstrap::getObjectManager()->get(\Magento\Framework\Math\Random::class) ->getUniqueHash(); $customer->changeResetPasswordLinkToken($token); + $customer->setData('confirmation', 'confirmation'); $customer->save(); $this->getRequest()->setParam('token', 'INVALIDTOKEN'); @@ -238,6 +243,19 @@ public function testCreatepasswordActionInvalidToken() $response = $this->getResponse(); $this->assertEquals(302, $response->getHttpResponseCode()); $this->assertContains('customer/account/forgotpassword', $response->getHeader('Location')->getFieldValue()); + $this->assertCustomerConfirmationEquals(1, 'confirmation'); + } + + /** + * @param int $customerId + * @param string|null $confirmation + */ + private function assertCustomerConfirmationEquals(int $customerId, string $confirmation = null) + { + /** @var \Magento\Customer\Model\Customer $customer */ + $customer = Bootstrap::getObjectManager() + ->create(\Magento\Customer\Model\Customer::class)->load($customerId); + $this->assertEquals($confirmation, $customer->getConfirmation()); } /** @@ -297,11 +315,13 @@ public function testWithConfirmCreatePostAction() $this->dispatch('customer/account/createPost'); $this->assertRedirect($this->stringContains('customer/account/index/')); $this->assertSessionMessages( - $this->equalTo([ - 'You must confirm your account. Please check your email for the confirmation link or ' + $this->equalTo( + [ + 'You must confirm your account. Please check your email for the confirmation link or ' . '<a href="http://localhost/index.php/customer/account/confirmation/' . '?email=test2%40email.com">click here</a> for a new link.' - ]), + ] + ), MessageInterface::TYPE_SUCCESS ); } @@ -315,10 +335,14 @@ public function testExistingEmailCreatePostAction() $this->dispatch('customer/account/createPost'); $this->assertRedirect($this->stringContains('customer/account/create/')); $this->assertSessionMessages( - $this->equalTo(['There is already an account with this email address. ' . - 'If you are sure that it is your email address, ' . - '<a href="http://localhost/index.php/customer/account/forgotpassword/">click here</a>' . - ' to get your password and access your account.', ]), + $this->equalTo( + [ + 'There is already an account with this email address. ' . + 'If you are sure that it is your email address, ' . + '<a href="http://localhost/index.php/customer/account/forgotpassword/">click here</a>' . + ' to get your password and access your account.', + ] + ), MessageInterface::TYPE_ERROR ); } @@ -330,7 +354,11 @@ public function testInactiveUserConfirmationAction() { $this->getRequest() ->setMethod('POST') - ->setPostValue(['email' => 'customer@needAconfirmation.com']); + ->setPostValue( + [ + 'email' => 'customer@needAconfirmation.com', + ] + ); $this->dispatch('customer/account/confirmation'); $this->assertRedirect($this->stringContains('customer/account/index')); @@ -347,14 +375,20 @@ public function testActiveUserConfirmationAction() { $this->getRequest() ->setMethod('POST') - ->setPostValue([ - 'email' => 'customer@example.com', - ]); + ->setPostValue( + [ + 'email' => 'customer@example.com', + ] + ); $this->dispatch('customer/account/confirmation'); $this->assertRedirect($this->stringContains('customer/account/index')); $this->assertSessionMessages( - $this->equalTo(['This email does not require confirmation.']), + $this->equalTo( + [ + 'This email does not require confirmation.', + ] + ), MessageInterface::TYPE_SUCCESS ); } @@ -395,9 +429,11 @@ public function testForgotPasswordPostWithBadEmailAction() { $this->getRequest()->setMethod(HttpRequest::METHOD_POST); $this->getRequest() - ->setPostValue([ - 'email' => 'bad@email', - ]); + ->setPostValue( + [ + 'email' => 'bad@email', + ] + ); $this->dispatch('customer/account/forgotPasswordPost'); $this->assertRedirect($this->stringContains('customer/account/forgotpassword')); @@ -416,10 +452,12 @@ public function testResetPasswordPostNoTokenAction() ->setParam('id', 1) ->setParam('token', '8ed8677e6c79e68b94e61658bd756ea5') ->setMethod('POST') - ->setPostValue([ - 'password' => 'new-password', - 'password_confirmation' => 'new-password', - ]); + ->setPostValue( + [ + 'password' => 'new-password', + 'password_confirmation' => 'new-password', + ] + ); $this->dispatch('customer/account/resetPasswordPost'); $this->assertRedirect($this->stringContains('customer/account/')); @@ -439,10 +477,12 @@ public function testResetPasswordPostAction() ->setQueryValue('id', 1) ->setQueryValue('token', '8ed8677e6c79e68b94e61658bd756ea5') ->setMethod('POST') - ->setPostValue([ - 'password' => 'new-Password1', - 'password_confirmation' => 'new-Password1', - ]); + ->setPostValue( + [ + 'password' => 'new-Password1', + 'password_confirmation' => 'new-Password1', + ] + ); $this->dispatch('customer/account/resetPasswordPost'); $this->assertRedirect($this->stringContains('customer/account/login')); @@ -465,8 +505,11 @@ public function testEditAction() $this->assertEquals(200, $this->getResponse()->getHttpResponseCode(), $body); $this->assertContains('<div class="field field-name-firstname required">', $body); // Verify the password check box is not checked - $this->assertContains('<input type="checkbox" name="change_password" id="change-password" ' - . 'data-role="change-password" value="1" title="Change Password" class="checkbox" />', $body); + $this->assertContains( + '<input type="checkbox" name="change_password" id="change-password" ' + . 'data-role="change-password" value="1" title="Change Password" class="checkbox" />', + $body + ); } /** @@ -510,14 +553,16 @@ public function testEditPostAction() $this->login(1); $this->getRequest() ->setMethod('POST') - ->setPostValue([ - 'form_key' => $this->_objectManager->get(FormKey::class)->getFormKey(), - 'firstname' => 'John', - 'lastname' => 'Doe', - 'email' => 'johndoe@email.com', - 'change_email' => 1, - 'current_password' => 'password' - ]); + ->setPostValue( + [ + 'form_key' => $this->_objectManager->get(FormKey::class)->getFormKey(), + 'firstname' => 'John', + 'lastname' => 'Doe', + 'email' => 'johndoe@email.com', + 'change_email' => 1, + 'current_password' => 'password' + ] + ); $this->dispatch('customer/account/editPost'); @@ -648,16 +693,18 @@ public function testWrongConfirmationEditPostAction() $this->login(1); $this->getRequest() ->setMethod('POST') - ->setPostValue([ - 'form_key' => $this->_objectManager->get(FormKey::class)->getFormKey(), - 'firstname' => 'John', - 'lastname' => 'Doe', - 'email' => 'johndoe@email.com', - 'change_password' => 1, - 'current_password' => 'password', - 'password' => 'new-password', - 'password_confirmation' => 'new-password-no-match', - ]); + ->setPostValue( + [ + 'form_key' => $this->_objectManager->get(FormKey::class)->getFormKey(), + 'firstname' => 'John', + 'lastname' => 'Doe', + 'email' => 'johndoe@email.com', + 'change_password' => 1, + 'current_password' => 'password', + 'password' => 'new-password', + 'password_confirmation' => 'new-password-no-match', + ] + ); $this->dispatch('customer/account/editPost'); @@ -841,13 +888,15 @@ private function getCustomerByEmail($email) */ private function prepareRequest() { - $post = new Parameters([ - 'form_key' => $this->_objectManager->get(FormKey::class)->getFormKey(), - 'login' => [ - 'username' => 'customer@example.com', - 'password' => 'password' + $post = new Parameters( + [ + 'form_key' => $this->_objectManager->get(FormKey::class)->getFormKey(), + 'login' => [ + 'username' => 'customer@example.com', + 'password' => 'password' + ] ] - ]); + ); $request = $this->getRequest(); $formKey = $this->_objectManager->get(FormKey::class); $request->setParam('form_key', $formKey->getFormKey()); @@ -913,6 +962,7 @@ private function getConfirmationUrlFromMessageContent(string $content): string if (preg_match('<a\s*href="(?<url>.*?)".*>', $content, $matches)) { $confirmationUrl = $matches['url']; $confirmationUrl = str_replace('http://localhost/index.php/', '', $confirmationUrl); + // phpcs:ignore Magento2.Functions.DiscouragedFunction $confirmationUrl = html_entity_decode($confirmationUrl); } diff --git a/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagementTest.php b/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagementTest.php index 4810b6c28caef..754c949747d61 100644 --- a/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagementTest.php +++ b/dev/tests/integration/testsuite/Magento/Customer/Model/AccountManagementTest.php @@ -14,6 +14,7 @@ use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\Exception\State\ExpiredException; use Magento\Framework\Reflection\DataObjectProcessor; +use Magento\Store\Model\StoreManagerInterface; use Magento\TestFramework\Helper\Bootstrap; /** @@ -53,6 +54,9 @@ class AccountManagementTest extends \PHPUnit\Framework\TestCase /** @var \Magento\Framework\Api\ExtensibleDataObjectConverter */ private $extensibleDataObjectConverter; + /** @var StoreManagerInterface */ + private $storeManager; + /** @var \Magento\Framework\Api\DataObjectHelper */ protected $dataObjectHelper; @@ -114,6 +118,9 @@ protected function setUp() $this->extensibleDataObjectConverter = $this->objectManager ->create(\Magento\Framework\Api\ExtensibleDataObjectConverter::class); + + $this->storeManager = $this->objectManager + ->create(StoreManagerInterface::class); } /** @@ -844,6 +851,44 @@ public function testCreateNewCustomerWithPasswordHash() ); } + /** + * Customer has two addresses one of it is allowed in website and second is not + * + * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoDataFixture Magento/Customer/_files/customer_two_addresses.php + * @magentoDataFixture Magento/Store/_files/websites_different_countries.php + * @magentoConfigFixture fixture_second_store_store general/country/allow UA + * @return void + */ + public function testCreateNewCustomerWithPasswordHashWithNotAllowedCountry() + { + $customerId = 1; + $allowedCountryIdForSecondWebsite = 'UA'; + $store = $this->storeManager->getStore('fixture_second_store'); + $customerData = $this->customerRepository->getById($customerId); + $customerData->getAddresses()[1]->setRegion(null)->setCountryId($allowedCountryIdForSecondWebsite) + ->setRegionId(null); + $customerData->setStoreId($store->getId())->setWebsiteId($store->getWebsiteId())->setId(null); + $encryptor = $this->objectManager->get(\Magento\Framework\Encryption\EncryptorInterface::class); + /** @var \Magento\Framework\Math\Random $mathRandom */ + $password = $this->objectManager->get(\Magento\Framework\Math\Random::class)->getRandomString(8); + $passwordHash = $encryptor->getHash($password, true); + $savedCustomer = $this->accountManagement->createAccountWithPasswordHash( + $customerData, + $passwordHash + ); + $this->assertCount( + 1, + $savedCustomer->getAddresses(), + 'The wrong address quantity was saved' + ); + $this->assertSame( + 'UA', + $savedCustomer->getAddresses()[0]->getCountryId(), + 'The address with the disallowed country was saved' + ); + } + /** * @magentoAppArea frontend * @magentoDataFixture Magento/Customer/_files/customer.php diff --git a/dev/tests/integration/testsuite/Magento/Customer/Model/ResourceModel/Grid/CollectionReindexOnAccountLockTest.php b/dev/tests/integration/testsuite/Magento/Customer/Model/ResourceModel/Grid/CollectionReindexOnAccountLockTest.php new file mode 100644 index 0000000000000..a665046ceef0d --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/Model/ResourceModel/Grid/CollectionReindexOnAccountLockTest.php @@ -0,0 +1,89 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Customer\Model\ResourceModel\Grid; + +use Magento\Customer\Api\AccountManagementInterface; +use Magento\Customer\Model\CustomerRegistry; +use Magento\Framework\Exception\InvalidEmailOrPasswordException; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\Indexer\TestCase; +use Magento\Tests\NamingConvention\true\mixed; + +/** + * Test if customer account lock on too many failed authentication attempts triggers customer grid reindex + * + * @SuppressWarnings(PHPMD) + */ +class CollectionReindexOnAccountLockTest extends TestCase +{ + /** + * Trigger customer account lock by making 10 failed authentication attempts + */ + private function lockCustomerAccountWithInvalidAuthentications() + { + /** @var AccountManagementInterface */ + $accountManagement = Bootstrap::getObjectManager()->create(AccountManagementInterface::class); + + for ($i = 0; $i < 10; $i++) { + try { + $accountManagement->authenticate('customer@example.com', 'wrongPassword'); + // phpcs:ignore Magento2.CodeAnalysis.EmptyBlock + } catch (InvalidEmailOrPasswordException $e) { + } + } + } + + /** + * @return mixed + * @throws NoSuchEntityException + */ + private function getCustomerLockExpire(): ?string + { + /** @var CustomerRegistry $customerRegistry */ + $customerRegistry = Bootstrap::getObjectManager()->create(CustomerRegistry::class); + $customerModel = $customerRegistry->retrieve(1); + $this->assertNotEmpty($customerModel); + + return $customerModel->getData('lock_expires'); + } + + /** + * @return mixed + */ + private function getCustomerGridLockExpire(): ?string + { + /** @var Collection */ + $gridCustomerCollection = Bootstrap::getObjectManager()->create(Collection::class); + $gridCustomerItem = $gridCustomerCollection->getItemById(1); + $this->assertNotEmpty($gridCustomerItem); + + return $gridCustomerItem->getData('lock_expires'); + } + + /** + * Test if customer account lock on too many failed authentication attempts triggers customer grid reindex + * + * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoAppIsolation enabled + * @magentoDbIsolation disabled + */ + public function testCustomerAccountReindexOnLock() + { + $this->assertSame( + $this->getCustomerGridLockExpire(), + $this->getCustomerLockExpire() + ); + + $this->lockCustomerAccountWithInvalidAuthentications(); + + $this->assertSame( + $this->getCustomerGridLockExpire(), + $this->getCustomerLockExpire() + ); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_one_address.php b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_one_address.php new file mode 100644 index 0000000000000..1ae532e32a958 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_one_address.php @@ -0,0 +1,78 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Customer\Model\CustomerRegistry; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Model\Customer; +use Magento\Customer\Model\Address; +use Magento\Customer\Api\AddressRepositoryInterface; +use Magento\Customer\Model\AddressRegistry; + +$objectManager = Bootstrap::getObjectManager(); +//Creating customer +/** @var $repository CustomerRepositoryInterface */ +$repository = $objectManager->create(CustomerRepositoryInterface::class); +/** @var Customer $customer */ +$customer = $objectManager->create(Customer::class); +/** @var CustomerRegistry $customerRegistry */ +$customerRegistry = $objectManager->get(CustomerRegistry::class); +$customer->setWebsiteId(1) + ->setEmail('customer_one_address@test.com') + ->setPassword('password') + ->setGroupId(1) + ->setStoreId(1) + ->setIsActive(1) + ->setPrefix('Mr.') + ->setFirstname('John') + ->setMiddlename('A') + ->setLastname('Smith') + ->setSuffix('Esq.') + ->setTaxvat('12') + ->setGender(0) + ->setId(1); + +$customer->isObjectNew(true); +$customer->save(); +$customerRegistry->remove($customer->getId()); + +//Creating address +/** @var Address $customerAddress */ +$customerAddress = $objectManager->create(Address::class); +$customerAddress->isObjectNew(true); +$customerAddress->setData( + [ + 'attribute_set_id' => 2, + 'telephone' => 3468676, + 'postcode' => 75477, + 'country_id' => 'US', + 'city' => 'CityM', + 'company' => 'CompanyName', + 'street' => 'CustomerAddress1', + 'lastname' => 'Smith', + 'firstname' => 'John', + 'parent_id' => $customer->getId(), + 'region_id' => 1, + ] +); +$customerAddress->save(); +/** @var AddressRepositoryInterface $addressRepository */ +$addressRepository = $objectManager->get(AddressRepositoryInterface::class); +$customerAddress = $addressRepository->getById($customerAddress->getId()); +$customerAddress->setCustomerId($customer->getId()); +$customerAddress->isDefaultBilling(true); +$customerAddress->setIsDefaultShipping(true); +$customerAddress = $addressRepository->save($customerAddress); + +$customer->setDefaultBilling($customerAddress->getId()); +$customer->setDefaultShipping($customerAddress->getId()); +$customer->save(); + +$customerRegistry->remove($customerAddress->getCustomerId()); +/** @var AddressRegistry $addressRegistry */ +$addressRegistry = $objectManager->get(AddressRegistry::class); +$addressRegistry->remove($customerAddress->getId()); diff --git a/dev/tests/integration/testsuite/Magento/Customer/_files/customer_one_address_rollback.php b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_one_address_rollback.php new file mode 100644 index 0000000000000..441700389840d --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Customer/_files/customer_one_address_rollback.php @@ -0,0 +1,34 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\TestFramework\Helper\Bootstrap; +use Magento\Customer\Api\CustomerRepositoryInterface; +use Magento\Customer\Api\AddressRepositoryInterface; +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\Registry; + +$objectManager = Bootstrap::getObjectManager(); +/** @var Registry $registry */ +$registry = $objectManager->get(Registry::class); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); +/** @var CustomerRepositoryInterface $customerRepo */ +$customerRepo = $objectManager->get(CustomerRepositoryInterface::class); +try { + $customer = $customerRepo->get('customer_with_addresses@test.com'); + /** @var AddressRepositoryInterface $addressRepo */ + $addressRepo = $objectManager->get(AddressRepositoryInterface::class); + foreach ($customer->getAddresses() as $address) { + $addressRepo->delete($address); + } + $customerRepo->delete($customer); +// phpcs:ignore Magento2.CodeAnalysis.EmptyBlock +} catch (NoSuchEntityException $exception) { + //Already deleted +} +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', false); diff --git a/dev/tests/integration/testsuite/Magento/GraphQl/Catalog/_files/simple_product_without_visibility.php b/dev/tests/integration/testsuite/Magento/GraphQl/Catalog/_files/simple_product_without_visibility.php new file mode 100644 index 0000000000000..e43a546e4f4ff --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/GraphQl/Catalog/_files/simple_product_without_visibility.php @@ -0,0 +1,47 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\Data\ProductInterface; +use Magento\Catalog\Api\Data\ProductInterfaceFactory; +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\Product\Type; +use Magento\Catalog\Model\Product\Visibility; +use Magento\Framework\Api\DataObjectHelper; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +/** @var ProductInterfaceFactory $productFactory */ +$productFactory = $objectManager->get(ProductInterfaceFactory::class); +/** @var DataObjectHelper $dataObjectHelper */ +$dataObjectHelper = Bootstrap::getObjectManager()->get(DataObjectHelper::class); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); + +$product = $productFactory->create(); +$productData = [ + ProductInterface::TYPE_ID => Type::TYPE_SIMPLE, + ProductInterface::ATTRIBUTE_SET_ID => 4, + ProductInterface::SKU => 'simple_product_without_visibility', + ProductInterface::NAME => 'Simple Product Not Visible', + ProductInterface::PRICE => 10, + ProductInterface::VISIBILITY => Visibility::VISIBILITY_NOT_VISIBLE, + ProductInterface::STATUS => Status::STATUS_ENABLED, +]; +$dataObjectHelper->populateWithArray($product, $productData, ProductInterface::class); +/** Out of interface */ +$product + ->setWebsiteIds([1]) + ->setStockData( + [ + 'qty' => 85.5, + 'is_in_stock' => true, + 'manage_stock' => true, + 'is_qty_decimal' => true + ] + ); +$productRepository->save($product); diff --git a/dev/tests/integration/testsuite/Magento/GraphQl/Catalog/_files/simple_product_without_visibility_rollback.php b/dev/tests/integration/testsuite/Magento/GraphQl/Catalog/_files/simple_product_without_visibility_rollback.php new file mode 100644 index 0000000000000..47f3ad6c5d4aa --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/GraphQl/Catalog/_files/simple_product_without_visibility_rollback.php @@ -0,0 +1,32 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Catalog\Api\ProductRepositoryInterface; +use Magento\Framework\Registry; +use Magento\TestFramework\Helper\Bootstrap; + +$objectManager = Bootstrap::getObjectManager(); +/** @var ProductRepositoryInterface $productRepository */ +$productRepository = $objectManager->get(ProductRepositoryInterface::class); +/** @var Registry $registry */ +$registry = $objectManager->get(Registry::class); + +$currentArea = $registry->registry('isSecureArea'); +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', true); + +try { + $productRepository->deleteById('simple_product_without_visibility'); +// phpcs:ignore Magento2.CodeAnalysis.EmptyBlock +} catch (\Magento\Framework\Exception\NoSuchEntityException $e) { + /** + * Tests which are wrapped with MySQL transaction clear all data by transaction rollback. + */ +} + +$registry->unregister('isSecureArea'); +$registry->register('isSecureArea', $currentArea); diff --git a/dev/tests/integration/testsuite/Magento/GraphQl/Quote/GetMaskedQuoteIdByReservedOrderId.php b/dev/tests/integration/testsuite/Magento/GraphQl/Quote/GetMaskedQuoteIdByReservedOrderId.php new file mode 100644 index 0000000000000..9bb9bef9bdb09 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/GraphQl/Quote/GetMaskedQuoteIdByReservedOrderId.php @@ -0,0 +1,64 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\GraphQl\Quote; + +use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Quote\Model\ResourceModel\Quote as QuoteResource; +use Magento\Quote\Model\QuoteIdToMaskedQuoteIdInterface; +use Magento\Quote\Model\QuoteFactory; + +/** + * Get masked quote id by reserved order id + */ +class GetMaskedQuoteIdByReservedOrderId +{ + /** + * @var QuoteFactory + */ + private $quoteFactory; + + /** + * @var QuoteResource + */ + private $quoteResource; + + /** + * @var QuoteIdToMaskedQuoteIdInterface + */ + private $quoteIdToMaskedId; + + /** + * @param QuoteFactory $quoteFactory + * @param QuoteResource $quoteResource + * @param QuoteIdToMaskedQuoteIdInterface $quoteIdToMaskedId + */ + public function __construct( + QuoteFactory $quoteFactory, + QuoteResource $quoteResource, + QuoteIdToMaskedQuoteIdInterface $quoteIdToMaskedId + ) { + $this->quoteFactory = $quoteFactory; + $this->quoteResource = $quoteResource; + $this->quoteIdToMaskedId = $quoteIdToMaskedId; + } + + /** + * Get masked quote id by reserved order id + * + * @param string $reservedOrderId + * @return string + * @throws NoSuchEntityException + */ + public function execute(string $reservedOrderId): string + { + $quote = $this->quoteFactory->create(); + $this->quoteResource->load($quote, $reservedOrderId, 'reserved_order_id'); + + return $this->quoteIdToMaskedId->execute((int)$quote->getId()); + } +} diff --git a/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/enable_offline_payment_methods.php b/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/enable_offline_payment_methods.php index 9c15589ba82e5..79d61cf32b9f7 100644 --- a/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/enable_offline_payment_methods.php +++ b/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/enable_offline_payment_methods.php @@ -19,6 +19,7 @@ $configWriter->save('payment/cashondelivery/active', 1); $configWriter->save('payment/checkmo/active', 1); $configWriter->save('payment/purchaseorder/active', 1); +$configWriter->save('payment/authorizenet_acceptjs/active', 1); $scopeConfig = $objectManager->get(ScopeConfigInterface::class); $scopeConfig->clean(); diff --git a/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/enable_offline_payment_methods_rollback.php b/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/enable_offline_payment_methods_rollback.php index 61b7ed9737ff9..6c41e3ebabf0a 100644 --- a/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/enable_offline_payment_methods_rollback.php +++ b/dev/tests/integration/testsuite/Magento/GraphQl/Quote/_files/enable_offline_payment_methods_rollback.php @@ -18,3 +18,4 @@ $configWriter->delete('payment/cashondelivery/active'); $configWriter->delete('payment/checkmo/active'); $configWriter->delete('payment/purchaseorder/active'); +$configWriter->delete('payment/authorizenet_acceptjs/active'); diff --git a/dev/tests/integration/testsuite/Magento/PaypalGraphQl/AbstractTest.php b/dev/tests/integration/testsuite/Magento/PaypalGraphQl/AbstractTest.php new file mode 100644 index 0000000000000..b527d2a5559a8 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/PaypalGraphQl/AbstractTest.php @@ -0,0 +1,203 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\PaypalGraphQl; + +use Magento\Customer\Helper\Address; +use Magento\Directory\Model\CountryFactory; +use Magento\Directory\Model\RegionFactory; +use Magento\Framework\Exception\LocalizedExceptionFactory; +use Magento\Framework\HTTP\Adapter\CurlFactory; +use Magento\Framework\Locale\ResolverInterface; +use Magento\GraphQl\Controller\GraphQl; +use Magento\Payment\Model\Method\Logger; +use Magento\Paypal\Model\Api\Nvp; +use Magento\Paypal\Model\Api\PayflowNvp; +use Magento\Paypal\Model\Api\AbstractApi; +use Magento\Paypal\Model\Api\ProcessableExceptionFactory; +use Magento\Quote\Model\Quote; +use Magento\Quote\Model\QuoteFactory; +use Magento\TestFramework\ObjectManager; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\Paypal\Model\Api\Type\Factory as ApiFactory; +use Psr\Log\LoggerInterface; +use Magento\Config\Model\Config; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Framework\UrlInterface; + +/** + * Abstract class with common logic for Paypal GraphQl tests + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +abstract class AbstractTest extends TestCase +{ + /** + * @var AbstractApi|MockObject + */ + protected $nvpMock; + + /** + * @var ObjectManager + */ + protected $objectManager; + + /** + * @var GraphQl + */ + protected $graphqlController; + + protected function setUp() + { + $this->objectManager = Bootstrap::getObjectManager(); + + $apiFactoryMock = $this->getMockBuilder(ApiFactory::class) + ->disableOriginalConstructor() + ->setMethods(['create']) + ->getMock(); + $apiFactoryMock->method('create') + ->willReturnMap( + [ + [Nvp::class, [], $this->getNvpMock(Nvp::class)], + [PayflowNvp::class, [], $this->getNvpMock(PayflowNvp::class)] + ] + ); + + $this->objectManager->addSharedInstance($apiFactoryMock, ApiFactory::class); + + $this->graphqlController = $this->objectManager->get(GraphQl::class); + } + + protected function tearDown() + { + $this->disablePaypalPaymentMethods(); + $this->objectManager->removeSharedInstance(ApiFactory::class); + } + + /** + * Get quote by reserved order id + * + * @param $reservedOrderId + * @return Quote + */ + protected function getQuoteByReservedOrderId($reservedOrderId): Quote + { + $quoteFactory = $this->objectManager->get(QuoteFactory::class); + /** @var Quote $quote */ + $quote = $quoteFactory->create(); + + $quote->load($reservedOrderId, 'reserved_order_id'); + return $quote; + } + + /** + * Enables Paypal payment method by payment code + * + * @return void + */ + protected function enablePaymentMethod($methodCode): void + { + $config = $this->objectManager->get(Config::class); + $config->setScope(ScopeConfigInterface::SCOPE_TYPE_DEFAULT); + + $paymentMethodActive = 'payment/' . $methodCode . '/active'; + + $config->setDataByPath($paymentMethodActive, '1'); + $config->save(); + } + + /** + * Disables list of Paypal payment methods + * + * @return void + */ + protected function disablePaypalPaymentMethods(): void + { + $paypalMethods = [ + 'paypal_express', + 'payflow_express', + 'payflow_link' + ]; + $config = $this->objectManager->get(Config::class); + $config->setScope(ScopeConfigInterface::SCOPE_TYPE_DEFAULT); + + foreach ($paypalMethods as $method) { + $paymentMethodActive = 'payment/' . $method . '/active'; + $config->setDataByPath($paymentMethodActive, '0'); + $config->save(); + } + } + + /** + * Get mock of Nvp class + * + * @param string $nvpClass + * @return AbstractApi|MockObject + */ + private function getNvpMock(string $nvpClass) + { + if (empty($this->nvpMock)) { + $this->nvpMock = $this->getMockBuilder($nvpClass) + ->setConstructorArgs( + [ + 'customerAddress' => $this->objectManager->get(Address::class), + 'logger' => $this->objectManager->get(LoggerInterface::class), + 'customerLogger' => $this->objectManager->get(Logger::class), + 'resolverInterface' => $this->objectManager->get(ResolverInterface::class), + 'regionFactory' => $this->objectManager->get(RegionFactory::class), + 'countryFactory' => $this->objectManager->get(CountryFactory::class), + 'processableExceptionFactory' => $this->objectManager->get(ProcessableExceptionFactory::class), + 'frameworkExceptionFactory' => $this->objectManager->get(LocalizedExceptionFactory::class), + 'curlFactory' => $this->objectManager->get(CurlFactory::class), + 'data' => [] + ] + ) + ->setMethods(['call']) + ->getMock(); + } + return $this->nvpMock; + } + + /** + * Get GraphQl query for creating Paypal token + * + * @param string $cartId + * @param string $paymentMethod + * @return string + */ + protected function getCreateTokenMutation(string $cartId, string $paymentMethod): string + { + $url = $this->objectManager->get(UrlInterface::class); + $baseUrl = $url->getBaseUrl(); + + return <<<QUERY +mutation { + createPaypalExpressToken(input: { + cart_id: "{$cartId}", + code: "{$paymentMethod}", + urls: { + return_url: "{$baseUrl}paypal/express/return/", + cancel_url: "{$baseUrl}paypal/express/cancel/" + success_url: "{$baseUrl}checkout/onepage/success/", + pending_url: "{$baseUrl}checkout/onepage/pending/" + } + express_button: true + }) + { + __typename + token + paypal_urls{ + start + edit + } + } +} +QUERY; + } +} diff --git a/dev/tests/integration/testsuite/Magento/PaypalGraphQl/Model/Resolver/Customer/PaypalExpressSetPaymentMethodTest.php b/dev/tests/integration/testsuite/Magento/PaypalGraphQl/Model/Resolver/Customer/PaypalExpressSetPaymentMethodTest.php new file mode 100644 index 0000000000000..f358e52baf124 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/PaypalGraphQl/Model/Resolver/Customer/PaypalExpressSetPaymentMethodTest.php @@ -0,0 +1,247 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\PaypalGraphQl\Model\Resolver\Customer; + +use Magento\Framework\App\Request\Http; +use Magento\Framework\Webapi\Request; +use Magento\Paypal\Model\Api\Nvp; +use Magento\PaypalGraphQl\AbstractTest; +use Magento\Framework\Serialize\SerializerInterface; +use Magento\Quote\Model\QuoteIdToMaskedQuoteId; +use Magento\Framework\UrlInterface; + +/** + * Test ExpressSetPaymentMethodTest graphql endpoint for customer + * + * @magentoAppArea graphql + */ +class PaypalExpressSetPaymentMethodTest extends AbstractTest +{ + /** + * @var Http + */ + private $request; + + /** + * @var SerializerInterface + */ + private $json; + + /** + * @var QuoteIdToMaskedQuoteId + */ + private $quoteIdToMaskedId; + + protected function setUp() + { + parent::setUp(); + + $this->request = $this->objectManager->create(Http::class); + $this->json = $this->objectManager->get(SerializerInterface::class); + $this->quoteIdToMaskedId = $this->objectManager->get(QuoteIdToMaskedQuoteId::class); + } + + /** + * Test end to end test to process a paypal express order + * + * @param string $paymentMethod + * @return void + * @dataProvider getPaypalCodesProvider + * @magentoConfigFixture default_store paypal/wpp/sandbox_flag 1 + * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * @magentoDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php + * @magentoDataFixture Magento/GraphQl/Quote/_files/set_new_billing_address.php + * @magentoDataFixture Magento/GraphQl/Quote/_files/set_flatrate_shipping_method.php + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function testResolve(string $paymentMethod): void + { + $this->enablePaymentMethod($paymentMethod); + if ($paymentMethod === 'payflow_express') { + $this->enablePaymentMethod('payflow_link'); + } + + $payerId = 'PAYER123456'; + $token = 'EC-TOKEN1234'; + $correlationId = 'c123456789'; + $reservedQuoteId = 'test_quote'; + + $cart = $this->getQuoteByReservedOrderId($reservedQuoteId); + $cartId = $cart->getId(); + $maskedCartId = $this->quoteIdToMaskedId->execute((int) $cartId); + + $url = $this->objectManager->get(UrlInterface::class); + $baseUrl = $url->getBaseUrl(); + + $query = <<<QUERY +mutation { + createPaypalExpressToken(input: { + cart_id: "{$maskedCartId}", + code: "{$paymentMethod}", + urls: { + return_url: "{$baseUrl}paypal/express/return/", + cancel_url: "{$baseUrl}paypal/express/cancel/" + success_url: "{$baseUrl}checkout/onepage/success/", + pending_url: "{$baseUrl}checkout/onepage/pending/" + } + express_button: false + }) + { + __typename + token + paypal_urls{ + start + edit + } + } + setPaymentMethodOnCart(input: { + payment_method: { + code: "{$paymentMethod}", + additional_data: { + paypal_express: { + payer_id: "$payerId", + token: "$token" + } + payflow_express: { + payer_id: "$payerId", + token: "$token" + } + } + }, + cart_id: "{$maskedCartId}"}) + { + cart { + selected_payment_method { + code + } + } + } + placeOrder(input: {cart_id: "{$maskedCartId}"}) { + order { + order_id + } + } +} +QUERY; + + $postData = $this->json->serialize(['query' => $query]); + $this->request->setPathInfo('/graphql'); + $this->request->setMethod('POST'); + $this->request->setContent($postData); + + /** @var \Magento\Integration\Model\Oauth\Token $tokenModel */ + $tokenModel = $this->objectManager->create(\Magento\Integration\Model\Oauth\Token::class); + $customerToken = $tokenModel->createCustomerToken(1)->getToken(); + + $webApiRequest = $this->objectManager->get(Request::class); + $webApiRequest->getHeaders() + ->addHeaderLine('Content-Type', 'application/json') + ->addHeaderLine('Accept', 'application/json') + ->addHeaderLine('Authorization', 'Bearer ' . $customerToken); + $this->request->setHeaders($webApiRequest->getHeaders()); + + $paypalRequest = include __DIR__ . '/../../../_files/customer_paypal_create_token_request.php'; + $paypalResponse = [ + 'TOKEN' => $payerId, + 'CORRELATIONID' => $correlationId, + 'ACK' => 'Success' + ]; + + if ($paymentMethod == 'payflow_express') { + $paypalRequest['SOLUTIONTYPE'] = null; + } + + $paypalRequest['AMT'] = '30.00'; + $paypalRequest['SHIPPINGAMT'] = '10.00'; + + $this->nvpMock + ->expects($this->at(0)) + ->method('call') + ->with(Nvp::SET_EXPRESS_CHECKOUT, $paypalRequest) + ->willReturn($paypalResponse); + + $paypalRequestDetails = [ + 'TOKEN' => $token, + ]; + + $paypalRequestDetailsResponse = include __DIR__ . '/../../../_files/paypal_set_payer_id_repsonse.php'; + + $this->nvpMock + ->expects($this->at(1)) + ->method('call') + ->with(Nvp::GET_EXPRESS_CHECKOUT_DETAILS, $paypalRequestDetails) + ->willReturn($paypalRequestDetailsResponse); + + $paypalRequestPlaceOrder = include __DIR__ . '/../../../_files/paypal_place_order_request.php'; + + $paypalRequestPlaceOrder['EMAIL'] = 'customer@example.com'; + + $this->nvpMock + ->expects($this->at(2)) + ->method('call') + ->with(Nvp::DO_EXPRESS_CHECKOUT_PAYMENT, $paypalRequestPlaceOrder) + ->willReturn( + [ + 'RESULT' => '0', + 'PNREF' => 'B7PPAC033FF2', + 'RESPMSG' => 'Approved', + 'AVSADDR' => 'Y', + 'AVSZIP' => 'Y', + 'TOKEN' => $token, + 'PAYERID' => $payerId, + 'PPREF' => '7RK43642T8939154L', + 'CORRELATIONID' => $correlationId, + 'PAYMENTTYPE' => 'instant', + 'PENDINGREASON' => 'authorization', + ] + ); + + $response = $this->graphqlController->dispatch($this->request); + $responseData = $this->json->unserialize($response->getContent()); + + $this->assertArrayHasKey('data', $responseData); + $this->assertArrayHasKey('createPaypalExpressToken', $responseData['data']); + $createTokenData = $responseData['data']['createPaypalExpressToken']; + + $this->assertArrayNotHasKey('errors', $responseData); + $this->assertEquals($paypalResponse['TOKEN'], $createTokenData['token']); + $this->assertArrayHasKey('paypal_urls', $createTokenData); + + $this->assertTrue( + isset($responseData['data']['setPaymentMethodOnCart']['cart']['selected_payment_method']['code']) + ); + $this->assertEquals( + $paymentMethod, + $responseData['data']['setPaymentMethodOnCart']['cart']['selected_payment_method']['code'] + ); + + $this->assertTrue( + isset($responseData['data']['placeOrder']['order']['order_id']) + ); + $this->assertEquals( + 'test_quote', + $responseData['data']['placeOrder']['order']['order_id'] + ); + } + + /** + * Paypal method codes provider + * + * @return array + */ + public function getPaypalCodesProvider(): array + { + return [ + ['paypal_express'], + ['payflow_express'], + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/PaypalGraphQl/Model/Resolver/Customer/PaypalExpressTokenTest.php b/dev/tests/integration/testsuite/Magento/PaypalGraphQl/Model/Resolver/Customer/PaypalExpressTokenTest.php new file mode 100644 index 0000000000000..404f54c5a642b --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/PaypalGraphQl/Model/Resolver/Customer/PaypalExpressTokenTest.php @@ -0,0 +1,129 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\PaypalGraphQl\Model\Resolver\Customer; + +use Magento\Framework\App\Request\Http; +use Magento\Framework\Webapi\Request; +use Magento\Paypal\Model\Api\Nvp; +use Magento\PaypalGraphQl\AbstractTest; +use Magento\Framework\Serialize\SerializerInterface; +use Magento\Quote\Model\QuoteIdToMaskedQuoteId; + +/** + * Test createPaypalExpressToken graphql endpoint for customer + * + * @magentoAppArea graphql + */ +class PaypalExpressTokenTest extends AbstractTest +{ + /** + * @var Http + */ + private $request; + + /** + * @var SerializerInterface + */ + private $json; + + /** + * @var QuoteIdToMaskedQuoteId + */ + private $quoteIdToMaskedId; + + protected function setUp() + { + parent::setUp(); + + $this->request = $this->objectManager->create(Http::class); + $this->json = $this->objectManager->get(SerializerInterface::class); + $this->quoteIdToMaskedId = $this->objectManager->get(QuoteIdToMaskedQuoteId::class); + } + + /** + * Test create paypal token for customer + * + * @param string $paymentMethod + * @dataProvider getPaypalCodesProvider + * @magentoConfigFixture default_store paypal/wpp/sandbox_flag 1 + * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoDataFixture Magento/GraphQl/Quote/_files/customer/create_empty_cart.php + * @magentoDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * @magentoDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php + * @magentoDataFixture Magento/GraphQl/Quote/_files/set_new_billing_address.php + * @magentoDataFixture Magento/GraphQl/Quote/_files/set_flatrate_shipping_method.php + */ + public function testResolve($paymentMethod): void + { + $this->enablePaymentMethod($paymentMethod); + if ($paymentMethod === 'payflow_express') { + $this->enablePaymentMethod('payflow_link'); + } + + $reservedQuoteId = 'test_quote'; + $cart = $this->getQuoteByReservedOrderId($reservedQuoteId); + $cartId = $cart->getId(); + $maskedCartId = $this->quoteIdToMaskedId->execute((int) $cartId); + + $query = $this->getCreateTokenMutation($maskedCartId, $paymentMethod); + + $postData = $this->json->serialize(['query' => $query]); + $this->request->setPathInfo('/graphql'); + $this->request->setMethod('POST'); + $this->request->setContent($postData); + + /** @var \Magento\Integration\Model\Oauth\Token $tokenModel */ + $tokenModel = $this->objectManager->create(\Magento\Integration\Model\Oauth\Token::class); + $customerToken = $tokenModel->createCustomerToken(1)->getToken(); + + $webApiRequest = $this->objectManager->get(Request::class); + $webApiRequest->getHeaders() + ->addHeaderLine('Content-Type', 'application/json') + ->addHeaderLine('Accept', 'application/json') + ->addHeaderLine('Authorization', 'Bearer ' . $customerToken); + $this->request->setHeaders($webApiRequest->getHeaders()); + + $paypalRequest = include __DIR__ . '/../../../_files/customer_paypal_create_token_request.php'; + if ($paymentMethod == 'payflow_express') { + $paypalRequest['SOLUTIONTYPE'] = null; + } + + $paypalResponse = [ + 'TOKEN' => 'EC-TOKEN1234', + 'CORRELATIONID' => 'c123456789', + 'ACK' => 'Success' + ]; + + $this->nvpMock + ->expects($this->once()) + ->method('call') + ->with(Nvp::SET_EXPRESS_CHECKOUT, $paypalRequest) + ->willReturn($paypalResponse); + + $response = $this->graphqlController->dispatch($this->request); + $responseData = $this->json->unserialize($response->getContent()); + $createTokenData = $responseData['data']['createPaypalExpressToken']; + $this->assertArrayNotHasKey('errors', $responseData); + $this->assertEquals($paypalResponse['TOKEN'], $createTokenData['token']); + $this->assertArrayHasKey('paypal_urls', $createTokenData); + } + + /** + * Paypal method codes provider + * + * @return array + */ + public function getPaypalCodesProvider(): array + { + return [ + ['paypal_express'], + ['payflow_express'], + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/PaypalGraphQl/Model/Resolver/Guest/PaypalExpressSetPaymentMethodTest.php b/dev/tests/integration/testsuite/Magento/PaypalGraphQl/Model/Resolver/Guest/PaypalExpressSetPaymentMethodTest.php new file mode 100644 index 0000000000000..9fe6d43776a85 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/PaypalGraphQl/Model/Resolver/Guest/PaypalExpressSetPaymentMethodTest.php @@ -0,0 +1,238 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\PaypalGraphQl\Model\Resolver\Guest; + +use Magento\Framework\App\Request\Http; +use Magento\Paypal\Model\Api\Nvp; +use Magento\PaypalGraphQl\AbstractTest; +use Magento\Framework\Serialize\SerializerInterface; +use Magento\Quote\Model\QuoteIdToMaskedQuoteId; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\Framework\UrlInterface; + +/** + * Test ExpressSetPaymentMethodTest graphql endpoint for guest + * + * @magentoAppArea graphql + */ +class PaypalExpressSetPaymentMethodTest extends AbstractTest +{ + /** + * @var Http + */ + private $request; + + /** + * @var SerializerInterface + */ + private $json; + + /** + * @var QuoteIdToMaskedQuoteId + */ + private $quoteIdToMaskedId; + + protected function setUp() + { + parent::setUp(); + + $this->request = $this->objectManager->create(Http::class); + $this->json = $this->objectManager->get(SerializerInterface::class); + $this->quoteIdToMaskedId = $this->objectManager->get(QuoteIdToMaskedQuoteId::class); + + $this->objectManager = Bootstrap::getObjectManager(); + $this->graphqlController = $this->objectManager->get(\Magento\GraphQl\Controller\GraphQl::class); + $this->request = $this->objectManager->create(Http::class); + } + + /** + * Test end to end test to process a paypal express order + * + * @param string $paymentMethod + * @return void + * @dataProvider getPaypalCodesProvider + * @magentoDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php + * @magentoDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * @magentoDataFixture Magento/GraphQl/Quote/_files/guest/set_guest_email.php + * @magentoDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php + * @magentoDataFixture Magento/GraphQl/Quote/_files/set_new_billing_address.php + * @magentoDataFixture Magento/GraphQl/Quote/_files/set_flatrate_shipping_method.php + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function testResolveGuest(string $paymentMethod): void + { + $this->enablePaymentMethod($paymentMethod); + if ($paymentMethod === 'payflow_express') { + $this->enablePaymentMethod('payflow_link'); + } + + $reservedQuoteId = 'test_quote'; + $payerId = 'PAYER123456'; + $token = 'EC-TOKEN1234'; + $correlationId = 'c123456789'; + + $cart = $this->getQuoteByReservedOrderId($reservedQuoteId); + $cartId = $this->quoteIdToMaskedId->execute((int)$cart->getId()); + + $url = $this->objectManager->get(UrlInterface::class); + $baseUrl = $url->getBaseUrl(); + + $query = <<<QUERY +mutation { + createPaypalExpressToken(input: { + cart_id: "{$cartId}", + code: "{$paymentMethod}", + urls: { + return_url: "{$baseUrl}paypal/express/return/", + cancel_url: "{$baseUrl}paypal/express/cancel/" + success_url: "{$baseUrl}checkout/onepage/success/", + pending_url: "{$baseUrl}checkout/onepage/pending/" + } + express_button: false + }) + { + __typename + token + paypal_urls{ + start + edit + } + } + setPaymentMethodOnCart(input: { + payment_method: { + code: "{$paymentMethod}", + additional_data: { + paypal_express: { + payer_id: "$payerId", + token: "$token" + } + payflow_express: { + payer_id: "$payerId", + token: "$token" + } + } + }, + cart_id: "{$cartId}"}) + { + cart { + selected_payment_method { + code + } + } + } + placeOrder(input: {cart_id: "{$cartId}"}) { + order { + order_id + } + } +} +QUERY; + + $postData = $this->json->serialize(['query' => $query]); + $this->request->setPathInfo('/graphql'); + $this->request->setMethod('POST'); + $this->request->setContent($postData); + $headers = $this->objectManager->create(\Zend\Http\Headers::class) + ->addHeaders(['Content-Type' => 'application/json']); + $this->request->setHeaders($headers); + + $paypalRequest = include __DIR__ . '/../../../_files/guest_paypal_create_token_request.php'; + $paypalResponse = [ + 'TOKEN' => $token, + 'CORRELATIONID' => $correlationId, + 'ACK' => 'Success' + ]; + + if ($paymentMethod == 'payflow_express') { + $paypalRequest['SOLUTIONTYPE'] = null; + } + + $paypalRequest['AMT'] = '30.00'; + $paypalRequest['SHIPPINGAMT'] = '10.00'; + + $this->nvpMock + ->expects($this->at(0)) + ->method('call') + ->with(Nvp::SET_EXPRESS_CHECKOUT, $paypalRequest) + ->willReturn($paypalResponse); + + $paypalRequestDetails = [ + 'TOKEN' => $token, + ]; + + $paypalRequestDetailsResponse = include __DIR__ . '/../../../_files/paypal_set_payer_id_repsonse.php'; + + $this->nvpMock + ->expects($this->at(1)) + ->method('call') + ->with(Nvp::GET_EXPRESS_CHECKOUT_DETAILS, $paypalRequestDetails) + ->willReturn($paypalRequestDetailsResponse); + + $paypalRequestPlaceOrder = include __DIR__ . '/../../../_files/paypal_place_order_request.php'; + + $this->nvpMock + ->expects($this->at(2)) + ->method('call') + ->with(Nvp::DO_EXPRESS_CHECKOUT_PAYMENT, $paypalRequestPlaceOrder) + ->willReturn( + [ + 'RESULT' => '0', + 'PNREF' => 'B7PPAC033FF2', + 'RESPMSG' => 'Approved', + 'AVSADDR' => 'Y', + 'AVSZIP' => 'Y', + 'TOKEN' => $token, + 'PAYERID' => $payerId, + 'PPREF' => '7RK43642T8939154L', + 'CORRELATIONID' => $correlationId, + 'PAYMENTTYPE' => 'instant', + 'PENDINGREASON' => 'authorization', + ] + ); + + $response = $this->graphqlController->dispatch($this->request); + $responseData = $this->json->unserialize($response->getContent()); + + $this->assertArrayHasKey('data', $responseData); + $this->assertArrayHasKey('createPaypalExpressToken', $responseData['data']); + $createTokenData = $responseData['data']['createPaypalExpressToken']; + $this->assertArrayNotHasKey('errors', $responseData); + $this->assertEquals($paypalResponse['TOKEN'], $createTokenData['token']); + $this->assertArrayHasKey('paypal_urls', $createTokenData); + + $this->assertTrue( + isset($responseData['data']['setPaymentMethodOnCart']['cart']['selected_payment_method']['code']) + ); + $this->assertEquals( + $paymentMethod, + $responseData['data']['setPaymentMethodOnCart']['cart']['selected_payment_method']['code'] + ); + + $this->assertTrue( + isset($responseData['data']['placeOrder']['order']['order_id']) + ); + $this->assertEquals( + 'test_quote', + $responseData['data']['placeOrder']['order']['order_id'] + ); + } + + /** + * Paypal method codes provider + * + * @return array + */ + public function getPaypalCodesProvider(): array + { + return [ + ['paypal_express'], + ['payflow_express'], + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/PaypalGraphQl/Model/Resolver/Guest/PaypalExpressTokenTest.php b/dev/tests/integration/testsuite/Magento/PaypalGraphQl/Model/Resolver/Guest/PaypalExpressTokenTest.php new file mode 100644 index 0000000000000..d68f91e9fd623 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/PaypalGraphQl/Model/Resolver/Guest/PaypalExpressTokenTest.php @@ -0,0 +1,232 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\PaypalGraphQl\Model\Resolver\Guest; + +use Magento\Framework\App\Request\Http; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\GraphQl\Exception\GraphQlInputException; +use Magento\Paypal\Model\Api\Nvp; +use Magento\PaypalGraphQl\AbstractTest; +use Magento\Framework\Serialize\SerializerInterface; +use Magento\Quote\Model\QuoteIdToMaskedQuoteId; + +/** + * Test createPaypalExpressToken graphql endpoint for guest + * + * @magentoAppArea graphql + */ +class PaypalExpressTokenTest extends AbstractTest +{ + /** + * @var Http + */ + private $request; + + /** + * @var SerializerInterface + */ + private $json; + + /** + * @var QuoteIdToMaskedQuoteId + */ + private $quoteIdToMaskedId; + + protected function setUp() + { + parent::setUp(); + + $this->request = $this->objectManager->create(Http::class); + $this->json = $this->objectManager->get(SerializerInterface::class); + $this->quoteIdToMaskedId = $this->objectManager->get(QuoteIdToMaskedQuoteId::class); + } + + /** + * Test create paypal token for guest + * + * @param string $paymentMethod + * @return void + * @dataProvider getPaypalCodesProvider + * @magentoDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php + * @magentoDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * @magentoDataFixture Magento/GraphQl/Quote/_files/guest/set_guest_email.php + * @magentoDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php + * @magentoDataFixture Magento/GraphQl/Quote/_files/set_new_billing_address.php + * @magentoDataFixture Magento/GraphQl/Quote/_files/set_flatrate_shipping_method.php + */ + public function testResolve($paymentMethod): void + { + $this->enablePaymentMethod($paymentMethod); + if ($paymentMethod === 'payflow_express') { + $this->enablePaymentMethod('payflow_link'); + } + $reservedQuoteId = 'test_quote'; + $cart = $this->getQuoteByReservedOrderId($reservedQuoteId); + + $cartId = $this->quoteIdToMaskedId->execute((int)$cart->getId()); + $query = $this->getCreateTokenMutation($cartId, $paymentMethod); + + $postData = $this->json->serialize(['query' => $query]); + $this->request->setPathInfo('/graphql'); + $this->request->setMethod('POST'); + $this->request->setContent($postData); + $headers = $this->objectManager->create(\Zend\Http\Headers::class) + ->addHeaders(['Content-Type' => 'application/json']); + $this->request->setHeaders($headers); + + $paypalRequest = include __DIR__ . '/../../../_files/guest_paypal_create_token_request.php'; + if ($paymentMethod == 'payflow_express') { + $paypalRequest['SOLUTIONTYPE'] = null; + } + $paypalResponse = [ + 'TOKEN' => 'EC-TOKEN1234', + 'CORRELATIONID' => 'c123456789', + 'ACK' => 'Success' + ]; + + $this->nvpMock + ->expects($this->once()) + ->method('call') + ->with(Nvp::SET_EXPRESS_CHECKOUT, $paypalRequest) + ->willReturn($paypalResponse); + + $response = $this->graphqlController->dispatch($this->request); + $responseData = $this->json->unserialize($response->getContent()); + $createTokenData = $responseData['data']['createPaypalExpressToken']; + + $this->assertArrayNotHasKey('errors', $responseData); + $this->assertEquals($paypalResponse['TOKEN'], $createTokenData['token']); + $this->assertArrayHasKey('paypal_urls', $createTokenData); + } + + /** + * Test create paypal token for guest + * + * @param string $paymentMethod + * @return void + * @dataProvider getPaypalCodesProvider + * @magentoDataFixture Magento/GraphQl/Catalog/_files/simple_product.php + * @magentoDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php + * @magentoDataFixture Magento/GraphQl/Quote/_files/add_simple_product.php + * @magentoDataFixture Magento/GraphQl/Quote/_files/guest/set_guest_email.php + * @magentoDataFixture Magento/GraphQl/Quote/_files/set_new_shipping_address.php + * @magentoDataFixture Magento/GraphQl/Quote/_files/set_new_billing_address.php + * @magentoDataFixture Magento/GraphQl/Quote/_files/set_flatrate_shipping_method.php + */ + public function testResolveWithPaypalError($paymentMethod): void + { + $this->enablePaymentMethod($paymentMethod); + if ($paymentMethod === 'payflow_express') { + $this->enablePaymentMethod('payflow_link'); + } + $reservedQuoteId = 'test_quote'; + $cart = $this->getQuoteByReservedOrderId($reservedQuoteId); + + $cartId = $this->quoteIdToMaskedId->execute((int)$cart->getId()); + $query = $this->getCreateTokenMutation($cartId, $paymentMethod); + + $postData = $this->json->serialize(['query' => $query]); + $this->request->setPathInfo('/graphql'); + $this->request->setMethod('POST'); + $this->request->setContent($postData); + $headers = $this->objectManager->create(\Zend\Http\Headers::class) + ->addHeaders(['Content-Type' => 'application/json']); + $this->request->setHeaders($headers); + + $paypalRequest = include __DIR__ . '/../../../_files/guest_paypal_create_token_request.php'; + if ($paymentMethod == 'payflow_express') { + $paypalRequest['SOLUTIONTYPE'] = null; + } + $expectedExceptionMessage = "PayPal gateway has rejected request. Sample PayPal Error."; + $expectedException = new LocalizedException(__($expectedExceptionMessage)); + + $this->nvpMock + ->expects($this->once()) + ->method('call') + ->with(Nvp::SET_EXPRESS_CHECKOUT, $paypalRequest) + ->willThrowException($expectedException); + + $response = $this->graphqlController->dispatch($this->request); + $responseData = $this->json->unserialize($response->getContent()); + $this->assertArrayHasKey('createPaypalExpressToken', $responseData['data']); + $this->assertEmpty($responseData['data']['createPaypalExpressToken']); + $this->assertArrayHasKey('errors', $responseData); + $actualError = $responseData['errors'][0]; + $this->assertEquals($expectedExceptionMessage, $actualError['message']); + $this->assertEquals(GraphQlInputException::EXCEPTION_CATEGORY, $actualError['category']); + } + + /** + * Test redirect Urls are validated + * + * @return void + * @magentoDataFixture Magento/GraphQl/Quote/_files/guest/create_empty_cart.php + */ + public function testResolveWithInvalidRedirectUrl(): void + { + $paymentMethod = 'paypal_express'; + $this->enablePaymentMethod($paymentMethod); + $reservedQuoteId = 'test_quote'; + $cart = $this->getQuoteByReservedOrderId($reservedQuoteId); + + $cartId = $this->quoteIdToMaskedId->execute((int)$cart->getId()); + $query = <<<QUERY +mutation { + createPaypalExpressToken(input: { + cart_id: "{$cartId}", + code: "{$paymentMethod}", + urls: { + return_url: "http://mangeto.test/paypal/express/return/", + cancel_url: "http://mangeto.test/paypal/express/cancel/" + success_url: "not/a/url", + pending_url: "http://mangeto.test/checkout/onepage/pending/" + } + }) + { + __typename + token + paypal_urls{ + start + edit + } + } +} +QUERY; + + $postData = $this->json->serialize(['query' => $query]); + $this->request->setPathInfo('/graphql'); + $this->request->setMethod('POST'); + $this->request->setContent($postData); + $headers = $this->objectManager->create(\Zend\Http\Headers::class) + ->addHeaders(['Content-Type' => 'application/json']); + $this->request->setHeaders($headers); + + $expectedExceptionMessage = "Invalid URL 'not/a/url'."; + + $response = $this->graphqlController->dispatch($this->request); + $responseData = $this->json->unserialize($response->getContent()); + $this->assertArrayHasKey('errors', $responseData); + $actualError = $responseData['errors'][0]; + $this->assertEquals($expectedExceptionMessage, $actualError['message']); + $this->assertEquals(GraphQlInputException::EXCEPTION_CATEGORY, $actualError['category']); + } + + /** + * Paypal method codes provider + * + * @return array + */ + public function getPaypalCodesProvider(): array + { + return [ + ['paypal_express'], + ['payflow_express'], + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/PaypalGraphQl/_files/customer_paypal_create_token_request.php b/dev/tests/integration/testsuite/Magento/PaypalGraphQl/_files/customer_paypal_create_token_request.php new file mode 100644 index 0000000000000..342074daec1d2 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/PaypalGraphQl/_files/customer_paypal_create_token_request.php @@ -0,0 +1,56 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Framework\UrlInterface; +use Magento\TestFramework\ObjectManager; + +$url = ObjectManager::getInstance()->get(UrlInterface::class); +$baseUrl = $url->getBaseUrl(); + +return [ + 'PAYMENTACTION' => 'Authorization', + 'AMT' => '30.00', + 'CURRENCYCODE' => 'USD', + 'RETURNURL' => $baseUrl . 'paypal/express/return/', + 'CANCELURL' => $baseUrl . 'paypal/express/cancel/', + 'INVNUM' => 'test_quote', + 'SOLUTIONTYPE' => 'Mark', + 'GIROPAYCANCELURL' => $baseUrl . 'paypal/express/cancel/', + 'GIROPAYSUCCESSURL' => $baseUrl . 'checkout/onepage/success/', + 'BANKTXNPENDINGURL' => $baseUrl . 'checkout/onepage/pending/', + 'SHIPPINGAMT' => '10.00', + 'ITEMAMT' => '20.00', + 'TAXAMT' => '0.00', + 'L_NUMBER0' => null, + 'L_NAME0' => 'Simple Product', + 'L_QTY0' => 2, + 'L_AMT0' => '10.00', + 'BUSINESS' => 'CompanyName', + 'NOTETEXT' => null, + 'EMAIL' => 'customer@example.com', + 'FIRSTNAME' => 'John', + 'LASTNAME' => 'Smith', + 'MIDDLENAME' => null, + 'SALUTATION' => null, + 'SUFFIX' => null, + 'COUNTRYCODE' => 'US', + 'STATE' => 'AL', + 'CITY' => 'CityM', + 'STREET' => 'Green str, 67', + 'ZIP' => '75477', + 'PHONENUM' => '3468676', + 'SHIPTOCOUNTRYCODE' => 'US', + 'SHIPTOSTATE' => 'AL', + 'SHIPTOCITY' => 'CityM', + 'SHIPTOSTREET' => 'Green str, 67', + 'SHIPTOZIP' => '75477', + 'SHIPTOPHONENUM' => '3468676', + 'SHIPTOSTREET2' => '', + 'STREET2' => '', + 'SHIPTONAME' => 'John Smith', + 'ADDROVERRIDE' => 1 +]; diff --git a/dev/tests/integration/testsuite/Magento/PaypalGraphQl/_files/guest_paypal_create_token_request.php b/dev/tests/integration/testsuite/Magento/PaypalGraphQl/_files/guest_paypal_create_token_request.php new file mode 100644 index 0000000000000..37bb11e3f075c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/PaypalGraphQl/_files/guest_paypal_create_token_request.php @@ -0,0 +1,56 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Framework\UrlInterface; +use Magento\TestFramework\ObjectManager; + +$url = ObjectManager::getInstance()->get(UrlInterface::class); +$baseUrl = $url->getBaseUrl(); + +return [ + 'PAYMENTACTION' => 'Authorization', + 'AMT' => '30.00', + 'CURRENCYCODE' => 'USD', + 'RETURNURL' => $baseUrl . 'paypal/express/return/', + 'CANCELURL' => $baseUrl . 'paypal/express/cancel/', + 'INVNUM' => 'test_quote', + 'SOLUTIONTYPE' => 'Mark', + 'GIROPAYCANCELURL' => $baseUrl . 'paypal/express/cancel/', + 'GIROPAYSUCCESSURL' => $baseUrl . 'checkout/onepage/success/', + 'BANKTXNPENDINGURL' => $baseUrl . 'checkout/onepage/pending/', + 'SHIPPINGAMT' => '10.00', + 'ITEMAMT' => '20.00', + 'TAXAMT' => '0.00', + 'L_NUMBER0' => null, + 'L_NAME0' => 'Simple Product', + 'L_QTY0' => 2, + 'L_AMT0' => '10.00', + 'BUSINESS' => 'CompanyName', + 'NOTETEXT' => null, + 'EMAIL' => 'guest@example.com', + 'FIRSTNAME' => 'John', + 'LASTNAME' => 'Smith', + 'MIDDLENAME' => null, + 'SALUTATION' => null, + 'SUFFIX' => null, + 'COUNTRYCODE' => 'US', + 'STATE' => 'AL', + 'CITY' => 'CityM', + 'STREET' => 'Green str, 67', + 'ZIP' => '75477', + 'PHONENUM' => '3468676', + 'SHIPTOCOUNTRYCODE' => 'US', + 'SHIPTOSTATE' => 'AL', + 'SHIPTOCITY' => 'CityM', + 'SHIPTOSTREET' => 'Green str, 67', + 'SHIPTOZIP' => '75477', + 'SHIPTOPHONENUM' => '3468676', + 'SHIPTOSTREET2' => '', + 'STREET2' => '', + 'SHIPTONAME' => 'John Smith', + 'ADDROVERRIDE' => 1 +]; diff --git a/dev/tests/integration/testsuite/Magento/PaypalGraphQl/_files/paypal_place_order_request.php b/dev/tests/integration/testsuite/Magento/PaypalGraphQl/_files/paypal_place_order_request.php new file mode 100644 index 0000000000000..e74409485c78a --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/PaypalGraphQl/_files/paypal_place_order_request.php @@ -0,0 +1,57 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Framework\App\ProductMetadataInterface; +use Magento\Framework\UrlInterface; +use Magento\TestFramework\ObjectManager; + +$url = ObjectManager::getInstance()->get(UrlInterface::class); +$baseUrl = $url->getBaseUrl(); + +$productMetadata = ObjectManager::getInstance()->get(ProductMetadataInterface::class); +$button = 'Magento_Cart_' . $productMetadata->getEdition(); + +return [ + 'TOKEN' => $token, + 'PAYERID' => $payerId, + 'PAYMENTACTION' => 'Authorization', + 'AMT' => '30.00', + 'CURRENCYCODE' => 'USD', + 'BUTTONSOURCE' => $button, + 'NOTIFYURL' => $baseUrl . 'paypal/ipn/', + 'RETURNFMFDETAILS' => 1, + 'SHIPPINGAMT' => '10.00', + 'ITEMAMT' => '20.00', + 'TAXAMT' => '0.00', + 'L_NUMBER0' => null, + 'L_NAME0' => 'Simple Product', + 'L_QTY0' => 2, + 'L_AMT0' => '10.00', + 'BUSINESS' => 'CompanyName', + 'EMAIL' => 'guest@example.com', + 'FIRSTNAME' => 'John', + 'LASTNAME' => 'Smith', + 'MIDDLENAME' => null, + 'SALUTATION' => null, + 'SUFFIX' => null, + 'COUNTRYCODE' => 'US', + 'STATE' => 'AL', + 'CITY' => 'CityM', + 'STREET' => 'Green str, 67', + 'ZIP' => '75477', + 'PHONENUM' => '3468676', + 'SHIPTOCOUNTRYCODE' => 'US', + 'SHIPTOSTATE' => 'AL', + 'SHIPTOCITY' => 'CityM', + 'SHIPTOSTREET' => 'Green str, 67', + 'SHIPTOZIP' => '75477', + 'SHIPTOPHONENUM' => '3468676', + 'SHIPTOSTREET2' => '', + 'STREET2' => '', + 'SHIPTONAME' => 'John Smith', + 'ADDROVERRIDE' => 1, +]; diff --git a/dev/tests/integration/testsuite/Magento/PaypalGraphQl/_files/paypal_set_payer_id_repsonse.php b/dev/tests/integration/testsuite/Magento/PaypalGraphQl/_files/paypal_set_payer_id_repsonse.php new file mode 100644 index 0000000000000..a3fa6e5533418 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/PaypalGraphQl/_files/paypal_set_payer_id_repsonse.php @@ -0,0 +1,35 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +return [ + 'RESULT' => '0', + 'RESPMSG' => 'Approved', + 'AVSADDR' => 'Y', + 'AVSZIP' => 'Y', + 'TOKEN' => $token, + 'PAYERID' => $payerId, + 'CORRELATIONID' => $correlationId, + 'EMAIL' => 'guest@example.com', + 'PAYERSTATUS' => 'verified', + 'INVNUM' => 'test_quote', + 'FIRSTNAME' => 'John', + 'LASTNAME' => 'Smith', + 'SHIPTOBUSINESS' => 'Magento', + 'SHIPTOSTREET' => 'Green str, 67', + 'SHIPTOCITY' => 'CityM', + 'SHIPTOSTATE' => 'AL', + 'SHIPTOZIP' => '75477', + 'SHIPTOCOUNTRY' => 'US', + 'SHIPTOPHONENUM' => '3468676', + 'SHIPTONAME' => 'John Smith', + 'STREET' => 'Green str, 67', + 'CITY' => 'CityM', + 'STATE' => 'TX', + 'ZIP' => '75477', + 'COUNTRYCODE' => 'US', + 'ADDRESSSTATUS' => 'Y', +]; diff --git a/dev/tests/integration/testsuite/Magento/Quote/Fixtures/quote_without_customer_email.php b/dev/tests/integration/testsuite/Magento/Quote/Fixtures/quote_without_customer_email.php new file mode 100644 index 0000000000000..49fd11593c798 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Quote/Fixtures/quote_without_customer_email.php @@ -0,0 +1,28 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Quote\Api\CartRepositoryInterface; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\TestFramework\ObjectManager; + +require __DIR__ . '/../../Sales/_files/quote_with_customer.php'; + +/** @var ObjectManager $objectManager */ +$objectManager = Bootstrap::getObjectManager(); + +$quote->getPayment() + ->setMethod('checkmo'); +$quote->getShippingAddress() + ->setShippingMethod('flatrate_flatrate') + ->setCollectShippingRates(true); +$quote->collectTotals(); + +$quote->setCustomerEmail(''); + +/** @var CartRepositoryInterface $repository */ +$repository = $objectManager->get(CartRepositoryInterface::class); +$repository->save($quote); diff --git a/dev/tests/integration/testsuite/Magento/Quote/Model/Quote/AddressTest.php b/dev/tests/integration/testsuite/Magento/Quote/Model/Quote/AddressTest.php index 62e417d27c51b..545638bcb0c57 100644 --- a/dev/tests/integration/testsuite/Magento/Quote/Model/Quote/AddressTest.php +++ b/dev/tests/integration/testsuite/Magento/Quote/Model/Quote/AddressTest.php @@ -25,6 +25,15 @@ class AddressTest extends \Magento\TestFramework\Indexer\TestCase /**@var \Magento\Customer\Api\CustomerRepositoryInterface $customerRepository */ protected $customerRepository; + /** @var \Magento\Customer\Api\AddressRepositoryInterface $addressRepository */ + protected $addressRepository; + + /** @var \Magento\Framework\Reflection\DataObjectProcessor */ + protected $dataProcessor; + + /** + * phpcs:ignoreFile + */ public static function setUpBeforeClass() { $db = \Magento\TestFramework\Helper\Bootstrap::getInstance()->getBootstrap() @@ -61,6 +70,14 @@ public function setUp() $this->_address->setId(1); $this->_address->load($this->_address->getId()); $this->_address->setQuote($this->_quote); + + $this->addressRepository = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + \Magento\Customer\Api\AddressRepositoryInterface::class + ); + + $this->dataProcessor = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + \Magento\Framework\Reflection\DataObjectProcessor::class + ); } protected function tearDown() @@ -322,4 +339,23 @@ public function dataProvider() [[123, true], [123, true]] ]; } + + public function testSaveShippingAddressWithEmptyRegionId() + { + $customerAddress = $this->addressRepository->getById(1); + $customerAddress->setRegionId(0); + + $address = $this->dataProcessor->buildOutputDataArray( + $customerAddress, + \Magento\Customer\Api\Data\AddressInterface::class + ); + + $shippingAddress = $this->_quote->getShippingAddress(); + $shippingAddress->addData($address); + + $shippingAddress->save(); + + $this->assertEquals(0, $shippingAddress->getRegionId()); + $this->assertEquals(0, $shippingAddress->getRegion()); + } } diff --git a/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteManagementTest.php b/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteManagementTest.php index 356117f2b3dc8..dc784aa55efc4 100644 --- a/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteManagementTest.php +++ b/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteManagementTest.php @@ -12,9 +12,11 @@ use Magento\Framework\Api\SearchCriteriaBuilder; use Magento\Quote\Api\CartManagementInterface; use Magento\Quote\Api\CartRepositoryInterface; +use Magento\Sales\Api\OrderManagementInterface; use Magento\Sales\Api\OrderRepositoryInterface; use Magento\TestFramework\Helper\Bootstrap; use Magento\TestFramework\ObjectManager; +use PHPUnit\Framework\ExpectationFailedException; /** * Class for testing QuoteManagement model @@ -101,6 +103,33 @@ public function testSubmitWithItemOutOfStock() $this->cartManagement->placeOrder($quote->getId()); } + /** + * Tries to create an order using quote with empty customer email. + * + * Order should not start placing if order validation is failed. + * + * @magentoDataFixture Magento/Quote/Fixtures/quote_without_customer_email.php + * @expectedException \Magento\Framework\Exception\LocalizedException + * @expectedExceptionMessage Email has a wrong format + */ + public function testSubmitWithEmptyCustomerEmail() + { + $quote = $this->getQuote('test01'); + $orderManagement = $this->createMock(OrderManagementInterface::class); + $orderManagement->expects($this->never()) + ->method('place'); + $cartManagement = $this->objectManager->create( + CartManagementInterface::class, + ['orderManagement' => $orderManagement] + ); + + try { + $cartManagement->placeOrder($quote->getId()); + } catch (ExpectationFailedException $e) { + $this->fail('Place order method was not expected to be called if order validation is failed'); + } + } + /** * Gets quote by reserved order ID. * diff --git a/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteTest.php b/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteTest.php index 15f555a67e722..3667f4cc37fab 100644 --- a/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteTest.php +++ b/dev/tests/integration/testsuite/Magento/Quote/Model/QuoteTest.php @@ -330,6 +330,26 @@ public function testAssignCustomerWithAddressChange(): void } } + /** + * Customer has address with country which not allowed in website + * + * @magentoDataFixture Magento/Customer/_files/customer.php + * @magentoDataFixture Magento/Customer/_files/customer_address.php + * @magentoDataFixture Magento/Backend/_files/allowed_countries_fr.php + * @return void + */ + public function testAssignCustomerWithAddressChangeWithNotAllowedCountry() + { + /** @var Quote $quote */ + $quote = $this->objectManager->create(Quote::class); + $customerData = $this->_prepareQuoteForTestAssignCustomerWithAddressChange($quote); + $quote->assignCustomerWithAddressChange($customerData); + + /** Check that addresses are empty */ + $this->assertNull($quote->getBillingAddress()->getCountryId()); + $this->assertNull($quote->getShippingAddress()->getCountryId()); + } + /** * @magentoDataFixture Magento/Catalog/_files/product_simple_duplicated.php * @return void diff --git a/dev/tests/integration/testsuite/Magento/Reports/Controller/Adminhtml/Report/Product/SoldTest.php b/dev/tests/integration/testsuite/Magento/Reports/Controller/Adminhtml/Report/Product/SoldTest.php new file mode 100644 index 0000000000000..951ec465123bd --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Reports/Controller/Adminhtml/Report/Product/SoldTest.php @@ -0,0 +1,22 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Reports\Controller\Adminhtml\Report\Product; + +/** + * @magentoAppArea adminhtml + */ +class SoldTest extends \Magento\TestFramework\TestCase\AbstractBackendController +{ + public function testExecute() + { + $this->dispatch('backend/reports/report_product/sold'); + $actual = $this->getResponse()->getBody(); + $this->assertContains('Ordered Products Report', $actual); + //verify if SKU column is presented on grid + $this->assertContains('SKU', $actual); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Reports/Model/ResourceModel/Report/Sold/CollectionTest.php b/dev/tests/integration/testsuite/Magento/Reports/Model/ResourceModel/Report/Sold/CollectionTest.php new file mode 100644 index 0000000000000..4623fa803d3b7 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Reports/Model/ResourceModel/Report/Sold/CollectionTest.php @@ -0,0 +1,41 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +namespace Magento\Reports\Model\ResourceModel\Report\Sold; + +/** + * Class CollectionTest + */ +class CollectionTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var \Magento\Reports\Model\ResourceModel\Product\Sold\Collection + */ + private $collection; + + protected function setUp() + { + /** + * @var \Magento\Reports\Model\ResourceModel\Product\Sold\Collection + */ + $this->collection = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( + \Magento\Reports\Model\ResourceModel\Product\Sold\Collection::class + ); + } + + /** + * @magentoDataFixture Magento/Sales/_files/order_item_with_configurable_for_reorder.php + */ + public function testFilterByProductTypeException() + { + $items = $this->collection->addOrderedQty()->getItems(); + $this->assertEquals(1, count($items)); + $orderItem = array_shift($items); + $this->assertEquals('1.0000', $orderItem['ordered_qty']); + $this->assertEquals('Configurable Product', $orderItem['order_items_name']); + //verify if order_item_sku exists in return data + $this->assertEquals('simple_20', $orderItem['order_items_sku']); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Review/Block/Product/ReviewRendererTest.php b/dev/tests/integration/testsuite/Magento/Review/Block/Product/ReviewRendererTest.php new file mode 100644 index 0000000000000..164bb14361c7e --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Review/Block/Product/ReviewRendererTest.php @@ -0,0 +1,34 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Review\Block\Product; + +/** + * Class ReviewRendererTest + */ +class ReviewRendererTest extends \PHPUnit\Framework\TestCase +{ + /** + * Test verifies ReviewRenderer::getReviewsSummaryHtml call with $displayIfNoReviews = false + * The reviews summary will be shown as expected only if there is at least one review available + * + * @magentoDataFixture Magento/Review/_files/different_reviews.php + * @magentoAppArea frontend + */ + public function testGetReviewSummaryHtml() + { + $productSku = 'simple'; + $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + /** @var \Magento\Catalog\Api\ProductRepositoryInterface $productRepository */ + $productRepository = $objectManager->create(\Magento\Catalog\Api\ProductRepositoryInterface::class); + $product = $productRepository->get($productSku); + /** @var ReviewRenderer $reviewRenderer */ + $reviewRenderer = $objectManager->create(ReviewRenderer::class); + $actualResult = $reviewRenderer->getReviewsSummaryHtml($product); + $this->assertEquals(2, $reviewRenderer->getReviewsCount()); + $this->assertContains('<span itemprop="reviewCount">2</span>', $actualResult); + } +} diff --git a/dev/tests/integration/testsuite/Magento/Review/_files/different_reviews.php b/dev/tests/integration/testsuite/Magento/Review/_files/different_reviews.php index 627434111fe79..67d7dd961d4e3 100644 --- a/dev/tests/integration/testsuite/Magento/Review/_files/different_reviews.php +++ b/dev/tests/integration/testsuite/Magento/Review/_files/different_reviews.php @@ -4,6 +4,7 @@ * See COPYING.txt for license details. */ +// phpcs:ignore Magento2.Security.IncludeFile require __DIR__ . '/../../../Magento/Catalog/_files/product_simple.php'; $review = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create( @@ -71,3 +72,4 @@ )->getStore()->getId() ] )->save(); +$review->aggregate(); diff --git a/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/Create/Form/AddressTest.php b/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/Create/Form/AddressTest.php index f5a22ec19ccf3..b908e870cbdd0 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/Create/Form/AddressTest.php +++ b/dev/tests/integration/testsuite/Magento/Sales/Block/Adminhtml/Order/Create/Form/AddressTest.php @@ -122,6 +122,10 @@ public function testGetAddressCollectionJson() 'postcode' => '90230', 'telephone' => '3468676', 'vat_id' => false, + 'prefix' => false, + 'middlename' => false, + 'suffix' => false, + 'fax' => false ], $addresses[1]->getId() => [ 'telephone' => '845454465', @@ -135,6 +139,10 @@ public function testGetAddressCollectionJson() 'region' => false, 'region_id' => 0, 'vat_id' => false, + 'prefix' => false, + 'middlename' => false, + 'suffix' => false, + 'fax' => false ] ]; diff --git a/dev/tests/integration/testsuite/Magento/Sales/Model/Order/AddressRepositoryTest.php b/dev/tests/integration/testsuite/Magento/Sales/Model/Order/AddressRepositoryTest.php index 7a38c14685073..deb4e09009da1 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/Model/Order/AddressRepositoryTest.php +++ b/dev/tests/integration/testsuite/Magento/Sales/Model/Order/AddressRepositoryTest.php @@ -3,19 +3,23 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Sales\Model\Order; +use Magento\Framework\ObjectManagerInterface; +use Magento\Sales\Api\Data\OrderAddressInterface; +use Magento\Sales\Api\OrderAddressRepositoryInterface; use Magento\TestFramework\Helper\Bootstrap; use Magento\Framework\Api\FilterBuilder; use Magento\Framework\Api\SearchCriteriaBuilder; use Magento\Framework\Api\SortOrderBuilder; +use PHPUnit\Framework\TestCase; /** * Class AddressRepositoryTest - * @package Magento\Sales\Model\Order] - * @magentoDbIsolation enabled */ -class AddressRepositoryTest extends \PHPUnit\Framework\TestCase +class AddressRepositoryTest extends TestCase { /** @var AddressRepository */ protected $repository; @@ -29,25 +33,28 @@ class AddressRepositoryTest extends \PHPUnit\Framework\TestCase /** @var SearchCriteriaBuilder */ private $searchCriteriaBuilder; + /** @var ObjectManagerInterface */ + private $objectManager; + + /** + * @inheritdoc + */ protected function setUp() { - $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - $this->repository = $objectManager->create(AddressRepository::class); - $this->searchCriteriaBuilder = $objectManager->create( - \Magento\Framework\Api\SearchCriteriaBuilder::class - ); - $this->filterBuilder = $objectManager->get( - \Magento\Framework\Api\FilterBuilder::class - ); - $this->sortOrderBuilder = $objectManager->get( - \Magento\Framework\Api\SortOrderBuilder::class - ); + $this->objectManager = Bootstrap::getObjectManager(); + $this->repository = $this->objectManager->get(AddressRepository::class); + $this->searchCriteriaBuilder = $this->objectManager->get(SearchCriteriaBuilder::class); + $this->filterBuilder = $this->objectManager->get(FilterBuilder::class); + $this->sortOrderBuilder = $this->objectManager->get(SortOrderBuilder::class); } /** + * Test for get list with multiple filters and sorting + * + * @return void * @magentoDataFixture Magento/Sales/_files/address_list.php */ - public function testGetListWithMultipleFiltersAndSorting() + public function testGetListWithMultipleFiltersAndSorting(): void { $filter1 = $this->filterBuilder ->setField('postcode') @@ -78,4 +85,23 @@ public function testGetListWithMultipleFiltersAndSorting() $this->assertEquals('ZX0789', array_shift($items)->getPostcode()); $this->assertEquals('47676', array_shift($items)->getPostcode()); } + + /** + * Test for formatting custom sales address multi-attribute + * + * @return void + * @magentoDataFixture Magento/Sales/_files/order_address_with_multi_attribute.php + */ + public function testFormatSalesAddressCustomMultiAttribute(): void + { + $address = $this->objectManager->get(OrderAddressInterface::class) + ->load('multiattribute@example.com', 'email'); + $address->setData('fixture_address_multiselect_attribute', ['dog', 'cat']); + $address->setData('fixture_address_multiline_attribute', ['dog', 'cat']); + + $this->objectManager->get(OrderAddressRepositoryInterface::class) + ->save($address); + $this->assertEquals('dog,cat', $address->getData('fixture_address_multiselect_attribute')); + $this->assertEquals('dog'.PHP_EOL.'cat', $address->getData('fixture_address_multiline_attribute')); + } } diff --git a/dev/tests/integration/testsuite/Magento/Sales/Model/Service/InvoiceServiceTest.php b/dev/tests/integration/testsuite/Magento/Sales/Model/Service/InvoiceServiceTest.php index e35c480e44c66..c9a2c065b0ccd 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/Model/Service/InvoiceServiceTest.php +++ b/dev/tests/integration/testsuite/Magento/Sales/Model/Service/InvoiceServiceTest.php @@ -89,4 +89,98 @@ public function prepareInvoiceSimpleProductDataProvider() 'partial invoice' => [1] ]; } + + /** + * Checks if ordered and invoiced qty of bundle product does match. + * + * @param array $qtyToInvoice + * @param array $qtyInvoiced + * @param string $errorMsg + * @return void + * @throws \Magento\Framework\Exception\LocalizedException + * @magentoDataFixture Magento/Sales/_files/order_with_bundle.php + * @dataProvider bundleProductQtyOrderedDataProvider + */ + public function testPrepareInvoiceBundleProduct( + array $qtyToInvoice, + array $qtyInvoiced, + string $errorMsg + ): void { + /** @var Order $order */ + $order = Bootstrap::getObjectManager()->create(Order::class) + ->load('100000001', 'increment_id'); + + $predefinedQtyToInvoice = $this->getPredefinedQtyToInvoice($order, $qtyToInvoice); + $invoice = $this->invoiceService->prepareInvoice($order, $predefinedQtyToInvoice); + + foreach ($invoice->getItems() as $invoiceItem) { + if (isset($qtyInvoiced[$invoiceItem->getSku()])) { + $this->assertEquals( + $qtyInvoiced[$invoiceItem->getSku()], + $invoiceItem->getQty(), + sprintf($errorMsg, $invoiceItem->getSku()) + ); + } + } + } + + /** + * Data provider for invoice creation with and w/o predefined qty to invoice. + * + * @return array + */ + public function bundleProductQtyOrderedDataProvider(): array + { + return [ + 'Create invoice w/o predefined qty' => [ + 'Qty to invoice' => [], + 'Qty ordered' => [ + 'bundle_1' => 2, + 'bundle_simple_1' => 10, + ], + 'Error msg' => 'Invoiced qty for product %s does not match.', + ], + 'Create invoice with predefined qty' => [ + 'Qty to invoice' => [ + 'bundle_1' => 2, + 'bundle_simple_1' => 10, + ], + 'Qty ordered' => [ + 'bundle_1' => 2, + 'bundle_simple_1' => 10, + ], + 'Error msg' => 'Invoiced qty for product %s does not match.', + ], + 'Create invoice with partial predefined qty for bundle' => [ + 'Qty to invoice' => [ + 'bundle_1' => 1, + ], + 'Qty ordered' => [ + 'bundle_1' => 1, + 'bundle_simple_1' => 5, + ], + 'Error msg' => 'Invoiced qty for product %s does not match.', + ], + ]; + } + + /** + * Associate product qty to invoice to order item id. + * + * @param Order $order + * @param array $qtyToInvoice + * @return array + */ + private function getPredefinedQtyToInvoice(Order $order, array $qtyToInvoice): array + { + $predefinedQtyToInvoice = []; + + foreach ($order->getAllItems() as $orderItem) { + if (array_key_exists($orderItem->getSku(), $qtyToInvoice)) { + $predefinedQtyToInvoice[$orderItem->getId()] = $qtyToInvoice[$orderItem->getSku()]; + } + } + + return $predefinedQtyToInvoice; + } } diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_address_with_multi_attribute.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_address_with_multi_attribute.php new file mode 100644 index 0000000000000..be454aef872a8 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_address_with_multi_attribute.php @@ -0,0 +1,100 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\TestFramework\Helper\Bootstrap; +use Magento\Eav\Model\Config; +use Magento\Eav\Model\Entity\Attribute\Set; +use Magento\Customer\Model\Attribute; +use Magento\Eav\Model\Entity\Type; +use Magento\Eav\Model\Entity\Attribute\Backend\ArrayBackend; +use Magento\Sales\Model\Order\Address; + +$objectManager = Bootstrap::getObjectManager(); +$addressData = [ + 'region' => 'CA', + 'region_id' => '12', + 'postcode' => '11111', + 'lastname' => 'lastname', + 'firstname' => 'firstname', + 'street' => 'street', + 'city' => 'Los Angeles', + 'email' => 'multiattribute@example.com', + 'telephone' => '2222222', + 'country_id' => 'US' +]; + +/** @var $entityType Type */ +$entityType = $objectManager->get(Config::class) + ->getEntityType('customer_address'); +/** @var $attributeSet Set */ +$attributeSet = $objectManager->get(Set::class); + +$attributeMultiselect = $objectManager->create( + Attribute::class, + [ + 'data' => [ + 'frontend_input' => 'multiselect', + 'frontend_label' => ['Multiselect Attribute'], + 'sort_order' => '0', + 'backend_type' => 'varchar', + 'is_user_defined' => 1, + 'is_system' => 0, + 'is_required' => '0', + 'is_visible' => '0', + 'attribute_set_id' => $entityType->getDefaultAttributeSetId(), + 'attribute_group_id' => $attributeSet->getDefaultGroupId($entityType->getDefaultAttributeSetId()), + 'entity_type_id' => $entityType->getId(), + 'backend_model' => ArrayBackend::class, + 'used_in_forms' => ['customer_register_address'], + 'option' => [ + 'value' => [ + 'dog' => ['Dog'], + 'cat' => ['Cat'], + ], + 'order' => [ + 'dog' => 1, + 'cat' => 2, + ], + ], + ] + ] +); + +$attributeMultiselect->setAttributeCode('fixture_address_multiselect_attribute'); +$attributeMultiselect->save(); + +$attributeMultiline = $objectManager->create( + Attribute::class, + [ + 'data' => [ + 'frontend_input' => 'multiline', + 'frontend_label' => ['Multiline Attribute'], + 'multiline_count' => 2, + 'sort_order' => '0', + 'backend_type' => 'varchar', + 'is_user_defined' => 1, + 'is_system' => 0, + 'is_required' => '0', + 'is_visible' => '0', + 'attribute_set_id' => $entityType->getDefaultAttributeSetId(), + 'attribute_group_id' => $attributeSet->getDefaultGroupId($entityType->getDefaultAttributeSetId()), + 'entity_type_id' => $entityType->getId(), + 'backend_model' => ArrayBackend::class, + 'used_in_forms' => ['customer_register_address'], + ] + ] +); + +$attributeMultiline->setAttributeCode('fixture_address_multiline_attribute'); +$attributeMultiline->save(); + +$billingAddress = $objectManager->create( + Address::class, + ['data' => $addressData] +); +$billingAddress->setAddressType('billing'); +$billingAddress->save(); diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_address_with_multi_attribute_rollback.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_address_with_multi_attribute_rollback.php new file mode 100644 index 0000000000000..28e5fb2f8182f --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_address_with_multi_attribute_rollback.php @@ -0,0 +1,62 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +use Magento\Framework\Api\FilterBuilder; +use Magento\Framework\Api\SearchCriteriaBuilder; +use Magento\TestFramework\Helper\Bootstrap; +use Magento\Sales\Api\OrderAddressRepositoryInterface; +use Magento\Sales\Api\Data\OrderAddressInterface; +use Magento\Eav\Api\AttributeRepositoryInterface; + +$attributeCodes = [ + 'fixture_address_multiselect_attribute', + 'fixture_address_multiline_attribute', +]; +$eavConfigType = 'customer_address'; + +$objectManager = Bootstrap::getObjectManager(); +/** @var OrderAddressRepositoryInterface $salesAddressRepository */ +$salesAddressRepository = $objectManager->get(OrderAddressRepositoryInterface::class); +/** @var SearchCriteriaBuilder $searchCriteriaBuilder */ +$searchCriteriaBuilder = $objectManager->get(SearchCriteriaBuilder::class); +/** @var FilterBuilder $filterBuilder */ +$filterBuilder = $objectManager->get(FilterBuilder::class); +$filters = [ + $filterBuilder->setField(OrderAddressInterface::EMAIL) + ->setValue('multiattribute@example.com') + ->create(), +]; +$searchCriteria = $searchCriteriaBuilder->addFilters($filters) + ->create(); +$saleAddresses = $salesAddressRepository->getList($searchCriteria) + ->getItems(); +foreach ($saleAddresses as $saleAddress) { + $salesAddressRepository->delete($saleAddress); +} + +/** @var AttributeRepositoryInterface $attributerepository */ +$attributeRepository = $objectManager->get(AttributeRepositoryInterface::class); +/** @var FilterBuilder $filterBuilder */ +$filterBuilder = $objectManager->get(FilterBuilder::class); +$filters = [ + $filterBuilder->setField('attribute_code') + ->setValue( + [ + 'fixture_address_multiline_attribute', + 'fixture_address_multiselect_attribute', + ] + ) + ->setConditionType('IN') + ->create(), +]; +$searchCriteria = $searchCriteriaBuilder->addFilters($filters) + ->create(); +$attributes = $attributeRepository->getList($eavConfigType, $searchCriteria) + ->getItems(); +foreach ($attributes as $attribute) { + $attributeRepository->delete($attribute); +} diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_item_with_configurable_for_reorder.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_item_with_configurable_for_reorder.php index 67f80ca9b2d09..8f25b066fa3ce 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/_files/order_item_with_configurable_for_reorder.php +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_item_with_configurable_for_reorder.php @@ -7,10 +7,11 @@ use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Catalog\Model\ProductRepository; +// phpcs:ignore Magento2.Security.IncludeFile require __DIR__ . '/../../../Magento/ConfigurableProduct/_files/product_configurable.php'; $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); - +// phpcs:ignore Magento2.Security.IncludeFile $addressData = include __DIR__ . '/../../../Magento/Sales/_files/address_data.php'; $billingAddress = $objectManager->create(\Magento\Sales\Model\Order\Address::class, ['data' => $addressData]); @@ -62,6 +63,8 @@ $orderItem->setProductType($product->getTypeId()); $orderItem->setProductOptions(['info_buyRequest' => $requestInfo]); $orderItemSimple->setProductId($simpleProduct->getId()); +$orderItem->setName($product->getName()); +$orderItem->setSku($simpleProduct->getSku()); $orderItemSimple->setParentItem($orderItem); $orderItemSimple->setStoreId(0); $orderItemSimple->setProductType($simpleProduct->getTypeId()); diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_bundle.php b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_bundle.php index 4473fb8dfe72c..a962548f7a932 100644 --- a/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_bundle.php +++ b/dev/tests/integration/testsuite/Magento/Sales/_files/order_with_bundle.php @@ -18,11 +18,12 @@ $orderItems = [ [ + OrderItemInterface::SKU => 'bundle_1', + OrderItemInterface::NAME => 'bundle_1', OrderItemInterface::PRODUCT_ID => 2, OrderItemInterface::BASE_PRICE => 100, OrderItemInterface::ORDER_ID => $order->getId(), OrderItemInterface::QTY_ORDERED => 2, - OrderItemInterface::QTY_INVOICED => 2, OrderItemInterface::PRICE => 100, OrderItemInterface::ROW_TOTAL => 102, OrderItemInterface::PRODUCT_TYPE => 'bundle', @@ -35,18 +36,17 @@ ], 'children' => [ [ + OrderItemInterface::SKU => 'bundle_simple_1', + OrderItemInterface::NAME => 'bundle_simple_1', OrderItemInterface::PRODUCT_ID => 13, OrderItemInterface::ORDER_ID => $order->getId(), OrderItemInterface::QTY_ORDERED => 10, - OrderItemInterface::QTY_INVOICED => 10, OrderItemInterface::BASE_PRICE => 90, OrderItemInterface::PRICE => 90, OrderItemInterface::ROW_TOTAL => 92, OrderItemInterface::PRODUCT_TYPE => 'simple', 'product_options' => [ - 'bundle_selection_attributes' => [ - 'qty' => 2, - ], + 'bundle_selection_attributes' => '{"qty":5}', ], ], ], diff --git a/dev/tests/integration/testsuite/Magento/SendFriend/Controller/SendmailTest.php b/dev/tests/integration/testsuite/Magento/SendFriend/Controller/SendmailTest.php index a075398e9cdb7..f5851a55d760a 100644 --- a/dev/tests/integration/testsuite/Magento/SendFriend/Controller/SendmailTest.php +++ b/dev/tests/integration/testsuite/Magento/SendFriend/Controller/SendmailTest.php @@ -85,6 +85,24 @@ public function testSendActionAsGuestWithInvalidData() ); } + /** + * Share the product invisible in catalog to friend as guest customer + * + * @magentoDbIsolation enabled + * @magentoAppIsolation enabled + * @magentoConfigFixture default_store sendfriend/email/enabled 1 + * @magentoConfigFixture default_store sendfriend/email/allow_guest 1 + * @magentoDataFixture Magento/Catalog/_files/simple_products_not_visible_individually.php + */ + public function testSendInvisibleProduct() + { + $product = $this->getInvisibleProduct(); + $this->prepareRequestData(); + + $this->dispatch('sendfriend/product/sendmail/id/' . $product->getId()); + $this->assert404NotFound(); + } + /** * @return ProductInterface */ @@ -93,6 +111,14 @@ private function getProduct() return $this->_objectManager->get(ProductRepositoryInterface::class)->get('custom-design-simple-product'); } + /** + * @return ProductInterface + */ + private function getInvisibleProduct() + { + return $this->_objectManager->get(ProductRepositoryInterface::class)->get('simple_not_visible_1'); + } + /** * Login the user * diff --git a/dev/tests/integration/testsuite/Magento/Store/_files/core_second_third_fixturestore.php b/dev/tests/integration/testsuite/Magento/Store/_files/core_second_third_fixturestore.php index 3cb429822eb9a..912e8e895f9c1 100644 --- a/dev/tests/integration/testsuite/Magento/Store/_files/core_second_third_fixturestore.php +++ b/dev/tests/integration/testsuite/Magento/Store/_files/core_second_third_fixturestore.php @@ -13,7 +13,7 @@ /** @var \Magento\Store\Model\Store $store */ $store = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Store\Model\Store::class); -$store->setCode('secondstore')->setName('Second Store')->setSortOrder(10)->setIsActive(1); +$store->setCode('secondstore')->setWebsiteId($websiteId)->setName('Second Store')->setSortOrder(10)->setIsActive(1); $store->save(); /** @var \Magento\Store\Model\Website $website */ @@ -25,5 +25,5 @@ /** @var \Magento\Store\Model\Store $store */ $store = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Store\Model\Store::class); -$store->setCode('thirdstore')->setName('Third Store')->setSortOrder(10)->setIsActive(1); +$store->setCode('thirdstore')->setWebsiteId($websiteId)->setName('Third Store')->setSortOrder(10)->setIsActive(1); $store->save(); diff --git a/dev/tests/integration/testsuite/Magento/Translation/Model/Js/PreProcessorTest.php b/dev/tests/integration/testsuite/Magento/Translation/Model/Js/PreProcessorTest.php new file mode 100644 index 0000000000000..f844f3c271e6e --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Translation/Model/Js/PreProcessorTest.php @@ -0,0 +1,189 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\Translation\Model\Js; + +use Magento\TestFramework\Helper\Bootstrap; +use Magento\Framework\View\FileSystem; +use Magento\TestFramework\Helper\CacheCleaner; +use Magento\Framework\Translate; +use Magento\Framework\App\AreaList; +use Magento\Framework\Phrase; +use Magento\Framework\Phrase\RendererInterface; + +/** + * Class for testing translation. + */ +class PreProcessorTest extends \PHPUnit\Framework\TestCase +{ + /** + * @var PreProcessor + */ + private $model; + + /** + * @var RendererInterface + */ + private $origRenderer; + + /** + * Set up. + */ + protected function setUp() + { + $viewFileSystem = $this->createPartialMock(FileSystem::class, ['getLocaleFileName']); + $viewFileSystem->expects($this->any())->method('getLocaleFileName') + ->willReturn( + // phpcs:ignore Magento2.Functions.DiscouragedFunction + dirname(__DIR__) . '/_files/Magento/Store/i18n/en_AU.csv' + ); + + $objectManager = Bootstrap::getObjectManager(); + $objectManager->addSharedInstance($viewFileSystem, FileSystem::class); + $translator = $objectManager->create(Translate::class); + $objectManager->addSharedInstance($translator, Translate::class); + $areaList = $this->getMockBuilder(AreaList::class)->disableOriginalConstructor()->getMock(); + $this->origRenderer = Phrase::getRenderer(); + Phrase::setRenderer( + $objectManager->get(RendererInterface::class) + ); + + $this->model = $objectManager->create( + PreProcessor::class, + [ + 'translate' => $translator, + 'areaList' => $areaList + ] + ); + + $translator->setLocale('en_AU'); + $translator->loadData(); + } + + /** + * Tear down. + */ + protected function tearDown() + { + Phrase::setRenderer($this->origRenderer); + } + + /** + * Test for backend translation strategy. + * + * @param string $content + * @param string $translation + * @return void + * @dataProvider contentForTranslateDataProvider + */ + public function testProcess(string $content, string $translation) + { + CacheCleaner::cleanAll(); + $this->assertEquals($translation, $this->model->translate($content)); + } + + /** + * Data provider for translation. + * + * @return array + */ + public function contentForTranslateDataProvider() + { + return [ + 'i18n_js_file_error' => [ + 'setTranslateProp = function (el, original) { + var location = $(el).prop(\'tagName\').toLowerCase(), + translated = $.mage.__(original), + translationData = { + shown: translated, + translated: translated, + original: original + }, + translateAttr = composeTranslateAttr(translationData, location); + + $(el).attr(\'data-translate\', translateAttr); + + setText(el, translationData.shown); + },', + 'setTranslateProp = function (el, original) { + var location = $(el).prop(\'tagName\').toLowerCase(), + translated = $.mage.__(original), + translationData = { + shown: translated, + translated: translated, + original: original + }, + translateAttr = composeTranslateAttr(translationData, location); + + $(el).attr(\'data-translate\', translateAttr); + + setText(el, translationData.shown); + },' + ], + 'checkTranslationWithWhiteSpaces' => [ + <<<i18n + title: $.mage.__( + 'Original value for Magento_Store module' + ), + title: \$t( + 'Original value for Magento_Store module' + ); + title: jQuery.mage.__( + 'Original value for Magento_Store module' + ); +i18n + , + <<<i18n + title: 'Translated value for Magento_Store module in en_AU', + title: 'Translated value for Magento_Store module in en_AU'; + title: 'Translated value for Magento_Store module in en_AU'; +i18n + ], + 'checkTranslationWithReplace' => [ + <<<i18n + $.mage.__('The maximum you may purchase is %1.').replace('%1', params.maxAllowed); + \$t('The maximum you may purchase is %1.').replace('%1', params.maxAllowed); +i18n + , + <<<i18n + 'The maximum you may purchase is %1.'.replace('%1', params.maxAllowed); + 'The maximum you may purchase is %1.'.replace('%1', params.maxAllowed); +i18n + ], + 'checkAvoidingMatchingWithJsInString' => [ + <<<i18n + \$t('Payment ' + this.getTitle() + ' can\'t be initialized') + \$t( + 'Set unique country-state combinations within the same fixed product tax. ' + + 'Verify the combinations and try again.' + ) +i18n + , + <<<i18n + \$t('Payment ' + this.getTitle() + ' can\'t be initialized') + \$t( + 'Set unique country-state combinations within the same fixed product tax. ' + + 'Verify the combinations and try again.' + ) +i18n + ], + 'checkAvoidMatchingPhtml' => [ + <<<i18n + globalMessageList.addErrorMessage({ + message: \$t(<?= /* @noEscape */ json_encode(\$params['error_msg'])?>) + }); +i18n + , + <<<i18n + globalMessageList.addErrorMessage({ + message: \$t(<?= /* @noEscape */ json_encode(\$params['error_msg'])?>) + }); +i18n + ] + ]; + } +} diff --git a/dev/tests/integration/testsuite/Magento/UrlRewrite/_files/url_rewrite_not_existing_entity.php b/dev/tests/integration/testsuite/Magento/UrlRewrite/_files/url_rewrite_not_existing_entity.php new file mode 100644 index 0000000000000..052eac6f79d48 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/UrlRewrite/_files/url_rewrite_not_existing_entity.php @@ -0,0 +1,25 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\TestFramework\Helper\Bootstrap; +use Magento\UrlRewrite\Model\UrlRewrite; + +$objectManager = Bootstrap::getObjectManager(); + +/** @var int Product Id */ +$productId = 708579; + +/** @var UrlRewrite $urlRewrite */ +$urlRewrite = $objectManager->create(UrlRewrite::class); +$urlRewrite->setEntityType('custom') + ->setRequestPath('non-exist-entity.html') + ->setTargetPath('catalog/product/view/id/' . $productId) + ->setRedirectType(0) + ->setStoreId(1) + ->setDescription(null) + ->setIsAutogenerated(0); + +$urlRewrite->save(); diff --git a/dev/tests/integration/testsuite/Magento/UrlRewrite/_files/url_rewrite_not_existing_entity_rollback.php b/dev/tests/integration/testsuite/Magento/UrlRewrite/_files/url_rewrite_not_existing_entity_rollback.php new file mode 100644 index 0000000000000..12cbd2c704bfb --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/UrlRewrite/_files/url_rewrite_not_existing_entity_rollback.php @@ -0,0 +1,15 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +use Magento\TestFramework\Helper\Bootstrap; +use Magento\UrlRewrite\Model\UrlRewrite; + +$objectManager = Bootstrap::getObjectManager(); + +/** @var UrlRewrite $urlRewrite */ +$urlRewrite = $objectManager->create(UrlRewrite::class); +$urlRewrite->load('non-exist-entity.html', 'request_path'); +$urlRewrite->delete(); diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Braintree/frontend/js/paypal/button.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Braintree/frontend/js/paypal/button.test.js deleted file mode 100644 index 5614a9a1bc6e1..0000000000000 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Braintree/frontend/js/paypal/button.test.js +++ /dev/null @@ -1,93 +0,0 @@ -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -/* eslint-disable max-nested-callbacks */ -define([ - 'squire', - 'jquery' -], function (Squire) { - 'use strict'; - - describe('Magento_Braintree/js/paypal/button', function () { - var injector, - mocks, - braintree, - component, - registry, - btnId = 'braintree_paypal_btn', - tplElement = jQuery('<button id="' + btnId + '"></button>')[0]; - - require.config({ - map: { - '*': { - 'braintree': 'braintree' - } - } - }); - - injector = new Squire(); - mocks = { - 'braintree': { - paypal: { - /** Stub */ - initAuthFlow: function () {} - }, - - /** Stub */ - setup: function () {} - } - }; - - beforeEach(function (done) { - injector.mock(mocks); - - injector.require([ - 'braintree', - 'uiRegistry', - 'Magento_Braintree/js/paypal/button' - ], function (adapter, reg, Constr) { - braintree = adapter; - registry = reg; - jQuery(document.body).append(tplElement); - - spyOn(braintree, 'setup').and.callFake(function () { - registry.set('braintreePaypal.currentIntegration', braintree); - jQuery('#' + btnId).removeAttr('disabled'); - }); - - component = new Constr({ - id: btnId - }); - done(); - }); - }); - - afterEach(function () { - try { - injector.clean(); - injector.remove(); - } catch (e) {} - }); - - afterAll(function (done) { - tplElement.remove(); - registry.remove(component.integrationName); - - done(); - }); - - it('The PayPal::initAuthFlow throws an exception.', function () { - var $selector = jQuery('#' + component.id); - - spyOn(braintree.paypal, 'initAuthFlow').and.callFake(function () { - throw new TypeError('Cannot read property of undefined'); - }); - - $selector.trigger('click'); - - expect($selector.prop('disabled')).toEqual(true); - }); - }); -}); diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Braintree/frontend/js/view/payment/method-renderer/cc-form.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Braintree/frontend/js/view/payment/method-renderer/cc-form.test.js deleted file mode 100644 index 429342b43bcb2..0000000000000 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Braintree/frontend/js/view/payment/method-renderer/cc-form.test.js +++ /dev/null @@ -1,112 +0,0 @@ -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -/* eslint-disable max-nested-callbacks */ -define([ - 'jquery', - 'squire', - 'ko', - 'Magento_Ui/js/model/messages' -], function ($, Squire, ko, Messages) { - 'use strict'; - - describe('Magento_Braintree/js/view/payment/method-renderer/cc-form', function () { - var injector = new Squire(), - mocks = { - 'Magento_Checkout/js/model/checkout-data-resolver': { - - /** Stub */ - applyBillingAddress: function () { - return true; - }, - - /** Stub */ - resolveBillingAddress: function () { - return true; - } - }, - 'Magento_Checkout/js/model/quote': { - billingAddress: ko.observable(), - shippingAddress: ko.observable(), - paymentMethod: ko.observable(), - totals: ko.observable({}), - - /** Stub */ - isVirtual: function () { - return false; - } - }, - 'Magento_Braintree/js/view/payment/validator-handler': jasmine.createSpyObj( - 'validator-handler', - ['initialize'] - ), - 'Magento_Braintree/js/view/payment/adapter': jasmine.createSpyObj( - 'adapter', - ['setup', 'setConfig', 'showError'] - ) - }, - braintreeCcForm; - - beforeAll(function (done) { - window.checkoutConfig = { - quoteData: {}, - payment: { - braintree: { - hasFraudProtection: true - } - } - }; - injector.mock(mocks); - injector.require(['Magento_Braintree/js/view/payment/method-renderer/cc-form'], function (Constr) { - braintreeCcForm = new Constr({ - provider: 'provName', - name: 'test', - index: 'test', - item: { - title: 'Braintree' - } - }); - - done(); - }); - }); - - afterEach(function () { - try { - injector.clean(); - injector.remove(); - } catch (e) {} - }); - - it('Check if payment code and message container are restored after onActiveChange call.', function () { - var expectedMessageContainer = braintreeCcForm.messageContainer, - expectedCode = braintreeCcForm.code; - - braintreeCcForm.code = 'braintree-vault'; - braintreeCcForm.messageContainer = new Messages(); - - braintreeCcForm.onActiveChange(true); - - expect(braintreeCcForm.getCode()).toEqual(expectedCode); - expect(braintreeCcForm.messageContainer).toEqual(expectedMessageContainer); - }); - - it('Check if form validation fails when "Place Order" button should be active.', function () { - var errorMessage = 'Something went wrong.', - - /** - * Anonymous wrapper - */ - func = function () { - braintreeCcForm.clientConfig.onError({ - 'message': errorMessage - }); - }; - - expect(func).toThrow(errorMessage); - expect(braintreeCcForm.isPlaceOrderActionAllowed()).toBeTruthy(); - }); - }); -}); diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Braintree/frontend/js/view/payment/method-renderer/paypal.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Braintree/frontend/js/view/payment/method-renderer/paypal.test.js deleted file mode 100644 index 6ba0ed0b58f03..0000000000000 --- a/dev/tests/js/jasmine/tests/app/code/Magento/Braintree/frontend/js/view/payment/method-renderer/paypal.test.js +++ /dev/null @@ -1,120 +0,0 @@ -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ - -/* eslint-disable max-nested-callbacks */ -define([ - 'squire', - 'ko' -], function (Squire, ko) { - 'use strict'; - - describe('Magento_Braintree/js/view/payment/method-renderer/paypal', function () { - - var injector = new Squire(), - mocks = { - 'Magento_Checkout/js/model/checkout-data-resolver': { - - /** Stub */ - applyBillingAddress: function () { - return true; - }, - - /** Stub */ - resolveBillingAddress: function () { - return true; - } - }, - 'Magento_Checkout/js/model/quote': { - billingAddress: ko.observable(), - shippingAddress: ko.observable({ - postcode: '', - street: [], - canUseForBilling: ko.observable() - }), - paymentMethod: ko.observable(), - totals: ko.observable({ - 'base_grand_total': 0 - }), - - /** Stub */ - isVirtual: function () { - return false; - } - }, - 'Magento_Braintree/js/view/payment/adapter': { - config: {}, - - /** Stub */ - onReady: function () {}, - - /** Stub */ - setConfig: function (config) { - this.config = config; - }, - - /** Stub */ - setup: function () { - this.config.onReady(this.checkout); - }, - - checkout: { - /** Stub */ - teardown: function () {}, - paypal: { - /** Stub */ - initAuthFlow: function () {} - } - } - } - }, - braintreeAdapter, - component, - additionalValidator; - - beforeEach(function (done) { - window.checkoutConfig = { - quoteData: {}, - payment: { - 'braintree_paypal': { - title: 'Braintree PayPal' - } - }, - vault: {} - }; - - injector.mock(mocks); - - injector.require([ - 'Magento_Braintree/js/view/payment/adapter', - 'Magento_Checkout/js/model/payment/additional-validators', - 'Magento_Braintree/js/view/payment/method-renderer/paypal' - ], function (adapter, validator, Constr) { - braintreeAdapter = adapter; - additionalValidator = validator; - component = new Constr(); - done(); - }); - }); - - afterEach(function () { - try { - injector.clean(); - injector.remove(); - } catch (e) {} - }); - - it('The PayPal::initAuthFlow throws an exception.', function () { - - spyOn(additionalValidator, 'validate').and.returnValue(true); - spyOn(braintreeAdapter.checkout.paypal, 'initAuthFlow').and.callFake(function () { - throw new TypeError('Cannot read property of undefined'); - }); - spyOn(component.messageContainer, 'addErrorMessage'); - - component.payWithPayPal(); - expect(component.messageContainer.addErrorMessage).toHaveBeenCalled(); - }); - }); -}); diff --git a/dev/tests/static/.gitignore b/dev/tests/static/.gitignore index 651969a59ce46..175be896e8def 100644 --- a/dev/tests/static/.gitignore +++ b/dev/tests/static/.gitignore @@ -1,4 +1,6 @@ /*.xml -framework/tests/unit/*.xml -report/ -tmp/ +/framework/tests/unit/*.xml +/framework/tests/unit/var/allure-results/ +/report/ +/tmp/ +/var/allure-results/ diff --git a/dev/tests/static/framework/Magento/CodeMessDetector/Rule/Design/AllPurposeAction.php b/dev/tests/static/framework/Magento/CodeMessDetector/Rule/Design/AllPurposeAction.php index fb5938be61328..ca257c1f6eb39 100644 --- a/dev/tests/static/framework/Magento/CodeMessDetector/Rule/Design/AllPurposeAction.php +++ b/dev/tests/static/framework/Magento/CodeMessDetector/Rule/Design/AllPurposeAction.php @@ -27,6 +27,10 @@ class AllPurposeAction extends AbstractRule implements ClassAware */ public function apply(AbstractNode $node) { + // Skip validation for Abstract Controllers + if ($node->isAbstract()) { + return; + } try { $impl = class_implements($node->getFullQualifiedName(), true); } catch (\Throwable $exception) { diff --git a/dev/tests/static/framework/Magento/Sniffs/Annotation/AnnotationFormatValidator.php b/dev/tests/static/framework/Magento/Sniffs/Annotation/AnnotationFormatValidator.php index 33df6e8ae54f1..20442f9388235 100644 --- a/dev/tests/static/framework/Magento/Sniffs/Annotation/AnnotationFormatValidator.php +++ b/dev/tests/static/framework/Magento/Sniffs/Annotation/AnnotationFormatValidator.php @@ -22,7 +22,7 @@ class AnnotationFormatValidator * @param int $commentEndPtr * @return int */ - private function getShortDescriptionEndPosition(File $phpcsFile, int $shortPtr, $commentEndPtr) : int + private function getShortDescriptionEndPosition(File $phpcsFile, int $shortPtr, $commentEndPtr): int { $tokens = $phpcsFile->getTokens(); $shortPtrEnd = $shortPtr; @@ -49,22 +49,22 @@ private function validateMultiLinesInShortDescription( File $phpcsFile, int $shortPtr, int $commentEndPtr - ) : void { + ): void { $tokens = $phpcsFile->getTokens(); $shortPtrEnd = $this->getShortDescriptionEndPosition( $phpcsFile, - (int) $shortPtr, + (int)$shortPtr, $commentEndPtr ); $shortPtrEndContent = $tokens[$shortPtrEnd]['content']; if (preg_match('/^[a-z]/', $shortPtrEndContent) && $shortPtrEnd != $shortPtr && !preg_match('/\bSee\b/', $shortPtrEndContent) - && $tokens[$shortPtr]['line']+1 === $tokens[$shortPtrEnd]['line'] + && $tokens[$shortPtr]['line'] + 1 === $tokens[$shortPtrEnd]['line'] && $tokens[$shortPtrEnd]['code'] !== T_DOC_COMMENT_TAG ) { $error = 'Short description should not be in multi lines'; - $phpcsFile->addFixableError($error, $shortPtrEnd+1, 'MethodAnnotation'); + $phpcsFile->addFixableError($error, $shortPtrEnd + 1, 'MethodAnnotation'); } } @@ -81,17 +81,17 @@ private function validateSpacingBetweenShortAndLongDescriptions( int $shortPtr, int $commentEndPtr, array $emptyTypeTokens - ) : void { + ): void { $tokens = $phpcsFile->getTokens(); $shortPtrEnd = $this->getShortDescriptionEndPosition( $phpcsFile, - (int) $shortPtr, + (int)$shortPtr, $commentEndPtr ); $shortPtrEndContent = $tokens[$shortPtrEnd]['content']; if (preg_match('/^[A-Z]/', $shortPtrEndContent) && !preg_match('/\bSee\b/', $shortPtrEndContent) - && $tokens[$shortPtr]['line']+1 === $tokens[$shortPtrEnd]['line'] + && $tokens[$shortPtr]['line'] + 1 === $tokens[$shortPtrEnd]['line'] && $tokens[$shortPtrEnd]['code'] !== T_DOC_COMMENT_TAG ) { $error = 'There must be exactly one blank line between lines'; @@ -119,7 +119,7 @@ private function validateShortDescriptionFormat( int $stackPtr, int $commentEndPtr, array $emptyTypeTokens - ) : void { + ): void { $tokens = $phpcsFile->getTokens(); if ($tokens[$shortPtr]['line'] !== $tokens[$stackPtr]['line'] + 1) { $error = 'No blank lines are allowed before short description'; @@ -166,7 +166,7 @@ private function validateLongDescriptionFormat( int $shortPtrEnd, int $commentEndPtr, array $emptyTypeTokens - ) : void { + ): void { $tokens = $phpcsFile->getTokens(); $longPtr = $phpcsFile->findNext($emptyTypeTokens, $shortPtrEnd + 1, $commentEndPtr - 1, true); if (strtolower($tokens[$longPtr]['content']) === '@inheritdoc') { @@ -192,7 +192,7 @@ private function validateLongDescriptionFormat( * @param int $commentStartPtr * @param array $emptyTypeTokens */ - public function validateTagsSpacingFormat(File $phpcsFile, int $commentStartPtr, array $emptyTypeTokens) : void + public function validateTagsSpacingFormat(File $phpcsFile, int $commentStartPtr, array $emptyTypeTokens): void { $tokens = $phpcsFile->getTokens(); if (isset($tokens[$commentStartPtr]['comment_tags'][0])) { @@ -214,7 +214,7 @@ public function validateTagsSpacingFormat(File $phpcsFile, int $commentStartPtr, * @param File $phpcsFile * @param int $commentStartPtr */ - public function validateTagGroupingFormat(File $phpcsFile, int $commentStartPtr) : void + public function validateTagGroupingFormat(File $phpcsFile, int $commentStartPtr): void { $tokens = $phpcsFile->getTokens(); $tagGroups = []; @@ -256,7 +256,7 @@ public function validateTagGroupingFormat(File $phpcsFile, int $commentStartPtr) * @param File $phpcsFile * @param int $commentStartPtr */ - public function validateTagAligningFormat(File $phpcsFile, int $commentStartPtr) : void + public function validateTagAligningFormat(File $phpcsFile, int $commentStartPtr): void { $tokens = $phpcsFile->getTokens(); $noAlignmentPositions = []; @@ -287,7 +287,7 @@ public function validateTagAligningFormat(File $phpcsFile, int $commentStartPtr) * @param array $actualPositions * @return bool */ - private function allTagsAligned(array $actualPositions) + private function allTagsAligned(array $actualPositions): bool { return count(array_unique($actualPositions)) === 1; } @@ -299,7 +299,7 @@ private function allTagsAligned(array $actualPositions) * @param array $noAlignmentPositions * @return bool */ - private function noneTagsAligned(array $actualPositions, array $noAlignmentPositions) + private function noneTagsAligned(array $actualPositions, array $noAlignmentPositions): bool { return $actualPositions === $noAlignmentPositions; } @@ -317,7 +317,7 @@ private function validateNoExtraNewLineBeforeShortDescription( int $commentStartPtr, int $commentEndPtr, array $emptyTypeTokens - ) : void { + ): void { $tokens = $phpcsFile->getTokens(); $prevPtr = $phpcsFile->findPrevious($emptyTypeTokens, $commentEndPtr - 1, $commentStartPtr, true); if ($tokens[$prevPtr]['line'] < ($tokens[$commentEndPtr]['line'] - 1)) { @@ -341,7 +341,7 @@ public function validateDescriptionFormatStructure( int $shortPtr, int $commentEndPtr, array $emptyTypeTokens - ) : void { + ): void { $tokens = $phpcsFile->getTokens(); if (isset($tokens[$commentStartPtr]['comment_tags'][0]) ) { @@ -355,7 +355,7 @@ public function validateDescriptionFormatStructure( } else { $this->validateShortDescriptionFormat( $phpcsFile, - (int) $shortPtr, + (int)$shortPtr, (int)$commentStartPtr, (int)$commentEndPtr, $emptyTypeTokens @@ -364,7 +364,7 @@ public function validateDescriptionFormatStructure( } else { $this->validateShortDescriptionFormat( $phpcsFile, - (int) $shortPtr, + (int)$shortPtr, $commentStartPtr, $commentEndPtr, $emptyTypeTokens diff --git a/dev/tests/static/framework/Magento/Sniffs/Annotation/ClassAnnotationStructureSniff.php b/dev/tests/static/framework/Magento/Sniffs/Annotation/ClassAnnotationStructureSniff.php index c37f0b500fe39..43df5658bbe0d 100644 --- a/dev/tests/static/framework/Magento/Sniffs/Annotation/ClassAnnotationStructureSniff.php +++ b/dev/tests/static/framework/Magento/Sniffs/Annotation/ClassAnnotationStructureSniff.php @@ -97,8 +97,12 @@ public function process(File $phpcsFile, $stackPtr) { $tokens = $phpcsFile->getTokens(); $previousCommentClosePtr = $phpcsFile->findPrevious(T_DOC_COMMENT_CLOSE_TAG, $stackPtr - 1, 0); - $this->validateAnnotationBlockExists($phpcsFile, (int)$previousCommentClosePtr, (int)$stackPtr); - $commentStartPtr = (int)$phpcsFile->findPrevious(T_DOC_COMMENT_OPEN_TAG, $stackPtr - 1, 0); + if (!$previousCommentClosePtr) { + $phpcsFile->addError('Comment block is missing', $stackPtr -1, 'MethodArguments'); + return; + } + $this->validateAnnotationBlockExists($phpcsFile, $previousCommentClosePtr, $stackPtr); + $commentStartPtr = $phpcsFile->findPrevious(T_DOC_COMMENT_OPEN_TAG, $stackPtr - 1, 0); $commentCloserPtr = $tokens[$commentStartPtr]['comment_closer']; $emptyTypeTokens = [ T_DOC_COMMENT_WHITESPACE, @@ -111,7 +115,7 @@ public function process(File $phpcsFile, $stackPtr) } else { $this->annotationFormatValidator->validateDescriptionFormatStructure( $phpcsFile, - (int)$commentStartPtr, + $commentStartPtr, (int) $shortPtr, $previousCommentClosePtr, $emptyTypeTokens diff --git a/dev/tests/static/framework/Magento/Sniffs/Annotation/MethodAnnotationStructureSniff.php b/dev/tests/static/framework/Magento/Sniffs/Annotation/MethodAnnotationStructureSniff.php index f05ae64a170d7..3df0753349483 100644 --- a/dev/tests/static/framework/Magento/Sniffs/Annotation/MethodAnnotationStructureSniff.php +++ b/dev/tests/static/framework/Magento/Sniffs/Annotation/MethodAnnotationStructureSniff.php @@ -4,10 +4,11 @@ * See COPYING.txt for license details. */ declare(strict_types=1); + namespace Magento\Sniffs\Annotation; -use PHP_CodeSniffer\Sniffs\Sniff; use PHP_CodeSniffer\Files\File; +use PHP_CodeSniffer\Sniffs\Sniff; /** * Sniff to validate structure of public, private, protected method annotations @@ -45,8 +46,12 @@ public function process(File $phpcsFile, $stackPtr) $tokens = $phpcsFile->getTokens(); $commentStartPtr = $phpcsFile->findPrevious(T_DOC_COMMENT_OPEN_TAG, ($stackPtr), 0); $commentEndPtr = $phpcsFile->findPrevious(T_DOC_COMMENT_CLOSE_TAG, ($stackPtr), 0); + if (!$commentStartPtr) { + $phpcsFile->addError('Comment block is missing', $stackPtr, 'MethodArguments'); + return; + } $commentCloserPtr = $tokens[$commentStartPtr]['comment_closer']; - $functionPtrContent = $tokens[$stackPtr+2]['content'] ; + $functionPtrContent = $tokens[$stackPtr + 2]['content']; if (preg_match('/(?i)__construct/', $functionPtrContent)) { return; } @@ -62,7 +67,7 @@ public function process(File $phpcsFile, $stackPtr) $this->annotationFormatValidator->validateDescriptionFormatStructure( $phpcsFile, $commentStartPtr, - (int) $shortPtr, + (int)$shortPtr, $commentEndPtr, $emptyTypeTokens ); diff --git a/dev/tests/static/framework/Magento/Sniffs/Annotation/MethodArgumentsSniff.php b/dev/tests/static/framework/Magento/Sniffs/Annotation/MethodArgumentsSniff.php index 50efca9b1ed23..9bc90ff883a6d 100644 --- a/dev/tests/static/framework/Magento/Sniffs/Annotation/MethodArgumentsSniff.php +++ b/dev/tests/static/framework/Magento/Sniffs/Annotation/MethodArgumentsSniff.php @@ -3,7 +3,6 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ - declare(strict_types=1); namespace Magento\Sniffs\Annotation; @@ -124,10 +123,9 @@ private function getMethodArguments(File $phpcsFile, int $openParenthesisPtr, in private function getMethodParameters(array $paramDefinitions): array { $paramName = []; - $paramCount = count($paramDefinitions); - for ($i = 0; $i < $paramCount; $i++) { - if (isset($paramDefinitions[$i]['paramName'])) { - $paramName[] = $paramDefinitions[$i]['paramName']; + foreach ($paramDefinitions as $paramDefinition) { + if (isset($paramDefinition['paramName'])) { + $paramName[] = $paramDefinition['paramName']; } } return $paramName; @@ -372,13 +370,12 @@ private function validateDuplicateAnnotationDoesnotExists( $parametersCount = count($paramPointers); if ($argumentsCount <= $parametersCount && $argumentsCount > 0) { $duplicateParameters = []; - $paramCount = count($paramDefinitions); - for ($i = 0; $i < $paramCount; $i++) { - if (isset($paramDefinitions[$i]['paramName'])) { - $parameterContent = $paramDefinitions[$i]['paramName']; - for ($j = $i + 1; $j < $paramCount; $j++) { - if (isset($paramDefinitions[$j]['paramName']) - && $parameterContent === $paramDefinitions[$j]['paramName'] + foreach ($paramDefinitions as $i => $paramDefinition) { + if (isset($paramDefinition['paramName'])) { + $parameterContent = $paramDefinition['paramName']; + foreach (array_slice($paramDefinitions, $i + 1) as $nextParamDefinition) { + if (isset($nextParamDefinition['paramName']) + && $parameterContent === $nextParamDefinition['paramName'] ) { $duplicateParameters[] = $parameterContent; } @@ -473,6 +470,8 @@ private function validateParameterAnnotationFormatIsCorrect( * @param array $methodArguments * @param int $previousCommentOpenPtr * @param int $previousCommentClosePtr + * + * @SuppressWarnings(PHPMD.UnusedLocalVariable) */ private function validateMethodParameterAnnotations( int $stackPtr, @@ -519,7 +518,8 @@ private function validateMethodParameterAnnotations( $paramPointers ); $tokens = $phpcsFile->getTokens(); - for ($ptr = 0; $ptr < $argumentCount; $ptr++) { + + foreach ($methodArguments as $ptr => $methodArgument) { if (isset($paramPointers[$ptr])) { $this->validateArgumentNameInParameterAnnotationExists( $stackPtr, @@ -550,8 +550,12 @@ public function process(File $phpcsFile, $stackPtr) $numTokens = count($tokens); $previousCommentOpenPtr = $phpcsFile->findPrevious(T_DOC_COMMENT_OPEN_TAG, $stackPtr - 1, 0); $previousCommentClosePtr = $phpcsFile->findPrevious(T_DOC_COMMENT_CLOSE_TAG, $stackPtr - 1, 0); - if (!$this->validateCommentBlockExists($phpcsFile, $previousCommentClosePtr, $stackPtr)) { - $phpcsFile->addError('Comment block is missing', $stackPtr, 'MethodArguments'); + if ($previousCommentClosePtr && $previousCommentOpenPtr) { + if (!$this->validateCommentBlockExists($phpcsFile, $previousCommentClosePtr, $stackPtr)) { + $phpcsFile->addError('Comment block is missing', $stackPtr, 'MethodArguments'); + return; + } + } else { return; } $openParenthesisPtr = $phpcsFile->findNext(T_OPEN_PARENTHESIS, $stackPtr + 1, $numTokens); @@ -605,6 +609,8 @@ public function process(File $phpcsFile, $stackPtr) * @param File $phpcsFile * @param array $paramPointers * + * @SuppressWarnings(PHPMD.UnusedLocalVariable) + * * @see https://devdocs.magento.com/guides/v2.3/coding-standards/docblock-standard-general.html#format-consistency */ private function validateFormattingConsistency( @@ -616,20 +622,19 @@ private function validateFormattingConsistency( $argumentPositions = []; $commentPositions = []; $tokens = $phpcsFile->getTokens(); - $argumentCount = count($methodArguments); - for ($ptr = 0; $ptr < $argumentCount; $ptr++) { + foreach ($methodArguments as $ptr => $methodArgument) { if (isset($paramPointers[$ptr])) { $paramContent = $tokens[$paramPointers[$ptr] + 2]['content']; $paramDefinition = $paramDefinitions[$ptr]; $argumentPositions[] = strpos($paramContent, $paramDefinition['paramName']); $commentPositions[] = $paramDefinition['comment'] - ? strpos($paramContent, $paramDefinition['comment']) : null; + ? strrpos($paramContent, $paramDefinition['comment']) : null; } } if (!$this->allParamsAligned($argumentPositions, $commentPositions) && !$this->noneParamsAligned($argumentPositions, $commentPositions, $paramDefinitions)) { $phpcsFile->addFixableError( - 'Visual alignment must be consistent', + 'Method arguments visual alignment must be consistent', $paramPointers[0], 'MethodArguments' ); @@ -663,6 +668,9 @@ private function noneParamsAligned(array $argumentPositions, array $commentPosit foreach ($argumentPositions as $index => $argumentPosition) { $commentPosition = $commentPositions[$index]; $type = $paramDefinitions[$index]['type']; + if ($type === null) { + continue; + } $paramName = $paramDefinitions[$index]['paramName']; if (($argumentPosition !== strlen($type) + 1) || (isset($commentPosition) && ($commentPosition !== $argumentPosition + strlen($paramName) + 1))) { diff --git a/dev/tests/static/framework/Magento/ruleset.xml b/dev/tests/static/framework/Magento/ruleset.xml index 70d1810d1eb2f..89020150035bb 100644 --- a/dev/tests/static/framework/Magento/ruleset.xml +++ b/dev/tests/static/framework/Magento/ruleset.xml @@ -15,4 +15,25 @@ <exclude-pattern>*/Test/*</exclude-pattern> <exclude-pattern>*Test.php</exclude-pattern> </rule> + <rule ref="Magento2.Files.LineLength.MaxExceeded"> + <exclude-pattern>*.phtml</exclude-pattern> + </rule> + <rule ref="Squiz.Operators.IncrementDecrementUsage"> + <exclude-pattern>*.phtml</exclude-pattern> + </rule> + <rule ref="PEAR.ControlStructures.ControlSignature"> + <exclude-pattern>*.phtml</exclude-pattern> + </rule> + <rule ref="Squiz.WhiteSpace.ScopeClosingBrace"> + <exclude-pattern>*.phtml</exclude-pattern> + </rule> + <rule ref="PEAR.Functions.FunctionCallSignature"> + <exclude-pattern>*.phtml</exclude-pattern> + </rule> + <rule ref="Squiz.ControlStructures.ControlSignature.NewlineAfterOpenBrace"> + <exclude-pattern>*.phtml</exclude-pattern> + </rule> + <rule ref="Magento2.Security.LanguageConstruct.DirectOutput"> + <exclude-pattern>*.phtml</exclude-pattern> + </rule> </ruleset> diff --git a/dev/tests/static/testsuite/Magento/Test/Php/_files/whitelist/exempt_modules/ce.php b/dev/tests/static/testsuite/Magento/Test/Php/_files/whitelist/exempt_modules/ce.php deleted file mode 100644 index 0d99320b15e7f..0000000000000 --- a/dev/tests/static/testsuite/Magento/Test/Php/_files/whitelist/exempt_modules/ce.php +++ /dev/null @@ -1,51 +0,0 @@ -<?php -/** - * Copyright © Magento, Inc. All rights reserved. - * See COPYING.txt for license details. - */ -/* These are the modules that have not been refactored from @escapeNotVerified yet. */ -return [ - 'Magento_AdminNotification', - 'Magento_AdvancedSearch', - 'Magento_Backend', - 'Magento_Backup', - 'Magento_Bundle', - 'Magento_Catalog', - 'Magento_CatalogInventory', - 'Magento_CatalogRule', - 'Magento_CatalogSearch', - 'Magento_Checkout', - 'Magento_CheckoutAgreements', - 'Magento_Config', - 'Magento_ConfigurableProduct', - 'Magento_CurrencySymbol', - 'Magento_Downloadable', - 'Magento_GiftMessage', - 'Magento_GoogleAdwords', - 'Magento_GoogleAnalytics', - 'Magento_GroupedProduct', - 'Magento_ImportExport', - 'Magento_Integration', - 'Magento_LayeredNavigation', - 'Magento_Marketplace', - 'Magento_MediaStorage', - 'Magento_Msrp', - 'Magento_Multishipping', - 'Magento_PageCache', - 'Magento_Paypal', - 'Magento_ProductVideo', - 'Magento_Reports', - 'Magento_Sales', - 'Magento_Search', - 'Magento_Shipping', - 'Magento_Store', - 'Magento_Swagger', - 'Magento_Swatches', - 'Magento_Tax', - 'Magento_TaxImportExport', - 'Magento_Theme', - 'Magento_Translation', - 'Magento_Ui', - 'Magento_User', - 'Magento_Weee', -]; diff --git a/dev/tests/unit/.gitignore b/dev/tests/unit/.gitignore index 319b3826f9338..944850d16608e 100644 --- a/dev/tests/unit/.gitignore +++ b/dev/tests/unit/.gitignore @@ -1 +1,2 @@ /phpunit.xml +/var/allure-results/ diff --git a/lib/internal/Magento/Framework/Api/ExtensibleDataObjectConverter.php b/lib/internal/Magento/Framework/Api/ExtensibleDataObjectConverter.php index 538f4e646d060..e51788ad9ecf5 100644 --- a/lib/internal/Magento/Framework/Api/ExtensibleDataObjectConverter.php +++ b/lib/internal/Magento/Framework/Api/ExtensibleDataObjectConverter.php @@ -45,6 +45,30 @@ public function toNestedArray( } $dataObjectArray = $this->dataObjectProcessor->buildOutputDataArray($dataObject, $dataObjectType); //process custom attributes if present + $dataObjectArray = $this->processCustomAttributes($dataObjectArray, $skipAttributes); + + if (!empty($dataObjectArray[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY])) { + /** @var array $extensionAttributes */ + $extensionAttributes = $dataObjectArray[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]; + unset($dataObjectArray[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]); + foreach ($extensionAttributes as $attributeKey => $attributeValue) { + if (!in_array($attributeKey, $skipAttributes)) { + $dataObjectArray[$attributeKey] = $attributeValue; + } + } + } + return $dataObjectArray; + } + + /** + * Recursive process array to process customer attributes + * + * @param array $dataObjectArray + * @param array $skipAttributes + * @return array + */ + private function processCustomAttributes(array $dataObjectArray, array $skipAttributes): array + { if (!empty($dataObjectArray[AbstractExtensibleObject::CUSTOM_ATTRIBUTES_KEY])) { /** @var AttributeValue[] $customAttributes */ $customAttributes = $dataObjectArray[AbstractExtensibleObject::CUSTOM_ATTRIBUTES_KEY]; @@ -56,14 +80,9 @@ public function toNestedArray( } } } - if (!empty($dataObjectArray[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY])) { - /** @var array $extensionAttributes */ - $extensionAttributes = $dataObjectArray[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]; - unset($dataObjectArray[ExtensibleDataInterface::EXTENSION_ATTRIBUTES_KEY]); - foreach ($extensionAttributes as $attributeKey => $attributeValue) { - if (!in_array($attributeKey, $skipAttributes)) { - $dataObjectArray[$attributeKey] = $attributeValue; - } + foreach ($dataObjectArray as $key => $value) { + if (is_array($value)) { + $dataObjectArray[$key] = $this->processCustomAttributes($value, $skipAttributes); } } return $dataObjectArray; diff --git a/lib/internal/Magento/Framework/Api/Test/Unit/ExtensibleDataObjectConverterTest.php b/lib/internal/Magento/Framework/Api/Test/Unit/ExtensibleDataObjectConverterTest.php index f0e333aa5e1d5..08fad90fe2f29 100644 --- a/lib/internal/Magento/Framework/Api/Test/Unit/ExtensibleDataObjectConverterTest.php +++ b/lib/internal/Magento/Framework/Api/Test/Unit/ExtensibleDataObjectConverterTest.php @@ -10,6 +10,9 @@ use Magento\Framework\Api\AbstractExtensibleObject; use Magento\Framework\Api\AttributeValue; +/** + * Class ExtensibleDataObjectConverterTest + */ class ExtensibleDataObjectConverterTest extends \PHPUnit\Framework\TestCase { /** @var \Magento\Framework\Api\ExtensibleDataObjectConverter */ @@ -83,6 +86,17 @@ public function testToNestedArrayCustom() AttributeValue::VALUE => 'custom_attribute_value_skip', ], ], + 'test' => [ + 0 => [ + '3rd_attribute_key' => '3rd_attribute_value', + AbstractExtensibleObject::CUSTOM_ATTRIBUTES_KEY => [ + [ + AttributeValue::ATTRIBUTE_CODE => 'another_custom_attribute_code', + AttributeValue::VALUE => 'another_custom_attribute_value', + ] + ] + ] + ] ]; $resultArray = [ @@ -92,6 +106,12 @@ public function testToNestedArrayCustom() 'custom_attribute_value_multi_1', 'custom_attribute_value_multi_2', ], + 'test' => [ + 0 => [ + '3rd_attribute_key' => '3rd_attribute_value', + 'another_custom_attribute_code' => 'another_custom_attribute_value', + ] + ] ]; $this->processor->expects($this->any()) diff --git a/lib/internal/Magento/Framework/App/Cache/FlushCacheByTags.php b/lib/internal/Magento/Framework/App/Cache/FlushCacheByTags.php index e2392b198492b..8f8dfd3baf1b6 100644 --- a/lib/internal/Magento/Framework/App/Cache/FlushCacheByTags.php +++ b/lib/internal/Magento/Framework/App/Cache/FlushCacheByTags.php @@ -1,18 +1,24 @@ <?php /** - * * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Framework\App\Cache; +use Magento\Framework\App\Cache\Tag\Resolver; +use Magento\Framework\App\Cache\Type\FrontendPool; +use Magento\Framework\Model\AbstractModel; +use Magento\Framework\Model\ResourceModel\AbstractResource; + /** * Automatic cache cleaner plugin */ class FlushCacheByTags { /** - * @var Type\FrontendPool + * @var FrontendPool */ private $cachePool; @@ -27,23 +33,21 @@ class FlushCacheByTags private $cacheState; /** - * @var Tag\Resolver + * @var Resolver */ private $tagResolver; /** - * FlushCacheByTags constructor. - * - * @param Type\FrontendPool $cachePool + * @param FrontendPool $cachePool * @param StateInterface $cacheState - * @param array $cacheList - * @param Tag\Resolver $tagResolver + * @param string[] $cacheList + * @param Resolver $tagResolver */ public function __construct( - \Magento\Framework\App\Cache\Type\FrontendPool $cachePool, - \Magento\Framework\App\Cache\StateInterface $cacheState, + FrontendPool $cachePool, + StateInterface $cacheState, array $cacheList, - \Magento\Framework\App\Cache\Tag\Resolver $tagResolver + Resolver $tagResolver ) { $this->cachePool = $cachePool; $this->cacheState = $cacheState; @@ -54,17 +58,14 @@ public function __construct( /** * Clean cache on save object * - * @param \Magento\Framework\Model\ResourceModel\AbstractResource $subject + * @param AbstractResource $subject * @param \Closure $proceed - * @param \Magento\Framework\Model\AbstractModel $object - * @return \Magento\Framework\Model\ResourceModel\AbstractResource + * @param AbstractModel $object + * @return AbstractResource * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function aroundSave( - \Magento\Framework\Model\ResourceModel\AbstractResource $subject, - \Closure $proceed, - \Magento\Framework\Model\AbstractModel $object - ) { + public function aroundSave(AbstractResource $subject, \Closure $proceed, AbstractModel $object): AbstractResource + { $result = $proceed($object); $tags = $this->tagResolver->getTags($object); $this->cleanCacheByTags($tags); @@ -75,39 +76,37 @@ public function aroundSave( /** * Clean cache on delete object * - * @param \Magento\Framework\Model\ResourceModel\AbstractResource $subject + * @param AbstractResource $subject * @param \Closure $proceed - * @param \Magento\Framework\Model\AbstractModel $object - * @return \Magento\Framework\Model\ResourceModel\AbstractResource + * @param AbstractModel $object + * @return AbstractResource * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function aroundDelete( - \Magento\Framework\Model\ResourceModel\AbstractResource $subject, - \Closure $proceed, - \Magento\Framework\Model\AbstractModel $object - ) { + public function aroundDelete(AbstractResource $subject, \Closure $proceed, AbstractModel $object): AbstractResource + { $tags = $this->tagResolver->getTags($object); $result = $proceed($object); $this->cleanCacheByTags($tags); + return $result; } /** * Clean cache by tags * - * @param string[] $tags + * @param string[] $tags * @return void */ - private function cleanCacheByTags($tags) + private function cleanCacheByTags(array $tags): void { - if (empty($tags)) { + if (!$tags) { return; } foreach ($this->cacheList as $cacheType) { if ($this->cacheState->isEnabled($cacheType)) { $this->cachePool->get($cacheType)->clean( - \Zend_Cache::CLEANING_MODE_MATCHING_TAG, - array_unique($tags) + \Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG, + \array_unique($tags) ); } } diff --git a/lib/internal/Magento/Framework/App/Test/Unit/BootstrapTest.php b/lib/internal/Magento/Framework/App/Test/Unit/BootstrapTest.php index 32e495ed00a82..b9f0023f2b6e8 100644 --- a/lib/internal/Magento/Framework/App/Test/Unit/BootstrapTest.php +++ b/lib/internal/Magento/Framework/App/Test/Unit/BootstrapTest.php @@ -298,4 +298,13 @@ public function assertInstalledDataProvider() [true, false], ]; } + + /** + * Restore error handler after Bootstrap->run method + */ + public function tearDown() + { + restore_error_handler(); + setCustomErrorHandler(); + } } diff --git a/lib/internal/Magento/Framework/App/Test/Unit/Cache/FlushCacheByTagsTest.php b/lib/internal/Magento/Framework/App/Test/Unit/Cache/FlushCacheByTagsTest.php index e05399cd0bfcb..60dba582177eb 100644 --- a/lib/internal/Magento/Framework/App/Test/Unit/Cache/FlushCacheByTagsTest.php +++ b/lib/internal/Magento/Framework/App/Test/Unit/Cache/FlushCacheByTagsTest.php @@ -3,9 +3,20 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); namespace Magento\Framework\App\Test\Unit\Cache; +use Magento\Framework\App\Cache\FlushCacheByTags; +use Magento\Framework\App\Cache\StateInterface; +use Magento\Framework\App\Cache\Tag\Resolver; +use Magento\Framework\App\Cache\Type\FrontendPool; +use Magento\Framework\Model\AbstractModel; +use Magento\Framework\Model\ResourceModel\AbstractResource; + +/** + * Unit tests for the \Magento\Framework\App\Cache\FlushCacheByTags class. + */ class FlushCacheByTagsTest extends \PHPUnit\Framework\TestCase { /** @@ -28,13 +39,16 @@ class FlushCacheByTagsTest extends \PHPUnit\Framework\TestCase */ private $plugin; + /** + * @inheritdoc + */ protected function setUp() { - $this->cacheState = $this->getMockForAbstractClass(\Magento\Framework\App\Cache\StateInterface::class); - $this->frontendPool = $this->createMock(\Magento\Framework\App\Cache\Type\FrontendPool::class); - $this->tagResolver = $this->createMock(\Magento\Framework\App\Cache\Tag\Resolver::class); + $this->cacheState = $this->getMockForAbstractClass(StateInterface::class); + $this->frontendPool = $this->createMock(FrontendPool::class); + $this->tagResolver = $this->createMock(Resolver::class); - $this->plugin = new \Magento\Framework\App\Cache\FlushCacheByTags( + $this->plugin = new FlushCacheByTags( $this->frontendPool, $this->cacheState, ['test'], @@ -42,14 +56,19 @@ protected function setUp() ); } - public function testAroundSave() + /** + * @return void + */ + public function testAroundSave(): void { - $resource = $this->getMockBuilder(\Magento\Framework\Model\ResourceModel\AbstractResource::class) + $resource = $this->getMockBuilder(AbstractResource::class) ->disableOriginalConstructor() ->getMockForAbstractClass(); - $model = $this->getMockBuilder(\Magento\Framework\Model\AbstractModel::class) + $model = $this->getMockBuilder(AbstractModel::class) ->disableOriginalConstructor() ->getMockForAbstractClass(); + $this->tagResolver->expects($this->atLeastOnce())->method('getTags')->with($model)->willReturn([]); + $result = $this->plugin->aroundSave( $resource, function () use ($resource) { @@ -57,17 +76,23 @@ function () use ($resource) { }, $model ); + $this->assertSame($resource, $result); } - public function testAroundDelete() + /** + * @return void + */ + public function testAroundDelete(): void { - $resource = $this->getMockBuilder(\Magento\Framework\Model\ResourceModel\AbstractResource::class) + $resource = $this->getMockBuilder(AbstractResource::class) ->disableOriginalConstructor() ->getMockForAbstractClass(); - $model = $this->getMockBuilder(\Magento\Framework\Model\AbstractModel::class) + $model = $this->getMockBuilder(AbstractModel::class) ->disableOriginalConstructor() ->getMockForAbstractClass(); + $this->tagResolver->expects($this->atLeastOnce())->method('getTags')->with($model)->willReturn([]); + $result = $this->plugin->aroundDelete( $resource, function () use ($resource) { @@ -75,25 +100,7 @@ function () use ($resource) { }, $model ); - $this->assertSame($resource, $result); - } - public function testAroundSaveWithInterface() - { - $resource = $this->getMockBuilder(\Magento\Framework\Model\ResourceModel\AbstractResource::class) - - ->disableOriginalConstructor() - ->getMockForAbstractClass(); - $model = $this->getMockBuilder(\Magento\Framework\Model\AbstractModel::class) - ->disableOriginalConstructor() - ->getMockForAbstractClass(); - $result = $this->plugin->aroundSave( - $resource, - function () use ($resource) { - return $resource; - }, - $model - ); $this->assertSame($resource, $result); } } diff --git a/lib/internal/Magento/Framework/Data/Collection/AbstractDb.php b/lib/internal/Magento/Framework/Data/Collection/AbstractDb.php index 1b28e367dcc3a..6082c2c6c5205 100644 --- a/lib/internal/Magento/Framework/Data/Collection/AbstractDb.php +++ b/lib/internal/Magento/Framework/Data/Collection/AbstractDb.php @@ -16,6 +16,7 @@ /** * Base items collection class * + * phpcs:disable Magento2.Classes.AbstractApi * @api * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ @@ -217,7 +218,7 @@ public function getSize() { if ($this->_totalRecords === null) { $sql = $this->getSelectCountSql(); - $this->_totalRecords = $this->getConnection()->fetchOne($sql, $this->_bindParams); + $this->_totalRecords = $this->_totalRecords ?? $this->getConnection()->fetchOne($sql, $this->_bindParams); } return (int)$this->_totalRecords; } @@ -367,10 +368,12 @@ protected function _renderFilters() * Hook for operations before rendering filters * * @return void + * phpcs:disable Magento2.CodeAnalysis.EmptyBlock */ protected function _renderFiltersBefore() { } + // phpcs:enable /** * Add field filter to collection @@ -730,6 +733,7 @@ public function loadData($printQuery = false, $logQuery = false) public function printLogQuery($printQuery = false, $logQuery = false, $sql = null) { if ($printQuery || $this->getFlag('print_query')) { + // phpcs:ignore Magento2.Security.LanguageConstruct echo $sql === null ? $this->getSelect()->__toString() : $sql; } @@ -822,11 +826,13 @@ public function __clone() * Init select * * @return void + * phpcs:disable Magento2.CodeAnalysis.EmptyBlock */ protected function _initSelect() { // no implementation, should be overridden in children classes } + // phpcs:enable /** * Join extension attribute. diff --git a/lib/internal/Magento/Framework/Filter/Test/Unit/TranslitTest.php b/lib/internal/Magento/Framework/Filter/Test/Unit/TranslitTest.php index dd78fb3559933..cb8e34ca28f5c 100644 --- a/lib/internal/Magento/Framework/Filter/Test/Unit/TranslitTest.php +++ b/lib/internal/Magento/Framework/Filter/Test/Unit/TranslitTest.php @@ -5,6 +5,9 @@ */ namespace Magento\Framework\Filter\Test\Unit; +/** + * Translit test. + */ class TranslitTest extends \PHPUnit\Framework\TestCase { /** @@ -45,8 +48,8 @@ public function filterDataProvider() ['привет мир', 'privet mir', 'privet mir', $isIconv], [ 'Weiß, Goldmann, Göbel, Weiss, Göthe, Goethe und Götz', - 'Weiss, Goldmann, Gobel, Weiss, Gothe, Goethe und Gotz', - 'Weiss, Goldmann, Gobel, Weiss, Gothe, Goethe und Gotz', + 'Weiss, Goldmann, Goebel, Weiss, Goethe, Goethe und Goetz', + 'Weiss, Goldmann, Goebel, Weiss, Goethe, Goethe und Goetz', $isIconv ], [ diff --git a/lib/internal/Magento/Framework/Filter/Test/Unit/TranslitUrlTest.php b/lib/internal/Magento/Framework/Filter/Test/Unit/TranslitUrlTest.php index 166edd36df667..0bd568d920399 100644 --- a/lib/internal/Magento/Framework/Filter/Test/Unit/TranslitUrlTest.php +++ b/lib/internal/Magento/Framework/Filter/Test/Unit/TranslitUrlTest.php @@ -5,6 +5,9 @@ */ namespace Magento\Framework\Filter\Test\Unit; +/** + * Translit url test. + */ class TranslitUrlTest extends \PHPUnit\Framework\TestCase { /** @@ -45,8 +48,8 @@ public function filterDataProvider() ['привет мир', 'privet-mir', 'privet-mir', $isIconv], [ 'Weiß, Goldmann, Göbel, Weiss, Göthe, Goethe und Götz', - 'weiss-goldmann-gobel-weiss-gothe-goethe-und-gotz', - 'weiss-goldmann-gobel-weiss-gothe-goethe-und-gotz', + 'weiss-goldmann-goebel-weiss-goethe-goethe-und-goetz', + 'weiss-goldmann-goebel-weiss-goethe-goethe-und-goetz', $isIconv ], [ diff --git a/lib/internal/Magento/Framework/Filter/Translit.php b/lib/internal/Magento/Framework/Filter/Translit.php index a6162aa7a7fff..21df38f7bdf30 100644 --- a/lib/internal/Magento/Framework/Filter/Translit.php +++ b/lib/internal/Magento/Framework/Filter/Translit.php @@ -25,7 +25,7 @@ class Translit implements \Zend_Filter_Interface 'À' => 'a', 'Á' => 'a', 'Â' => 'a', - 'Ä' => 'a', + 'Ä' => 'ae', 'Å' => 'a', 'Æ' => 'ae', 'Ç' => 'c', @@ -40,18 +40,18 @@ class Translit implements \Zend_Filter_Interface 'Ó' => 'o', 'Ô' => 'o', 'Õ' => 'o', - 'Ö' => 'o', + 'Ö' => 'oe', 'Ø' => 'o', 'Ù' => 'u', 'Ú' => 'u', 'Û' => 'u', - 'Ü' => 'u', + 'Ü' => 'ue', 'Ý' => 'y', 'ß' => 'ss', 'à' => 'a', 'á' => 'a', 'â' => 'a', - 'ä' => 'a', + 'ä' => 'ae', 'å' => 'a', 'æ' => 'ae', 'ç' => 'c', @@ -67,12 +67,12 @@ class Translit implements \Zend_Filter_Interface 'ó' => 'o', 'ô' => 'o', 'õ' => 'o', - 'ö' => 'o', + 'ö' => 'oe', 'ø' => 'o', 'ù' => 'u', 'ú' => 'u', 'û' => 'u', - 'ü' => 'u', + 'ü' => 'ue', 'ý' => 'y', 'þ' => 'p', 'ÿ' => 'y', diff --git a/lib/internal/Magento/Framework/Locale/Format.php b/lib/internal/Magento/Framework/Locale/Format.php index adcffe01b910e..934c9638c7392 100644 --- a/lib/internal/Magento/Framework/Locale/Format.php +++ b/lib/internal/Magento/Framework/Locale/Format.php @@ -10,6 +10,11 @@ */ class Format implements \Magento\Framework\Locale\FormatInterface { + /** + * Japan locale code + */ + private const JAPAN_LOCALE_CODE = 'ja_JP'; + /** * @var \Magento\Framework\App\ScopeResolverInterface */ @@ -81,7 +86,16 @@ public function getNumber($value) $value = str_replace(',', '', $value); } } elseif ($separatorComa !== false) { - $value = str_replace(',', '.', $value); + $locale = $this->_localeResolver->getLocale(); + /** + * It's hard code for Japan locale. + * Comma separator uses as group separator: 4,000 saves as 4,000.00 + */ + $value = str_replace( + ',', + $locale === self::JAPAN_LOCALE_CODE ? '' : '.', + $value + ); } return (float)$value; diff --git a/lib/internal/Magento/Framework/Locale/Test/Unit/FormatTest.php b/lib/internal/Magento/Framework/Locale/Test/Unit/FormatTest.php index 73a029a5a1411..69ff02beb8d17 100644 --- a/lib/internal/Magento/Framework/Locale/Test/Unit/FormatTest.php +++ b/lib/internal/Magento/Framework/Locale/Test/Unit/FormatTest.php @@ -6,6 +6,9 @@ namespace Magento\Framework\Locale\Test\Unit; +/** + * Tests class for Number locale format + */ class FormatTest extends \PHPUnit\Framework\TestCase { /** @@ -103,10 +106,14 @@ public function getPriceFormatDataProvider(): array * * @param mixed $value * @param float $expected + * @param string $locale * @dataProvider provideNumbers */ - public function testGetNumber($value, $expected): void + public function testGetNumber(string $value, float $expected, string $locale = null): void { + if ($locale !== null) { + $this->localeResolver->method('getLocale')->willReturn($locale); + } $this->assertEquals($expected, $this->formatModel->getNumber($value)); } @@ -127,6 +134,8 @@ public function provideNumbers(): array ['2 054.52', 2054.52], ['2,46 GB', 2.46], ['2,054.00', 2054], + ['4,000', 4000.0, 'ja_JP'], + ['4,000', 4.0, 'en_US'], ]; } } diff --git a/lib/internal/Magento/Framework/Model/ResourceModel/Db/AbstractDb.php b/lib/internal/Magento/Framework/Model/ResourceModel/Db/AbstractDb.php index 0cadb10aaafe2..3c88ee9dc5fed 100644 --- a/lib/internal/Magento/Framework/Model/ResourceModel/Db/AbstractDb.php +++ b/lib/internal/Magento/Framework/Model/ResourceModel/Db/AbstractDb.php @@ -18,6 +18,7 @@ * @SuppressWarnings(PHPMD.NumberOfChildren) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) + * phpcs:disable Magento2.Classes.AbstractApi * @api */ abstract class AbstractDb extends AbstractResource @@ -604,7 +605,7 @@ protected function _checkUnique(\Magento\Framework\Model\AbstractModel $object) $fields = $this->getUniqueFields(); if (!empty($fields)) { if (!is_array($fields)) { - $this->_uniqueFields = [['field' => $fields, 'title' => $fields]]; + $fields = $this->_uniqueFields = [['field' => $fields, 'title' => $fields]]; } $data = new \Magento\Framework\DataObject($this->_prepareDataForSave($object)); diff --git a/lib/internal/Magento/Framework/Mview/Test/Unit/View/ChangelogTest.php b/lib/internal/Magento/Framework/Mview/Test/Unit/View/ChangelogTest.php index b16b7c87e87ac..2d585fe33aba3 100644 --- a/lib/internal/Magento/Framework/Mview/Test/Unit/View/ChangelogTest.php +++ b/lib/internal/Magento/Framework/Mview/Test/Unit/View/ChangelogTest.php @@ -6,6 +6,11 @@ namespace Magento\Framework\Mview\Test\Unit\View; +/** + * Test Coverage for Changelog View. + * + * @see \Magento\Framework\Mview\View\Changelog + */ class ChangelogTest extends \PHPUnit\Framework\TestCase { /** @@ -45,7 +50,7 @@ public function testInstanceOf() } /** - * @expectedException \Exception + * @expectedException \Magento\Framework\DB\Adapter\ConnectionException * @expectedExceptionMessage The write connection to the database isn't available. Please try again later. */ public function testCheckConnectionException() @@ -74,7 +79,7 @@ public function testGetViewId() } /** - * @expectedException \Exception + * @expectedException \DomainException * @expectedExceptionMessage View's identifier is not set */ public function testGetNameWithException() @@ -101,6 +106,10 @@ public function testGetVersion() $this->assertEquals(10, $this->model->getVersion()); } + /** + * @expectedException \Magento\Framework\Exception\RuntimeException + * @expectedExceptionMessage Table status for viewIdtest_cl is incorrect. Can`t fetch version id. + */ public function testGetVersionWithExceptionNoAutoincrement() { $changelogTableName = 'viewIdtest_cl'; @@ -111,8 +120,6 @@ public function testGetVersionWithExceptionNoAutoincrement() ->method('fetchRow') ->will($this->returnValue([])); - $this->expectException('Exception'); - $this->expectExceptionMessage("Table status for `{$changelogTableName}` is incorrect. Can`t fetch version id."); $this->model->setViewId('viewIdtest'); $this->model->getVersion(); } @@ -215,10 +222,10 @@ public function testGetList() $this->connectionMock->expects($this->once()) ->method('fetchCol') ->with($selectMock) - ->will($this->returnValue(['some_data'])); + ->will($this->returnValue([1])); $this->model->setViewId('viewIdtest'); - $this->assertEquals(['some_data'], $this->model->getList(1, 2)); + $this->assertEquals([1], $this->model->getList(1, 2)); } public function testGetListWithException() diff --git a/lib/internal/Magento/Framework/Mview/View.php b/lib/internal/Magento/Framework/Mview/View.php index 1b32238813f86..20736eb21963a 100644 --- a/lib/internal/Magento/Framework/Mview/View.php +++ b/lib/internal/Magento/Framework/Mview/View.php @@ -285,7 +285,7 @@ public function update() for ($vsFrom = $lastVersionId; $vsFrom < $currentVersionId; $vsFrom += $versionBatchSize) { // Don't go past the current version for atomicy. $versionTo = min($currentVersionId, $vsFrom + $versionBatchSize); - $ids = array_map('intval', $this->getChangelog()->getList($vsFrom, $versionTo)); + $ids = $this->getChangelog()->getList($vsFrom, $versionTo); // We run the actual indexer in batches. // Chunked AFTER loading to avoid duplicates in separate chunks. diff --git a/lib/internal/Magento/Framework/Mview/View/Changelog.php b/lib/internal/Magento/Framework/Mview/View/Changelog.php index 4fb06ce3f06fd..d51f5b13f01a2 100644 --- a/lib/internal/Magento/Framework/Mview/View/Changelog.php +++ b/lib/internal/Magento/Framework/Mview/View/Changelog.php @@ -6,8 +6,13 @@ namespace Magento\Framework\Mview\View; +use Magento\Framework\DB\Adapter\ConnectionException; +use Magento\Framework\Exception\RuntimeException; use Magento\Framework\Phrase; +/** + * Class Changelog for manipulations with the mview_state table. + */ class Changelog implements ChangelogInterface { /** @@ -41,6 +46,7 @@ class Changelog implements ChangelogInterface /** * @param \Magento\Framework\App\ResourceConnection $resource + * @throws ConnectionException */ public function __construct(\Magento\Framework\App\ResourceConnection $resource) { @@ -53,12 +59,14 @@ public function __construct(\Magento\Framework\App\ResourceConnection $resource) * Check DB connection * * @return void - * @throws \Exception + * @throws ConnectionException */ protected function checkConnection() { if (!$this->connection) { - throw new \Exception("The write connection to the database isn't available. Please try again later."); + throw new ConnectionException( + new Phrase("The write connection to the database isn't available. Please try again later.") + ); } } @@ -154,14 +162,15 @@ public function getList($fromVersionId, $toVersionId) (int)$toVersionId ); - return $this->connection->fetchCol($select); + return array_map('intval', $this->connection->fetchCol($select)); } /** * Get maximum version_id from changelog + * * @return int * @throws ChangelogTableNotExistsException - * @throws \Exception + * @throws RuntimeException */ public function getVersion() { @@ -173,7 +182,9 @@ public function getVersion() if (isset($row['Auto_increment'])) { return (int)$row['Auto_increment'] - 1; } else { - throw new \Exception("Table status for `{$changelogTableName}` is incorrect. Can`t fetch version id."); + throw new RuntimeException( + new Phrase("Table status for %1 is incorrect. Can`t fetch version id.", [$changelogTableName]) + ); } } @@ -182,13 +193,15 @@ public function getVersion() * * Build a changelog name by concatenating view identifier and changelog name suffix. * - * @throws \Exception + * @throws \DomainException * @return string */ public function getName() { if (strlen($this->viewId) == 0) { - throw new \Exception("View's identifier is not set"); + throw new \DomainException( + new Phrase("View's identifier is not set") + ); } return $this->viewId . '_' . self::NAME_SUFFIX; } @@ -216,6 +229,8 @@ public function setViewId($viewId) } /** + * Get view's identifier + * * @return string */ public function getViewId() diff --git a/lib/internal/Magento/Framework/Pricing/Render/PriceBox.php b/lib/internal/Magento/Framework/Pricing/Render/PriceBox.php index ae572d8fe5f80..e43dc0a562da7 100644 --- a/lib/internal/Magento/Framework/Pricing/Render/PriceBox.php +++ b/lib/internal/Magento/Framework/Pricing/Render/PriceBox.php @@ -8,8 +8,8 @@ use Magento\Framework\DataObject\IdentityInterface; use Magento\Framework\Pricing\Amount\AmountInterface; -use Magento\Framework\Pricing\SaleableInterface; use Magento\Framework\Pricing\Price\PriceInterface; +use Magento\Framework\Pricing\SaleableInterface; use Magento\Framework\View\Element\Template; /** @@ -21,7 +21,7 @@ class PriceBox extends Template implements PriceBoxRenderInterface, IdentityInterface { /** Default block lifetime */ - const DEFAULT_LIFETIME = 3600; + const DEFAULT_LIFETIME = 86400; /** * @var SaleableInterface @@ -39,11 +39,11 @@ class PriceBox extends Template implements PriceBoxRenderInterface, IdentityInte protected $rendererPool; /** - * @param Template\Context $context + * @param Template\Context $context * @param SaleableInterface $saleableItem - * @param PriceInterface $price - * @param RendererPool $rendererPool - * @param array $data + * @param PriceInterface $price + * @param RendererPool $rendererPool + * @param array $data */ public function __construct( Template\Context $context, @@ -59,7 +59,7 @@ public function __construct( } /** - * @return string + * @inheritdoc */ protected function _toHtml() { @@ -70,9 +70,7 @@ protected function _toHtml() } /** - * Get Key for caching block content - * - * @return string + * @inheritdoc */ public function getCacheKey() { @@ -80,17 +78,15 @@ public function getCacheKey() } /** - * Get block cache life time - * - * @return int + * @inheritdoc */ protected function getCacheLifetime() { - return parent::hasCacheLifetime() ? parent::getCacheLifetime() : null; + return parent::hasCacheLifetime() ? parent::getCacheLifetime() : self::DEFAULT_LIFETIME; } - + /** - * @return SaleableInterface + * @inheritdoc */ public function getSaleableItem() { @@ -98,7 +94,7 @@ public function getSaleableItem() } /** - * @return PriceInterface + * @inheritdoc */ public function getPrice() { @@ -136,9 +132,7 @@ public function getPriceType($priceCode) } /** - * @param AmountInterface $amount - * @param array $arguments - * @return string + * @inheritdoc */ public function renderAmount(AmountInterface $amount, array $arguments = []) { @@ -149,6 +143,8 @@ public function renderAmount(AmountInterface $amount, array $arguments = []) } /** + * Get amount render. + * * @param AmountInterface $amount * @param array $arguments * @return AmountRenderInterface @@ -164,6 +160,8 @@ protected function getAmountRender(AmountInterface $amount, array $arguments = [ } /** + * Get renderer pool. + * * @return RendererPool */ public function getRendererPool() @@ -172,9 +170,7 @@ public function getRendererPool() } /** - * Return unique ID(s) for each object in system - * - * @return array + * @inheritdoc */ public function getIdentities() { diff --git a/lib/internal/Magento/Framework/Pricing/Test/Unit/Render/PriceBoxTest.php b/lib/internal/Magento/Framework/Pricing/Test/Unit/Render/PriceBoxTest.php index f4588f7d25672..1b819f00fa03c 100644 --- a/lib/internal/Magento/Framework/Pricing/Test/Unit/Render/PriceBoxTest.php +++ b/lib/internal/Magento/Framework/Pricing/Test/Unit/Render/PriceBoxTest.php @@ -248,17 +248,4 @@ public function testGetRendererPool() { $this->assertEquals($this->rendererPool, $this->model->getRendererPool()); } - - /** - * This tests ensures that protected method getCacheLifetime() returns a null value when cacheLifeTime is not - * explicitly set in the parent block - */ - public function testCacheLifetime() - { - $reflectionClass = new \ReflectionClass(get_class($this->model)); - $methodReflection = $reflectionClass->getMethod('getCacheLifetime'); - $methodReflection->setAccessible(true); - $cacheLifeTime = $methodReflection->invoke($this->model); - $this->assertNull($cacheLifeTime, 'Expected null cache lifetime'); - } } diff --git a/lib/internal/Magento/Framework/Serialize/Test/Unit/Serializer/JsonConverterTest.php b/lib/internal/Magento/Framework/Serialize/Test/Unit/Serializer/JsonConverterTest.php new file mode 100644 index 0000000000000..a3b914979a427 --- /dev/null +++ b/lib/internal/Magento/Framework/Serialize/Test/Unit/Serializer/JsonConverterTest.php @@ -0,0 +1,32 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ + +namespace Magento\Framework\Serialize\Test\Unit\Serializer; + +/** + * Class JsonConverterTest + */ +class JsonConverterTest extends \PHPUnit\Framework\TestCase +{ + public function testConvert() + { + $data = [ + 'key' => 'value' + ]; + + $this->assertEquals(json_encode($data), \Magento\Framework\Serialize\JsonConverter::convert($data)); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Unable to serialize value. + */ + public function testConvertWithException() + { + //verify that exception will be thrown with invalid UTF8 sequence + \Magento\Framework\Serialize\JsonConverter::convert("\xB1\x31"); + } +} diff --git a/lib/internal/Magento/Framework/Setup/Declaration/Schema/Db/StatementAggregator.php b/lib/internal/Magento/Framework/Setup/Declaration/Schema/Db/StatementAggregator.php index 2003ff18900ff..6efece3c98fbd 100644 --- a/lib/internal/Magento/Framework/Setup/Declaration/Schema/Db/StatementAggregator.php +++ b/lib/internal/Magento/Framework/Setup/Declaration/Schema/Db/StatementAggregator.php @@ -36,7 +36,7 @@ private function canDoMerge(Statement $bankStatement, Statement $statement) } /** - * If we add trigger after some specific statement, than we say that statement is final + * If we add trigger after some specific statement, then we say that statement is final * and can`t be updated anymore. Otherwise trigger can fail. * * Example: while migrating data from one column to another and another column should be removed, diff --git a/lib/internal/Magento/Framework/Setup/Declaration/Schema/Dto/Columns/ColumnNullableAwareInterface.php b/lib/internal/Magento/Framework/Setup/Declaration/Schema/Dto/Columns/ColumnNullableAwareInterface.php index 0ae403abd5702..086a4466a17b0 100644 --- a/lib/internal/Magento/Framework/Setup/Declaration/Schema/Dto/Columns/ColumnNullableAwareInterface.php +++ b/lib/internal/Magento/Framework/Setup/Declaration/Schema/Dto/Columns/ColumnNullableAwareInterface.php @@ -7,7 +7,7 @@ /** * Provides nullable flag for element. - * If column element implement this interface, than it will have NULL or NOT NULL flag in column definition. + * If column element implement this interface, then it will have NULL or NOT NULL flag in column definition. */ interface ColumnNullableAwareInterface { diff --git a/lib/internal/Magento/Framework/Setup/Declaration/Schema/Dto/Columns/ColumnUnsignedAwareInterface.php b/lib/internal/Magento/Framework/Setup/Declaration/Schema/Dto/Columns/ColumnUnsignedAwareInterface.php index be5d6bf91ef75..c5fc550cf7886 100644 --- a/lib/internal/Magento/Framework/Setup/Declaration/Schema/Dto/Columns/ColumnUnsignedAwareInterface.php +++ b/lib/internal/Magento/Framework/Setup/Declaration/Schema/Dto/Columns/ColumnUnsignedAwareInterface.php @@ -7,7 +7,7 @@ /** * Unsigned flag provider for element. - * If column element implement this interface, than it will have UNSIGNED flag in column + * If column element implement this interface, then it will have UNSIGNED flag in column * definition. */ interface ColumnUnsignedAwareInterface diff --git a/lib/internal/Magento/Framework/Setup/Declaration/Schema/Operations/AddColumn.php b/lib/internal/Magento/Framework/Setup/Declaration/Schema/Operations/AddColumn.php index 71a1e2f92dfd8..2bb19d941fcd1 100644 --- a/lib/internal/Magento/Framework/Setup/Declaration/Schema/Operations/AddColumn.php +++ b/lib/internal/Magento/Framework/Setup/Declaration/Schema/Operations/AddColumn.php @@ -164,7 +164,7 @@ private function setupTriggersIfExists(Statement $statement, ElementHistory $ele } $statements = [$statement]; /** - * If column has triggers, only than we need to create temporary index on it. + * If column has triggers, only then we need to create temporary index on it. * As triggers means, that we will not enable primary key until all data will be transferred, * so column can left without key (as primary key is disabled) and this cause an error. */ diff --git a/lib/internal/Magento/Framework/Setup/Patch/PatchApplier.php b/lib/internal/Magento/Framework/Setup/Patch/PatchApplier.php index d81d36d383f67..bdaca77e5b4eb 100644 --- a/lib/internal/Magento/Framework/Setup/Patch/PatchApplier.php +++ b/lib/internal/Magento/Framework/Setup/Patch/PatchApplier.php @@ -10,7 +10,7 @@ use Magento\Framework\Module\ModuleList; use Magento\Framework\ObjectManagerInterface; use Magento\Framework\Phrase; -use Magento\Framework\Setup\Exception; +use Magento\Framework\Setup\Exception as SetupException; use Magento\Framework\Setup\ModuleDataSetupInterface; /** @@ -129,8 +129,8 @@ public function __construct( /** * Apply all patches for one module * - * @param null | string $moduleName - * @throws Exception + * @param null|string $moduleName + * @throws SetupException */ public function applyDataPatch($moduleName = null) { @@ -149,7 +149,7 @@ public function applyDataPatch($moduleName = null) ['moduleDataSetup' => $this->moduleDataSetup] ); if (!$dataPatch instanceof DataPatchInterface) { - throw new Exception( + throw new SetupException( new Phrase("Patch %1 should implement DataPatchInterface", [get_class($dataPatch)]) ); } @@ -164,7 +164,17 @@ public function applyDataPatch($moduleName = null) $this->moduleDataSetup->getConnection()->commit(); } catch (\Exception $e) { $this->moduleDataSetup->getConnection()->rollBack(); - throw new Exception(new Phrase($e->getMessage())); + throw new SetupException( + new Phrase( + 'Unable to apply data patch %1 for module %2. Original exception message: %3', + [ + get_class($dataPatch), + $moduleName, + $e->getMessage() + ] + ), + $e + ); } finally { unset($dataPatch); } @@ -173,8 +183,7 @@ public function applyDataPatch($moduleName = null) } /** - * Register all patches in registry in order to manipulate chains and dependencies of patches - * of patches + * Register all patches in registry in order to manipulate chains and dependencies of patches of patches * * @param string $moduleName * @param string $patchType @@ -207,8 +216,8 @@ private function prepareRegistry($moduleName, $patchType) * * Please note: that schema patches are not revertable * - * @param null | string $moduleName - * @throws Exception + * @param null|string $moduleName + * @throws SetupException */ public function applySchemaPatch($moduleName = null) { @@ -229,7 +238,7 @@ public function applySchemaPatch($moduleName = null) $schemaPatch->apply(); $this->patchHistory->fixPatch(get_class($schemaPatch)); } catch (\Exception $e) { - throw new Exception( + throw new SetupException( new Phrase( 'Unable to apply patch %1 for module %2. Original exception message: %3', [ @@ -248,8 +257,8 @@ public function applySchemaPatch($moduleName = null) /** * Revert data patches for specific module * - * @param null | string $moduleName - * @throws Exception + * @param null|string $moduleName + * @throws SetupException */ public function revertDataPatches($moduleName = null) { @@ -270,7 +279,7 @@ public function revertDataPatches($moduleName = null) $adapter->commit(); } catch (\Exception $e) { $adapter->rollBack(); - throw new Exception(new Phrase($e->getMessage())); + throw new SetupException(new Phrase($e->getMessage())); } finally { unset($dataPatch); } diff --git a/lib/internal/Magento/Framework/Setup/Patch/PatchInterface.php b/lib/internal/Magento/Framework/Setup/Patch/PatchInterface.php index c69f754d4b978..7063404276cc7 100644 --- a/lib/internal/Magento/Framework/Setup/Patch/PatchInterface.php +++ b/lib/internal/Magento/Framework/Setup/Patch/PatchInterface.php @@ -19,7 +19,7 @@ public function getAliases(); /** * Run code inside patch - * If code fails, patch must be reverted, in case when we are speaking about schema - than under revert + * If code fails, patch must be reverted, in case when we are speaking about schema - then under revert * means run PatchInterface::revert() * * If we speak about data, under revert means: $transaction->rollback() diff --git a/lib/internal/Magento/Framework/Setup/Patch/PatchRegistry.php b/lib/internal/Magento/Framework/Setup/Patch/PatchRegistry.php index 08e8f96bd5b60..25f2bb64d8100 100644 --- a/lib/internal/Magento/Framework/Setup/Patch/PatchRegistry.php +++ b/lib/internal/Magento/Framework/Setup/Patch/PatchRegistry.php @@ -68,7 +68,7 @@ public function __construct(PatchFactory $patchFactory, PatchHistory $patchHisto /** * Register all dependents to patch * - * @param string | DependentPatchInterface $patchName + * @param string|DependentPatchInterface $patchName */ private function registerDependents(string $patchName) { @@ -130,6 +130,8 @@ private function getDependentPatches(string $patch) } /** + * Get patch dependencies. + * * @param string $patch * @return string[] */ @@ -146,7 +148,7 @@ private function getDependencies(string $patch) $depInstance = $this->registerPatch($dep); /** - * If a patch already have applied dependency - than we definently know + * If a patch already have applied dependency - then we definitely know * that all other dependencies in dependency chain are applied too, so we can skip this dep */ if (!$depInstance) { @@ -189,7 +191,7 @@ public function getReverseIterator() /** * Retrieve iterator of all patch instances * - * If patch have dependencies, than first of all dependencies should be installed and only then desired patch + * If patch have dependencies, then first of all dependencies should be installed and only then desired patch * * @return \ArrayIterator */ diff --git a/lib/internal/Magento/Framework/Setup/Patch/PatchVersionInterface.php b/lib/internal/Magento/Framework/Setup/Patch/PatchVersionInterface.php index 9fca3ffc364f9..fb2b0e2c379f4 100644 --- a/lib/internal/Magento/Framework/Setup/Patch/PatchVersionInterface.php +++ b/lib/internal/Magento/Framework/Setup/Patch/PatchVersionInterface.php @@ -14,11 +14,10 @@ interface PatchVersionInterface { /** * This version associate patch with Magento setup version. - * For example, if Magento current setup version is 2.0.3 and patch version is 2.0.2 than + * For example, if Magento current setup version is 2.0.3 and patch version is 2.0.2 then * this patch will be added to registry, but will not be applied, because it is already applied * by old mechanism of UpgradeData.php script * - * * @return string * @deprecated since appearance, required for backward compatibility */ diff --git a/lib/internal/Magento/Framework/Setup/Test/Unit/Declaration/Schema/Db/SchemaBuilderTest.php b/lib/internal/Magento/Framework/Setup/Test/Unit/Declaration/Schema/Db/SchemaBuilderTest.php index 4e0c129204012..b43a37a41388b 100644 --- a/lib/internal/Magento/Framework/Setup/Test/Unit/Declaration/Schema/Db/SchemaBuilderTest.php +++ b/lib/internal/Magento/Framework/Setup/Test/Unit/Declaration/Schema/Db/SchemaBuilderTest.php @@ -312,7 +312,7 @@ public function testBuildUnknownIndexColumn(array $columns, array $references, a Schema::class, ['resourceConnection' => $resourceConnectionMock] ); - $this->expectException(\Exception::class); + $this->expectException(\PHPUnit\Framework\Exception::class); $this->expectExceptionMessage( 'User Warning: Column unknown_column does not exist for index/constraint FIRST_INDEX in table second_table.' ); diff --git a/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone.php b/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone.php index cf747bfa2b735..45c31d367b34a 100644 --- a/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone.php +++ b/lib/internal/Magento/Framework/Stdlib/DateTime/Timezone.php @@ -196,7 +196,8 @@ public function date($date = null, $locale = null, $useTimezone = true, $include public function scopeDate($scope = null, $date = null, $includeTime = false) { $timezone = $this->_scopeConfig->getValue($this->getDefaultTimezonePath(), $this->_scopeType, $scope); - $date = new \DateTime(is_numeric($date) ? '@' . $date : $date, new \DateTimeZone($timezone)); + $date = new \DateTime(is_numeric($date) ? '@' . $date : $date); + $date->setTimezone(new \DateTimeZone($timezone)); if (!$includeTime) { $date->setTime(0, 0, 0); } @@ -247,13 +248,8 @@ public function isScopeDateInInterval($scope, $dateFrom = null, $dateTo = null) $toTimeStamp += 86400; } - $result = false; - if (!$this->_dateTime->isEmptyDate($dateFrom) && $scopeTimeStamp < $fromTimeStamp) { - } elseif (!$this->_dateTime->isEmptyDate($dateTo) && $scopeTimeStamp > $toTimeStamp) { - } else { - $result = true; - } - return $result; + return !(!$this->_dateTime->isEmptyDate($dateFrom) && $scopeTimeStamp < $fromTimeStamp || + !$this->_dateTime->isEmptyDate($dateTo) && $scopeTimeStamp > $toTimeStamp); } /** diff --git a/lib/internal/Magento/Framework/Stdlib/Test/Unit/DateTime/TimezoneTest.php b/lib/internal/Magento/Framework/Stdlib/Test/Unit/DateTime/TimezoneTest.php index d57525590b9e8..3d7d14a394629 100644 --- a/lib/internal/Magento/Framework/Stdlib/Test/Unit/DateTime/TimezoneTest.php +++ b/lib/internal/Magento/Framework/Stdlib/Test/Unit/DateTime/TimezoneTest.php @@ -253,4 +253,15 @@ private function scopeConfigWillReturnConfiguredTimezone($configuredTimezone) { $this->scopeConfig->method('getValue')->with('', '', null)->willReturn($configuredTimezone); } + + public function testCheckIfScopeDateSetsTimeZone() + { + $scopeDate = new \DateTime('now', new \DateTimeZone('America/Vancouver')); + $this->scopeConfig->method('getValue')->willReturn('America/Vancouver'); + + $this->assertEquals( + $scopeDate->getTimezone(), + $this->getTimezone()->scopeDate(0, $scopeDate->getTimestamp())->getTimezone() + ); + } } diff --git a/lib/internal/Magento/Framework/Unserialize/Test/Unit/UnserializeTest.php b/lib/internal/Magento/Framework/Unserialize/Test/Unit/UnserializeTest.php index f053a9afff74d..0cf803be079d1 100644 --- a/lib/internal/Magento/Framework/Unserialize/Test/Unit/UnserializeTest.php +++ b/lib/internal/Magento/Framework/Unserialize/Test/Unit/UnserializeTest.php @@ -3,11 +3,16 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Framework\Unserialize\Test\Unit; use Magento\Framework\Serialize\Serializer\Serialize; use Magento\Framework\Unserialize\Unserialize; +/** + * Test unserializer that does not unserialize objects. + */ class UnserializeTest extends \PHPUnit\Framework\TestCase { /** @@ -52,6 +57,10 @@ public function testUnserializeArray() */ public function testUnserializeObject($serialized) { + $this->expectException(\PHPUnit\Framework\Exception::class); + $this->expectExceptionMessage( + 'String contains serialized object' + ); $this->assertFalse($this->unserialize->unserialize($serialized)); } diff --git a/lib/internal/Magento/Framework/View/Element/AbstractBlock.php b/lib/internal/Magento/Framework/View/Element/AbstractBlock.php index 6c4746d8218ea..43614e9cae83b 100644 --- a/lib/internal/Magento/Framework/View/Element/AbstractBlock.php +++ b/lib/internal/Magento/Framework/View/Element/AbstractBlock.php @@ -680,10 +680,13 @@ public function toHtml() 'html' => $html, ] ); - $this->_eventManager->dispatch('view_block_abstract_to_html_after', [ - 'block' => $this, - 'transport' => $transportObject - ]); + $this->_eventManager->dispatch( + 'view_block_abstract_to_html_after', + [ + 'block' => $this, + 'transport' => $transportObject + ] + ); $html = $transportObject->getHtml(); return $html; @@ -726,7 +729,7 @@ protected function _toHtml() */ public function getUiId($arg1 = null, $arg2 = null, $arg3 = null, $arg4 = null, $arg5 = null) { - return ' data-ui-id="' . $this->getJsId($arg1, $arg2, $arg3, $arg4, $arg5) . '" '; + return ' data-ui-id="' . $this->escapeHtmlAttr($this->getJsId($arg1, $arg2, $arg3, $arg4, $arg5)) . '" '; } /** @@ -973,8 +976,8 @@ public function escapeXssInUrl($data) * * Use $addSlashes = false for escaping js that inside html attribute (onClick, onSubmit etc) * - * @param string $data - * @param bool $addSlashes + * @param string $data + * @param bool $addSlashes * @return string * @deprecated 100.2.0 */ diff --git a/lib/internal/Magento/Framework/View/Element/UiComponent/DataProvider/FulltextFilter.php b/lib/internal/Magento/Framework/View/Element/UiComponent/DataProvider/FulltextFilter.php index f683e248aec91..e2920cf400a49 100644 --- a/lib/internal/Magento/Framework/View/Element/UiComponent/DataProvider/FulltextFilter.php +++ b/lib/internal/Magento/Framework/View/Element/UiComponent/DataProvider/FulltextFilter.php @@ -64,12 +64,13 @@ function ($column) use ($alias) { /** * Escape against value + * * @param string $value * @return string */ private function escapeAgainstValue(string $value): string { - return preg_replace('/([+\-><\(\)~*\"@]+)/', ' ', $value); + return preg_replace('/([+\-><\(\)~*@]+)/', ' ', $value); } /** diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Element/AbstractBlockTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Element/AbstractBlockTest.php index dba775ea894f4..fbaa7ec670794 100644 --- a/lib/internal/Magento/Framework/View/Test/Unit/Element/AbstractBlockTest.php +++ b/lib/internal/Magento/Framework/View/Test/Unit/Element/AbstractBlockTest.php @@ -7,6 +7,7 @@ namespace Magento\Framework\View\Test\Unit\Element; use Magento\Framework\Cache\LockGuardedCacheLoader; +use Magento\Framework\Escaper; use Magento\Framework\View\Element\AbstractBlock; use Magento\Framework\View\Element\Context; use Magento\Framework\Config\View; @@ -52,6 +53,11 @@ class AbstractBlockTest extends \PHPUnit\Framework\TestCase */ private $sessionMock; + /** + * @var Escaper|\PHPUnit_Framework_MockObject_MockObject + */ + private $escaperMock; + /** * @var LockGuardedCacheLoader|\PHPUnit_Framework_MockObject_MockObject */ @@ -71,6 +77,9 @@ protected function setUp() ->getMockForAbstractClass(); $this->sidResolverMock = $this->getMockForAbstractClass(SidResolverInterface::class); $this->sessionMock = $this->getMockForAbstractClass(SessionManagerInterface::class); + $this->escaperMock = $this->getMockBuilder(Escaper::class) + ->disableOriginalConstructor() + ->getMock(); $contextMock = $this->createMock(Context::class); $contextMock->expects($this->once()) ->method('getEventManager') @@ -87,6 +96,9 @@ protected function setUp() $contextMock->expects($this->once()) ->method('getSession') ->willReturn($this->sessionMock); + $contextMock->expects($this->once()) + ->method('getEscaper') + ->willReturn($this->escaperMock); $this->block = $this->getMockForAbstractClass( AbstractBlock::class, [ @@ -105,6 +117,13 @@ protected function setUp() */ public function testGetUiId($expectedResult, $nameInLayout, $methodArguments) { + $this->escaperMock->expects($this->once()) + ->method('escapeHtmlAttr') + ->willReturnCallback( + function ($string) { + return $string; + } + ); $this->block->setNameInLayout($nameInLayout); $this->assertEquals($expectedResult, call_user_func_array([$this->block, 'getUiId'], $methodArguments)); } @@ -151,10 +170,12 @@ public function testGetVar() $config->expects($this->any()) ->method('getVarValue') - ->willReturnMap([ - ['Magento_Theme', 'v1', 'one'], - [$module, 'v2', 'two'] - ]); + ->willReturnMap( + [ + ['Magento_Theme', 'v1', 'one'], + [$module, 'v2', 'two'] + ] + ); $configManager = $this->createMock(ConfigInterface::class); $configManager->expects($this->exactly(2))->method('getViewConfig')->willReturn($config); diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Element/MessagesTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Element/MessagesTest.php index 57fefe4660cb2..223b9a364b1fe 100644 --- a/lib/internal/Magento/Framework/View/Test/Unit/Element/MessagesTest.php +++ b/lib/internal/Magento/Framework/View/Test/Unit/Element/MessagesTest.php @@ -9,13 +9,15 @@ */ namespace Magento\Framework\View\Test\Unit\Element; +use Magento\Framework\Escaper; use Magento\Framework\Message\Manager; use Magento\Framework\View\Element\Message\InterpretationStrategyInterface; use \Magento\Framework\View\Element\Messages; - -use Magento\Framework\Message\ManagerInterface; use Magento\Framework\Message\MessageInterface; +/** + * Unit test for \Magento\Framework\View\Element\Messages + */ class MessagesTest extends \PHPUnit\Framework\TestCase { /** @@ -38,6 +40,11 @@ class MessagesTest extends \PHPUnit\Framework\TestCase */ protected $messageInterpretationStrategy; + /** + * @var Escaper|\PHPUnit_Framework_MockObject_MockObject + */ + private $escaperMock; + protected function setUp() { $this->collectionFactory = $this->getMockBuilder(\Magento\Framework\Message\CollectionFactory::class) @@ -52,13 +59,18 @@ protected function setUp() \Magento\Framework\View\Element\Message\InterpretationStrategyInterface::class ); + $this->escaperMock = $this->getMockBuilder(Escaper::class) + ->disableOriginalConstructor() + ->getMock(); + $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); $this->messages = $objectManager->getObject( \Magento\Framework\View\Element\Messages::class, [ 'collectionFactory' => $this->collectionFactory, 'messageFactory' => $this->messageFactory, - 'interpretationStrategy' => $this->messageInterpretationStrategy + 'interpretationStrategy' => $this->messageInterpretationStrategy, + 'escaper' => $this->escaperMock, ] ); } @@ -317,6 +329,14 @@ public function testGetGroupedHtml() ] ); + $this->escaperMock->expects($this->any()) + ->method('escapeHtmlAttr') + ->willReturnCallback( + function ($string) { + return $string; + } + ); + $this->assertEquals($resultHtml, $this->messages->getGroupedHtml()); } } diff --git a/lib/internal/Magento/Framework/View/Test/Unit/TemplateEngine/PhpTest.php b/lib/internal/Magento/Framework/View/Test/Unit/TemplateEngine/PhpTest.php index 5b9f8935a0938..b7f60f498565b 100644 --- a/lib/internal/Magento/Framework/View/Test/Unit/TemplateEngine/PhpTest.php +++ b/lib/internal/Magento/Framework/View/Test/Unit/TemplateEngine/PhpTest.php @@ -3,8 +3,13 @@ * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ +declare(strict_types=1); + namespace Magento\Framework\View\Test\Unit\TemplateEngine; +/** + * Test template engine that enables PHP templates to be used for rendering. + */ class PhpTest extends \PHPUnit\Framework\TestCase { const TEST_PROP_VALUE = 'TEST_PROP_VALUE'; @@ -55,7 +60,7 @@ public function testRender() * Test the render() function with a nonexistent filename. * * Expect an exception if the specified file does not exist. - * @expectedException \Exception + * @expectedException PHPUnit\Framework\Exception */ public function testRenderException() { @@ -66,6 +71,7 @@ public function testRenderException() )->disableOriginalConstructor()->getMock(); $filename = 'This_is_not_a_file'; + $this->_phpEngine->render($blockMock, $filename); } diff --git a/lib/web/css/docs/source/_breadcrumbs.less b/lib/web/css/docs/source/_breadcrumbs.less index 848af20c0728e..5b5df991e3cdc 100644 --- a/lib/web/css/docs/source/_breadcrumbs.less +++ b/lib/web/css/docs/source/_breadcrumbs.less @@ -8,7 +8,7 @@ // // Breadcrumbs layout with links can be separated by any symbol, for example <code>></code> symbol. // -// The following markup corresponds to the accesibility demands: +// The following markup corresponds to the accessibility demands: // ```html // <div class="example-breadcrumbs-1"> // <span class="label" id="breadcrumb-label">You are here:</span> diff --git a/lib/web/css/docs/source/_icons.less b/lib/web/css/docs/source/_icons.less index b3d14f5e1fd77..367e05ff5888e 100644 --- a/lib/web/css/docs/source/_icons.less +++ b/lib/web/css/docs/source/_icons.less @@ -256,7 +256,7 @@ // <td>@icon-sprite__grid</td> // <td class="vars_value">26px</td> // <td class="vars_value">'' | false | value</td> -// <td>The size of the grid (in pixels) that the individal images are placed on</td> +// <td>The size of the grid (in pixels) that the individual images are placed on</td> // </tr> // <tr> // <th>@_icon-sprite-position</th> diff --git a/lib/web/css/docs/source/_pages.less b/lib/web/css/docs/source/_pages.less index 6b01fa7549e92..59eb121112d28 100644 --- a/lib/web/css/docs/source/_pages.less +++ b/lib/web/css/docs/source/_pages.less @@ -4,7 +4,7 @@ // */ // # Pagination HTML markup -// Pagination is used to display numbers of pages in case content exceeds page limits. The markup corresponds to accesibility demands. +// Pagination is used to display numbers of pages in case content exceeds page limits. The markup corresponds to accessibility demands. // // Required HTML markup: // ```html diff --git a/lib/web/css/docs/source/_popups.less b/lib/web/css/docs/source/_popups.less index b192219b6438f..7251cde45bc3c 100644 --- a/lib/web/css/docs/source/_popups.less +++ b/lib/web/css/docs/source/_popups.less @@ -245,7 +245,7 @@ // <td>@popup-title-headings__level</td> // <td class="vars_value">h3</td> // <td class="vars_value">h1 | h2 | h3 | h4 | h5 | h6</td> -// <td>What heading style is applyed to the popup title</td> +// <td>What heading style is applied to the popup title</td> // </tr> // <tr> // <th colspan="5" class="vars_section">Popup close button</th> diff --git a/lib/web/css/docs/source/_rating.less b/lib/web/css/docs/source/_rating.less index 88de27b24cb87..6f7ec12b77bff 100644 --- a/lib/web/css/docs/source/_rating.less +++ b/lib/web/css/docs/source/_rating.less @@ -236,7 +236,7 @@ // # Accessible rating with vote // -// The following markup corresponds to **accesibility** demands +// The following markup corresponds to **accessibility** demands // ``` html // <fieldset class="exapmle-ratings-5 fieldset ratings vote"> // <legend>How do you rate this product?</legend> diff --git a/lib/web/css/docs/source/_responsive.less b/lib/web/css/docs/source/_responsive.less index 46c19bc23a66c..8e7397d6ad6f3 100644 --- a/lib/web/css/docs/source/_responsive.less +++ b/lib/web/css/docs/source/_responsive.less @@ -81,7 +81,7 @@ // // ## Gathering // -// Everything that you include in collector mixins above will go in place where they declarated. +// Everything that you include in collector mixins above will go in place where they declared. // As example all // ```css // .media-width(@extremum, @break) { diff --git a/lib/web/css/source/lib/_typography.less b/lib/web/css/source/lib/_typography.less index 3ca09d3782619..db4eaaf584f4a 100644 --- a/lib/web/css/source/lib/_typography.less +++ b/lib/web/css/source/lib/_typography.less @@ -10,10 +10,11 @@ .lib-font-face( @family-name, @font-path, + @font-format: false, @font-weight: normal, @font-style: normal, @font-display: auto -) { +) when (@font-format = false) { @font-face { font-family: @family-name; src: url('@{font-path}.woff2') format('woff2'), @@ -24,6 +25,23 @@ } } +.lib-font-face( + @family-name, + @font-path, + @font-format: false, + @font-weight: normal, + @font-style: normal, + @font-display: auto +) when not (@font-format = false) { + @font-face { + font-family: @family-name; + src: url('@{font-path}.@{font-format}') format(@font-format); + font-weight: @font-weight; + font-style: @font-style; + font-display: @font-display; + } +} + // Rem font size .lib-font-size(@sizeValue) when not (ispercentage(@sizeValue)) and not (@sizeValue = false) and (isunit(@sizeValue, @font-size-unit)) { .lib-css(font-size, @sizeValue); diff --git a/lib/web/jquery.js b/lib/web/jquery.js index 9a98ef778ed58..6190232d75bec 100644 --- a/lib/web/jquery.js +++ b/lib/web/jquery.js @@ -12,6 +12,11 @@ * Date: 2016-05-20T17:17Z */ +/* + * includes patch for CVE-2019-11358 + * prototype pollution vulnerability in jQuery before 3.4.0 + */ + (function( global, factory ) { if ( typeof module === "object" && typeof module.exports === "object" ) { @@ -209,8 +214,9 @@ src = target[ name ]; copy = options[ name ]; + // Prevent Object.prototype pollution // Prevent never-ending loop - if ( target === copy ) { + if ( name === "__proto__" || target === copy ) { continue; } diff --git a/lib/web/jquery/jquery.min.js b/lib/web/jquery/jquery.min.js index bfe47d639170f..ad2aa21ba942a 100644 --- a/lib/web/jquery/jquery.min.js +++ b/lib/web/jquery/jquery.min.js @@ -1,5 +1,6 @@ /*! jQuery v1.12.4 | (c) jQuery Foundation | jquery.org/license */ -!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=a.document,e=c.slice,f=c.concat,g=c.push,h=c.indexOf,i={},j=i.toString,k=i.hasOwnProperty,l={},m="1.12.4",n=function(a,b){return new n.fn.init(a,b)},o=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,p=/^-ms-/,q=/-([\da-z])/gi,r=function(a,b){return b.toUpperCase()};n.fn=n.prototype={jquery:m,constructor:n,selector:"",length:0,toArray:function(){return e.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:e.call(this)},pushStack:function(a){var b=n.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a){return n.each(this,a)},map:function(a){return this.pushStack(n.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(e.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor()},push:g,sort:c.sort,splice:c.splice},n.extend=n.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||n.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(e=arguments[h]))for(d in e)a=g[d],c=e[d],g!==c&&(j&&c&&(n.isPlainObject(c)||(b=n.isArray(c)))?(b?(b=!1,f=a&&n.isArray(a)?a:[]):f=a&&n.isPlainObject(a)?a:{},g[d]=n.extend(j,f,c)):void 0!==c&&(g[d]=c));return g},n.extend({expando:"jQuery"+(m+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===n.type(a)},isArray:Array.isArray||function(a){return"array"===n.type(a)},isWindow:function(a){return null!=a&&a==a.window},isNumeric:function(a){var b=a&&a.toString();return!n.isArray(a)&&b-parseFloat(b)+1>=0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},isPlainObject:function(a){var b;if(!a||"object"!==n.type(a)||a.nodeType||n.isWindow(a))return!1;try{if(a.constructor&&!k.call(a,"constructor")&&!k.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}if(!l.ownFirst)for(b in a)return k.call(a,b);for(b in a);return void 0===b||k.call(a,b)},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?i[j.call(a)]||"object":typeof a},globalEval:function(b){b&&n.trim(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(p,"ms-").replace(q,r)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b){var c,d=0;if(s(a)){for(c=a.length;c>d;d++)if(b.call(a[d],d,a[d])===!1)break}else for(d in a)if(b.call(a[d],d,a[d])===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(o,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(s(Object(a))?n.merge(c,"string"==typeof a?[a]:a):g.call(c,a)),c},inArray:function(a,b,c){var d;if(b){if(h)return h.call(b,a,c);for(d=b.length,c=c?0>c?Math.max(0,d+c):c:0;d>c;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,b){var c=+b.length,d=0,e=a.length;while(c>d)a[e++]=b[d++];if(c!==c)while(void 0!==b[d])a[e++]=b[d++];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,e,g=0,h=[];if(s(a))for(d=a.length;d>g;g++)e=b(a[g],g,c),null!=e&&h.push(e);else for(g in a)e=b(a[g],g,c),null!=e&&h.push(e);return f.apply([],h)},guid:1,proxy:function(a,b){var c,d,f;return"string"==typeof b&&(f=a[b],b=a,a=f),n.isFunction(a)?(c=e.call(arguments,2),d=function(){return a.apply(b||this,c.concat(e.call(arguments)))},d.guid=a.guid=a.guid||n.guid++,d):void 0},now:function(){return+new Date},support:l}),"function"==typeof Symbol&&(n.fn[Symbol.iterator]=c[Symbol.iterator]),n.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),function(a,b){i["[object "+b+"]"]=b.toLowerCase()});function s(a){var b=!!a&&"length"in a&&a.length,c=n.type(a);return"function"===c||n.isWindow(a)?!1:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var t=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ga(),z=ga(),A=ga(),B=function(a,b){return a===b&&(l=!0),0},C=1<<31,D={}.hasOwnProperty,E=[],F=E.pop,G=E.push,H=E.push,I=E.slice,J=function(a,b){for(var c=0,d=a.length;d>c;c++)if(a[c]===b)return c;return-1},K="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",L="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",N="\\["+L+"*("+M+")(?:"+L+"*([*^$|!~]?=)"+L+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+M+"))|)"+L+"*\\]",O=":("+M+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+N+")*)|.*)\\)|)",P=new RegExp(L+"+","g"),Q=new RegExp("^"+L+"+|((?:^|[^\\\\])(?:\\\\.)*)"+L+"+$","g"),R=new RegExp("^"+L+"*,"+L+"*"),S=new RegExp("^"+L+"*([>+~]|"+L+")"+L+"*"),T=new RegExp("="+L+"*([^\\]'\"]*?)"+L+"*\\]","g"),U=new RegExp(O),V=new RegExp("^"+M+"$"),W={ID:new RegExp("^#("+M+")"),CLASS:new RegExp("^\\.("+M+")"),TAG:new RegExp("^("+M+"|[*])"),ATTR:new RegExp("^"+N),PSEUDO:new RegExp("^"+O),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+L+"*(even|odd|(([+-]|)(\\d*)n|)"+L+"*(?:([+-]|)"+L+"*(\\d+)|))"+L+"*\\)|)","i"),bool:new RegExp("^(?:"+K+")$","i"),needsContext:new RegExp("^"+L+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+L+"*((?:-\\d)?\\d*)"+L+"*\\)|)(?=[^-]|$)","i")},X=/^(?:input|select|textarea|button)$/i,Y=/^h\d$/i,Z=/^[^{]+\{\s*\[native \w/,$=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,_=/[+~]/,aa=/'|\\/g,ba=new RegExp("\\\\([\\da-f]{1,6}"+L+"?|("+L+")|.)","ig"),ca=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},da=function(){m()};try{H.apply(E=I.call(v.childNodes),v.childNodes),E[v.childNodes.length].nodeType}catch(ea){H={apply:E.length?function(a,b){G.apply(a,I.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function fa(a,b,d,e){var f,h,j,k,l,o,r,s,w=b&&b.ownerDocument,x=b?b.nodeType:9;if(d=d||[],"string"!=typeof a||!a||1!==x&&9!==x&&11!==x)return d;if(!e&&((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,p)){if(11!==x&&(o=$.exec(a)))if(f=o[1]){if(9===x){if(!(j=b.getElementById(f)))return d;if(j.id===f)return d.push(j),d}else if(w&&(j=w.getElementById(f))&&t(b,j)&&j.id===f)return d.push(j),d}else{if(o[2])return H.apply(d,b.getElementsByTagName(a)),d;if((f=o[3])&&c.getElementsByClassName&&b.getElementsByClassName)return H.apply(d,b.getElementsByClassName(f)),d}if(c.qsa&&!A[a+" "]&&(!q||!q.test(a))){if(1!==x)w=b,s=a;else if("object"!==b.nodeName.toLowerCase()){(k=b.getAttribute("id"))?k=k.replace(aa,"\\$&"):b.setAttribute("id",k=u),r=g(a),h=r.length,l=V.test(k)?"#"+k:"[id='"+k+"']";while(h--)r[h]=l+" "+qa(r[h]);s=r.join(","),w=_.test(a)&&oa(b.parentNode)||b}if(s)try{return H.apply(d,w.querySelectorAll(s)),d}catch(y){}finally{k===u&&b.removeAttribute("id")}}}return i(a.replace(Q,"$1"),b,d,e)}function ga(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ha(a){return a[u]=!0,a}function ia(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ja(a,b){var c=a.split("|"),e=c.length;while(e--)d.attrHandle[c[e]]=b}function ka(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||C)-(~a.sourceIndex||C);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function la(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function na(a){return ha(function(b){return b=+b,ha(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function oa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=fa.support={},f=fa.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=fa.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=n.documentElement,p=!f(n),(e=n.defaultView)&&e.top!==e&&(e.addEventListener?e.addEventListener("unload",da,!1):e.attachEvent&&e.attachEvent("onunload",da)),c.attributes=ia(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ia(function(a){return a.appendChild(n.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=Z.test(n.getElementsByClassName),c.getById=ia(function(a){return o.appendChild(a).id=u,!n.getElementsByName||!n.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c?[c]:[]}},d.filter.ID=function(a){var b=a.replace(ba,ca);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(ba,ca);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return"undefined"!=typeof b.getElementsByClassName&&p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=Z.test(n.querySelectorAll))&&(ia(function(a){o.appendChild(a).innerHTML="<a id='"+u+"'></a><select id='"+u+"-\r\\' msallowcapture=''><option selected=''></option></select>",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+L+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+L+"*(?:value|"+K+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ia(function(a){var b=n.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+L+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=Z.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ia(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",O)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=Z.test(o.compareDocumentPosition),t=b||Z.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===n||a.ownerDocument===v&&t(v,a)?-1:b===n||b.ownerDocument===v&&t(v,b)?1:k?J(k,a)-J(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,g=[a],h=[b];if(!e||!f)return a===n?-1:b===n?1:e?-1:f?1:k?J(k,a)-J(k,b):0;if(e===f)return ka(a,b);c=a;while(c=c.parentNode)g.unshift(c);c=b;while(c=c.parentNode)h.unshift(c);while(g[d]===h[d])d++;return d?ka(g[d],h[d]):g[d]===v?-1:h[d]===v?1:0},n):n},fa.matches=function(a,b){return fa(a,null,null,b)},fa.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(T,"='$1']"),c.matchesSelector&&p&&!A[b+" "]&&(!r||!r.test(b))&&(!q||!q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return fa(b,n,null,[a]).length>0},fa.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},fa.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&D.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},fa.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},fa.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=fa.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=fa.selectors={cacheLength:50,createPseudo:ha,match:W,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(ba,ca),a[3]=(a[3]||a[4]||a[5]||"").replace(ba,ca),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||fa.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&fa.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return W.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&U.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(ba,ca).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+L+")"+a+"("+L+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=fa.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(P," ")+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h,t=!1;if(q){if(f){while(p){m=b;while(m=m[p])if(h?m.nodeName.toLowerCase()===r:1===m.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){m=q,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n&&j[2],m=n&&q.childNodes[n];while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if(1===m.nodeType&&++t&&m===b){k[a]=[w,n,t];break}}else if(s&&(m=b,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n),t===!1)while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if((h?m.nodeName.toLowerCase()===r:1===m.nodeType)&&++t&&(s&&(l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),k[a]=[w,t]),m===b))break;return t-=e,t===d||t%d===0&&t/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||fa.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ha(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=J(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ha(function(a){var b=[],c=[],d=h(a.replace(Q,"$1"));return d[u]?ha(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ha(function(a){return function(b){return fa(a,b).length>0}}),contains:ha(function(a){return a=a.replace(ba,ca),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ha(function(a){return V.test(a||"")||fa.error("unsupported lang: "+a),a=a.replace(ba,ca).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Y.test(a.nodeName)},input:function(a){return X.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:na(function(){return[0]}),last:na(function(a,b){return[b-1]}),eq:na(function(a,b,c){return[0>c?c+b:c]}),even:na(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:na(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:na(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:na(function(a,b,c){for(var d=0>c?c+b:c;++d<b;)a.push(d);return a})}},d.pseudos.nth=d.pseudos.eq;for(b in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})d.pseudos[b]=la(b);for(b in{submit:!0,reset:!0})d.pseudos[b]=ma(b);function pa(){}pa.prototype=d.filters=d.pseudos,d.setFilters=new pa,g=fa.tokenize=function(a,b){var c,e,f,g,h,i,j,k=z[a+" "];if(k)return b?0:k.slice(0);h=a,i=[],j=d.preFilter;while(h){c&&!(e=R.exec(h))||(e&&(h=h.slice(e[0].length)||h),i.push(f=[])),c=!1,(e=S.exec(h))&&(c=e.shift(),f.push({value:c,type:e[0].replace(Q," ")}),h=h.slice(c.length));for(g in d.filter)!(e=W[g].exec(h))||j[g]&&!(e=j[g](e))||(c=e.shift(),f.push({value:c,type:g,matches:e}),h=h.slice(c.length));if(!c)break}return b?h.length:h?fa.error(a):z(a,i).slice(0)};function qa(a){for(var b=0,c=a.length,d="";c>b;b++)d+=a[b].value;return d}function ra(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j,k=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(j=b[u]||(b[u]={}),i=j[b.uniqueID]||(j[b.uniqueID]={}),(h=i[d])&&h[0]===w&&h[1]===f)return k[2]=h[2];if(i[d]=k,k[2]=a(b,c,g))return!0}}}function sa(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function ta(a,b,c){for(var d=0,e=b.length;e>d;d++)fa(a,b[d],c);return c}function ua(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(c&&!c(f,d,e)||(g.push(f),j&&b.push(h)));return g}function va(a,b,c,d,e,f){return d&&!d[u]&&(d=va(d)),e&&!e[u]&&(e=va(e,f)),ha(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||ta(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:ua(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=ua(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?J(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=ua(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):H.apply(g,r)})}function wa(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=ra(function(a){return a===b},h,!0),l=ra(function(a){return J(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];f>i;i++)if(c=d.relative[a[i].type])m=[ra(sa(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return va(i>1&&sa(m),i>1&&qa(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(Q,"$1"),c,e>i&&wa(a.slice(i,e)),f>e&&wa(a=a.slice(e)),f>e&&qa(a))}m.push(c)}return sa(m)}function xa(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,o,q,r=0,s="0",t=f&&[],u=[],v=j,x=f||e&&d.find.TAG("*",k),y=w+=null==v?1:Math.random()||.1,z=x.length;for(k&&(j=g===n||g||k);s!==z&&null!=(l=x[s]);s++){if(e&&l){o=0,g||l.ownerDocument===n||(m(l),h=!p);while(q=a[o++])if(q(l,g||n,h)){i.push(l);break}k&&(w=y)}c&&((l=!q&&l)&&r--,f&&t.push(l))}if(r+=s,c&&s!==r){o=0;while(q=b[o++])q(t,u,g,h);if(f){if(r>0)while(s--)t[s]||u[s]||(u[s]=F.call(i));u=ua(u)}H.apply(i,u),k&&!f&&u.length>0&&r+b.length>1&&fa.uniqueSort(i)}return k&&(w=y,j=v),t};return c?ha(f):f}return h=fa.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=wa(b[c]),f[u]?d.push(f):e.push(f);f=A(a,xa(e,d)),f.selector=a}return f},i=fa.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(ba,ca),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=W.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(ba,ca),_.test(j[0].type)&&oa(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&qa(j),!a)return H.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,!b||_.test(a)&&oa(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ia(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),ia(function(a){return a.innerHTML="<a href='#'></a>","#"===a.firstChild.getAttribute("href")})||ja("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ia(function(a){return a.innerHTML="<input/>",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ja("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),ia(function(a){return null==a.getAttribute("disabled")})||ja(K,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),fa}(a);n.find=t,n.expr=t.selectors,n.expr[":"]=n.expr.pseudos,n.uniqueSort=n.unique=t.uniqueSort,n.text=t.getText,n.isXMLDoc=t.isXML,n.contains=t.contains;var u=function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&n(a).is(c))break;d.push(a)}return d},v=function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c},w=n.expr.match.needsContext,x=/^<([\w-]+)\s*\/?>(?:<\/\1>|)$/,y=/^.[^:#\[\.,]*$/;function z(a,b,c){if(n.isFunction(b))return n.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return n.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(y.test(b))return n.filter(b,a,c);b=n.filter(b,a)}return n.grep(a,function(a){return n.inArray(a,b)>-1!==c})}n.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?n.find.matchesSelector(d,a)?[d]:[]:n.find.matches(a,n.grep(b,function(a){return 1===a.nodeType}))},n.fn.extend({find:function(a){var b,c=[],d=this,e=d.length;if("string"!=typeof a)return this.pushStack(n(a).filter(function(){for(b=0;e>b;b++)if(n.contains(d[b],this))return!0}));for(b=0;e>b;b++)n.find(a,d[b],c);return c=this.pushStack(e>1?n.unique(c):c),c.selector=this.selector?this.selector+" "+a:a,c},filter:function(a){return this.pushStack(z(this,a||[],!1))},not:function(a){return this.pushStack(z(this,a||[],!0))},is:function(a){return!!z(this,"string"==typeof a&&w.test(a)?n(a):a||[],!1).length}});var A,B=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,C=n.fn.init=function(a,b,c){var e,f;if(!a)return this;if(c=c||A,"string"==typeof a){if(e="<"===a.charAt(0)&&">"===a.charAt(a.length-1)&&a.length>=3?[null,a,null]:B.exec(a),!e||!e[1]&&b)return!b||b.jquery?(b||c).find(a):this.constructor(b).find(a);if(e[1]){if(b=b instanceof n?b[0]:b,n.merge(this,n.parseHTML(e[1],b&&b.nodeType?b.ownerDocument||b:d,!0)),x.test(e[1])&&n.isPlainObject(b))for(e in b)n.isFunction(this[e])?this[e](b[e]):this.attr(e,b[e]);return this}if(f=d.getElementById(e[2]),f&&f.parentNode){if(f.id!==e[2])return A.find(a);this.length=1,this[0]=f}return this.context=d,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):n.isFunction(a)?"undefined"!=typeof c.ready?c.ready(a):a(n):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),n.makeArray(a,this))};C.prototype=n.fn,A=n(d);var D=/^(?:parents|prev(?:Until|All))/,E={children:!0,contents:!0,next:!0,prev:!0};n.fn.extend({has:function(a){var b,c=n(a,this),d=c.length;return this.filter(function(){for(b=0;d>b;b++)if(n.contains(this,c[b]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=w.test(a)||"string"!=typeof a?n(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&n.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?n.uniqueSort(f):f)},index:function(a){return a?"string"==typeof a?n.inArray(this[0],n(a)):n.inArray(a.jquery?a[0]:a,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(n.uniqueSort(n.merge(this.get(),n(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function F(a,b){do a=a[b];while(a&&1!==a.nodeType);return a}n.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return u(a,"parentNode")},parentsUntil:function(a,b,c){return u(a,"parentNode",c)},next:function(a){return F(a,"nextSibling")},prev:function(a){return F(a,"previousSibling")},nextAll:function(a){return u(a,"nextSibling")},prevAll:function(a){return u(a,"previousSibling")},nextUntil:function(a,b,c){return u(a,"nextSibling",c)},prevUntil:function(a,b,c){return u(a,"previousSibling",c)},siblings:function(a){return v((a.parentNode||{}).firstChild,a)},children:function(a){return v(a.firstChild)},contents:function(a){return n.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:n.merge([],a.childNodes)}},function(a,b){n.fn[a]=function(c,d){var e=n.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=n.filter(d,e)),this.length>1&&(E[a]||(e=n.uniqueSort(e)),D.test(a)&&(e=e.reverse())),this.pushStack(e)}});var G=/\S+/g;function H(a){var b={};return n.each(a.match(G)||[],function(a,c){b[c]=!0}),b}n.Callbacks=function(a){a="string"==typeof a?H(a):n.extend({},a);var b,c,d,e,f=[],g=[],h=-1,i=function(){for(e=a.once,d=b=!0;g.length;h=-1){c=g.shift();while(++h<f.length)f[h].apply(c[0],c[1])===!1&&a.stopOnFalse&&(h=f.length,c=!1)}a.memory||(c=!1),b=!1,e&&(f=c?[]:"")},j={add:function(){return f&&(c&&!b&&(h=f.length-1,g.push(c)),function d(b){n.each(b,function(b,c){n.isFunction(c)?a.unique&&j.has(c)||f.push(c):c&&c.length&&"string"!==n.type(c)&&d(c)})}(arguments),c&&!b&&i()),this},remove:function(){return n.each(arguments,function(a,b){var c;while((c=n.inArray(b,f,c))>-1)f.splice(c,1),h>=c&&h--}),this},has:function(a){return a?n.inArray(a,f)>-1:f.length>0},empty:function(){return f&&(f=[]),this},disable:function(){return e=g=[],f=c="",this},disabled:function(){return!f},lock:function(){return e=!0,c||j.disable(),this},locked:function(){return!!e},fireWith:function(a,c){return e||(c=c||[],c=[a,c.slice?c.slice():c],g.push(c),b||i()),this},fire:function(){return j.fireWith(this,arguments),this},fired:function(){return!!d}};return j},n.extend({Deferred:function(a){var b=[["resolve","done",n.Callbacks("once memory"),"resolved"],["reject","fail",n.Callbacks("once memory"),"rejected"],["notify","progress",n.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return n.Deferred(function(c){n.each(b,function(b,f){var g=n.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&n.isFunction(a.promise)?a.promise().progress(c.notify).done(c.resolve).fail(c.reject):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?n.extend(a,d):d}},e={};return d.pipe=d.then,n.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=e.call(arguments),d=c.length,f=1!==d||a&&n.isFunction(a.promise)?d:0,g=1===f?a:n.Deferred(),h=function(a,b,c){return function(d){b[a]=this,c[a]=arguments.length>1?e.call(arguments):d,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(d>1)for(i=new Array(d),j=new Array(d),k=new Array(d);d>b;b++)c[b]&&n.isFunction(c[b].promise)?c[b].promise().progress(h(b,j,i)).done(h(b,k,c)).fail(g.reject):--f;return f||g.resolveWith(k,c),g.promise()}});var I;n.fn.ready=function(a){return n.ready.promise().done(a),this},n.extend({isReady:!1,readyWait:1,holdReady:function(a){a?n.readyWait++:n.ready(!0)},ready:function(a){(a===!0?--n.readyWait:n.isReady)||(n.isReady=!0,a!==!0&&--n.readyWait>0||(I.resolveWith(d,[n]),n.fn.triggerHandler&&(n(d).triggerHandler("ready"),n(d).off("ready"))))}});function J(){d.addEventListener?(d.removeEventListener("DOMContentLoaded",K),a.removeEventListener("load",K)):(d.detachEvent("onreadystatechange",K),a.detachEvent("onload",K))}function K(){(d.addEventListener||"load"===a.event.type||"complete"===d.readyState)&&(J(),n.ready())}n.ready.promise=function(b){if(!I)if(I=n.Deferred(),"complete"===d.readyState||"loading"!==d.readyState&&!d.documentElement.doScroll)a.setTimeout(n.ready);else if(d.addEventListener)d.addEventListener("DOMContentLoaded",K),a.addEventListener("load",K);else{d.attachEvent("onreadystatechange",K),a.attachEvent("onload",K);var c=!1;try{c=null==a.frameElement&&d.documentElement}catch(e){}c&&c.doScroll&&!function f(){if(!n.isReady){try{c.doScroll("left")}catch(b){return a.setTimeout(f,50)}J(),n.ready()}}()}return I.promise(b)},n.ready.promise();var L;for(L in n(l))break;l.ownFirst="0"===L,l.inlineBlockNeedsLayout=!1,n(function(){var a,b,c,e;c=d.getElementsByTagName("body")[0],c&&c.style&&(b=d.createElement("div"),e=d.createElement("div"),e.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(e).appendChild(b),"undefined"!=typeof b.style.zoom&&(b.style.cssText="display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1",l.inlineBlockNeedsLayout=a=3===b.offsetWidth,a&&(c.style.zoom=1)),c.removeChild(e))}),function(){var a=d.createElement("div");l.deleteExpando=!0;try{delete a.test}catch(b){l.deleteExpando=!1}a=null}();var M=function(a){var b=n.noData[(a.nodeName+" ").toLowerCase()],c=+a.nodeType||1;return 1!==c&&9!==c?!1:!b||b!==!0&&a.getAttribute("classid")===b},N=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,O=/([A-Z])/g;function P(a,b,c){if(void 0===c&&1===a.nodeType){var d="data-"+b.replace(O,"-$1").toLowerCase();if(c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:N.test(c)?n.parseJSON(c):c}catch(e){}n.data(a,b,c)}else c=void 0; +/* includes patch for CVE-2019-11358 - prototype pollution vulnerability in jQuery before 3.4.0 */ +!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=a.document,e=c.slice,f=c.concat,g=c.push,h=c.indexOf,i={},j=i.toString,k=i.hasOwnProperty,l={},m="1.12.4",n=function(a,b){return new n.fn.init(a,b)},o=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,p=/^-ms-/,q=/-([\da-z])/gi,r=function(a,b){return b.toUpperCase()};n.fn=n.prototype={jquery:m,constructor:n,selector:"",length:0,toArray:function(){return e.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:e.call(this)},pushStack:function(a){var b=n.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a){return n.each(this,a)},map:function(a){return this.pushStack(n.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(e.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor()},push:g,sort:c.sort,splice:c.splice},n.extend=n.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||n.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(e=arguments[h]))for(d in e)a=g[d],c=e[d],d!=="__proto__"&&g!==c&&(j&&c&&(n.isPlainObject(c)||(b=n.isArray(c)))?(b?(b=!1,f=a&&n.isArray(a)?a:[]):f=a&&n.isPlainObject(a)?a:{},g[d]=n.extend(j,f,c)):void 0!==c&&(g[d]=c));return g},n.extend({expando:"jQuery"+(m+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===n.type(a)},isArray:Array.isArray||function(a){return"array"===n.type(a)},isWindow:function(a){return null!=a&&a==a.window},isNumeric:function(a){var b=a&&a.toString();return!n.isArray(a)&&b-parseFloat(b)+1>=0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},isPlainObject:function(a){var b;if(!a||"object"!==n.type(a)||a.nodeType||n.isWindow(a))return!1;try{if(a.constructor&&!k.call(a,"constructor")&&!k.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}if(!l.ownFirst)for(b in a)return k.call(a,b);for(b in a);return void 0===b||k.call(a,b)},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?i[j.call(a)]||"object":typeof a},globalEval:function(b){b&&n.trim(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(p,"ms-").replace(q,r)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b){var c,d=0;if(s(a)){for(c=a.length;c>d;d++)if(b.call(a[d],d,a[d])===!1)break}else for(d in a)if(b.call(a[d],d,a[d])===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(o,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(s(Object(a))?n.merge(c,"string"==typeof a?[a]:a):g.call(c,a)),c},inArray:function(a,b,c){var d;if(b){if(h)return h.call(b,a,c);for(d=b.length,c=c?0>c?Math.max(0,d+c):c:0;d>c;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,b){var c=+b.length,d=0,e=a.length;while(c>d)a[e++]=b[d++];if(c!==c)while(void 0!==b[d])a[e++]=b[d++];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,e,g=0,h=[];if(s(a))for(d=a.length;d>g;g++)e=b(a[g],g,c),null!=e&&h.push(e);else for(g in a)e=b(a[g],g,c),null!=e&&h.push(e);return f.apply([],h)},guid:1,proxy:function(a,b){var c,d,f;return"string"==typeof b&&(f=a[b],b=a,a=f),n.isFunction(a)?(c=e.call(arguments,2),d=function(){return a.apply(b||this,c.concat(e.call(arguments)))},d.guid=a.guid=a.guid||n.guid++,d):void 0},now:function(){return+new Date},support:l}),"function"==typeof Symbol&&(n.fn[Symbol.iterator]=c[Symbol.iterator]),n.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),function(a,b){i["[object "+b+"]"]=b.toLowerCase()});function s(a){var b=!!a&&"length"in a&&a.length,c=n.type(a);return"function"===c||n.isWindow(a)?!1:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var t=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ga(),z=ga(),A=ga(),B=function(a,b){return a===b&&(l=!0),0},C=1<<31,D={}.hasOwnProperty,E=[],F=E.pop,G=E.push,H=E.push,I=E.slice,J=function(a,b){for(var c=0,d=a.length;d>c;c++)if(a[c]===b)return c;return-1},K="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",L="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",N="\\["+L+"*("+M+")(?:"+L+"*([*^$|!~]?=)"+L+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+M+"))|)"+L+"*\\]",O=":("+M+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+N+")*)|.*)\\)|)",P=new RegExp(L+"+","g"),Q=new RegExp("^"+L+"+|((?:^|[^\\\\])(?:\\\\.)*)"+L+"+$","g"),R=new RegExp("^"+L+"*,"+L+"*"),S=new RegExp("^"+L+"*([>+~]|"+L+")"+L+"*"),T=new RegExp("="+L+"*([^\\]'\"]*?)"+L+"*\\]","g"),U=new RegExp(O),V=new RegExp("^"+M+"$"),W={ID:new RegExp("^#("+M+")"),CLASS:new RegExp("^\\.("+M+")"),TAG:new RegExp("^("+M+"|[*])"),ATTR:new RegExp("^"+N),PSEUDO:new RegExp("^"+O),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+L+"*(even|odd|(([+-]|)(\\d*)n|)"+L+"*(?:([+-]|)"+L+"*(\\d+)|))"+L+"*\\)|)","i"),bool:new RegExp("^(?:"+K+")$","i"),needsContext:new RegExp("^"+L+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+L+"*((?:-\\d)?\\d*)"+L+"*\\)|)(?=[^-]|$)","i")},X=/^(?:input|select|textarea|button)$/i,Y=/^h\d$/i,Z=/^[^{]+\{\s*\[native \w/,$=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,_=/[+~]/,aa=/'|\\/g,ba=new RegExp("\\\\([\\da-f]{1,6}"+L+"?|("+L+")|.)","ig"),ca=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},da=function(){m()};try{H.apply(E=I.call(v.childNodes),v.childNodes),E[v.childNodes.length].nodeType}catch(ea){H={apply:E.length?function(a,b){G.apply(a,I.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function fa(a,b,d,e){var f,h,j,k,l,o,r,s,w=b&&b.ownerDocument,x=b?b.nodeType:9;if(d=d||[],"string"!=typeof a||!a||1!==x&&9!==x&&11!==x)return d;if(!e&&((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,p)){if(11!==x&&(o=$.exec(a)))if(f=o[1]){if(9===x){if(!(j=b.getElementById(f)))return d;if(j.id===f)return d.push(j),d}else if(w&&(j=w.getElementById(f))&&t(b,j)&&j.id===f)return d.push(j),d}else{if(o[2])return H.apply(d,b.getElementsByTagName(a)),d;if((f=o[3])&&c.getElementsByClassName&&b.getElementsByClassName)return H.apply(d,b.getElementsByClassName(f)),d}if(c.qsa&&!A[a+" "]&&(!q||!q.test(a))){if(1!==x)w=b,s=a;else if("object"!==b.nodeName.toLowerCase()){(k=b.getAttribute("id"))?k=k.replace(aa,"\\$&"):b.setAttribute("id",k=u),r=g(a),h=r.length,l=V.test(k)?"#"+k:"[id='"+k+"']";while(h--)r[h]=l+" "+qa(r[h]);s=r.join(","),w=_.test(a)&&oa(b.parentNode)||b}if(s)try{return H.apply(d,w.querySelectorAll(s)),d}catch(y){}finally{k===u&&b.removeAttribute("id")}}}return i(a.replace(Q,"$1"),b,d,e)}function ga(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ha(a){return a[u]=!0,a}function ia(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ja(a,b){var c=a.split("|"),e=c.length;while(e--)d.attrHandle[c[e]]=b}function ka(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||C)-(~a.sourceIndex||C);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function la(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function na(a){return ha(function(b){return b=+b,ha(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function oa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=fa.support={},f=fa.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=fa.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=n.documentElement,p=!f(n),(e=n.defaultView)&&e.top!==e&&(e.addEventListener?e.addEventListener("unload",da,!1):e.attachEvent&&e.attachEvent("onunload",da)),c.attributes=ia(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ia(function(a){return a.appendChild(n.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=Z.test(n.getElementsByClassName),c.getById=ia(function(a){return o.appendChild(a).id=u,!n.getElementsByName||!n.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c?[c]:[]}},d.filter.ID=function(a){var b=a.replace(ba,ca);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(ba,ca);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return"undefined"!=typeof b.getElementsByClassName&&p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=Z.test(n.querySelectorAll))&&(ia(function(a){o.appendChild(a).innerHTML="<a id='"+u+"'></a><select id='"+u+"-\r\\' msallowcapture=''><option selected=''></option></select>",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+L+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+L+"*(?:value|"+K+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ia(function(a){var b=n.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+L+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=Z.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ia(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",O)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=Z.test(o.compareDocumentPosition),t=b||Z.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===n||a.ownerDocument===v&&t(v,a)?-1:b===n||b.ownerDocument===v&&t(v,b)?1:k?J(k,a)-J(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,g=[a],h=[b];if(!e||!f)return a===n?-1:b===n?1:e?-1:f?1:k?J(k,a)-J(k,b):0;if(e===f)return ka(a,b);c=a;while(c=c.parentNode)g.unshift(c);c=b;while(c=c.parentNode)h.unshift(c);while(g[d]===h[d])d++;return d?ka(g[d],h[d]):g[d]===v?-1:h[d]===v?1:0},n):n},fa.matches=function(a,b){return fa(a,null,null,b)},fa.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(T,"='$1']"),c.matchesSelector&&p&&!A[b+" "]&&(!r||!r.test(b))&&(!q||!q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return fa(b,n,null,[a]).length>0},fa.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},fa.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&D.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},fa.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},fa.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=fa.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=fa.selectors={cacheLength:50,createPseudo:ha,match:W,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(ba,ca),a[3]=(a[3]||a[4]||a[5]||"").replace(ba,ca),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||fa.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&fa.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return W.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&U.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(ba,ca).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+L+")"+a+"("+L+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=fa.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(P," ")+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h,t=!1;if(q){if(f){while(p){m=b;while(m=m[p])if(h?m.nodeName.toLowerCase()===r:1===m.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){m=q,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n&&j[2],m=n&&q.childNodes[n];while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if(1===m.nodeType&&++t&&m===b){k[a]=[w,n,t];break}}else if(s&&(m=b,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n),t===!1)while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if((h?m.nodeName.toLowerCase()===r:1===m.nodeType)&&++t&&(s&&(l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),k[a]=[w,t]),m===b))break;return t-=e,t===d||t%d===0&&t/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||fa.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ha(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=J(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ha(function(a){var b=[],c=[],d=h(a.replace(Q,"$1"));return d[u]?ha(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ha(function(a){return function(b){return fa(a,b).length>0}}),contains:ha(function(a){return a=a.replace(ba,ca),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ha(function(a){return V.test(a||"")||fa.error("unsupported lang: "+a),a=a.replace(ba,ca).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Y.test(a.nodeName)},input:function(a){return X.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:na(function(){return[0]}),last:na(function(a,b){return[b-1]}),eq:na(function(a,b,c){return[0>c?c+b:c]}),even:na(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:na(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:na(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:na(function(a,b,c){for(var d=0>c?c+b:c;++d<b;)a.push(d);return a})}},d.pseudos.nth=d.pseudos.eq;for(b in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})d.pseudos[b]=la(b);for(b in{submit:!0,reset:!0})d.pseudos[b]=ma(b);function pa(){}pa.prototype=d.filters=d.pseudos,d.setFilters=new pa,g=fa.tokenize=function(a,b){var c,e,f,g,h,i,j,k=z[a+" "];if(k)return b?0:k.slice(0);h=a,i=[],j=d.preFilter;while(h){c&&!(e=R.exec(h))||(e&&(h=h.slice(e[0].length)||h),i.push(f=[])),c=!1,(e=S.exec(h))&&(c=e.shift(),f.push({value:c,type:e[0].replace(Q," ")}),h=h.slice(c.length));for(g in d.filter)!(e=W[g].exec(h))||j[g]&&!(e=j[g](e))||(c=e.shift(),f.push({value:c,type:g,matches:e}),h=h.slice(c.length));if(!c)break}return b?h.length:h?fa.error(a):z(a,i).slice(0)};function qa(a){for(var b=0,c=a.length,d="";c>b;b++)d+=a[b].value;return d}function ra(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j,k=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(j=b[u]||(b[u]={}),i=j[b.uniqueID]||(j[b.uniqueID]={}),(h=i[d])&&h[0]===w&&h[1]===f)return k[2]=h[2];if(i[d]=k,k[2]=a(b,c,g))return!0}}}function sa(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function ta(a,b,c){for(var d=0,e=b.length;e>d;d++)fa(a,b[d],c);return c}function ua(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(c&&!c(f,d,e)||(g.push(f),j&&b.push(h)));return g}function va(a,b,c,d,e,f){return d&&!d[u]&&(d=va(d)),e&&!e[u]&&(e=va(e,f)),ha(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||ta(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:ua(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=ua(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?J(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=ua(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):H.apply(g,r)})}function wa(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=ra(function(a){return a===b},h,!0),l=ra(function(a){return J(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];f>i;i++)if(c=d.relative[a[i].type])m=[ra(sa(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return va(i>1&&sa(m),i>1&&qa(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(Q,"$1"),c,e>i&&wa(a.slice(i,e)),f>e&&wa(a=a.slice(e)),f>e&&qa(a))}m.push(c)}return sa(m)}function xa(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,o,q,r=0,s="0",t=f&&[],u=[],v=j,x=f||e&&d.find.TAG("*",k),y=w+=null==v?1:Math.random()||.1,z=x.length;for(k&&(j=g===n||g||k);s!==z&&null!=(l=x[s]);s++){if(e&&l){o=0,g||l.ownerDocument===n||(m(l),h=!p);while(q=a[o++])if(q(l,g||n,h)){i.push(l);break}k&&(w=y)}c&&((l=!q&&l)&&r--,f&&t.push(l))}if(r+=s,c&&s!==r){o=0;while(q=b[o++])q(t,u,g,h);if(f){if(r>0)while(s--)t[s]||u[s]||(u[s]=F.call(i));u=ua(u)}H.apply(i,u),k&&!f&&u.length>0&&r+b.length>1&&fa.uniqueSort(i)}return k&&(w=y,j=v),t};return c?ha(f):f}return h=fa.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=wa(b[c]),f[u]?d.push(f):e.push(f);f=A(a,xa(e,d)),f.selector=a}return f},i=fa.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(ba,ca),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=W.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(ba,ca),_.test(j[0].type)&&oa(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&qa(j),!a)return H.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,!b||_.test(a)&&oa(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ia(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),ia(function(a){return a.innerHTML="<a href='#'></a>","#"===a.firstChild.getAttribute("href")})||ja("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ia(function(a){return a.innerHTML="<input/>",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ja("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),ia(function(a){return null==a.getAttribute("disabled")})||ja(K,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),fa}(a);n.find=t,n.expr=t.selectors,n.expr[":"]=n.expr.pseudos,n.uniqueSort=n.unique=t.uniqueSort,n.text=t.getText,n.isXMLDoc=t.isXML,n.contains=t.contains;var u=function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&n(a).is(c))break;d.push(a)}return d},v=function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c},w=n.expr.match.needsContext,x=/^<([\w-]+)\s*\/?>(?:<\/\1>|)$/,y=/^.[^:#\[\.,]*$/;function z(a,b,c){if(n.isFunction(b))return n.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return n.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(y.test(b))return n.filter(b,a,c);b=n.filter(b,a)}return n.grep(a,function(a){return n.inArray(a,b)>-1!==c})}n.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?n.find.matchesSelector(d,a)?[d]:[]:n.find.matches(a,n.grep(b,function(a){return 1===a.nodeType}))},n.fn.extend({find:function(a){var b,c=[],d=this,e=d.length;if("string"!=typeof a)return this.pushStack(n(a).filter(function(){for(b=0;e>b;b++)if(n.contains(d[b],this))return!0}));for(b=0;e>b;b++)n.find(a,d[b],c);return c=this.pushStack(e>1?n.unique(c):c),c.selector=this.selector?this.selector+" "+a:a,c},filter:function(a){return this.pushStack(z(this,a||[],!1))},not:function(a){return this.pushStack(z(this,a||[],!0))},is:function(a){return!!z(this,"string"==typeof a&&w.test(a)?n(a):a||[],!1).length}});var A,B=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,C=n.fn.init=function(a,b,c){var e,f;if(!a)return this;if(c=c||A,"string"==typeof a){if(e="<"===a.charAt(0)&&">"===a.charAt(a.length-1)&&a.length>=3?[null,a,null]:B.exec(a),!e||!e[1]&&b)return!b||b.jquery?(b||c).find(a):this.constructor(b).find(a);if(e[1]){if(b=b instanceof n?b[0]:b,n.merge(this,n.parseHTML(e[1],b&&b.nodeType?b.ownerDocument||b:d,!0)),x.test(e[1])&&n.isPlainObject(b))for(e in b)n.isFunction(this[e])?this[e](b[e]):this.attr(e,b[e]);return this}if(f=d.getElementById(e[2]),f&&f.parentNode){if(f.id!==e[2])return A.find(a);this.length=1,this[0]=f}return this.context=d,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):n.isFunction(a)?"undefined"!=typeof c.ready?c.ready(a):a(n):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),n.makeArray(a,this))};C.prototype=n.fn,A=n(d);var D=/^(?:parents|prev(?:Until|All))/,E={children:!0,contents:!0,next:!0,prev:!0};n.fn.extend({has:function(a){var b,c=n(a,this),d=c.length;return this.filter(function(){for(b=0;d>b;b++)if(n.contains(this,c[b]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=w.test(a)||"string"!=typeof a?n(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&n.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?n.uniqueSort(f):f)},index:function(a){return a?"string"==typeof a?n.inArray(this[0],n(a)):n.inArray(a.jquery?a[0]:a,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(n.uniqueSort(n.merge(this.get(),n(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function F(a,b){do a=a[b];while(a&&1!==a.nodeType);return a}n.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return u(a,"parentNode")},parentsUntil:function(a,b,c){return u(a,"parentNode",c)},next:function(a){return F(a,"nextSibling")},prev:function(a){return F(a,"previousSibling")},nextAll:function(a){return u(a,"nextSibling")},prevAll:function(a){return u(a,"previousSibling")},nextUntil:function(a,b,c){return u(a,"nextSibling",c)},prevUntil:function(a,b,c){return u(a,"previousSibling",c)},siblings:function(a){return v((a.parentNode||{}).firstChild,a)},children:function(a){return v(a.firstChild)},contents:function(a){return n.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:n.merge([],a.childNodes)}},function(a,b){n.fn[a]=function(c,d){var e=n.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=n.filter(d,e)),this.length>1&&(E[a]||(e=n.uniqueSort(e)),D.test(a)&&(e=e.reverse())),this.pushStack(e)}});var G=/\S+/g;function H(a){var b={};return n.each(a.match(G)||[],function(a,c){b[c]=!0}),b}n.Callbacks=function(a){a="string"==typeof a?H(a):n.extend({},a);var b,c,d,e,f=[],g=[],h=-1,i=function(){for(e=a.once,d=b=!0;g.length;h=-1){c=g.shift();while(++h<f.length)f[h].apply(c[0],c[1])===!1&&a.stopOnFalse&&(h=f.length,c=!1)}a.memory||(c=!1),b=!1,e&&(f=c?[]:"")},j={add:function(){return f&&(c&&!b&&(h=f.length-1,g.push(c)),function d(b){n.each(b,function(b,c){n.isFunction(c)?a.unique&&j.has(c)||f.push(c):c&&c.length&&"string"!==n.type(c)&&d(c)})}(arguments),c&&!b&&i()),this},remove:function(){return n.each(arguments,function(a,b){var c;while((c=n.inArray(b,f,c))>-1)f.splice(c,1),h>=c&&h--}),this},has:function(a){return a?n.inArray(a,f)>-1:f.length>0},empty:function(){return f&&(f=[]),this},disable:function(){return e=g=[],f=c="",this},disabled:function(){return!f},lock:function(){return e=!0,c||j.disable(),this},locked:function(){return!!e},fireWith:function(a,c){return e||(c=c||[],c=[a,c.slice?c.slice():c],g.push(c),b||i()),this},fire:function(){return j.fireWith(this,arguments),this},fired:function(){return!!d}};return j},n.extend({Deferred:function(a){var b=[["resolve","done",n.Callbacks("once memory"),"resolved"],["reject","fail",n.Callbacks("once memory"),"rejected"],["notify","progress",n.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return n.Deferred(function(c){n.each(b,function(b,f){var g=n.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&n.isFunction(a.promise)?a.promise().progress(c.notify).done(c.resolve).fail(c.reject):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?n.extend(a,d):d}},e={};return d.pipe=d.then,n.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=e.call(arguments),d=c.length,f=1!==d||a&&n.isFunction(a.promise)?d:0,g=1===f?a:n.Deferred(),h=function(a,b,c){return function(d){b[a]=this,c[a]=arguments.length>1?e.call(arguments):d,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(d>1)for(i=new Array(d),j=new Array(d),k=new Array(d);d>b;b++)c[b]&&n.isFunction(c[b].promise)?c[b].promise().progress(h(b,j,i)).done(h(b,k,c)).fail(g.reject):--f;return f||g.resolveWith(k,c),g.promise()}});var I;n.fn.ready=function(a){return n.ready.promise().done(a),this},n.extend({isReady:!1,readyWait:1,holdReady:function(a){a?n.readyWait++:n.ready(!0)},ready:function(a){(a===!0?--n.readyWait:n.isReady)||(n.isReady=!0,a!==!0&&--n.readyWait>0||(I.resolveWith(d,[n]),n.fn.triggerHandler&&(n(d).triggerHandler("ready"),n(d).off("ready"))))}});function J(){d.addEventListener?(d.removeEventListener("DOMContentLoaded",K),a.removeEventListener("load",K)):(d.detachEvent("onreadystatechange",K),a.detachEvent("onload",K))}function K(){(d.addEventListener||"load"===a.event.type||"complete"===d.readyState)&&(J(),n.ready())}n.ready.promise=function(b){if(!I)if(I=n.Deferred(),"complete"===d.readyState||"loading"!==d.readyState&&!d.documentElement.doScroll)a.setTimeout(n.ready);else if(d.addEventListener)d.addEventListener("DOMContentLoaded",K),a.addEventListener("load",K);else{d.attachEvent("onreadystatechange",K),a.attachEvent("onload",K);var c=!1;try{c=null==a.frameElement&&d.documentElement}catch(e){}c&&c.doScroll&&!function f(){if(!n.isReady){try{c.doScroll("left")}catch(b){return a.setTimeout(f,50)}J(),n.ready()}}()}return I.promise(b)},n.ready.promise();var L;for(L in n(l))break;l.ownFirst="0"===L,l.inlineBlockNeedsLayout=!1,n(function(){var a,b,c,e;c=d.getElementsByTagName("body")[0],c&&c.style&&(b=d.createElement("div"),e=d.createElement("div"),e.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(e).appendChild(b),"undefined"!=typeof b.style.zoom&&(b.style.cssText="display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1",l.inlineBlockNeedsLayout=a=3===b.offsetWidth,a&&(c.style.zoom=1)),c.removeChild(e))}),function(){var a=d.createElement("div");l.deleteExpando=!0;try{delete a.test}catch(b){l.deleteExpando=!1}a=null}();var M=function(a){var b=n.noData[(a.nodeName+" ").toLowerCase()],c=+a.nodeType||1;return 1!==c&&9!==c?!1:!b||b!==!0&&a.getAttribute("classid")===b},N=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,O=/([A-Z])/g;function P(a,b,c){if(void 0===c&&1===a.nodeType){var d="data-"+b.replace(O,"-$1").toLowerCase();if(c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:N.test(c)?n.parseJSON(c):c}catch(e){}n.data(a,b,c)}else c=void 0; }return c}function Q(a){var b;for(b in a)if(("data"!==b||!n.isEmptyObject(a[b]))&&"toJSON"!==b)return!1;return!0}function R(a,b,d,e){if(M(a)){var f,g,h=n.expando,i=a.nodeType,j=i?n.cache:a,k=i?a[h]:a[h]&&h;if(k&&j[k]&&(e||j[k].data)||void 0!==d||"string"!=typeof b)return k||(k=i?a[h]=c.pop()||n.guid++:h),j[k]||(j[k]=i?{}:{toJSON:n.noop}),"object"!=typeof b&&"function"!=typeof b||(e?j[k]=n.extend(j[k],b):j[k].data=n.extend(j[k].data,b)),g=j[k],e||(g.data||(g.data={}),g=g.data),void 0!==d&&(g[n.camelCase(b)]=d),"string"==typeof b?(f=g[b],null==f&&(f=g[n.camelCase(b)])):f=g,f}}function S(a,b,c){if(M(a)){var d,e,f=a.nodeType,g=f?n.cache:a,h=f?a[n.expando]:n.expando;if(g[h]){if(b&&(d=c?g[h]:g[h].data)){n.isArray(b)?b=b.concat(n.map(b,n.camelCase)):b in d?b=[b]:(b=n.camelCase(b),b=b in d?[b]:b.split(" ")),e=b.length;while(e--)delete d[b[e]];if(c?!Q(d):!n.isEmptyObject(d))return}(c||(delete g[h].data,Q(g[h])))&&(f?n.cleanData([a],!0):l.deleteExpando||g!=g.window?delete g[h]:g[h]=void 0)}}}n.extend({cache:{},noData:{"applet ":!0,"embed ":!0,"object ":"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(a){return a=a.nodeType?n.cache[a[n.expando]]:a[n.expando],!!a&&!Q(a)},data:function(a,b,c){return R(a,b,c)},removeData:function(a,b){return S(a,b)},_data:function(a,b,c){return R(a,b,c,!0)},_removeData:function(a,b){return S(a,b,!0)}}),n.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=n.data(f),1===f.nodeType&&!n._data(f,"parsedAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=n.camelCase(d.slice(5)),P(f,d,e[d])));n._data(f,"parsedAttrs",!0)}return e}return"object"==typeof a?this.each(function(){n.data(this,a)}):arguments.length>1?this.each(function(){n.data(this,a,b)}):f?P(f,a,n.data(f,a)):void 0},removeData:function(a){return this.each(function(){n.removeData(this,a)})}}),n.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=n._data(a,b),c&&(!d||n.isArray(c)?d=n._data(a,b,n.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=n.queue(a,b),d=c.length,e=c.shift(),f=n._queueHooks(a,b),g=function(){n.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return n._data(a,c)||n._data(a,c,{empty:n.Callbacks("once memory").add(function(){n._removeData(a,b+"queue"),n._removeData(a,c)})})}}),n.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length<c?n.queue(this[0],a):void 0===b?this:this.each(function(){var c=n.queue(this,a,b);n._queueHooks(this,a),"fx"===a&&"inprogress"!==c[0]&&n.dequeue(this,a)})},dequeue:function(a){return this.each(function(){n.dequeue(this,a)})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,b){var c,d=1,e=n.Deferred(),f=this,g=this.length,h=function(){--d||e.resolveWith(f,[f])};"string"!=typeof a&&(b=a,a=void 0),a=a||"fx";while(g--)c=n._data(f[g],a+"queueHooks"),c&&c.empty&&(d++,c.empty.add(h));return h(),e.promise(b)}}),function(){var a;l.shrinkWrapBlocks=function(){if(null!=a)return a;a=!1;var b,c,e;return c=d.getElementsByTagName("body")[0],c&&c.style?(b=d.createElement("div"),e=d.createElement("div"),e.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(e).appendChild(b),"undefined"!=typeof b.style.zoom&&(b.style.cssText="-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;display:block;margin:0;border:0;padding:1px;width:1px;zoom:1",b.appendChild(d.createElement("div")).style.width="5px",a=3!==b.offsetWidth),c.removeChild(e),a):void 0}}();var T=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,U=new RegExp("^(?:([+-])=|)("+T+")([a-z%]*)$","i"),V=["Top","Right","Bottom","Left"],W=function(a,b){return a=b||a,"none"===n.css(a,"display")||!n.contains(a.ownerDocument,a)};function X(a,b,c,d){var e,f=1,g=20,h=d?function(){return d.cur()}:function(){return n.css(a,b,"")},i=h(),j=c&&c[3]||(n.cssNumber[b]?"":"px"),k=(n.cssNumber[b]||"px"!==j&&+i)&&U.exec(n.css(a,b));if(k&&k[3]!==j){j=j||k[3],c=c||[],k=+i||1;do f=f||".5",k/=f,n.style(a,b,k+j);while(f!==(f=h()/i)&&1!==f&&--g)}return c&&(k=+k||+i||0,e=c[1]?k+(c[1]+1)*c[2]:+c[2],d&&(d.unit=j,d.start=k,d.end=e)),e}var Y=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===n.type(c)){e=!0;for(h in c)Y(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,n.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(n(a),c)})),b))for(;i>h;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},Z=/^(?:checkbox|radio)$/i,$=/<([\w:-]+)/,_=/^$|\/(?:java|ecma)script/i,aa=/^\s+/,ba="abbr|article|aside|audio|bdi|canvas|data|datalist|details|dialog|figcaption|figure|footer|header|hgroup|main|mark|meter|nav|output|picture|progress|section|summary|template|time|video";function ca(a){var b=ba.split("|"),c=a.createDocumentFragment();if(c.createElement)while(b.length)c.createElement(b.pop());return c}!function(){var a=d.createElement("div"),b=d.createDocumentFragment(),c=d.createElement("input");a.innerHTML=" <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",l.leadingWhitespace=3===a.firstChild.nodeType,l.tbody=!a.getElementsByTagName("tbody").length,l.htmlSerialize=!!a.getElementsByTagName("link").length,l.html5Clone="<:nav></:nav>"!==d.createElement("nav").cloneNode(!0).outerHTML,c.type="checkbox",c.checked=!0,b.appendChild(c),l.appendChecked=c.checked,a.innerHTML="<textarea>x</textarea>",l.noCloneChecked=!!a.cloneNode(!0).lastChild.defaultValue,b.appendChild(a),c=d.createElement("input"),c.setAttribute("type","radio"),c.setAttribute("checked","checked"),c.setAttribute("name","t"),a.appendChild(c),l.checkClone=a.cloneNode(!0).cloneNode(!0).lastChild.checked,l.noCloneEvent=!!a.addEventListener,a[n.expando]=1,l.attributes=!a.getAttribute(n.expando)}();var da={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],area:[1,"<map>","</map>"],param:[1,"<object>","</object>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:l.htmlSerialize?[0,"",""]:[1,"X<div>","</div>"]};da.optgroup=da.option,da.tbody=da.tfoot=da.colgroup=da.caption=da.thead,da.th=da.td;function ea(a,b){var c,d,e=0,f="undefined"!=typeof a.getElementsByTagName?a.getElementsByTagName(b||"*"):"undefined"!=typeof a.querySelectorAll?a.querySelectorAll(b||"*"):void 0;if(!f)for(f=[],c=a.childNodes||a;null!=(d=c[e]);e++)!b||n.nodeName(d,b)?f.push(d):n.merge(f,ea(d,b));return void 0===b||b&&n.nodeName(a,b)?n.merge([a],f):f}function fa(a,b){for(var c,d=0;null!=(c=a[d]);d++)n._data(c,"globalEval",!b||n._data(b[d],"globalEval"))}var ga=/<|&#?\w+;/,ha=/<tbody/i;function ia(a){Z.test(a.type)&&(a.defaultChecked=a.checked)}function ja(a,b,c,d,e){for(var f,g,h,i,j,k,m,o=a.length,p=ca(b),q=[],r=0;o>r;r++)if(g=a[r],g||0===g)if("object"===n.type(g))n.merge(q,g.nodeType?[g]:g);else if(ga.test(g)){i=i||p.appendChild(b.createElement("div")),j=($.exec(g)||["",""])[1].toLowerCase(),m=da[j]||da._default,i.innerHTML=m[1]+n.htmlPrefilter(g)+m[2],f=m[0];while(f--)i=i.lastChild;if(!l.leadingWhitespace&&aa.test(g)&&q.push(b.createTextNode(aa.exec(g)[0])),!l.tbody){g="table"!==j||ha.test(g)?"<table>"!==m[1]||ha.test(g)?0:i:i.firstChild,f=g&&g.childNodes.length;while(f--)n.nodeName(k=g.childNodes[f],"tbody")&&!k.childNodes.length&&g.removeChild(k)}n.merge(q,i.childNodes),i.textContent="";while(i.firstChild)i.removeChild(i.firstChild);i=p.lastChild}else q.push(b.createTextNode(g));i&&p.removeChild(i),l.appendChecked||n.grep(ea(q,"input"),ia),r=0;while(g=q[r++])if(d&&n.inArray(g,d)>-1)e&&e.push(g);else if(h=n.contains(g.ownerDocument,g),i=ea(p.appendChild(g),"script"),h&&fa(i),c){f=0;while(g=i[f++])_.test(g.type||"")&&c.push(g)}return i=null,p}!function(){var b,c,e=d.createElement("div");for(b in{submit:!0,change:!0,focusin:!0})c="on"+b,(l[b]=c in a)||(e.setAttribute(c,"t"),l[b]=e.attributes[c].expando===!1);e=null}();var ka=/^(?:input|select|textarea)$/i,la=/^key/,ma=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,na=/^(?:focusinfocus|focusoutblur)$/,oa=/^([^.]*)(?:\.(.+)|)/;function pa(){return!0}function qa(){return!1}function ra(){try{return d.activeElement}catch(a){}}function sa(a,b,c,d,e,f){var g,h;if("object"==typeof b){"string"!=typeof c&&(d=d||c,c=void 0);for(h in b)sa(a,h,c,d,b[h],f);return a}if(null==d&&null==e?(e=c,d=c=void 0):null==e&&("string"==typeof c?(e=d,d=void 0):(e=d,d=c,c=void 0)),e===!1)e=qa;else if(!e)return a;return 1===f&&(g=e,e=function(a){return n().off(a),g.apply(this,arguments)},e.guid=g.guid||(g.guid=n.guid++)),a.each(function(){n.event.add(this,b,e,d,c)})}n.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=n._data(a);if(r){c.handler&&(i=c,c=i.handler,e=i.selector),c.guid||(c.guid=n.guid++),(g=r.events)||(g=r.events={}),(k=r.handle)||(k=r.handle=function(a){return"undefined"==typeof n||a&&n.event.triggered===a.type?void 0:n.event.dispatch.apply(k.elem,arguments)},k.elem=a),b=(b||"").match(G)||[""],h=b.length;while(h--)f=oa.exec(b[h])||[],o=q=f[1],p=(f[2]||"").split(".").sort(),o&&(j=n.event.special[o]||{},o=(e?j.delegateType:j.bindType)||o,j=n.event.special[o]||{},l=n.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&n.expr.match.needsContext.test(e),namespace:p.join(".")},i),(m=g[o])||(m=g[o]=[],m.delegateCount=0,j.setup&&j.setup.call(a,d,p,k)!==!1||(a.addEventListener?a.addEventListener(o,k,!1):a.attachEvent&&a.attachEvent("on"+o,k))),j.add&&(j.add.call(a,l),l.handler.guid||(l.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,l):m.push(l),n.event.global[o]=!0);a=null}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=n.hasData(a)&&n._data(a);if(r&&(k=r.events)){b=(b||"").match(G)||[""],j=b.length;while(j--)if(h=oa.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=n.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,m=k[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),i=f=m.length;while(f--)g=m[f],!e&&q!==g.origType||c&&c.guid!==g.guid||h&&!h.test(g.namespace)||d&&d!==g.selector&&("**"!==d||!g.selector)||(m.splice(f,1),g.selector&&m.delegateCount--,l.remove&&l.remove.call(a,g));i&&!m.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||n.removeEvent(a,o,r.handle),delete k[o])}else for(o in k)n.event.remove(a,o+b[j],c,d,!0);n.isEmptyObject(k)&&(delete r.handle,n._removeData(a,"events"))}},trigger:function(b,c,e,f){var g,h,i,j,l,m,o,p=[e||d],q=k.call(b,"type")?b.type:b,r=k.call(b,"namespace")?b.namespace.split("."):[];if(i=m=e=e||d,3!==e.nodeType&&8!==e.nodeType&&!na.test(q+n.event.triggered)&&(q.indexOf(".")>-1&&(r=q.split("."),q=r.shift(),r.sort()),h=q.indexOf(":")<0&&"on"+q,b=b[n.expando]?b:new n.Event(q,"object"==typeof b&&b),b.isTrigger=f?2:3,b.namespace=r.join("."),b.rnamespace=b.namespace?new RegExp("(^|\\.)"+r.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=e),c=null==c?[b]:n.makeArray(c,[b]),l=n.event.special[q]||{},f||!l.trigger||l.trigger.apply(e,c)!==!1)){if(!f&&!l.noBubble&&!n.isWindow(e)){for(j=l.delegateType||q,na.test(j+q)||(i=i.parentNode);i;i=i.parentNode)p.push(i),m=i;m===(e.ownerDocument||d)&&p.push(m.defaultView||m.parentWindow||a)}o=0;while((i=p[o++])&&!b.isPropagationStopped())b.type=o>1?j:l.bindType||q,g=(n._data(i,"events")||{})[b.type]&&n._data(i,"handle"),g&&g.apply(i,c),g=h&&i[h],g&&g.apply&&M(i)&&(b.result=g.apply(i,c),b.result===!1&&b.preventDefault());if(b.type=q,!f&&!b.isDefaultPrevented()&&(!l._default||l._default.apply(p.pop(),c)===!1)&&M(e)&&h&&e[q]&&!n.isWindow(e)){m=e[h],m&&(e[h]=null),n.event.triggered=q;try{e[q]()}catch(s){}n.event.triggered=void 0,m&&(e[h]=m)}return b.result}},dispatch:function(a){a=n.event.fix(a);var b,c,d,f,g,h=[],i=e.call(arguments),j=(n._data(this,"events")||{})[a.type]||[],k=n.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=n.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,c=0;while((g=f.handlers[c++])&&!a.isImmediatePropagationStopped())a.rnamespace&&!a.rnamespace.test(g.namespace)||(a.handleObj=g,a.data=g.data,d=((n.event.special[g.origType]||{}).handle||g.handler).apply(f.elem,i),void 0!==d&&(a.result=d)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&("click"!==a.type||isNaN(a.button)||a.button<1))for(;i!=this;i=i.parentNode||this)if(1===i.nodeType&&(i.disabled!==!0||"click"!==a.type)){for(d=[],c=0;h>c;c++)f=b[c],e=f.selector+" ",void 0===d[e]&&(d[e]=f.needsContext?n(e,this).index(i)>-1:n.find(e,this,null,[i]).length),d[e]&&d.push(f);d.length&&g.push({elem:i,handlers:d})}return h<b.length&&g.push({elem:this,handlers:b.slice(h)}),g},fix:function(a){if(a[n.expando])return a;var b,c,e,f=a.type,g=a,h=this.fixHooks[f];h||(this.fixHooks[f]=h=ma.test(f)?this.mouseHooks:la.test(f)?this.keyHooks:{}),e=h.props?this.props.concat(h.props):this.props,a=new n.Event(g),b=e.length;while(b--)c=e[b],a[c]=g[c];return a.target||(a.target=g.srcElement||d),3===a.target.nodeType&&(a.target=a.target.parentNode),a.metaKey=!!a.metaKey,h.filter?h.filter(a,g):a},props:"altKey bubbles cancelable ctrlKey currentTarget detail eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(a,b){return null==a.which&&(a.which=null!=b.charCode?b.charCode:b.keyCode),a}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(a,b){var c,e,f,g=b.button,h=b.fromElement;return null==a.pageX&&null!=b.clientX&&(e=a.target.ownerDocument||d,f=e.documentElement,c=e.body,a.pageX=b.clientX+(f&&f.scrollLeft||c&&c.scrollLeft||0)-(f&&f.clientLeft||c&&c.clientLeft||0),a.pageY=b.clientY+(f&&f.scrollTop||c&&c.scrollTop||0)-(f&&f.clientTop||c&&c.clientTop||0)),!a.relatedTarget&&h&&(a.relatedTarget=h===a.target?b.toElement:h),a.which||void 0===g||(a.which=1&g?1:2&g?3:4&g?2:0),a}},special:{load:{noBubble:!0},focus:{trigger:function(){if(this!==ra()&&this.focus)try{return this.focus(),!1}catch(a){}},delegateType:"focusin"},blur:{trigger:function(){return this===ra()&&this.blur?(this.blur(),!1):void 0},delegateType:"focusout"},click:{trigger:function(){return n.nodeName(this,"input")&&"checkbox"===this.type&&this.click?(this.click(),!1):void 0},_default:function(a){return n.nodeName(a.target,"a")}},beforeunload:{postDispatch:function(a){void 0!==a.result&&a.originalEvent&&(a.originalEvent.returnValue=a.result)}}},simulate:function(a,b,c){var d=n.extend(new n.Event,c,{type:a,isSimulated:!0});n.event.trigger(d,null,b),d.isDefaultPrevented()&&c.preventDefault()}},n.removeEvent=d.removeEventListener?function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c)}:function(a,b,c){var d="on"+b;a.detachEvent&&("undefined"==typeof a[d]&&(a[d]=null),a.detachEvent(d,c))},n.Event=function(a,b){return this instanceof n.Event?(a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||void 0===a.defaultPrevented&&a.returnValue===!1?pa:qa):this.type=a,b&&n.extend(this,b),this.timeStamp=a&&a.timeStamp||n.now(),void(this[n.expando]=!0)):new n.Event(a,b)},n.Event.prototype={constructor:n.Event,isDefaultPrevented:qa,isPropagationStopped:qa,isImmediatePropagationStopped:qa,preventDefault:function(){var a=this.originalEvent;this.isDefaultPrevented=pa,a&&(a.preventDefault?a.preventDefault():a.returnValue=!1)},stopPropagation:function(){var a=this.originalEvent;this.isPropagationStopped=pa,a&&!this.isSimulated&&(a.stopPropagation&&a.stopPropagation(),a.cancelBubble=!0)},stopImmediatePropagation:function(){var a=this.originalEvent;this.isImmediatePropagationStopped=pa,a&&a.stopImmediatePropagation&&a.stopImmediatePropagation(),this.stopPropagation()}},n.each({mouseenter:"mouseover",mouseleave:"mouseout",pointerenter:"pointerover",pointerleave:"pointerout"},function(a,b){n.event.special[a]={delegateType:b,bindType:b,handle:function(a){var c,d=this,e=a.relatedTarget,f=a.handleObj;return e&&(e===d||n.contains(d,e))||(a.type=f.origType,c=f.handler.apply(this,arguments),a.type=b),c}}}),l.submit||(n.event.special.submit={setup:function(){return n.nodeName(this,"form")?!1:void n.event.add(this,"click._submit keypress._submit",function(a){var b=a.target,c=n.nodeName(b,"input")||n.nodeName(b,"button")?n.prop(b,"form"):void 0;c&&!n._data(c,"submit")&&(n.event.add(c,"submit._submit",function(a){a._submitBubble=!0}),n._data(c,"submit",!0))})},postDispatch:function(a){a._submitBubble&&(delete a._submitBubble,this.parentNode&&!a.isTrigger&&n.event.simulate("submit",this.parentNode,a))},teardown:function(){return n.nodeName(this,"form")?!1:void n.event.remove(this,"._submit")}}),l.change||(n.event.special.change={setup:function(){return ka.test(this.nodeName)?("checkbox"!==this.type&&"radio"!==this.type||(n.event.add(this,"propertychange._change",function(a){"checked"===a.originalEvent.propertyName&&(this._justChanged=!0)}),n.event.add(this,"click._change",function(a){this._justChanged&&!a.isTrigger&&(this._justChanged=!1),n.event.simulate("change",this,a)})),!1):void n.event.add(this,"beforeactivate._change",function(a){var b=a.target;ka.test(b.nodeName)&&!n._data(b,"change")&&(n.event.add(b,"change._change",function(a){!this.parentNode||a.isSimulated||a.isTrigger||n.event.simulate("change",this.parentNode,a)}),n._data(b,"change",!0))})},handle:function(a){var b=a.target;return this!==b||a.isSimulated||a.isTrigger||"radio"!==b.type&&"checkbox"!==b.type?a.handleObj.handler.apply(this,arguments):void 0},teardown:function(){return n.event.remove(this,"._change"),!ka.test(this.nodeName)}}),l.focusin||n.each({focus:"focusin",blur:"focusout"},function(a,b){var c=function(a){n.event.simulate(b,a.target,n.event.fix(a))};n.event.special[b]={setup:function(){var d=this.ownerDocument||this,e=n._data(d,b);e||d.addEventListener(a,c,!0),n._data(d,b,(e||0)+1)},teardown:function(){var d=this.ownerDocument||this,e=n._data(d,b)-1;e?n._data(d,b,e):(d.removeEventListener(a,c,!0),n._removeData(d,b))}}}),n.fn.extend({on:function(a,b,c,d){return sa(this,a,b,c,d)},one:function(a,b,c,d){return sa(this,a,b,c,d,1)},off:function(a,b,c){var d,e;if(a&&a.preventDefault&&a.handleObj)return d=a.handleObj,n(a.delegateTarget).off(d.namespace?d.origType+"."+d.namespace:d.origType,d.selector,d.handler),this;if("object"==typeof a){for(e in a)this.off(e,b,a[e]);return this}return b!==!1&&"function"!=typeof b||(c=b,b=void 0),c===!1&&(c=qa),this.each(function(){n.event.remove(this,a,c,b)})},trigger:function(a,b){return this.each(function(){n.event.trigger(a,b,this)})},triggerHandler:function(a,b){var c=this[0];return c?n.event.trigger(a,b,c,!0):void 0}});var ta=/ jQuery\d+="(?:null|\d+)"/g,ua=new RegExp("<(?:"+ba+")[\\s/>]","i"),va=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi,wa=/<script|<style|<link/i,xa=/checked\s*(?:[^=]|=\s*.checked.)/i,ya=/^true\/(.*)/,za=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g,Aa=ca(d),Ba=Aa.appendChild(d.createElement("div"));function Ca(a,b){return n.nodeName(a,"table")&&n.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function Da(a){return a.type=(null!==n.find.attr(a,"type"))+"/"+a.type,a}function Ea(a){var b=ya.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function Fa(a,b){if(1===b.nodeType&&n.hasData(a)){var c,d,e,f=n._data(a),g=n._data(b,f),h=f.events;if(h){delete g.handle,g.events={};for(c in h)for(d=0,e=h[c].length;e>d;d++)n.event.add(b,c,h[c][d])}g.data&&(g.data=n.extend({},g.data))}}function Ga(a,b){var c,d,e;if(1===b.nodeType){if(c=b.nodeName.toLowerCase(),!l.noCloneEvent&&b[n.expando]){e=n._data(b);for(d in e.events)n.removeEvent(b,d,e.handle);b.removeAttribute(n.expando)}"script"===c&&b.text!==a.text?(Da(b).text=a.text,Ea(b)):"object"===c?(b.parentNode&&(b.outerHTML=a.outerHTML),l.html5Clone&&a.innerHTML&&!n.trim(b.innerHTML)&&(b.innerHTML=a.innerHTML)):"input"===c&&Z.test(a.type)?(b.defaultChecked=b.checked=a.checked,b.value!==a.value&&(b.value=a.value)):"option"===c?b.defaultSelected=b.selected=a.defaultSelected:"input"!==c&&"textarea"!==c||(b.defaultValue=a.defaultValue)}}function Ha(a,b,c,d){b=f.apply([],b);var e,g,h,i,j,k,m=0,o=a.length,p=o-1,q=b[0],r=n.isFunction(q);if(r||o>1&&"string"==typeof q&&!l.checkClone&&xa.test(q))return a.each(function(e){var f=a.eq(e);r&&(b[0]=q.call(this,e,f.html())),Ha(f,b,c,d)});if(o&&(k=ja(b,a[0].ownerDocument,!1,a,d),e=k.firstChild,1===k.childNodes.length&&(k=e),e||d)){for(i=n.map(ea(k,"script"),Da),h=i.length;o>m;m++)g=k,m!==p&&(g=n.clone(g,!0,!0),h&&n.merge(i,ea(g,"script"))),c.call(a[m],g,m);if(h)for(j=i[i.length-1].ownerDocument,n.map(i,Ea),m=0;h>m;m++)g=i[m],_.test(g.type||"")&&!n._data(g,"globalEval")&&n.contains(j,g)&&(g.src?n._evalUrl&&n._evalUrl(g.src):n.globalEval((g.text||g.textContent||g.innerHTML||"").replace(za,"")));k=e=null}return a}function Ia(a,b,c){for(var d,e=b?n.filter(b,a):a,f=0;null!=(d=e[f]);f++)c||1!==d.nodeType||n.cleanData(ea(d)),d.parentNode&&(c&&n.contains(d.ownerDocument,d)&&fa(ea(d,"script")),d.parentNode.removeChild(d));return a}n.extend({htmlPrefilter:function(a){return a.replace(va,"<$1></$2>")},clone:function(a,b,c){var d,e,f,g,h,i=n.contains(a.ownerDocument,a);if(l.html5Clone||n.isXMLDoc(a)||!ua.test("<"+a.nodeName+">")?f=a.cloneNode(!0):(Ba.innerHTML=a.outerHTML,Ba.removeChild(f=Ba.firstChild)),!(l.noCloneEvent&&l.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||n.isXMLDoc(a)))for(d=ea(f),h=ea(a),g=0;null!=(e=h[g]);++g)d[g]&&Ga(e,d[g]);if(b)if(c)for(h=h||ea(a),d=d||ea(f),g=0;null!=(e=h[g]);g++)Fa(e,d[g]);else Fa(a,f);return d=ea(f,"script"),d.length>0&&fa(d,!i&&ea(a,"script")),d=h=e=null,f},cleanData:function(a,b){for(var d,e,f,g,h=0,i=n.expando,j=n.cache,k=l.attributes,m=n.event.special;null!=(d=a[h]);h++)if((b||M(d))&&(f=d[i],g=f&&j[f])){if(g.events)for(e in g.events)m[e]?n.event.remove(d,e):n.removeEvent(d,e,g.handle);j[f]&&(delete j[f],k||"undefined"==typeof d.removeAttribute?d[i]=void 0:d.removeAttribute(i),c.push(f))}}}),n.fn.extend({domManip:Ha,detach:function(a){return Ia(this,a,!0)},remove:function(a){return Ia(this,a)},text:function(a){return Y(this,function(a){return void 0===a?n.text(this):this.empty().append((this[0]&&this[0].ownerDocument||d).createTextNode(a))},null,a,arguments.length)},append:function(){return Ha(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Ca(this,a);b.appendChild(a)}})},prepend:function(){return Ha(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Ca(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return Ha(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return Ha(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},empty:function(){for(var a,b=0;null!=(a=this[b]);b++){1===a.nodeType&&n.cleanData(ea(a,!1));while(a.firstChild)a.removeChild(a.firstChild);a.options&&n.nodeName(a,"select")&&(a.options.length=0)}return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return n.clone(this,a,b)})},html:function(a){return Y(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a)return 1===b.nodeType?b.innerHTML.replace(ta,""):void 0;if("string"==typeof a&&!wa.test(a)&&(l.htmlSerialize||!ua.test(a))&&(l.leadingWhitespace||!aa.test(a))&&!da[($.exec(a)||["",""])[1].toLowerCase()]){a=n.htmlPrefilter(a);try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(n.cleanData(ea(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=[];return Ha(this,arguments,function(b){var c=this.parentNode;n.inArray(this,a)<0&&(n.cleanData(ea(this)),c&&c.replaceChild(b,this))},a)}}),n.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){n.fn[a]=function(a){for(var c,d=0,e=[],f=n(a),h=f.length-1;h>=d;d++)c=d===h?this:this.clone(!0),n(f[d])[b](c),g.apply(e,c.get());return this.pushStack(e)}});var Ja,Ka={HTML:"block",BODY:"block"};function La(a,b){var c=n(b.createElement(a)).appendTo(b.body),d=n.css(c[0],"display");return c.detach(),d}function Ma(a){var b=d,c=Ka[a];return c||(c=La(a,b),"none"!==c&&c||(Ja=(Ja||n("<iframe frameborder='0' width='0' height='0'/>")).appendTo(b.documentElement),b=(Ja[0].contentWindow||Ja[0].contentDocument).document,b.write(),b.close(),c=La(a,b),Ja.detach()),Ka[a]=c),c}var Na=/^margin/,Oa=new RegExp("^("+T+")(?!px)[a-z%]+$","i"),Pa=function(a,b,c,d){var e,f,g={};for(f in b)g[f]=a.style[f],a.style[f]=b[f];e=c.apply(a,d||[]);for(f in b)a.style[f]=g[f];return e},Qa=d.documentElement;!function(){var b,c,e,f,g,h,i=d.createElement("div"),j=d.createElement("div");if(j.style){j.style.cssText="float:left;opacity:.5",l.opacity="0.5"===j.style.opacity,l.cssFloat=!!j.style.cssFloat,j.style.backgroundClip="content-box",j.cloneNode(!0).style.backgroundClip="",l.clearCloneStyle="content-box"===j.style.backgroundClip,i=d.createElement("div"),i.style.cssText="border:0;width:8px;height:0;top:0;left:-9999px;padding:0;margin-top:1px;position:absolute",j.innerHTML="",i.appendChild(j),l.boxSizing=""===j.style.boxSizing||""===j.style.MozBoxSizing||""===j.style.WebkitBoxSizing,n.extend(l,{reliableHiddenOffsets:function(){return null==b&&k(),f},boxSizingReliable:function(){return null==b&&k(),e},pixelMarginRight:function(){return null==b&&k(),c},pixelPosition:function(){return null==b&&k(),b},reliableMarginRight:function(){return null==b&&k(),g},reliableMarginLeft:function(){return null==b&&k(),h}});function k(){var k,l,m=d.documentElement;m.appendChild(i),j.style.cssText="-webkit-box-sizing:border-box;box-sizing:border-box;position:relative;display:block;margin:auto;border:1px;padding:1px;top:1%;width:50%",b=e=h=!1,c=g=!0,a.getComputedStyle&&(l=a.getComputedStyle(j),b="1%"!==(l||{}).top,h="2px"===(l||{}).marginLeft,e="4px"===(l||{width:"4px"}).width,j.style.marginRight="50%",c="4px"===(l||{marginRight:"4px"}).marginRight,k=j.appendChild(d.createElement("div")),k.style.cssText=j.style.cssText="-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;display:block;margin:0;border:0;padding:0",k.style.marginRight=k.style.width="0",j.style.width="1px",g=!parseFloat((a.getComputedStyle(k)||{}).marginRight),j.removeChild(k)),j.style.display="none",f=0===j.getClientRects().length,f&&(j.style.display="",j.innerHTML="<table><tr><td></td><td>t</td></tr></table>",j.childNodes[0].style.borderCollapse="separate",k=j.getElementsByTagName("td"),k[0].style.cssText="margin:0;border:0;padding:0;display:none",f=0===k[0].offsetHeight,f&&(k[0].style.display="",k[1].style.display="none",f=0===k[0].offsetHeight)),m.removeChild(i)}}}();var Ra,Sa,Ta=/^(top|right|bottom|left)$/;a.getComputedStyle?(Ra=function(b){var c=b.ownerDocument.defaultView;return c&&c.opener||(c=a),c.getComputedStyle(b)},Sa=function(a,b,c){var d,e,f,g,h=a.style;return c=c||Ra(a),g=c?c.getPropertyValue(b)||c[b]:void 0,""!==g&&void 0!==g||n.contains(a.ownerDocument,a)||(g=n.style(a,b)),c&&!l.pixelMarginRight()&&Oa.test(g)&&Na.test(b)&&(d=h.width,e=h.minWidth,f=h.maxWidth,h.minWidth=h.maxWidth=h.width=g,g=c.width,h.width=d,h.minWidth=e,h.maxWidth=f),void 0===g?g:g+""}):Qa.currentStyle&&(Ra=function(a){return a.currentStyle},Sa=function(a,b,c){var d,e,f,g,h=a.style;return c=c||Ra(a),g=c?c[b]:void 0,null==g&&h&&h[b]&&(g=h[b]),Oa.test(g)&&!Ta.test(b)&&(d=h.left,e=a.runtimeStyle,f=e&&e.left,f&&(e.left=a.currentStyle.left),h.left="fontSize"===b?"1em":g,g=h.pixelLeft+"px",h.left=d,f&&(e.left=f)),void 0===g?g:g+""||"auto"});function Ua(a,b){return{get:function(){return a()?void delete this.get:(this.get=b).apply(this,arguments)}}}var Va=/alpha\([^)]*\)/i,Wa=/opacity\s*=\s*([^)]*)/i,Xa=/^(none|table(?!-c[ea]).+)/,Ya=new RegExp("^("+T+")(.*)$","i"),Za={position:"absolute",visibility:"hidden",display:"block"},$a={letterSpacing:"0",fontWeight:"400"},_a=["Webkit","O","Moz","ms"],ab=d.createElement("div").style;function bb(a){if(a in ab)return a;var b=a.charAt(0).toUpperCase()+a.slice(1),c=_a.length;while(c--)if(a=_a[c]+b,a in ab)return a}function cb(a,b){for(var c,d,e,f=[],g=0,h=a.length;h>g;g++)d=a[g],d.style&&(f[g]=n._data(d,"olddisplay"),c=d.style.display,b?(f[g]||"none"!==c||(d.style.display=""),""===d.style.display&&W(d)&&(f[g]=n._data(d,"olddisplay",Ma(d.nodeName)))):(e=W(d),(c&&"none"!==c||!e)&&n._data(d,"olddisplay",e?c:n.css(d,"display"))));for(g=0;h>g;g++)d=a[g],d.style&&(b&&"none"!==d.style.display&&""!==d.style.display||(d.style.display=b?f[g]||"":"none"));return a}function db(a,b,c){var d=Ya.exec(b);return d?Math.max(0,d[1]-(c||0))+(d[2]||"px"):b}function eb(a,b,c,d,e){for(var f=c===(d?"border":"content")?4:"width"===b?1:0,g=0;4>f;f+=2)"margin"===c&&(g+=n.css(a,c+V[f],!0,e)),d?("content"===c&&(g-=n.css(a,"padding"+V[f],!0,e)),"margin"!==c&&(g-=n.css(a,"border"+V[f]+"Width",!0,e))):(g+=n.css(a,"padding"+V[f],!0,e),"padding"!==c&&(g+=n.css(a,"border"+V[f]+"Width",!0,e)));return g}function fb(a,b,c){var d=!0,e="width"===b?a.offsetWidth:a.offsetHeight,f=Ra(a),g=l.boxSizing&&"border-box"===n.css(a,"boxSizing",!1,f);if(0>=e||null==e){if(e=Sa(a,b,f),(0>e||null==e)&&(e=a.style[b]),Oa.test(e))return e;d=g&&(l.boxSizingReliable()||e===a.style[b]),e=parseFloat(e)||0}return e+eb(a,b,c||(g?"border":"content"),d,f)+"px"}n.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=Sa(a,"opacity");return""===c?"1":c}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":l.cssFloat?"cssFloat":"styleFloat"},style:function(a,b,c,d){if(a&&3!==a.nodeType&&8!==a.nodeType&&a.style){var e,f,g,h=n.camelCase(b),i=a.style;if(b=n.cssProps[h]||(n.cssProps[h]=bb(h)||h),g=n.cssHooks[b]||n.cssHooks[h],void 0===c)return g&&"get"in g&&void 0!==(e=g.get(a,!1,d))?e:i[b];if(f=typeof c,"string"===f&&(e=U.exec(c))&&e[1]&&(c=X(a,b,e),f="number"),null!=c&&c===c&&("number"===f&&(c+=e&&e[3]||(n.cssNumber[h]?"":"px")),l.clearCloneStyle||""!==c||0!==b.indexOf("background")||(i[b]="inherit"),!(g&&"set"in g&&void 0===(c=g.set(a,c,d)))))try{i[b]=c}catch(j){}}},css:function(a,b,c,d){var e,f,g,h=n.camelCase(b);return b=n.cssProps[h]||(n.cssProps[h]=bb(h)||h),g=n.cssHooks[b]||n.cssHooks[h],g&&"get"in g&&(f=g.get(a,!0,c)),void 0===f&&(f=Sa(a,b,d)),"normal"===f&&b in $a&&(f=$a[b]),""===c||c?(e=parseFloat(f),c===!0||isFinite(e)?e||0:f):f}}),n.each(["height","width"],function(a,b){n.cssHooks[b]={get:function(a,c,d){return c?Xa.test(n.css(a,"display"))&&0===a.offsetWidth?Pa(a,Za,function(){return fb(a,b,d)}):fb(a,b,d):void 0},set:function(a,c,d){var e=d&&Ra(a);return db(a,c,d?eb(a,b,d,l.boxSizing&&"border-box"===n.css(a,"boxSizing",!1,e),e):0)}}}),l.opacity||(n.cssHooks.opacity={get:function(a,b){return Wa.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=n.isNumeric(b)?"alpha(opacity="+100*b+")":"",f=d&&d.filter||c.filter||"";c.zoom=1,(b>=1||""===b)&&""===n.trim(f.replace(Va,""))&&c.removeAttribute&&(c.removeAttribute("filter"),""===b||d&&!d.filter)||(c.filter=Va.test(f)?f.replace(Va,e):f+" "+e)}}),n.cssHooks.marginRight=Ua(l.reliableMarginRight,function(a,b){return b?Pa(a,{display:"inline-block"},Sa,[a,"marginRight"]):void 0}),n.cssHooks.marginLeft=Ua(l.reliableMarginLeft,function(a,b){return b?(parseFloat(Sa(a,"marginLeft"))||(n.contains(a.ownerDocument,a)?a.getBoundingClientRect().left-Pa(a,{ marginLeft:0},function(){return a.getBoundingClientRect().left}):0))+"px":void 0}),n.each({margin:"",padding:"",border:"Width"},function(a,b){n.cssHooks[a+b]={expand:function(c){for(var d=0,e={},f="string"==typeof c?c.split(" "):[c];4>d;d++)e[a+V[d]+b]=f[d]||f[d-2]||f[0];return e}},Na.test(a)||(n.cssHooks[a+b].set=db)}),n.fn.extend({css:function(a,b){return Y(this,function(a,b,c){var d,e,f={},g=0;if(n.isArray(b)){for(d=Ra(a),e=b.length;e>g;g++)f[b[g]]=n.css(a,b[g],!1,d);return f}return void 0!==c?n.style(a,b,c):n.css(a,b)},a,b,arguments.length>1)},show:function(){return cb(this,!0)},hide:function(){return cb(this)},toggle:function(a){return"boolean"==typeof a?a?this.show():this.hide():this.each(function(){W(this)?n(this).show():n(this).hide()})}});function gb(a,b,c,d,e){return new gb.prototype.init(a,b,c,d,e)}n.Tween=gb,gb.prototype={constructor:gb,init:function(a,b,c,d,e,f){this.elem=a,this.prop=c,this.easing=e||n.easing._default,this.options=b,this.start=this.now=this.cur(),this.end=d,this.unit=f||(n.cssNumber[c]?"":"px")},cur:function(){var a=gb.propHooks[this.prop];return a&&a.get?a.get(this):gb.propHooks._default.get(this)},run:function(a){var b,c=gb.propHooks[this.prop];return this.options.duration?this.pos=b=n.easing[this.easing](a,this.options.duration*a,0,1,this.options.duration):this.pos=b=a,this.now=(this.end-this.start)*b+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),c&&c.set?c.set(this):gb.propHooks._default.set(this),this}},gb.prototype.init.prototype=gb.prototype,gb.propHooks={_default:{get:function(a){var b;return 1!==a.elem.nodeType||null!=a.elem[a.prop]&&null==a.elem.style[a.prop]?a.elem[a.prop]:(b=n.css(a.elem,a.prop,""),b&&"auto"!==b?b:0)},set:function(a){n.fx.step[a.prop]?n.fx.step[a.prop](a):1!==a.elem.nodeType||null==a.elem.style[n.cssProps[a.prop]]&&!n.cssHooks[a.prop]?a.elem[a.prop]=a.now:n.style(a.elem,a.prop,a.now+a.unit)}}},gb.propHooks.scrollTop=gb.propHooks.scrollLeft={set:function(a){a.elem.nodeType&&a.elem.parentNode&&(a.elem[a.prop]=a.now)}},n.easing={linear:function(a){return a},swing:function(a){return.5-Math.cos(a*Math.PI)/2},_default:"swing"},n.fx=gb.prototype.init,n.fx.step={};var hb,ib,jb=/^(?:toggle|show|hide)$/,kb=/queueHooks$/;function lb(){return a.setTimeout(function(){hb=void 0}),hb=n.now()}function mb(a,b){var c,d={height:a},e=0;for(b=b?1:0;4>e;e+=2-b)c=V[e],d["margin"+c]=d["padding"+c]=a;return b&&(d.opacity=d.width=a),d}function nb(a,b,c){for(var d,e=(qb.tweeners[b]||[]).concat(qb.tweeners["*"]),f=0,g=e.length;g>f;f++)if(d=e[f].call(c,b,a))return d}function ob(a,b,c){var d,e,f,g,h,i,j,k,m=this,o={},p=a.style,q=a.nodeType&&W(a),r=n._data(a,"fxshow");c.queue||(h=n._queueHooks(a,"fx"),null==h.unqueued&&(h.unqueued=0,i=h.empty.fire,h.empty.fire=function(){h.unqueued||i()}),h.unqueued++,m.always(function(){m.always(function(){h.unqueued--,n.queue(a,"fx").length||h.empty.fire()})})),1===a.nodeType&&("height"in b||"width"in b)&&(c.overflow=[p.overflow,p.overflowX,p.overflowY],j=n.css(a,"display"),k="none"===j?n._data(a,"olddisplay")||Ma(a.nodeName):j,"inline"===k&&"none"===n.css(a,"float")&&(l.inlineBlockNeedsLayout&&"inline"!==Ma(a.nodeName)?p.zoom=1:p.display="inline-block")),c.overflow&&(p.overflow="hidden",l.shrinkWrapBlocks()||m.always(function(){p.overflow=c.overflow[0],p.overflowX=c.overflow[1],p.overflowY=c.overflow[2]}));for(d in b)if(e=b[d],jb.exec(e)){if(delete b[d],f=f||"toggle"===e,e===(q?"hide":"show")){if("show"!==e||!r||void 0===r[d])continue;q=!0}o[d]=r&&r[d]||n.style(a,d)}else j=void 0;if(n.isEmptyObject(o))"inline"===("none"===j?Ma(a.nodeName):j)&&(p.display=j);else{r?"hidden"in r&&(q=r.hidden):r=n._data(a,"fxshow",{}),f&&(r.hidden=!q),q?n(a).show():m.done(function(){n(a).hide()}),m.done(function(){var b;n._removeData(a,"fxshow");for(b in o)n.style(a,b,o[b])});for(d in o)g=nb(q?r[d]:0,d,m),d in r||(r[d]=g.start,q&&(g.end=g.start,g.start="width"===d||"height"===d?1:0))}}function pb(a,b){var c,d,e,f,g;for(c in a)if(d=n.camelCase(c),e=b[d],f=a[c],n.isArray(f)&&(e=f[1],f=a[c]=f[0]),c!==d&&(a[d]=f,delete a[c]),g=n.cssHooks[d],g&&"expand"in g){f=g.expand(f),delete a[d];for(c in f)c in a||(a[c]=f[c],b[c]=e)}else b[d]=e}function qb(a,b,c){var d,e,f=0,g=qb.prefilters.length,h=n.Deferred().always(function(){delete i.elem}),i=function(){if(e)return!1;for(var b=hb||lb(),c=Math.max(0,j.startTime+j.duration-b),d=c/j.duration||0,f=1-d,g=0,i=j.tweens.length;i>g;g++)j.tweens[g].run(f);return h.notifyWith(a,[j,f,c]),1>f&&i?c:(h.resolveWith(a,[j]),!1)},j=h.promise({elem:a,props:n.extend({},b),opts:n.extend(!0,{specialEasing:{},easing:n.easing._default},c),originalProperties:b,originalOptions:c,startTime:hb||lb(),duration:c.duration,tweens:[],createTween:function(b,c){var d=n.Tween(a,j.opts,b,c,j.opts.specialEasing[b]||j.opts.easing);return j.tweens.push(d),d},stop:function(b){var c=0,d=b?j.tweens.length:0;if(e)return this;for(e=!0;d>c;c++)j.tweens[c].run(1);return b?(h.notifyWith(a,[j,1,0]),h.resolveWith(a,[j,b])):h.rejectWith(a,[j,b]),this}}),k=j.props;for(pb(k,j.opts.specialEasing);g>f;f++)if(d=qb.prefilters[f].call(j,a,k,j.opts))return n.isFunction(d.stop)&&(n._queueHooks(j.elem,j.opts.queue).stop=n.proxy(d.stop,d)),d;return n.map(k,nb,j),n.isFunction(j.opts.start)&&j.opts.start.call(a,j),n.fx.timer(n.extend(i,{elem:a,anim:j,queue:j.opts.queue})),j.progress(j.opts.progress).done(j.opts.done,j.opts.complete).fail(j.opts.fail).always(j.opts.always)}n.Animation=n.extend(qb,{tweeners:{"*":[function(a,b){var c=this.createTween(a,b);return X(c.elem,a,U.exec(b),c),c}]},tweener:function(a,b){n.isFunction(a)?(b=a,a=["*"]):a=a.match(G);for(var c,d=0,e=a.length;e>d;d++)c=a[d],qb.tweeners[c]=qb.tweeners[c]||[],qb.tweeners[c].unshift(b)},prefilters:[ob],prefilter:function(a,b){b?qb.prefilters.unshift(a):qb.prefilters.push(a)}}),n.speed=function(a,b,c){var d=a&&"object"==typeof a?n.extend({},a):{complete:c||!c&&b||n.isFunction(a)&&a,duration:a,easing:c&&b||b&&!n.isFunction(b)&&b};return d.duration=n.fx.off?0:"number"==typeof d.duration?d.duration:d.duration in n.fx.speeds?n.fx.speeds[d.duration]:n.fx.speeds._default,null!=d.queue&&d.queue!==!0||(d.queue="fx"),d.old=d.complete,d.complete=function(){n.isFunction(d.old)&&d.old.call(this),d.queue&&n.dequeue(this,d.queue)},d},n.fn.extend({fadeTo:function(a,b,c,d){return this.filter(W).css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){var e=n.isEmptyObject(a),f=n.speed(b,c,d),g=function(){var b=qb(this,n.extend({},a),f);(e||n._data(this,"finish"))&&b.stop(!0)};return g.finish=g,e||f.queue===!1?this.each(g):this.queue(f.queue,g)},stop:function(a,b,c){var d=function(a){var b=a.stop;delete a.stop,b(c)};return"string"!=typeof a&&(c=b,b=a,a=void 0),b&&a!==!1&&this.queue(a||"fx",[]),this.each(function(){var b=!0,e=null!=a&&a+"queueHooks",f=n.timers,g=n._data(this);if(e)g[e]&&g[e].stop&&d(g[e]);else for(e in g)g[e]&&g[e].stop&&kb.test(e)&&d(g[e]);for(e=f.length;e--;)f[e].elem!==this||null!=a&&f[e].queue!==a||(f[e].anim.stop(c),b=!1,f.splice(e,1));!b&&c||n.dequeue(this,a)})},finish:function(a){return a!==!1&&(a=a||"fx"),this.each(function(){var b,c=n._data(this),d=c[a+"queue"],e=c[a+"queueHooks"],f=n.timers,g=d?d.length:0;for(c.finish=!0,n.queue(this,a,[]),e&&e.stop&&e.stop.call(this,!0),b=f.length;b--;)f[b].elem===this&&f[b].queue===a&&(f[b].anim.stop(!0),f.splice(b,1));for(b=0;g>b;b++)d[b]&&d[b].finish&&d[b].finish.call(this);delete c.finish})}}),n.each(["toggle","show","hide"],function(a,b){var c=n.fn[b];n.fn[b]=function(a,d,e){return null==a||"boolean"==typeof a?c.apply(this,arguments):this.animate(mb(b,!0),a,d,e)}}),n.each({slideDown:mb("show"),slideUp:mb("hide"),slideToggle:mb("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){n.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),n.timers=[],n.fx.tick=function(){var a,b=n.timers,c=0;for(hb=n.now();c<b.length;c++)a=b[c],a()||b[c]!==a||b.splice(c--,1);b.length||n.fx.stop(),hb=void 0},n.fx.timer=function(a){n.timers.push(a),a()?n.fx.start():n.timers.pop()},n.fx.interval=13,n.fx.start=function(){ib||(ib=a.setInterval(n.fx.tick,n.fx.interval))},n.fx.stop=function(){a.clearInterval(ib),ib=null},n.fx.speeds={slow:600,fast:200,_default:400},n.fn.delay=function(b,c){return b=n.fx?n.fx.speeds[b]||b:b,c=c||"fx",this.queue(c,function(c,d){var e=a.setTimeout(c,b);d.stop=function(){a.clearTimeout(e)}})},function(){var a,b=d.createElement("input"),c=d.createElement("div"),e=d.createElement("select"),f=e.appendChild(d.createElement("option"));c=d.createElement("div"),c.setAttribute("className","t"),c.innerHTML=" <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",a=c.getElementsByTagName("a")[0],b.setAttribute("type","checkbox"),c.appendChild(b),a=c.getElementsByTagName("a")[0],a.style.cssText="top:1px",l.getSetAttribute="t"!==c.className,l.style=/top/.test(a.getAttribute("style")),l.hrefNormalized="/a"===a.getAttribute("href"),l.checkOn=!!b.value,l.optSelected=f.selected,l.enctype=!!d.createElement("form").enctype,e.disabled=!0,l.optDisabled=!f.disabled,b=d.createElement("input"),b.setAttribute("value",""),l.input=""===b.getAttribute("value"),b.value="t",b.setAttribute("type","radio"),l.radioValue="t"===b.value}();var rb=/\r/g,sb=/[\x20\t\r\n\f]+/g;n.fn.extend({val:function(a){var b,c,d,e=this[0];{if(arguments.length)return d=n.isFunction(a),this.each(function(c){var e;1===this.nodeType&&(e=d?a.call(this,c,n(this).val()):a,null==e?e="":"number"==typeof e?e+="":n.isArray(e)&&(e=n.map(e,function(a){return null==a?"":a+""})),b=n.valHooks[this.type]||n.valHooks[this.nodeName.toLowerCase()],b&&"set"in b&&void 0!==b.set(this,e,"value")||(this.value=e))});if(e)return b=n.valHooks[e.type]||n.valHooks[e.nodeName.toLowerCase()],b&&"get"in b&&void 0!==(c=b.get(e,"value"))?c:(c=e.value,"string"==typeof c?c.replace(rb,""):null==c?"":c)}}}),n.extend({valHooks:{option:{get:function(a){var b=n.find.attr(a,"value");return null!=b?b:n.trim(n.text(a)).replace(sb," ")}},select:{get:function(a){for(var b,c,d=a.options,e=a.selectedIndex,f="select-one"===a.type||0>e,g=f?null:[],h=f?e+1:d.length,i=0>e?h:f?e:0;h>i;i++)if(c=d[i],(c.selected||i===e)&&(l.optDisabled?!c.disabled:null===c.getAttribute("disabled"))&&(!c.parentNode.disabled||!n.nodeName(c.parentNode,"optgroup"))){if(b=n(c).val(),f)return b;g.push(b)}return g},set:function(a,b){var c,d,e=a.options,f=n.makeArray(b),g=e.length;while(g--)if(d=e[g],n.inArray(n.valHooks.option.get(d),f)>-1)try{d.selected=c=!0}catch(h){d.scrollHeight}else d.selected=!1;return c||(a.selectedIndex=-1),e}}}}),n.each(["radio","checkbox"],function(){n.valHooks[this]={set:function(a,b){return n.isArray(b)?a.checked=n.inArray(n(a).val(),b)>-1:void 0}},l.checkOn||(n.valHooks[this].get=function(a){return null===a.getAttribute("value")?"on":a.value})});var tb,ub,vb=n.expr.attrHandle,wb=/^(?:checked|selected)$/i,xb=l.getSetAttribute,yb=l.input;n.fn.extend({attr:function(a,b){return Y(this,n.attr,a,b,arguments.length>1)},removeAttr:function(a){return this.each(function(){n.removeAttr(this,a)})}}),n.extend({attr:function(a,b,c){var d,e,f=a.nodeType;if(3!==f&&8!==f&&2!==f)return"undefined"==typeof a.getAttribute?n.prop(a,b,c):(1===f&&n.isXMLDoc(a)||(b=b.toLowerCase(),e=n.attrHooks[b]||(n.expr.match.bool.test(b)?ub:tb)),void 0!==c?null===c?void n.removeAttr(a,b):e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:(a.setAttribute(b,c+""),c):e&&"get"in e&&null!==(d=e.get(a,b))?d:(d=n.find.attr(a,b),null==d?void 0:d))},attrHooks:{type:{set:function(a,b){if(!l.radioValue&&"radio"===b&&n.nodeName(a,"input")){var c=a.value;return a.setAttribute("type",b),c&&(a.value=c),b}}}},removeAttr:function(a,b){var c,d,e=0,f=b&&b.match(G);if(f&&1===a.nodeType)while(c=f[e++])d=n.propFix[c]||c,n.expr.match.bool.test(c)?yb&&xb||!wb.test(c)?a[d]=!1:a[n.camelCase("default-"+c)]=a[d]=!1:n.attr(a,c,""),a.removeAttribute(xb?c:d)}}),ub={set:function(a,b,c){return b===!1?n.removeAttr(a,c):yb&&xb||!wb.test(c)?a.setAttribute(!xb&&n.propFix[c]||c,c):a[n.camelCase("default-"+c)]=a[c]=!0,c}},n.each(n.expr.match.bool.source.match(/\w+/g),function(a,b){var c=vb[b]||n.find.attr;yb&&xb||!wb.test(b)?vb[b]=function(a,b,d){var e,f;return d||(f=vb[b],vb[b]=e,e=null!=c(a,b,d)?b.toLowerCase():null,vb[b]=f),e}:vb[b]=function(a,b,c){return c?void 0:a[n.camelCase("default-"+b)]?b.toLowerCase():null}}),yb&&xb||(n.attrHooks.value={set:function(a,b,c){return n.nodeName(a,"input")?void(a.defaultValue=b):tb&&tb.set(a,b,c)}}),xb||(tb={set:function(a,b,c){var d=a.getAttributeNode(c);return d||a.setAttributeNode(d=a.ownerDocument.createAttribute(c)),d.value=b+="","value"===c||b===a.getAttribute(c)?b:void 0}},vb.id=vb.name=vb.coords=function(a,b,c){var d;return c?void 0:(d=a.getAttributeNode(b))&&""!==d.value?d.value:null},n.valHooks.button={get:function(a,b){var c=a.getAttributeNode(b);return c&&c.specified?c.value:void 0},set:tb.set},n.attrHooks.contenteditable={set:function(a,b,c){tb.set(a,""===b?!1:b,c)}},n.each(["width","height"],function(a,b){n.attrHooks[b]={set:function(a,c){return""===c?(a.setAttribute(b,"auto"),c):void 0}}})),l.style||(n.attrHooks.style={get:function(a){return a.style.cssText||void 0},set:function(a,b){return a.style.cssText=b+""}});var zb=/^(?:input|select|textarea|button|object)$/i,Ab=/^(?:a|area)$/i;n.fn.extend({prop:function(a,b){return Y(this,n.prop,a,b,arguments.length>1)},removeProp:function(a){return a=n.propFix[a]||a,this.each(function(){try{this[a]=void 0,delete this[a]}catch(b){}})}}),n.extend({prop:function(a,b,c){var d,e,f=a.nodeType;if(3!==f&&8!==f&&2!==f)return 1===f&&n.isXMLDoc(a)||(b=n.propFix[b]||b,e=n.propHooks[b]),void 0!==c?e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:a[b]=c:e&&"get"in e&&null!==(d=e.get(a,b))?d:a[b]},propHooks:{tabIndex:{get:function(a){var b=n.find.attr(a,"tabindex");return b?parseInt(b,10):zb.test(a.nodeName)||Ab.test(a.nodeName)&&a.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),l.hrefNormalized||n.each(["href","src"],function(a,b){n.propHooks[b]={get:function(a){return a.getAttribute(b,4)}}}),l.optSelected||(n.propHooks.selected={get:function(a){var b=a.parentNode;return b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex),null},set:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex)}}),n.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){n.propFix[this.toLowerCase()]=this}),l.enctype||(n.propFix.enctype="encoding");var Bb=/[\t\r\n\f]/g;function Cb(a){return n.attr(a,"class")||""}n.fn.extend({addClass:function(a){var b,c,d,e,f,g,h,i=0;if(n.isFunction(a))return this.each(function(b){n(this).addClass(a.call(this,b,Cb(this)))});if("string"==typeof a&&a){b=a.match(G)||[];while(c=this[i++])if(e=Cb(c),d=1===c.nodeType&&(" "+e+" ").replace(Bb," ")){g=0;while(f=b[g++])d.indexOf(" "+f+" ")<0&&(d+=f+" ");h=n.trim(d),e!==h&&n.attr(c,"class",h)}}return this},removeClass:function(a){var b,c,d,e,f,g,h,i=0;if(n.isFunction(a))return this.each(function(b){n(this).removeClass(a.call(this,b,Cb(this)))});if(!arguments.length)return this.attr("class","");if("string"==typeof a&&a){b=a.match(G)||[];while(c=this[i++])if(e=Cb(c),d=1===c.nodeType&&(" "+e+" ").replace(Bb," ")){g=0;while(f=b[g++])while(d.indexOf(" "+f+" ")>-1)d=d.replace(" "+f+" "," ");h=n.trim(d),e!==h&&n.attr(c,"class",h)}}return this},toggleClass:function(a,b){var c=typeof a;return"boolean"==typeof b&&"string"===c?b?this.addClass(a):this.removeClass(a):n.isFunction(a)?this.each(function(c){n(this).toggleClass(a.call(this,c,Cb(this),b),b)}):this.each(function(){var b,d,e,f;if("string"===c){d=0,e=n(this),f=a.match(G)||[];while(b=f[d++])e.hasClass(b)?e.removeClass(b):e.addClass(b)}else void 0!==a&&"boolean"!==c||(b=Cb(this),b&&n._data(this,"__className__",b),n.attr(this,"class",b||a===!1?"":n._data(this,"__className__")||""))})},hasClass:function(a){var b,c,d=0;b=" "+a+" ";while(c=this[d++])if(1===c.nodeType&&(" "+Cb(c)+" ").replace(Bb," ").indexOf(b)>-1)return!0;return!1}}),n.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(a,b){n.fn[b]=function(a,c){return arguments.length>0?this.on(b,null,a,c):this.trigger(b)}}),n.fn.extend({hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}});var Db=a.location,Eb=n.now(),Fb=/\?/,Gb=/(,)|(\[|{)|(}|])|"(?:[^"\\\r\n]|\\["\\\/bfnrt]|\\u[\da-fA-F]{4})*"\s*:?|true|false|null|-?(?!0\d)\d+(?:\.\d+|)(?:[eE][+-]?\d+|)/g;n.parseJSON=function(b){if(a.JSON&&a.JSON.parse)return a.JSON.parse(b+"");var c,d=null,e=n.trim(b+"");return e&&!n.trim(e.replace(Gb,function(a,b,e,f){return c&&b&&(d=0),0===d?a:(c=e||b,d+=!f-!e,"")}))?Function("return "+e)():n.error("Invalid JSON: "+b)},n.parseXML=function(b){var c,d;if(!b||"string"!=typeof b)return null;try{a.DOMParser?(d=new a.DOMParser,c=d.parseFromString(b,"text/xml")):(c=new a.ActiveXObject("Microsoft.XMLDOM"),c.async="false",c.loadXML(b))}catch(e){c=void 0}return c&&c.documentElement&&!c.getElementsByTagName("parsererror").length||n.error("Invalid XML: "+b),c};var Hb=/#.*$/,Ib=/([?&])_=[^&]*/,Jb=/^(.*?):[ \t]*([^\r\n]*)\r?$/gm,Kb=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Lb=/^(?:GET|HEAD)$/,Mb=/^\/\//,Nb=/^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/,Ob={},Pb={},Qb="*/".concat("*"),Rb=Db.href,Sb=Nb.exec(Rb.toLowerCase())||[];function Tb(a){return function(b,c){"string"!=typeof b&&(c=b,b="*");var d,e=0,f=b.toLowerCase().match(G)||[];if(n.isFunction(c))while(d=f[e++])"+"===d.charAt(0)?(d=d.slice(1)||"*",(a[d]=a[d]||[]).unshift(c)):(a[d]=a[d]||[]).push(c)}}function Ub(a,b,c,d){var e={},f=a===Pb;function g(h){var i;return e[h]=!0,n.each(a[h]||[],function(a,h){var j=h(b,c,d);return"string"!=typeof j||f||e[j]?f?!(i=j):void 0:(b.dataTypes.unshift(j),g(j),!1)}),i}return g(b.dataTypes[0])||!e["*"]&&g("*")}function Vb(a,b){var c,d,e=n.ajaxSettings.flatOptions||{};for(d in b)void 0!==b[d]&&((e[d]?a:c||(c={}))[d]=b[d]);return c&&n.extend(!0,a,c),a}function Wb(a,b,c){var d,e,f,g,h=a.contents,i=a.dataTypes;while("*"===i[0])i.shift(),void 0===e&&(e=a.mimeType||b.getResponseHeader("Content-Type"));if(e)for(g in h)if(h[g]&&h[g].test(e)){i.unshift(g);break}if(i[0]in c)f=i[0];else{for(g in c){if(!i[0]||a.converters[g+" "+i[0]]){f=g;break}d||(d=g)}f=f||d}return f?(f!==i[0]&&i.unshift(f),c[f]):void 0}function Xb(a,b,c,d){var e,f,g,h,i,j={},k=a.dataTypes.slice();if(k[1])for(g in a.converters)j[g.toLowerCase()]=a.converters[g];f=k.shift();while(f)if(a.responseFields[f]&&(c[a.responseFields[f]]=b),!i&&d&&a.dataFilter&&(b=a.dataFilter(b,a.dataType)),i=f,f=k.shift())if("*"===f)f=i;else if("*"!==i&&i!==f){if(g=j[i+" "+f]||j["* "+f],!g)for(e in j)if(h=e.split(" "),h[1]===f&&(g=j[i+" "+h[0]]||j["* "+h[0]])){g===!0?g=j[e]:j[e]!==!0&&(f=h[0],k.unshift(h[1]));break}if(g!==!0)if(g&&a["throws"])b=g(b);else try{b=g(b)}catch(l){return{state:"parsererror",error:g?l:"No conversion from "+i+" to "+f}}}return{state:"success",data:b}}n.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:Rb,type:"GET",isLocal:Kb.test(Sb[1]),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Qb,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":n.parseJSON,"text xml":n.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(a,b){return b?Vb(Vb(a,n.ajaxSettings),b):Vb(n.ajaxSettings,a)},ajaxPrefilter:Tb(Ob),ajaxTransport:Tb(Pb),ajax:function(b,c){"object"==typeof b&&(c=b,b=void 0),c=c||{};var d,e,f,g,h,i,j,k,l=n.ajaxSetup({},c),m=l.context||l,o=l.context&&(m.nodeType||m.jquery)?n(m):n.event,p=n.Deferred(),q=n.Callbacks("once memory"),r=l.statusCode||{},s={},t={},u=0,v="canceled",w={readyState:0,getResponseHeader:function(a){var b;if(2===u){if(!k){k={};while(b=Jb.exec(g))k[b[1].toLowerCase()]=b[2]}b=k[a.toLowerCase()]}return null==b?null:b},getAllResponseHeaders:function(){return 2===u?g:null},setRequestHeader:function(a,b){var c=a.toLowerCase();return u||(a=t[c]=t[c]||a,s[a]=b),this},overrideMimeType:function(a){return u||(l.mimeType=a),this},statusCode:function(a){var b;if(a)if(2>u)for(b in a)r[b]=[r[b],a[b]];else w.always(a[w.status]);return this},abort:function(a){var b=a||v;return j&&j.abort(b),y(0,b),this}};if(p.promise(w).complete=q.add,w.success=w.done,w.error=w.fail,l.url=((b||l.url||Rb)+"").replace(Hb,"").replace(Mb,Sb[1]+"//"),l.type=c.method||c.type||l.method||l.type,l.dataTypes=n.trim(l.dataType||"*").toLowerCase().match(G)||[""],null==l.crossDomain&&(d=Nb.exec(l.url.toLowerCase()),l.crossDomain=!(!d||d[1]===Sb[1]&&d[2]===Sb[2]&&(d[3]||("http:"===d[1]?"80":"443"))===(Sb[3]||("http:"===Sb[1]?"80":"443")))),l.data&&l.processData&&"string"!=typeof l.data&&(l.data=n.param(l.data,l.traditional)),Ub(Ob,l,c,w),2===u)return w;i=n.event&&l.global,i&&0===n.active++&&n.event.trigger("ajaxStart"),l.type=l.type.toUpperCase(),l.hasContent=!Lb.test(l.type),f=l.url,l.hasContent||(l.data&&(f=l.url+=(Fb.test(f)?"&":"?")+l.data,delete l.data),l.cache===!1&&(l.url=Ib.test(f)?f.replace(Ib,"$1_="+Eb++):f+(Fb.test(f)?"&":"?")+"_="+Eb++)),l.ifModified&&(n.lastModified[f]&&w.setRequestHeader("If-Modified-Since",n.lastModified[f]),n.etag[f]&&w.setRequestHeader("If-None-Match",n.etag[f])),(l.data&&l.hasContent&&l.contentType!==!1||c.contentType)&&w.setRequestHeader("Content-Type",l.contentType),w.setRequestHeader("Accept",l.dataTypes[0]&&l.accepts[l.dataTypes[0]]?l.accepts[l.dataTypes[0]]+("*"!==l.dataTypes[0]?", "+Qb+"; q=0.01":""):l.accepts["*"]);for(e in l.headers)w.setRequestHeader(e,l.headers[e]);if(l.beforeSend&&(l.beforeSend.call(m,w,l)===!1||2===u))return w.abort();v="abort";for(e in{success:1,error:1,complete:1})w[e](l[e]);if(j=Ub(Pb,l,c,w)){if(w.readyState=1,i&&o.trigger("ajaxSend",[w,l]),2===u)return w;l.async&&l.timeout>0&&(h=a.setTimeout(function(){w.abort("timeout")},l.timeout));try{u=1,j.send(s,y)}catch(x){if(!(2>u))throw x;y(-1,x)}}else y(-1,"No Transport");function y(b,c,d,e){var k,s,t,v,x,y=c;2!==u&&(u=2,h&&a.clearTimeout(h),j=void 0,g=e||"",w.readyState=b>0?4:0,k=b>=200&&300>b||304===b,d&&(v=Wb(l,w,d)),v=Xb(l,v,w,k),k?(l.ifModified&&(x=w.getResponseHeader("Last-Modified"),x&&(n.lastModified[f]=x),x=w.getResponseHeader("etag"),x&&(n.etag[f]=x)),204===b||"HEAD"===l.type?y="nocontent":304===b?y="notmodified":(y=v.state,s=v.data,t=v.error,k=!t)):(t=y,!b&&y||(y="error",0>b&&(b=0))),w.status=b,w.statusText=(c||y)+"",k?p.resolveWith(m,[s,y,w]):p.rejectWith(m,[w,y,t]),w.statusCode(r),r=void 0,i&&o.trigger(k?"ajaxSuccess":"ajaxError",[w,l,k?s:t]),q.fireWith(m,[w,y]),i&&(o.trigger("ajaxComplete",[w,l]),--n.active||n.event.trigger("ajaxStop")))}return w},getJSON:function(a,b,c){return n.get(a,b,c,"json")},getScript:function(a,b){return n.get(a,void 0,b,"script")}}),n.each(["get","post"],function(a,b){n[b]=function(a,c,d,e){return n.isFunction(c)&&(e=e||d,d=c,c=void 0),n.ajax(n.extend({url:a,type:b,dataType:e,data:c,success:d},n.isPlainObject(a)&&a))}}),n._evalUrl=function(a){return n.ajax({url:a,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,"throws":!0})},n.fn.extend({wrapAll:function(a){if(n.isFunction(a))return this.each(function(b){n(this).wrapAll(a.call(this,b))});if(this[0]){var b=n(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&1===a.firstChild.nodeType)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){return n.isFunction(a)?this.each(function(b){n(this).wrapInner(a.call(this,b))}):this.each(function(){var b=n(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=n.isFunction(a);return this.each(function(c){n(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){n.nodeName(this,"body")||n(this).replaceWith(this.childNodes)}).end()}});function Yb(a){return a.style&&a.style.display||n.css(a,"display")}function Zb(a){if(!n.contains(a.ownerDocument||d,a))return!0;while(a&&1===a.nodeType){if("none"===Yb(a)||"hidden"===a.type)return!0;a=a.parentNode}return!1}n.expr.filters.hidden=function(a){return l.reliableHiddenOffsets()?a.offsetWidth<=0&&a.offsetHeight<=0&&!a.getClientRects().length:Zb(a)},n.expr.filters.visible=function(a){return!n.expr.filters.hidden(a)};var $b=/%20/g,_b=/\[\]$/,ac=/\r?\n/g,bc=/^(?:submit|button|image|reset|file)$/i,cc=/^(?:input|select|textarea|keygen)/i;function dc(a,b,c,d){var e;if(n.isArray(b))n.each(b,function(b,e){c||_b.test(a)?d(a,e):dc(a+"["+("object"==typeof e&&null!=e?b:"")+"]",e,c,d)});else if(c||"object"!==n.type(b))d(a,b);else for(e in b)dc(a+"["+e+"]",b[e],c,d)}n.param=function(a,b){var c,d=[],e=function(a,b){b=n.isFunction(b)?b():null==b?"":b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};if(void 0===b&&(b=n.ajaxSettings&&n.ajaxSettings.traditional),n.isArray(a)||a.jquery&&!n.isPlainObject(a))n.each(a,function(){e(this.name,this.value)});else for(c in a)dc(c,a[c],b,e);return d.join("&").replace($b,"+")},n.fn.extend({serialize:function(){return n.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var a=n.prop(this,"elements");return a?n.makeArray(a):this}).filter(function(){var a=this.type;return this.name&&!n(this).is(":disabled")&&cc.test(this.nodeName)&&!bc.test(a)&&(this.checked||!Z.test(a))}).map(function(a,b){var c=n(this).val();return null==c?null:n.isArray(c)?n.map(c,function(a){return{name:b.name,value:a.replace(ac,"\r\n")}}):{name:b.name,value:c.replace(ac,"\r\n")}}).get()}}),n.ajaxSettings.xhr=void 0!==a.ActiveXObject?function(){return this.isLocal?ic():d.documentMode>8?hc():/^(get|post|head|put|delete|options)$/i.test(this.type)&&hc()||ic()}:hc;var ec=0,fc={},gc=n.ajaxSettings.xhr();a.attachEvent&&a.attachEvent("onunload",function(){for(var a in fc)fc[a](void 0,!0)}),l.cors=!!gc&&"withCredentials"in gc,gc=l.ajax=!!gc,gc&&n.ajaxTransport(function(b){if(!b.crossDomain||l.cors){var c;return{send:function(d,e){var f,g=b.xhr(),h=++ec;if(g.open(b.type,b.url,b.async,b.username,b.password),b.xhrFields)for(f in b.xhrFields)g[f]=b.xhrFields[f];b.mimeType&&g.overrideMimeType&&g.overrideMimeType(b.mimeType),b.crossDomain||d["X-Requested-With"]||(d["X-Requested-With"]="XMLHttpRequest");for(f in d)void 0!==d[f]&&g.setRequestHeader(f,d[f]+"");g.send(b.hasContent&&b.data||null),c=function(a,d){var f,i,j;if(c&&(d||4===g.readyState))if(delete fc[h],c=void 0,g.onreadystatechange=n.noop,d)4!==g.readyState&&g.abort();else{j={},f=g.status,"string"==typeof g.responseText&&(j.text=g.responseText);try{i=g.statusText}catch(k){i=""}f||!b.isLocal||b.crossDomain?1223===f&&(f=204):f=j.text?200:404}j&&e(f,i,j,g.getAllResponseHeaders())},b.async?4===g.readyState?a.setTimeout(c):g.onreadystatechange=fc[h]=c:c()},abort:function(){c&&c(void 0,!0)}}}});function hc(){try{return new a.XMLHttpRequest}catch(b){}}function ic(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}n.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(a){return n.globalEval(a),a}}}),n.ajaxPrefilter("script",function(a){void 0===a.cache&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),n.ajaxTransport("script",function(a){if(a.crossDomain){var b,c=d.head||n("head")[0]||d.documentElement;return{send:function(e,f){b=d.createElement("script"),b.async=!0,a.scriptCharset&&(b.charset=a.scriptCharset),b.src=a.url,b.onload=b.onreadystatechange=function(a,c){(c||!b.readyState||/loaded|complete/.test(b.readyState))&&(b.onload=b.onreadystatechange=null,b.parentNode&&b.parentNode.removeChild(b),b=null,c||f(200,"success"))},c.insertBefore(b,c.firstChild)},abort:function(){b&&b.onload(void 0,!0)}}}});var jc=[],kc=/(=)\?(?=&|$)|\?\?/;n.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var a=jc.pop()||n.expando+"_"+Eb++;return this[a]=!0,a}}),n.ajaxPrefilter("json jsonp",function(b,c,d){var e,f,g,h=b.jsonp!==!1&&(kc.test(b.url)?"url":"string"==typeof b.data&&0===(b.contentType||"").indexOf("application/x-www-form-urlencoded")&&kc.test(b.data)&&"data");return h||"jsonp"===b.dataTypes[0]?(e=b.jsonpCallback=n.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,h?b[h]=b[h].replace(kc,"$1"+e):b.jsonp!==!1&&(b.url+=(Fb.test(b.url)?"&":"?")+b.jsonp+"="+e),b.converters["script json"]=function(){return g||n.error(e+" was not called"),g[0]},b.dataTypes[0]="json",f=a[e],a[e]=function(){g=arguments},d.always(function(){void 0===f?n(a).removeProp(e):a[e]=f,b[e]&&(b.jsonpCallback=c.jsonpCallback,jc.push(e)),g&&n.isFunction(f)&&f(g[0]),g=f=void 0}),"script"):void 0}),n.parseHTML=function(a,b,c){if(!a||"string"!=typeof a)return null;"boolean"==typeof b&&(c=b,b=!1),b=b||d;var e=x.exec(a),f=!c&&[];return e?[b.createElement(e[1])]:(e=ja([a],b,f),f&&f.length&&n(f).remove(),n.merge([],e.childNodes))};var lc=n.fn.load;n.fn.load=function(a,b,c){if("string"!=typeof a&&lc)return lc.apply(this,arguments);var d,e,f,g=this,h=a.indexOf(" ");return h>-1&&(d=n.trim(a.slice(h,a.length)),a=a.slice(0,h)),n.isFunction(b)?(c=b,b=void 0):b&&"object"==typeof b&&(e="POST"),g.length>0&&n.ajax({url:a,type:e||"GET",dataType:"html",data:b}).done(function(a){f=arguments,g.html(d?n("<div>").append(n.parseHTML(a)).find(d):a)}).always(c&&function(a,b){g.each(function(){c.apply(this,f||[a.responseText,b,a])})}),this},n.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(a,b){n.fn[b]=function(a){return this.on(b,a)}}),n.expr.filters.animated=function(a){return n.grep(n.timers,function(b){return a===b.elem}).length};function mc(a){return n.isWindow(a)?a:9===a.nodeType?a.defaultView||a.parentWindow:!1}n.offset={setOffset:function(a,b,c){var d,e,f,g,h,i,j,k=n.css(a,"position"),l=n(a),m={};"static"===k&&(a.style.position="relative"),h=l.offset(),f=n.css(a,"top"),i=n.css(a,"left"),j=("absolute"===k||"fixed"===k)&&n.inArray("auto",[f,i])>-1,j?(d=l.position(),g=d.top,e=d.left):(g=parseFloat(f)||0,e=parseFloat(i)||0),n.isFunction(b)&&(b=b.call(a,c,n.extend({},h))),null!=b.top&&(m.top=b.top-h.top+g),null!=b.left&&(m.left=b.left-h.left+e),"using"in b?b.using.call(a,m):l.css(m)}},n.fn.extend({offset:function(a){if(arguments.length)return void 0===a?this:this.each(function(b){n.offset.setOffset(this,a,b)});var b,c,d={top:0,left:0},e=this[0],f=e&&e.ownerDocument;if(f)return b=f.documentElement,n.contains(b,e)?("undefined"!=typeof e.getBoundingClientRect&&(d=e.getBoundingClientRect()),c=mc(f),{top:d.top+(c.pageYOffset||b.scrollTop)-(b.clientTop||0),left:d.left+(c.pageXOffset||b.scrollLeft)-(b.clientLeft||0)}):d},position:function(){if(this[0]){var a,b,c={top:0,left:0},d=this[0];return"fixed"===n.css(d,"position")?b=d.getBoundingClientRect():(a=this.offsetParent(),b=this.offset(),n.nodeName(a[0],"html")||(c=a.offset()),c.top+=n.css(a[0],"borderTopWidth",!0),c.left+=n.css(a[0],"borderLeftWidth",!0)),{top:b.top-c.top-n.css(d,"marginTop",!0),left:b.left-c.left-n.css(d,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var a=this.offsetParent;while(a&&!n.nodeName(a,"html")&&"static"===n.css(a,"position"))a=a.offsetParent;return a||Qa})}}),n.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,b){var c=/Y/.test(b);n.fn[a]=function(d){return Y(this,function(a,d,e){var f=mc(a);return void 0===e?f?b in f?f[b]:f.document.documentElement[d]:a[d]:void(f?f.scrollTo(c?n(f).scrollLeft():e,c?e:n(f).scrollTop()):a[d]=e)},a,d,arguments.length,null)}}),n.each(["top","left"],function(a,b){n.cssHooks[b]=Ua(l.pixelPosition,function(a,c){return c?(c=Sa(a,b),Oa.test(c)?n(a).position()[b]+"px":c):void 0})}),n.each({Height:"height",Width:"width"},function(a,b){n.each({ padding:"inner"+a,content:b,"":"outer"+a},function(c,d){n.fn[d]=function(d,e){var f=arguments.length&&(c||"boolean"!=typeof d),g=c||(d===!0||e===!0?"margin":"border");return Y(this,function(b,c,d){var e;return n.isWindow(b)?b.document.documentElement["client"+a]:9===b.nodeType?(e=b.documentElement,Math.max(b.body["scroll"+a],e["scroll"+a],b.body["offset"+a],e["offset"+a],e["client"+a])):void 0===d?n.css(b,c,g):n.style(b,c,d,g)},b,f?d:void 0,f,null)}})}),n.fn.extend({bind:function(a,b,c){return this.on(a,null,b,c)},unbind:function(a,b){return this.off(a,null,b)},delegate:function(a,b,c,d){return this.on(b,a,c,d)},undelegate:function(a,b,c){return 1===arguments.length?this.off(a,"**"):this.off(b,a||"**",c)}}),n.fn.size=function(){return this.length},n.fn.andSelf=n.fn.addBack,"function"==typeof define&&define.amd&&define("jquery",[],function(){return n});var nc=a.jQuery,oc=a.$;return n.noConflict=function(b){return a.$===n&&(a.$=oc),b&&a.jQuery===n&&(a.jQuery=nc),n},b||(a.jQuery=a.$=n),n}); \ No newline at end of file diff --git a/lib/web/mage/adminhtml/grid.js b/lib/web/mage/adminhtml/grid.js index 5f7a709f04fea..1c9319f95a647 100644 --- a/lib/web/mage/adminhtml/grid.js +++ b/lib/web/mage/adminhtml/grid.js @@ -368,6 +368,51 @@ define([ return url; }, + /** + * Builds the form with fields containing the and submits + * + * @param {String} url + * @param {String} varName + * @param {String} varValue + * @private + */ + _buildFormAndSubmit: function (url, varName, varValue) { + var re = new RegExp('\/(' + varName + '\/.*?\/)'), + parts = url.split(new RegExp('\\?')), + form = jQuery('<form/>'), + inputProps = [ + { + name: varName, + value: varValue + }, + { + name: 'form_key', + value: window.FORM_KEY + } + ], + input; + + url = parts[0].replace(re, '/'); + + if (parts.size() > 1) { + url += '?' + parts[1]; + } + + form.attr('action', url); + form.attr('method', 'POST'); + + inputProps.forEach(function (item) { + input = jQuery('<input/>'); + input.attr('name', item.name); + input.attr('type', 'hidden'); + input.val(item.value); + form.append(input); + }); + jQuery('[data-container="body"]').append(form); + form.submit(); + form.remove(); + }, + /** * @param {*} varName * @param {*} varValue @@ -389,13 +434,14 @@ define([ exportUrl = $(this.containerId + '_export').value; if (this.massaction && this.massaction.checkedString) { - exportUrl = this._addVarToUrl( + this._buildFormAndSubmit( exportUrl, this.massaction.formFieldNameInternal, this.massaction.checkedString ); + } else { + location.href = exportUrl; } - location.href = exportUrl; } }, diff --git a/lib/web/mage/menu.js b/lib/web/mage/menu.js index 70501e5cac0c6..b1875947cd54e 100644 --- a/lib/web/mage/menu.js +++ b/lib/web/mage/menu.js @@ -462,7 +462,7 @@ define([ this.categoryLink = $('<a>') .attr('href', categoryUrl) - .text($.mage.__('All ') + category); + .text($.mage.__('All %1').replace('%1', category)); this.categoryParent = $('<li>') .addClass('ui-menu-item all-category') diff --git a/lib/web/mage/trim-input.js b/lib/web/mage/trim-input.js index 678192dcf61ac..d077dad8dc302 100644 --- a/lib/web/mage/trim-input.js +++ b/lib/web/mage/trim-input.js @@ -41,9 +41,20 @@ define([ * @private */ _trimInput: function () { - var input = this._getInputValue().trim(); + // Safari caret position workaround: storing carter position + var caretStart, caretEnd, input; + + caretStart = this.options.cache.input.get(0).selectionStart; + caretEnd = this.options.cache.input.get(0).selectionEnd; + + input = this._getInputValue().trim(); this.options.cache.input.val(input); + + // Safari caret position workaround: setting caret position to previously stored values + if (caretStart !== null && caretEnd !== null) { + this.options.cache.input.get(0).setSelectionRange(caretStart, caretEnd); + } }, /** diff --git a/lib/web/mage/validation.js b/lib/web/mage/validation.js index 73c5ef4d4f02f..6c53eaa6a6146 100644 --- a/lib/web/mage/validation.js +++ b/lib/web/mage/validation.js @@ -990,8 +990,8 @@ minValidRange = $.mage.parseNumber(validRange[1]); maxValidRange = $.mage.parseNumber(validRange[2]); result = result && - (isNaN(minValidRange) || minValue >= minValidRange) && - (isNaN(maxValidRange) || maxValue <= maxValidRange); + (isNaN(minValidRange) || minValue >= minValidRange) && + (isNaN(maxValidRange) || maxValue <= maxValidRange); } } @@ -1115,8 +1115,8 @@ options = p.find('input'); return options.map(function (el) { - return $(el).val(); - }).length > 0; + return $(el).val(); + }).length > 0; }, $.mage.__('Please select one of the options above.') ], @@ -1932,15 +1932,15 @@ * @param {jQuery.Event} event * @param {Object} validation */ - listenFormValidateHandler: function (event, validation) { + listenFormValidateHandler: function (event, validation) { var firstActive = $(validation.errorList[0].element || []), lastActive = $(validation.findLastActive() || validation.errorList.length && validation.errorList[0].element || []), - parent, windowHeight, successList; + windowHeight = $(window).height(), + parent, successList; if (lastActive.is(':hidden')) { parent = lastActive.parent(); - windowHeight = $(window).height(); $('html, body').animate({ scrollTop: parent.offset().top - windowHeight / 2 }); @@ -1958,8 +1958,8 @@ } if (firstActive.length) { - $('html, body').stop().animate({ - scrollTop: firstActive.offset().top + $('body').stop().animate({ + scrollTop: firstActive.offset().top - windowHeight / 2 }); firstActive.focus(); } diff --git a/pub/health_check.php b/pub/health_check.php index fc6d73daa2079..4bdda68851bde 100644 --- a/pub/health_check.php +++ b/pub/health_check.php @@ -51,7 +51,10 @@ $cacheConfigs = $deploymentConfig->get(ConfigOptionsListConstants::KEY_CACHE_FRONTEND); if ($cacheConfigs) { foreach ($cacheConfigs as $cacheConfig) { - if (!isset($cacheConfig[ConfigOptionsListConstants::CONFIG_PATH_BACKEND]) || + // allow config if only available "id_prefix" + if (count($cacheConfig) === 1 && isset($cacheConfig['id_prefix'])) { + continue; + } elseif (!isset($cacheConfig[ConfigOptionsListConstants::CONFIG_PATH_BACKEND]) || !isset($cacheConfig[ConfigOptionsListConstants::CONFIG_PATH_BACKEND_OPTIONS])) { http_response_code(500); $logger->error("Cache configuration is invalid"); diff --git a/setup/performance-toolkit/README.md b/setup/performance-toolkit/README.md index 7bf7a1fbd4721..ede66e040753f 100644 --- a/setup/performance-toolkit/README.md +++ b/setup/performance-toolkit/README.md @@ -54,49 +54,139 @@ There are two JMeter scenarios located in `setup/performance-toolkit` folder: `b The following parameters can be passed to the `benchmark.jmx` scenario: +Main parameters: + | Parameter Name | Default Value | Description | | --------------------------------------------- | ------------------- | ---------------------------------------------------------------------------------------- | | host | localhost | URL component 'host' of application being tested (URL or IP). | | base_path | / | Base path for tested site. | +| files_folder | ./files/ | Path to various files that are used in scenario (`setup/performance-toolkit/files`). | +| request_protocol | http | Hypertext Transfer Protocol (http or https). | +| graphql_port_number | | Port number for GraphQL. | +| admin_password | 123123q | Admin backend password. | | admin_path | admin | Admin backend path. | | admin_user | admin | Admin backend user. | -| admin_password | 123123q | Admin backend password. | -| customer_password | 123123q | Storefront customer password. | -| customers_page_size | 50 | Page size for customers grid in Magento Admin. | -| files_folder | ./files/ | Path to various files that are used in scenario (`setup/performance-toolkit/files`). | +| cache_hits_percentage | 100 | Cache hits percentage. | +| seedForRandom | 1 | System option for setting random number method | | loops | 1 | Number of loops to run. | -| frontendPoolUsers | 1 | Total number of Frontend threads. | -| adminPoolUsers | 1 | Total number of Admin threads. | -| browseCatalogByGuestPercentage | 30 | Percentage of threads in Frontend Pool that emulate catalog browsing activities. | -| browseCatalogByCustomerPercentage | 0 | Percentage of threads in Frontend Pool that emulate catalog browsing activities. | -| siteSearchPercentage | 30 | Percentage of threads in Frontend Pool that emulate catalog search activities. | -| searchQuickPercentage | 60 | Percentage of threads in Frontend Pool that emulate catalog search activities. | -| searchQuickFilterPercentage | 30 | Percentage of threads in Frontend Pool that emulate catalog search activities. | -| searchAdvancedPercentage | 10 | Percentage of threads in Frontend Pool that emulate catalog search activities. | -| checkoutByGuestPercentage | 4 | Percentage of threads in Frontend Pool that emulate checkout by guest. | -| checkoutByCustomerPercentage | 4 | Percentage of threads in Frontend Pool that emulate checkout by customer. | -| addToCartByGuestPercentage | 28 | Percentage of threads in Frontend Pool that emulate abandoned cart activities. | -| addToWishlistPercentage | 2 | Percentage of threads in Frontend Pool that emulate adding products to Wishlist. | -| compareProductsPercentage | 2 | Percentage of threads in Frontend Pool that emulate products comparison. | -| productCompareDelay | 0 | Delay (s) between iterations of product comparison. | -| promotionRulesPercentage | 10 | Percentage of threads in Admin Pool that emulate creation of promotion rules. | -| adminPromotionsManagementDelay | 0 | Delay (s) between creation of promotion rules. | -| adminCategoryManagementPercentage | 10 | Percentage of threads in Merchandising Pool that emulate category management activities. | -| adminProductEditingPercentage | 35 | Percentage of threads in Merchandising Pool that emulate product editing. | -| adminProductCreationPercentage | 25 | Percentage of threads in Merchandising Pool that emulate creation of products. | -| adminPromotionRulesPercentage | 15 | Percentage of threads in Admin Pool that emulate admin rules creating activities. | -| adminCategoryManagementDelay | 0 | Delay (s) between iterations of category management activities. | -| apiProcessOrders | 5 | Number of orders for process in Admin API - Process Orders. | -| adminEditOrderPercentage | 15 | Percentage of threads in Admin Pool that emulate order edit. | -| csrPoolUsers | 0 | Users of Customer Support Request (CSR) Pool. | -| othersPoolUsers | 0 | Users of Others Pool. | -| browseCustomerGridPercentage | 10 | Percentage of threads in CSR Pool that emulate customers browsing activities. | -| adminCreateOrderPercentage | 70 | Percentage of threads in CSR Pool that emulate creation of orders. | -| adminReturnsManagementPercentage | 20 | Percentage of threads in CSR Pool that emulate creation/processing of returns. | -| adminCreateProcessReturnsDelay | 0 | Delay (s) between creation of returns. | -| wishlistDelay | 0 | Delay (s) between adding products to Wishlist. | -| categories_count | 100 | Total number of categories that are be used in scenario. | -| simple_products_count | 30 | Total number of simple products that are be used in scenario. | +| frontendPoolUsers | 0 | Total number of Frontend threads. | +| adminPoolUsers | 0 | Total number of Admin threads. | +| csrPoolUsers | 0 | Total number of CSR threads. | +| apiPoolUsers | 0 | Total number of API threads. | +| oneThreadScenariosPoolUsers | 0 | Total number of One Thread Scenarios threads. | +| graphQLPoolUsers | 0 | Total number of GraphQL threads. | +| combinedBenchmarkPoolUsers | 0 | Total number of Combined Benchmark threads. | + +Parameters for Frontend pool: + +| Parameter Name | Default Value | Description | +| --------------------------------------------- | ------------------- | ----------------------------------------------------------------------------------------- | +| browseCatalogByCustomerPercentage | 0 | Percentage of threads in Frontend Pool that emulate customer catalog browsing activities. | +| browseCatalogByGuestPercentage | 0 | Percentage of threads in Frontend Pool that emulate guest catalog browsing activities. | +| siteSearchPercentage | 0 | Percentage of threads in Frontend Pool that emulate catalog search activities. | +| addToCartByGuestPercentage | 0 | Percentage of threads in Frontend Pool that emulate abandoned cart activities by guest. | +| addToWishlistPercentage | 0 | Percentage of threads in Frontend Pool that emulate adding products to Wishlist. | +| compareProductsPercentage | 0 | Percentage of threads in Frontend Pool that emulate products comparison. | +| checkoutByGuestPercentage | 0 | Percentage of threads in Frontend Pool that emulate checkout by guest. | +| checkoutByCustomerPercentage | 0 | Percentage of threads in Frontend Pool that emulate checkout by customer. | +| reviewByCustomerPercentage | 0 | Percentage of threads in Frontend Pool that emulate reviewing products. | +| addToCartByCustomerPercentage | 0 | Percentage of threads in Frontend Pool that emulate abandoned cart activities by customer.| +| accountManagementPercentage | 0 | Percentage of threads in Frontend Pool that emulate account management. | + +Parameters for Admin pool: + +| Parameter Name | Default Value | Description | +| --------------------------------------------- | ------------------- | ----------------------------------------------------------------------------------------- | +| adminCMSManagementPercentage | 0 | Percentage of threads in Admin Pool that emulate CMS management activities. | +| browseProductGridPercentage | 0 | Percentage of threads in Admin Pool that emulate products grid browsing activities. | +| browseOrderGridPercentage | 0 | Percentage of threads in Admin Pool that emulate orders grid browsing activities. | +| adminProductCreationPercentage | 0 | Percentage of threads in Admin Pool that emulate product creation activities. | +| adminProductEditingPercentage | 0 | Percentage of threads in Admin Pool that emulate product editing activities. | + +Parameters for CSR pool: + +| Parameter Name | Default Value | Description | +| --------------------------------------------- | ------------------- | ----------------------------------------------------------------------------------------- | +| adminReturnsManagementPercentage | 0 | Percentage of threads in CSR Pool that emulate admin returns management activities. | +| browseCustomerGridPercentage | 0 | Percentage of threads in CSR Pool that emulate customers grid browsing activities. | +| adminCreateOrderPercentage | 0 | Percentage of threads in CSR Pool that emulate creating orders activities. | + +Parameters for API pool: + +| Parameter Name | Default Value | Description | +| --------------------------------------------- | ------------------- | ----------------------------------------------------------------------------------------- | +| apiBasePercentage | 0 | Percentage of threads in API Pool that emulate API requests activities. | + +Parameters for One Thread Scenarios pool: + +| Parameter Name | Default Value | Description | +| --------------------------------------------- | ------------------- | ----------------------------------------------------------------------------- | +| productGridMassActionPercentage | 0 | Percentage of threads that emulate product mass action activities. | +| importProductsPercentage | 0 | Percentage of threads that emulate products import activities. | +| importCustomersPercentage | 0 | Percentage of threads that emulate customers import activities. | +| exportProductsPercentage | 0 | Percentage of threads that emulate products export activities. | +| exportCustomersPercentage | 0 | Percentage of threads that emulate customers export activities. | +| apiSinglePercentage | 0 | Percentage of threads that emulate API nonparallel requests activities. | +| adminCategoryManagementPercentage | 0 | Percentage of threads that emulate category management activities. | +| adminPromotionRulesPercentage | 0 | Percentage of threads that emulate promotion rules activities. | +| adminCustomerManagementPercentage | 0 | Percentage of threads that emulate customer management activities. | +| adminEditOrderPercentage | 0 | Percentage of threads that emulate edit order activities. | +| catalogGraphQLPercentage | 0 | Percentage of threads that emulate nonparallel catalogGraphQL activities. | + +Parameters for GraphQL pool: + +| Parameter Name | Default Value | Description | +| ----------------------------------------------------------------- | ------------------- | ----------------------------------------------------------------------------------------- | +| graphqlGetListOfProductsByCategoryIdPercentage | 0 | Percentage of threads in GraphQL Pool that emulate GraphQL requests activities. | +| graphqlGetSimpleProductDetailsByProductUrlKeyPercentage | 0 | Percentage of threads in GraphQL Pool that emulate GraphQL requests activities. | +| graphqlGetSimpleProductDetailsByNamePercentage | 0 | Percentage of threads in GraphQL Pool that emulate GraphQL requests activities. | +| graphqlGetConfigurableProductDetailsByProductUrlKeyPercentage | 0 | Percentage of threads in GraphQL Pool that emulate GraphQL requests activities. | +| graphqlGetConfigurableProductDetailsByNamePercentage | 0 | Percentage of threads in GraphQL Pool that emulate GraphQL requests activities. | +| graphqlGetProductSearchByTextAndCategoryIdPercentage | 0 | Percentage of threads in GraphQL Pool that emulate GraphQL requests activities. | +| graphqlGetCategoryListByCategoryIdPercentage | 0 | Percentage of threads in GraphQL Pool that emulate GraphQL requests activities. | +| graphqlUrlInfoByUrlKeyPercentage | 0 | Percentage of threads in GraphQL Pool that emulate GraphQL requests activities. | +| graphqlGetCmsPageByIdPercentage | 0 | Percentage of threads in GraphQL Pool that emulate GraphQL requests activities. | +| graphqlGetNavigationMenuByCategoryIdPercentage | 0 | Percentage of threads in GraphQL Pool that emulate GraphQL requests activities. | +| graphqlCreateEmptyCartPercentage | 0 | Percentage of threads in GraphQL Pool that emulate GraphQL requests activities. | +| graphqlGetEmptyCartPercentage | 0 | Percentage of threads in GraphQL Pool that emulate GraphQL requests activities. | +| graphqlSetShippingAddressOnCartPercentage | 0 | Percentage of threads in GraphQL Pool that emulate GraphQL requests activities. | +| graphqlSetBillingAddressOnCartPercentage | 0 | Percentage of threads in GraphQL Pool that emulate GraphQL requests activities. | +| graphqlAddSimpleProductToCartPercentage | 0 | Percentage of threads in GraphQL Pool that emulate GraphQL requests activities. | +| graphqlAddConfigurableProductToCartPercentage | 0 | Percentage of threads in GraphQL Pool that emulate GraphQL requests activities. | +| graphqlUpdateSimpleProductQtyInCartPercentage | 0 | Percentage of threads in GraphQL Pool that emulate GraphQL requests activities. | +| graphqlUpdateConfigurableProductQtyInCartPercentage | 0 | Percentage of threads in GraphQL Pool that emulate GraphQL requests activities. | +| graphqlRemoveSimpleProductFromCartPercentage | 0 | Percentage of threads in GraphQL Pool that emulate GraphQL requests activities. | +| graphqlRemoveConfigurableProductFromCartPercentage | 0 | Percentage of threads in GraphQL Pool that emulate GraphQL requests activities. | +| graphqlApplyCouponToCartPercentage | 0 | Percentage of threads in GraphQL Pool that emulate GraphQL requests activities. | +| graphqlRemoveCouponFromCartPercentage | 0 | Percentage of threads in GraphQL Pool that emulate GraphQL requests activities. | +| graphqlCatalogBrowsingByGuestPercentage | 0 | Percentage of threads in GraphQL Pool that emulate GraphQL requests activities. | +| graphqlCheckoutByGuestPercentage | 0 | Percentage of threads in GraphQL Pool that emulate GraphQL requests activities. | + +Parameters for Combined Benchmark pool: + +| Parameter Name | Default Value | Description | +| ----------------------------------------------------------------- | ------------------- | ---------------------------------------------------------------------------------------------------- | +| cBrowseCatalogByGuestPercentage | 29 | Percentage of threads in Combined Benchmark Pool that emulate customer catalog browsing activities. | +| cSiteSearchPercentage | 29 | Percentage of threads in Combined Benchmark Pool that emulate catalog search activities. | +| cAddToCartByGuestPercentage | 26 | Percentage of threads in Combined Benchmark Pool that emulate abandoned cart activities. | +| cAddToWishlistPercentage | 1.5 | Percentage of threads in Combined Benchmark Pool that emulate adding products to Wishlist. | +| cCompareProductsPercentage | 1.5 | Percentage of threads in Combined Benchmark Pool that emulate products comparison. | +| cCheckoutByGuestPercentage | 3.5 | Percentage of threads in Combined Benchmark Pool that emulate checkout by guest. | +| cCheckoutByCustomerPercentage | 3.5 | Percentage of threads in Combined Benchmark Pool that emulate checkout by customer. | +| cAccountManagementPercentage | 1 | Percentage of threads in Combined Benchmark Pool that emulate account management. | +| cAdminCMSManagementPercentage | 0.35 | Percentage of threads in Combined Benchmark Pool that emulate CMS management activities. | +| cAdminBrowseProductGridPercentage | 0.2 | Percentage of threads in Combined Benchmark Pool that emulate products grid browsing activities. | +| cAdminBrowseOrderGridPercentage | 0.2 | Percentage of threads in Combined Benchmark Pool that emulate orders grid browsing activities. | +| cAdminProductCreationPercentage | 0.5 | Percentage of threads in Combined Benchmark Pool that emulate product creation activities. | +| cAdminProductEditingPercentage | 0.65 | Percentage of threads in Combined Benchmark Pool that emulate product editing activities. | +| cAdminReturnsManagementPercentage | 0.75 | Percentage of threads in Combined Benchmark Pool that emulate admin returns management activities. | +| cAdminBrowseCustomerGridPercentage | 0.1 | Percentage of threads in Combined Benchmark Pool that emulate customers grid browsing activities. | +| cAdminCreateOrderPercentage | 0.5 | Percentage of threads in Combined Benchmark Pool that emulate creating orders activities. | +| cAdminCategoryManagementPercentage | 0.15 | Percentage of threads in Combined Benchmark Pool that emulate admin category management activities. | +| cAdminPromotionRulesPercentage | 0.2 | Percentage of threads in Combined Benchmark Pool that emulate admin promotion rules activities. | +| cAdminCustomerManagementPercentage | 0.4 | Percentage of threads in Combined Benchmark Pool that emulate admin customers management activities. | +| cAdminEditOrderPercentage | 1 | Percentage of threads in Combined Benchmark Pool that emulate admin edit order activities. | + Parameters must be passed to the command line with the `J` prefix: @@ -113,10 +203,17 @@ There are some options that you should pass to JMeter in the console mode: To get more details about available JMeter options, read [Non-GUI Mode](http://jmeter.apache.org/usermanual/get-started.html#non_gui). -For example, you can run the B2C scenario via console with 90 threads for the Frontend Pool and 10 threads for the Admin Pool: +For example, you can run the B2C scenario via console with: +90 threads for the Frontend Pool where: +- 80% - guest catalog browsing activities. +- 20% - checkout by customer. + +10 threads for the Admin Pool where: +- 10% - admin products grid browsing activities. +- 90% - admin product creation activities. cd {JMeter path}/bin/ - jmeter -n -t {path to performance toolkit}/benchmark.jmx -j ./jmeter.log -l ./jmeter-results.jtl -Jhost=magento2.dev -Jbase_path=/ -Jadmin_path=admin -JfrontendPoolUsers=90 -JadminPoolUsers=10 + jmeter -n -t {path to performance toolkit}/benchmark.jmx -j ./jmeter.log -l ./jmeter-results.jtl -Jhost=magento2.dev -Jbase_path=/ -Jadmin_path=admin -JfrontendPoolUsers=90 -JadminPoolUsers=10 -JbrowseCatalogByGuestPercentage=80 -JcheckoutByCustomerPercentage=20 -JbrowseProductGridPercentage=10 -JadminProductCreationPercentage=90 As a result, you will get `jmeter.log` and `jmeter-results.jtl`. The`jmeter.log` contains information about the test run and can be helpful in determining the cause of an error. The JTL file is a text file containing the results of a test run. It can be opened in the GUI mode to perform analysis of the results (see the *Output* section below). @@ -190,42 +287,21 @@ For more details, read [Summary Report](http://jmeter.apache.org/usermanual/comp ### Scenarios -`benchmark.jmx` scenario has the following pools and default percentage breakdown for each scenario: +`benchmark.jmx` scenario has the following pools: **Frontend Pool** (frontendPoolUsers) -| Scenario Name | % of Pool | -| ------------------------- | --------- | -| Catalog Browsing By Guest | 30 | -| Site Search | 30 | -| Add To Cart By Guest | 28 | -| Add to Wishlist | 2 | -| Compare Products | 2 | -| Checkout By Guest | 4 | -| Checkout By Customer | 4 | - -Site Search thread group contains 3 variations: -- Quick Search (60%) -- Quick Search With Filtration (30%) -- Advanced Search (10%) - **Admin Pool** (adminPoolUsers) -| Scenario Name |% of Pool | -| ----------------------------| --------- | -| Admin Promotion Rules | 15 | -| Admin Edit Order | 15 | -| Admin Category Management | 10 | -| Admin Edit Product | 35 | -| Admin Create Product | 25 | - **CSR Pool** (csrPoolUsers) -| Scenario Name | % of Pool | -| -------------------------- | --------- | -| Browse Customer Grid | 10 | -| Admin Create Order | 70 | -| Admin Returns Management | 20 | +**API Pool** (apiPoolUsers) + +**One Thread Scenarios Pool** (oneThreadScenariosPoolUsers) + +**GraphQL Pool** (graphQLPoolUsers) + +**Combined Benchmark Pool** (combinedBenchmarkPoolUsers) **Legacy Threads** diff --git a/setup/performance-toolkit/benchmark.jmx b/setup/performance-toolkit/benchmark.jmx index a8a7e419373b8..c2e64b9c244b8 100644 --- a/setup/performance-toolkit/benchmark.jmx +++ b/setup/performance-toolkit/benchmark.jmx @@ -54,6 +54,11 @@ <stringProp name="Argument.value">${__P(admin_user,admin)}</stringProp> <stringProp name="Argument.metadata">=</stringProp> </elementProp> + <elementProp name="cache_hits_percentage" elementType="Argument"> + <stringProp name="Argument.name">cache_hits_percentage</stringProp> + <stringProp name="Argument.value">${__P(cache_hits_percentage,100)}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + </elementProp> <elementProp name="seedForRandom" elementType="Argument"> <stringProp name="Argument.name">seedForRandom</stringProp> <stringProp name="Argument.value">${__P(seedForRandom,1)}</stringProp> @@ -64,6 +69,41 @@ <stringProp name="Argument.value">${__P(loops,1)}</stringProp> <stringProp name="Argument.metadata">=</stringProp> </elementProp> + <elementProp name="frontendPoolUsers" elementType="Argument"> + <stringProp name="Argument.name">frontendPoolUsers</stringProp> + <stringProp name="Argument.value">${__P(frontendPoolUsers,0)}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + </elementProp> + <elementProp name="adminPoolUsers" elementType="Argument"> + <stringProp name="Argument.name">adminPoolUsers</stringProp> + <stringProp name="Argument.value">${__P(adminPoolUsers,0)}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + </elementProp> + <elementProp name="csrPoolUsers" elementType="Argument"> + <stringProp name="Argument.name">csrPoolUsers</stringProp> + <stringProp name="Argument.value">${__P(csrPoolUsers,0)}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + </elementProp> + <elementProp name="apiPoolUsers" elementType="Argument"> + <stringProp name="Argument.name">apiPoolUsers</stringProp> + <stringProp name="Argument.value">${__P(apiPoolUsers,0)}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + </elementProp> + <elementProp name="oneThreadScenariosPoolUsers" elementType="Argument"> + <stringProp name="Argument.name">oneThreadScenariosPoolUsers</stringProp> + <stringProp name="Argument.value">${__P(oneThreadScenariosPoolUsers,0)}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + </elementProp> + <elementProp name="graphQLPoolUsers" elementType="Argument"> + <stringProp name="Argument.name">graphQLPoolUsers</stringProp> + <stringProp name="Argument.value">${__P(graphQLPoolUsers,0)}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + </elementProp> + <elementProp name="combinedBenchmarkPoolUsers" elementType="Argument"> + <stringProp name="Argument.name">combinedBenchmarkPoolUsers</stringProp> + <stringProp name="Argument.value">${__P(combinedBenchmarkPoolUsers,0)}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + </elementProp> <elementProp name="accountManagementPercentage" elementType="Argument"> <stringProp name="Argument.name">accountManagementPercentage</stringProp> <stringProp name="Argument.value">${__P(accountManagementPercentage,0)}</stringProp> @@ -76,12 +116,12 @@ </elementProp> <elementProp name="addToCartByGuestPercentage" elementType="Argument"> <stringProp name="Argument.name">addToCartByGuestPercentage</stringProp> - <stringProp name="Argument.value">${__P(addToCartByGuestPercentage,28)}</stringProp> + <stringProp name="Argument.value">${__P(addToCartByGuestPercentage,0)}</stringProp> <stringProp name="Argument.metadata">=</stringProp> </elementProp> <elementProp name="addToWishlistPercentage" elementType="Argument"> <stringProp name="Argument.name">addToWishlistPercentage</stringProp> - <stringProp name="Argument.value">${__P(addToWishlistPercentage,2)}</stringProp> + <stringProp name="Argument.value">${__P(addToWishlistPercentage,0)}</stringProp> <stringProp name="Argument.metadata">=</stringProp> </elementProp> <elementProp name="adminCMSManagementDelay" elementType="Argument"> @@ -106,12 +146,12 @@ </elementProp> <elementProp name="adminCategoryManagementPercentage" elementType="Argument"> <stringProp name="Argument.name">adminCategoryManagementPercentage</stringProp> - <stringProp name="Argument.value">${__P(adminCategoryManagementPercentage,10)}</stringProp> + <stringProp name="Argument.value">${__P(adminCategoryManagementPercentage,0)}</stringProp> <stringProp name="Argument.metadata">=</stringProp> </elementProp> <elementProp name="adminCreateOrderPercentage" elementType="Argument"> <stringProp name="Argument.name">adminCreateOrderPercentage</stringProp> - <stringProp name="Argument.value">${__P(adminCreateOrderPercentage,70)}</stringProp> + <stringProp name="Argument.value">${__P(adminCreateOrderPercentage,0)}</stringProp> <stringProp name="Argument.metadata">=</stringProp> </elementProp> <elementProp name="adminCreateProcessReturns" elementType="Argument"> @@ -134,19 +174,9 @@ <stringProp name="Argument.value">${__P(adminCustomerManagementPercentage,0)}</stringProp> <stringProp name="Argument.metadata">=</stringProp> </elementProp> - <elementProp name="adminEditOrder" elementType="Argument"> - <stringProp name="Argument.name">adminEditOrder</stringProp> - <stringProp name="Argument.value">${__P(adminEditOrder,0)}</stringProp> - <stringProp name="Argument.metadata">=</stringProp> - </elementProp> <elementProp name="adminEditOrderPercentage" elementType="Argument"> <stringProp name="Argument.name">adminEditOrderPercentage</stringProp> - <stringProp name="Argument.value">${__P(adminEditOrderPercentage,15)}</stringProp> - <stringProp name="Argument.metadata">=</stringProp> - </elementProp> - <elementProp name="adminEditProduct" elementType="Argument"> - <stringProp name="Argument.name">adminEditProduct</stringProp> - <stringProp name="Argument.value">${__P(adminEditProduct,0)}</stringProp> + <stringProp name="Argument.value">${__P(adminEditOrderPercentage,0)}</stringProp> <stringProp name="Argument.metadata">=</stringProp> </elementProp> <elementProp name="adminImportCustomerBehavior" elementType="Argument"> @@ -169,24 +199,19 @@ <stringProp name="Argument.value">${__P(adminImportProductFilePath,import_products/product_import_append_1.csv)}</stringProp> <stringProp name="Argument.metadata">=</stringProp> </elementProp> - <elementProp name="adminPoolUsers" elementType="Argument"> - <stringProp name="Argument.name">adminPoolUsers</stringProp> - <stringProp name="Argument.value">${__P(adminPoolUsers,1)}</stringProp> - <stringProp name="Argument.metadata">=</stringProp> - </elementProp> <elementProp name="adminProductCreationPercentage" elementType="Argument"> <stringProp name="Argument.name">adminProductCreationPercentage</stringProp> - <stringProp name="Argument.value">${__P(adminProductCreationPercentage,25)}</stringProp> + <stringProp name="Argument.value">${__P(adminProductCreationPercentage,0)}</stringProp> <stringProp name="Argument.metadata">=</stringProp> </elementProp> <elementProp name="adminProductEditingPercentage" elementType="Argument"> <stringProp name="Argument.name">adminProductEditingPercentage</stringProp> - <stringProp name="Argument.value">${__P(adminProductEditingPercentage,35)}</stringProp> + <stringProp name="Argument.value">${__P(adminProductEditingPercentage,0)}</stringProp> <stringProp name="Argument.metadata">=</stringProp> </elementProp> <elementProp name="adminPromotionRulesPercentage" elementType="Argument"> <stringProp name="Argument.name">adminPromotionRulesPercentage</stringProp> - <stringProp name="Argument.value">${__P(adminPromotionRulesPercentage,15)}</stringProp> + <stringProp name="Argument.value">${__P(adminPromotionRulesPercentage,0)}</stringProp> <stringProp name="Argument.metadata">=</stringProp> </elementProp> <elementProp name="adminPromotionsManagement" elementType="Argument"> @@ -201,7 +226,7 @@ </elementProp> <elementProp name="adminReturnsManagementPercentage" elementType="Argument"> <stringProp name="Argument.name">adminReturnsManagementPercentage</stringProp> - <stringProp name="Argument.value">${__P(adminReturnsManagementPercentage,20)}</stringProp> + <stringProp name="Argument.value">${__P(adminReturnsManagementPercentage,0)}</stringProp> <stringProp name="Argument.metadata">=</stringProp> </elementProp> <elementProp name="admin_browse_customer_filter_text" elementType="Argument"> @@ -234,16 +259,16 @@ <stringProp name="Argument.value">${__P(apiOrderInvoiceShipmentSync,0)}</stringProp> <stringProp name="Argument.metadata">=</stringProp> </elementProp> - <elementProp name="apiPoolUsers" elementType="Argument"> - <stringProp name="Argument.name">apiPoolUsers</stringProp> - <stringProp name="Argument.value">${__P(apiPoolUsers,0)}</stringProp> - <stringProp name="Argument.metadata">=</stringProp> - </elementProp> <elementProp name="apiProcessOrders" elementType="Argument"> <stringProp name="Argument.name">apiProcessOrders</stringProp> <stringProp name="Argument.value">${__P(apiProcessOrders,1)}</stringProp> <stringProp name="Argument.metadata">=</stringProp> </elementProp> + <elementProp name="apiSinglePercentage" elementType="Argument"> + <stringProp name="Argument.name">apiSinglePercentage</stringProp> + <stringProp name="Argument.value">${__P(apiSinglePercentage,0)}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + </elementProp> <elementProp name="bamboo_build_number" elementType="Argument"> <stringProp name="Argument.name">bamboo_build_number</stringProp> <stringProp name="Argument.value">${__P(bamboo_build_number,)}</stringProp> @@ -256,12 +281,12 @@ </elementProp> <elementProp name="browseCatalogByGuestPercentage" elementType="Argument"> <stringProp name="Argument.name">browseCatalogByGuestPercentage</stringProp> - <stringProp name="Argument.value">${__P(browseCatalogByGuestPercentage,30)}</stringProp> + <stringProp name="Argument.value">${__P(browseCatalogByGuestPercentage,0)}</stringProp> <stringProp name="Argument.metadata">=</stringProp> </elementProp> <elementProp name="browseCustomerGridPercentage" elementType="Argument"> <stringProp name="Argument.name">browseCustomerGridPercentage</stringProp> - <stringProp name="Argument.value">${__P(browseCustomerGridPercentage,10)}</stringProp> + <stringProp name="Argument.value">${__P(browseCustomerGridPercentage,0)}</stringProp> <stringProp name="Argument.metadata">=</stringProp> </elementProp> <elementProp name="browseOrderGridPercentage" elementType="Argument"> @@ -274,9 +299,104 @@ <stringProp name="Argument.value">${__P(browseProductGridPercentage,0)}</stringProp> <stringProp name="Argument.metadata">=</stringProp> </elementProp> - <elementProp name="cache_hits_percentage" elementType="Argument"> - <stringProp name="Argument.name">cache_hits_percentage</stringProp> - <stringProp name="Argument.value">${__P(cache_hits_percentage,100)}</stringProp> + <elementProp name="cAccountManagementPercentage" elementType="Argument"> + <stringProp name="Argument.name">cAccountManagementPercentage</stringProp> + <stringProp name="Argument.value">${__P(cAccountManagementPercentage,1)}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + </elementProp> + <elementProp name="cAddToCartByGuestPercentage" elementType="Argument"> + <stringProp name="Argument.name">cAddToCartByGuestPercentage</stringProp> + <stringProp name="Argument.value">${__P(cAddToCartByGuestPercentage,26)}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + </elementProp> + <elementProp name="cAddToWishlistPercentage" elementType="Argument"> + <stringProp name="Argument.name">cAddToWishlistPercentage</stringProp> + <stringProp name="Argument.value">${__P(cAddToWishlistPercentage,1.5)}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + </elementProp> + <elementProp name="cAdminBrowseCustomerGridPercentage" elementType="Argument"> + <stringProp name="Argument.name">cAdminBrowseCustomerGridPercentage</stringProp> + <stringProp name="Argument.value">${__P(cAdminBrowseCustomerGridPercentage,0.1)}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + </elementProp> + <elementProp name="cAdminBrowseOrderGridPercentage" elementType="Argument"> + <stringProp name="Argument.name">cAdminBrowseOrderGridPercentage</stringProp> + <stringProp name="Argument.value">${__P(cAdminBrowseOrderGridPercentage,0.2)}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + </elementProp> + <elementProp name="cAdminBrowseProductGridPercentage" elementType="Argument"> + <stringProp name="Argument.name">cAdminBrowseProductGridPercentage</stringProp> + <stringProp name="Argument.value">${__P(cAdminBrowseProductGridPercentage,0.2)}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + </elementProp> + <elementProp name="cAdminCMSManagementPercentage" elementType="Argument"> + <stringProp name="Argument.name">cAdminCMSManagementPercentage</stringProp> + <stringProp name="Argument.value">${__P(cAdminCMSManagementPercentage,0.35)}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + </elementProp> + <elementProp name="cAdminCategoryManagementPercentage" elementType="Argument"> + <stringProp name="Argument.name">cAdminCategoryManagementPercentage</stringProp> + <stringProp name="Argument.value">${__P(cAdminCategoryManagementPercentage,0.15)}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + </elementProp> + <elementProp name="cAdminCreateOrderPercentage" elementType="Argument"> + <stringProp name="Argument.name">cAdminCreateOrderPercentage</stringProp> + <stringProp name="Argument.value">${__P(cAdminCreateOrderPercentage,0.5)}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + </elementProp> + <elementProp name="cAdminCustomerManagementPercentage" elementType="Argument"> + <stringProp name="Argument.name">cAdminCustomerManagementPercentage</stringProp> + <stringProp name="Argument.value">${__P(cAdminCustomerManagementPercentage,0.4)}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + </elementProp> + <elementProp name="cAdminEditOrderPercentage" elementType="Argument"> + <stringProp name="Argument.name">cAdminEditOrderPercentage</stringProp> + <stringProp name="Argument.value">${__P(cAdminEditOrderPercentage,1)}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + </elementProp> + <elementProp name="cAdminProductCreationPercentage" elementType="Argument"> + <stringProp name="Argument.name">cAdminProductCreationPercentage</stringProp> + <stringProp name="Argument.value">${__P(cAdminProductCreationPercentage,0.5)}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + </elementProp> + <elementProp name="cAdminProductEditingPercentage" elementType="Argument"> + <stringProp name="Argument.name">cAdminProductEditingPercentage</stringProp> + <stringProp name="Argument.value">${__P(cAdminProductEditingPercentage,0.65)}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + </elementProp> + <elementProp name="cAdminPromotionRulesPercentage" elementType="Argument"> + <stringProp name="Argument.name">cAdminPromotionRulesPercentage</stringProp> + <stringProp name="Argument.value">${__P(cAdminPromotionRulesPercentage,0.2)}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + </elementProp> + <elementProp name="cAdminReturnsManagementPercentage" elementType="Argument"> + <stringProp name="Argument.name">cAdminReturnsManagementPercentage</stringProp> + <stringProp name="Argument.value">${__P(cAdminReturnsManagementPercentage,0.75)}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + </elementProp> + <elementProp name="cBrowseCatalogByGuestPercentage" elementType="Argument"> + <stringProp name="Argument.name">cBrowseCatalogByGuestPercentage</stringProp> + <stringProp name="Argument.value">${__P(cBrowseCatalogByGuestPercentage,29)}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + </elementProp> + <elementProp name="cCheckoutByCustomerPercentage" elementType="Argument"> + <stringProp name="Argument.name">cCheckoutByCustomerPercentage</stringProp> + <stringProp name="Argument.value">${__P(cCheckoutByCustomerPercentage,3.5)}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + </elementProp> + <elementProp name="cCheckoutByGuestPercentage" elementType="Argument"> + <stringProp name="Argument.name">cCheckoutByGuestPercentage</stringProp> + <stringProp name="Argument.value">${__P(cCheckoutByGuestPercentage,3.5)}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + </elementProp> + <elementProp name="cCompareProductsPercentage" elementType="Argument"> + <stringProp name="Argument.name">cCompareProductsPercentage</stringProp> + <stringProp name="Argument.value">${__P(cCompareProductsPercentage,1.5)}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + </elementProp> + <elementProp name="cSiteSearchPercentage" elementType="Argument"> + <stringProp name="Argument.name">cSiteSearchPercentage</stringProp> + <stringProp name="Argument.value">${__P(cSiteSearchPercentage,29)}</stringProp> <stringProp name="Argument.metadata">=</stringProp> </elementProp> <elementProp name="catalogGraphQLPercentage" elementType="Argument"> @@ -291,17 +411,17 @@ </elementProp> <elementProp name="checkoutByCustomerPercentage" elementType="Argument"> <stringProp name="Argument.name">checkoutByCustomerPercentage</stringProp> - <stringProp name="Argument.value">${__P(checkoutByCustomerPercentage,4)}</stringProp> + <stringProp name="Argument.value">${__P(checkoutByCustomerPercentage,0)}</stringProp> <stringProp name="Argument.metadata">=</stringProp> </elementProp> <elementProp name="checkoutByGuestPercentage" elementType="Argument"> <stringProp name="Argument.name">checkoutByGuestPercentage</stringProp> - <stringProp name="Argument.value">${__P(checkoutByGuestPercentage,4)}</stringProp> + <stringProp name="Argument.value">${__P(checkoutByGuestPercentage,0)}</stringProp> <stringProp name="Argument.metadata">=</stringProp> </elementProp> <elementProp name="compareProductsPercentage" elementType="Argument"> <stringProp name="Argument.name">compareProductsPercentage</stringProp> - <stringProp name="Argument.value">${__P(compareProductsPercentage,2)}</stringProp> + <stringProp name="Argument.value">${__P(compareProductsPercentage,0)}</stringProp> <stringProp name="Argument.metadata">=</stringProp> </elementProp> <elementProp name="configurable_products_count" elementType="Argument"> @@ -309,11 +429,6 @@ <stringProp name="Argument.value">${__P(configurable_products_count,15)}</stringProp> <stringProp name="Argument.metadata">=</stringProp> </elementProp> - <elementProp name="csrPoolUsers" elementType="Argument"> - <stringProp name="Argument.name">csrPoolUsers</stringProp> - <stringProp name="Argument.value">${__P(csrPoolUsers,0)}</stringProp> - <stringProp name="Argument.metadata">=</stringProp> - </elementProp> <elementProp name="customer_checkout_percent" elementType="Argument"> <stringProp name="Argument.name">customer_checkout_percent</stringProp> <stringProp name="Argument.value">${__P(customer_checkout_percent,100)}</stringProp> @@ -334,11 +449,6 @@ <stringProp name="Argument.value">${__P(dashboard_enabled,0)}</stringProp> <stringProp name="Argument.metadata">=</stringProp> </elementProp> - <elementProp name="deadLocksPoolUsers" elementType="Argument"> - <stringProp name="Argument.name">deadLocksPoolUsers</stringProp> - <stringProp name="Argument.value">${__P(deadLocksPoolUsers,1)}</stringProp> - <stringProp name="Argument.metadata">=</stringProp> - </elementProp> <elementProp name="exportCustomersPercentage" elementType="Argument"> <stringProp name="Argument.name">exportCustomersPercentage</stringProp> <stringProp name="Argument.value">${__P(exportCustomersPercentage,0)}</stringProp> @@ -354,16 +464,6 @@ <stringProp name="Argument.value">${__P(form_key,uVEW54r8kKday8Wk)}</stringProp> <stringProp name="Argument.metadata">=</stringProp> </elementProp> - <elementProp name="frontendPoolUsers" elementType="Argument"> - <stringProp name="Argument.name">frontendPoolUsers</stringProp> - <stringProp name="Argument.value">${__P(frontendPoolUsers,1)}</stringProp> - <stringProp name="Argument.metadata">=</stringProp> - </elementProp> - <elementProp name="graphQLPoolUsers" elementType="Argument"> - <stringProp name="Argument.name">graphQLPoolUsers</stringProp> - <stringProp name="Argument.value">${__P(graphQLPoolUsers,1)}</stringProp> - <stringProp name="Argument.metadata">=</stringProp> - </elementProp> <elementProp name="graphqlAddConfigurableProductToCartPercentage" elementType="Argument"> <stringProp name="Argument.name">graphqlAddConfigurableProductToCartPercentage</stringProp> <stringProp name="Argument.value">${__P(graphqlAddConfigurableProductToCartPercentage,0)}</stringProp> @@ -399,11 +499,6 @@ <stringProp name="Argument.value">${__P(graphqlGetCategoryListByCategoryIdPercentage,0)}</stringProp> <stringProp name="Argument.metadata">=</stringProp> </elementProp> - <elementProp name="graphqlGetCmsBlockByIdentifierPercentage" elementType="Argument"> - <stringProp name="Argument.name">graphqlGetCmsBlockByIdentifierPercentage</stringProp> - <stringProp name="Argument.value">${__P(graphqlGetCmsBlockByIdentifierPercentage,0)}</stringProp> - <stringProp name="Argument.metadata">=</stringProp> - </elementProp> <elementProp name="graphqlGetCmsPageByIdPercentage" elementType="Argument"> <stringProp name="Argument.name">graphqlGetCmsPageByIdPercentage</stringProp> <stringProp name="Argument.value">${__P(graphqlGetCmsPageByIdPercentage,0)}</stringProp> @@ -586,7 +681,7 @@ </elementProp> <elementProp name="siteSearchPercentage" elementType="Argument"> <stringProp name="Argument.name">siteSearchPercentage</stringProp> - <stringProp name="Argument.value">${__P(siteSearchPercentage,30)}</stringProp> + <stringProp name="Argument.value">${__P(siteSearchPercentage,0)}</stringProp> <stringProp name="Argument.metadata">=</stringProp> </elementProp> <elementProp name="sprint_identifier" elementType="Argument"> @@ -25634,7 +25729,7 @@ catch (java.lang.Exception e) { </hashTree> - <ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="API" enabled="true"> + <ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="API Pool" enabled="true"> <stringProp name="ThreadGroup.on_sample_error">continue</stringProp> <elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Loop Controller" enabled="true"> <boolProp name="LoopController.continue_forever">false</boolProp> @@ -27110,13 +27205,13 @@ if (testLabel </hashTree> - <ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="One Thread Scenarios (Vulnerable to deadlocks)" enabled="true"> + <ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="One Thread Scenarios Pool" enabled="true"> <stringProp name="ThreadGroup.on_sample_error">continue</stringProp> <elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Loop Controller" enabled="true"> <boolProp name="LoopController.continue_forever">false</boolProp> <stringProp name="LoopController.loops">${loops}</stringProp> </elementProp> - <stringProp name="ThreadGroup.num_threads">${deadLocksPoolUsers}</stringProp> + <stringProp name="ThreadGroup.num_threads">${oneThreadScenariosPoolUsers}</stringProp> <stringProp name="ThreadGroup.ramp_time">${ramp_period}</stringProp> <longProp name="ThreadGroup.start_time">1505803944000</longProp> <longProp name="ThreadGroup.end_time">1505803944000</longProp> @@ -28161,7 +28256,7 @@ vars.put("adminImportFilePath", filepath); </stringProp> <intProp name="ThroughputController.style">1</intProp> <boolProp name="ThroughputController.perThread">false</boolProp> <intProp name="ThroughputController.maxThroughput">1</intProp> - <stringProp name="ThroughputController.percentThroughput">${apiBasePercentage}</stringProp> + <stringProp name="ThroughputController.percentThroughput">${apiSinglePercentage}</stringProp> <stringProp name="TestPlan.comments">mpaf/tool/fragments/_system/scenario_controller_tmpl.jmx</stringProp></ThroughputController> <hashTree> <JSR223PreProcessor guiclass="TestBeanGUI" testclass="JSR223PreProcessor" testname="Set Test Label" enabled="true"> @@ -44333,6 +44428,25754 @@ vars.put("coupon_code", coupons[number].code); </hashTree> </hashTree> + </hashTree> + + + <ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="Combined Benchmark Pool" enabled="true"> + <stringProp name="ThreadGroup.on_sample_error">continue</stringProp> + <elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Loop Controller" enabled="true"> + <boolProp name="LoopController.continue_forever">false</boolProp> + <stringProp name="LoopController.loops">${loops}</stringProp> + </elementProp> + <stringProp name="ThreadGroup.num_threads">${combinedBenchmarkPoolUsers}</stringProp> + <stringProp name="ThreadGroup.ramp_time">${ramp_period}</stringProp> + <longProp name="ThreadGroup.start_time">1505803944000</longProp> + <longProp name="ThreadGroup.end_time">1505803944000</longProp> + <boolProp name="ThreadGroup.scheduler">false</boolProp> + <stringProp name="ThreadGroup.duration"/> + <stringProp name="ThreadGroup.delay"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/_system/thread_group.jmx</stringProp></ThreadGroup> + <hashTree> + <JSR223PreProcessor guiclass="TestBeanGUI" testclass="JSR223PreProcessor" testname="Cache hit miss" enabled="true"> + <stringProp name="scriptLanguage">javascript</stringProp> + <stringProp name="parameters"/> + <stringProp name="filename"/> + <stringProp name="cacheKey"/> + <stringProp name="script">var cacheHitPercent = vars.get("cache_hits_percentage"); + +if ( + cacheHitPercent < 100 && + sampler.getClass().getName() == 'org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy' +) { + doCache(); +} + +function doCache(){ + var random = Math.random() * 100; + if (cacheHitPercent < random) { + sampler.setPath(sampler.getPath() + "?cacheModifier=" + Math.random().toString(36).substring(2, 13)); + } +} +</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/cache_hit_miss.jmx</stringProp></JSR223PreProcessor> + <hashTree/> + + <ThroughputController guiclass="ThroughputControllerGui" testclass="ThroughputController" testname="[C] Catalog Browsing By Guest" enabled="true"> + <intProp name="ThroughputController.style">1</intProp> + <boolProp name="ThroughputController.perThread">false</boolProp> + <intProp name="ThroughputController.maxThroughput">1</intProp> + <stringProp name="ThroughputController.percentThroughput">${cBrowseCatalogByGuestPercentage}</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/_system/scenario_controller_tmpl.jmx</stringProp></ThroughputController> + <hashTree> + <JSR223PreProcessor guiclass="TestBeanGUI" testclass="JSR223PreProcessor" testname="Set Test Label" enabled="true"> + <stringProp name="script"> +var testLabel = "${testLabel}" ? " (${testLabel})" : ""; +if (testLabel + && sampler.getClass().getName() == 'org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy' +) { + if (sampler.getName().indexOf(testLabel) == -1) { + sampler.setName(sampler.getName() + testLabel); + } +} else if (sampler.getName().indexOf("SetUp - ") == -1) { + sampler.setName("SetUp - " + sampler.getName()); +} + </stringProp> + <stringProp name="scriptLanguage">javascript</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/_system/setup_label.jmx</stringProp></JSR223PreProcessor> + <hashTree/> + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Set Label" enabled="true"> + <stringProp name="BeanShellSampler.query"> + vars.put("testLabel", "[C] Catalog Browsing By Guest"); + </stringProp> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + </BeanShellSampler> + <hashTree/> + + <CookieManager guiclass="CookiePanel" testclass="CookieManager" testname="HTTP Cookie Manager" enabled="true"> + <collectionProp name="CookieManager.cookies"> + <elementProp name="product_list_limit" elementType="Cookie" testname="product_list_limit"> + <stringProp name="Cookie.value">30</stringProp> + <stringProp name="Cookie.domain">${host}</stringProp> + <stringProp name="Cookie.path">/</stringProp> + <boolProp name="Cookie.secure">false</boolProp> + <longProp name="Cookie.expires">0</longProp> + <boolProp name="Cookie.path_specified">true</boolProp> + <boolProp name="Cookie.domain_specified">true</boolProp> + </elementProp> + <elementProp name="product_list_limit" elementType="Cookie" testname="form_key"> + <stringProp name="Cookie.value">${form_key}</stringProp> + <stringProp name="Cookie.domain">${host}</stringProp> + <stringProp name="Cookie.path">${base_path}</stringProp> + <boolProp name="Cookie.secure">false</boolProp> + <longProp name="Cookie.expires">0</longProp> + <boolProp name="Cookie.path_specified">true</boolProp> + <boolProp name="Cookie.domain_specified">true</boolProp> + </elementProp> + </collectionProp> + <boolProp name="CookieManager.clearEachIteration">true</boolProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/http_cookie_manager.jmx</stringProp></CookieManager> + <hashTree/> + + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Init Random Generator" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/init_random_generator_setup.jmx</stringProp> + <stringProp name="BeanShellSampler.query"> +import java.util.Random; + +Random random = new Random(); +if (${seedForRandom} > 0) { + random.setSeed(${seedForRandom} + ${__threadNum}); +} + +vars.putObject("randomIntGenerator", random); + </stringProp> + <stringProp name="BeanShellSampler.filename"/> + <stringProp name="BeanShellSampler.parameters"/> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + </BeanShellSampler> + <hashTree/> + + <JSR223Sampler guiclass="TestBeanGUI" testclass="JSR223Sampler" testname="SetUp - Prepare Category Data" enabled="true"> + <stringProp name="scriptLanguage">javascript</stringProp> + <stringProp name="parameters"/> + <stringProp name="filename"/> + <stringProp name="cacheKey"/> + <stringProp name="script">random = vars.getObject("randomIntGenerator"); + +var categories = props.get("categories"); +number = random.nextInt(categories.length); + +vars.put("category_url_key", categories[number].url_key); +vars.put("category_name", categories[number].name); +vars.put("category_id", categories[number].id); +vars.putObject("category", categories[number]); + </stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/extract_category_setup.jmx</stringProp></JSR223Sampler> + <hashTree/> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Open Home Page" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/open_home_page.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="571386695"><title>Home page</title></stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Open Category" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${category_url_key}${url_suffix}</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/open_category.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="1210004667"><span class="base" data-ui-id="page-title">${category_name}</span></stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">6</intProp> + </ResponseAssertion> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Regular Expression Extractor: Extract category id" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">category_id</stringProp> + <stringProp name="RegexExtractor.regex"><li class="item category([^'"]+)">\s*<strong>${category_name}</strong>\s*</li></stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + <stringProp name="Scope.variable">simple_product_1_url_key</stringProp> + </RegexExtractor> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion: Assert category id" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="1191417111">^[0-9]+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">category_id</stringProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + + <LoopController guiclass="LoopControlPanel" testclass="LoopController" testname="View Simple Products" enabled="true"> + <boolProp name="LoopController.continue_forever">true</boolProp> + <stringProp name="LoopController.loops">2</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/loop_controller.jmx</stringProp></LoopController> + <hashTree> + <CounterConfig guiclass="CounterConfigGui" testclass="CounterConfig" testname="Counter" enabled="true"> + <stringProp name="CounterConfig.start">1</stringProp> + <stringProp name="CounterConfig.end"/> + <stringProp name="CounterConfig.incr">1</stringProp> + <stringProp name="CounterConfig.name">_counter</stringProp> + <stringProp name="CounterConfig.format"/> + <boolProp name="CounterConfig.per_user">true</boolProp> + <boolProp name="CounterConfig.reset_on_tg_iteration">true</boolProp> + </CounterConfig> + <hashTree/> + + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Prepare Simple Product Data" enabled="true"> + <stringProp name="BeanShellSampler.query"> +import java.util.Random; + +Random random = vars.getObject("randomIntGenerator"); +number = random.nextInt(props.get("simple_products_list").size()); +product = props.get("simple_products_list").get(number); + +vars.put("product_url_key", product.get("url_key")); +vars.put("product_id", product.get("id")); +vars.put("product_name", product.get("title")); +vars.put("product_uenc", product.get("uenc")); +vars.put("product_sku", product.get("sku")); + </stringProp> + <stringProp name="BeanShellSampler.filename"/> + <stringProp name="BeanShellSampler.parameters"/> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/product_browsing_and_adding_items_to_the_cart/simple_products_setup.jmx</stringProp></BeanShellSampler> + <hashTree/> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Simple Product ${_counter} View" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${product_url_key}${url_suffix}</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/product_browsing_and_adding_items_to_the_cart/product_view.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-1787050162"><span>In stock</span></stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + </hashTree> + + <LoopController guiclass="LoopControlPanel" testclass="LoopController" testname="View Configurable Products" enabled="true"> + <boolProp name="LoopController.continue_forever">true</boolProp> + <stringProp name="LoopController.loops">1</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/loop_controller.jmx</stringProp></LoopController> + <hashTree> + <CounterConfig guiclass="CounterConfigGui" testclass="CounterConfig" testname="Counter" enabled="true"> + <stringProp name="CounterConfig.start">1</stringProp> + <stringProp name="CounterConfig.end"/> + <stringProp name="CounterConfig.incr">1</stringProp> + <stringProp name="CounterConfig.name">_counter</stringProp> + <stringProp name="CounterConfig.format"/> + <boolProp name="CounterConfig.per_user">true</boolProp> + <boolProp name="CounterConfig.reset_on_tg_iteration">true</boolProp> + </CounterConfig> + <hashTree/> + + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Prepare Configurable Product Data" enabled="true"> + <stringProp name="BeanShellSampler.query"> +import java.util.Random; + +Random random = vars.getObject("randomIntGenerator"); +number = random.nextInt(props.get("configurable_products_list").size()); +product = props.get("configurable_products_list").get(number); + +vars.put("product_url_key", product.get("url_key")); +vars.put("product_id", product.get("id")); +vars.put("product_name", product.get("title")); +vars.put("product_uenc", product.get("uenc")); +vars.put("product_sku", product.get("sku")); + </stringProp> + <stringProp name="BeanShellSampler.filename"/> + <stringProp name="BeanShellSampler.parameters"/> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/product_browsing_and_adding_items_to_the_cart/configurable_products_setup.jmx</stringProp></BeanShellSampler> + <hashTree/> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Configurable Product ${_counter} View" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${product_url_key}${url_suffix}</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/product_browsing_and_adding_items_to_the_cart/product_view.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-1787050162"><span>In stock</span></stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + </hashTree> + </hashTree> + + + <ThroughputController guiclass="ThroughputControllerGui" testclass="ThroughputController" testname="[C] Site Search" enabled="true"> + <intProp name="ThroughputController.style">1</intProp> + <boolProp name="ThroughputController.perThread">false</boolProp> + <intProp name="ThroughputController.maxThroughput">1</intProp> + <stringProp name="ThroughputController.percentThroughput">${cSiteSearchPercentage}</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/_system/scenario_controller_tmpl.jmx</stringProp></ThroughputController> + <hashTree> + <JSR223PreProcessor guiclass="TestBeanGUI" testclass="JSR223PreProcessor" testname="Set Test Label" enabled="true"> + <stringProp name="script"> +var testLabel = "${testLabel}" ? " (${testLabel})" : ""; +if (testLabel + && sampler.getClass().getName() == 'org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy' +) { + if (sampler.getName().indexOf(testLabel) == -1) { + sampler.setName(sampler.getName() + testLabel); + } +} else if (sampler.getName().indexOf("SetUp - ") == -1) { + sampler.setName("SetUp - " + sampler.getName()); +} + </stringProp> + <stringProp name="scriptLanguage">javascript</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/_system/setup_label.jmx</stringProp></JSR223PreProcessor> + <hashTree/> + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Set Label" enabled="true"> + <stringProp name="BeanShellSampler.query"> + vars.put("testLabel", "[C] Site Search"); + </stringProp> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + </BeanShellSampler> + <hashTree/> + + <CSVDataSet guiclass="TestBeanGUI" testclass="CSVDataSet" testname="Search Terms" enabled="true"> + <stringProp name="filename">${files_folder}search_terms.csv</stringProp> + <stringProp name="fileEncoding">UTF-8</stringProp> + <stringProp name="variableNames"/> + <stringProp name="delimiter">,</stringProp> + <boolProp name="quotedData">false</boolProp> + <boolProp name="recycle">true</boolProp> + <boolProp name="stopThread">false</boolProp> + <stringProp name="shareMode">shareMode.thread</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/search/search_terms.jmx</stringProp></CSVDataSet> + <hashTree/> + + <JSR223PreProcessor guiclass="TestBeanGUI" testclass="JSR223PreProcessor" testname="Cache hit miss" enabled="true"> + <stringProp name="scriptLanguage">javascript</stringProp> + <stringProp name="parameters"/> + <stringProp name="filename"/> + <stringProp name="cacheKey"/> + <stringProp name="script">var cacheHitPercent = vars.get("cache_hits_percentage"); + +if ( + cacheHitPercent < 100 && + sampler.getClass().getName() == 'org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy' +) { + doCache(); +} + +function doCache(){ + var random = Math.random() * 100; + if (cacheHitPercent < random) { + sampler.setPath(sampler.getPath() + "?cacheModifier=" + Math.random().toString(36).substring(2, 13)); + } +} +</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/cache_hit_miss.jmx</stringProp></JSR223PreProcessor> + <hashTree/> + + <ThroughputController guiclass="ThroughputControllerGui" testclass="ThroughputController" testname="Quick Search" enabled="true"> + <intProp name="ThroughputController.style">1</intProp> + <boolProp name="ThroughputController.perThread">false</boolProp> + <intProp name="ThroughputController.maxThroughput">1</intProp> + <stringProp name="ThroughputController.percentThroughput">${searchQuickPercentage}</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/_system/scenario_controller_tmpl.jmx</stringProp></ThroughputController> + <hashTree> + <JSR223PreProcessor guiclass="TestBeanGUI" testclass="JSR223PreProcessor" testname="Set Test Label" enabled="true"> + <stringProp name="script"> +var testLabel = "${testLabel}" ? " (${testLabel})" : ""; +if (testLabel + && sampler.getClass().getName() == 'org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy' +) { + if (sampler.getName().indexOf(testLabel) == -1) { + sampler.setName(sampler.getName() + testLabel); + } +} else if (sampler.getName().indexOf("SetUp - ") == -1) { + sampler.setName("SetUp - " + sampler.getName()); +} + </stringProp> + <stringProp name="scriptLanguage">javascript</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/_system/setup_label.jmx</stringProp></JSR223PreProcessor> + <hashTree/> + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Set Label" enabled="true"> + <stringProp name="BeanShellSampler.query"> + vars.put("testLabel", "Quick Search"); + </stringProp> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + </BeanShellSampler> + <hashTree/> + + <CookieManager guiclass="CookiePanel" testclass="CookieManager" testname="HTTP Cookie Manager" enabled="true"> + <collectionProp name="CookieManager.cookies"> + <elementProp name="product_list_limit" elementType="Cookie" testname="product_list_limit"> + <stringProp name="Cookie.value">30</stringProp> + <stringProp name="Cookie.domain">${host}</stringProp> + <stringProp name="Cookie.path">/</stringProp> + <boolProp name="Cookie.secure">false</boolProp> + <longProp name="Cookie.expires">0</longProp> + <boolProp name="Cookie.path_specified">true</boolProp> + <boolProp name="Cookie.domain_specified">true</boolProp> + </elementProp> + <elementProp name="product_list_limit" elementType="Cookie" testname="form_key"> + <stringProp name="Cookie.value">${form_key}</stringProp> + <stringProp name="Cookie.domain">${host}</stringProp> + <stringProp name="Cookie.path">${base_path}</stringProp> + <boolProp name="Cookie.secure">false</boolProp> + <longProp name="Cookie.expires">0</longProp> + <boolProp name="Cookie.path_specified">true</boolProp> + <boolProp name="Cookie.domain_specified">true</boolProp> + </elementProp> + </collectionProp> + <boolProp name="CookieManager.clearEachIteration">true</boolProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/http_cookie_manager.jmx</stringProp></CookieManager> + <hashTree/> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Open Home Page" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/open_home_page.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="571386695"><title>Home page</title></stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Search" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="q" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">q</stringProp> + <stringProp name="Argument.value">${searchTerm}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}catalogsearch/result/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/search/search_quick.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion: Assert search result" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="56511661">Search results for: </stringProp> + <stringProp name="1533671447"><span class="toolbar-number">\d<\/span> Items|Items <span class="toolbar-number">1</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Regular Expression Extractor: Extract product url keys" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">product_url_keys</stringProp> + <stringProp name="RegexExtractor.regex"><a class="product-item-link"(?s).+?href="(?:http|https)://${host}${base_path}(index.php/)?([^'"]+)${url_suffix}">(?s).</stringProp> + <stringProp name="RegexExtractor.template">$2$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">-1</stringProp> + </RegexExtractor> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Regular Expression Extractor: isPageCacheable" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">isPageCacheable</stringProp> + <stringProp name="RegexExtractor.regex">catalogsearch/searchTermsLog/save</stringProp> + <stringProp name="RegexExtractor.template">$0$</stringProp> + <stringProp name="RegexExtractor.default">0</stringProp> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + </hashTree> + + <IfController guiclass="IfControllerPanel" testclass="IfController" testname="If Controller" enabled="true"> + <stringProp name="IfController.condition">"${isPageCacheable}" != "0"</stringProp> + <boolProp name="IfController.evaluateAll">false</boolProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/search/if_page_cacheable_controller.jmx</stringProp></IfController> + <hashTree> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Search Terms Log" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="q" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">q</stringProp> + <stringProp name="Argument.value">${searchTerm}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}catalogsearch/searchTermsLog/save/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/search/search_terms_log_save.jmx</stringProp> + </HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion: Assert search result" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-547797305">"success":true</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + </hashTree> + + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Set Arguments" enabled="true"> + <stringProp name="BeanShellSampler.query"> +foundProducts = Integer.parseInt(vars.get("product_url_keys_matchNr")); + +if (foundProducts > 3) { + foundProducts = 3; +} + +vars.put("foundProducts", String.valueOf(foundProducts)); + </stringProp> + <stringProp name="BeanShellSampler.filename"/> + <stringProp name="BeanShellSampler.parameters"/> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/search/set_found_items.jmx</stringProp></BeanShellSampler> + <hashTree/> + + <LoopController guiclass="LoopControlPanel" testclass="LoopController" testname="View Searched Products" enabled="true"> + <boolProp name="LoopController.continue_forever">true</boolProp> + <stringProp name="LoopController.loops">${foundProducts}</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/loop_controller.jmx</stringProp></LoopController> + <hashTree> + <CounterConfig guiclass="CounterConfigGui" testclass="CounterConfig" testname="Counter" enabled="true"> + <stringProp name="CounterConfig.start">1</stringProp> + <stringProp name="CounterConfig.end"/> + <stringProp name="CounterConfig.incr">1</stringProp> + <stringProp name="CounterConfig.name">_counter</stringProp> + <stringProp name="CounterConfig.format"/> + <boolProp name="CounterConfig.per_user">true</boolProp> + <boolProp name="CounterConfig.reset_on_tg_iteration">true</boolProp> + </CounterConfig> + <hashTree/> + + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Prepare Product Data" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/search/searched_products_setup.jmx</stringProp> + <stringProp name="BeanShellSampler.query"> +number = vars.get("_counter"); +product = vars.get("product_url_keys_"+number); + +vars.put("product_url_key", product); + </stringProp> + <stringProp name="BeanShellSampler.filename"/> + <stringProp name="BeanShellSampler.parameters"/> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + </BeanShellSampler> + <hashTree/> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Product ${_counter} View" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${product_url_key}${url_suffix}</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/product_browsing_and_adding_items_to_the_cart/product_view.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-1787050162"><span>In stock</span></stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + </hashTree> + </hashTree> + + + <ThroughputController guiclass="ThroughputControllerGui" testclass="ThroughputController" testname="Quick Search With Filtration" enabled="true"> + <intProp name="ThroughputController.style">1</intProp> + <boolProp name="ThroughputController.perThread">false</boolProp> + <intProp name="ThroughputController.maxThroughput">1</intProp> + <stringProp name="ThroughputController.percentThroughput">${searchQuickFilterPercentage}</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/_system/scenario_controller_tmpl.jmx</stringProp></ThroughputController> + <hashTree> + <JSR223PreProcessor guiclass="TestBeanGUI" testclass="JSR223PreProcessor" testname="Set Test Label" enabled="true"> + <stringProp name="script"> +var testLabel = "${testLabel}" ? " (${testLabel})" : ""; +if (testLabel + && sampler.getClass().getName() == 'org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy' +) { + if (sampler.getName().indexOf(testLabel) == -1) { + sampler.setName(sampler.getName() + testLabel); + } +} else if (sampler.getName().indexOf("SetUp - ") == -1) { + sampler.setName("SetUp - " + sampler.getName()); +} + </stringProp> + <stringProp name="scriptLanguage">javascript</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/_system/setup_label.jmx</stringProp></JSR223PreProcessor> + <hashTree/> + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Set Label" enabled="true"> + <stringProp name="BeanShellSampler.query"> + vars.put("testLabel", "Quick Search With Filtration"); + </stringProp> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + </BeanShellSampler> + <hashTree/> + + <CookieManager guiclass="CookiePanel" testclass="CookieManager" testname="HTTP Cookie Manager" enabled="true"> + <collectionProp name="CookieManager.cookies"> + <elementProp name="product_list_limit" elementType="Cookie" testname="product_list_limit"> + <stringProp name="Cookie.value">30</stringProp> + <stringProp name="Cookie.domain">${host}</stringProp> + <stringProp name="Cookie.path">/</stringProp> + <boolProp name="Cookie.secure">false</boolProp> + <longProp name="Cookie.expires">0</longProp> + <boolProp name="Cookie.path_specified">true</boolProp> + <boolProp name="Cookie.domain_specified">true</boolProp> + </elementProp> + <elementProp name="product_list_limit" elementType="Cookie" testname="form_key"> + <stringProp name="Cookie.value">${form_key}</stringProp> + <stringProp name="Cookie.domain">${host}</stringProp> + <stringProp name="Cookie.path">${base_path}</stringProp> + <boolProp name="Cookie.secure">false</boolProp> + <longProp name="Cookie.expires">0</longProp> + <boolProp name="Cookie.path_specified">true</boolProp> + <boolProp name="Cookie.domain_specified">true</boolProp> + </elementProp> + </collectionProp> + <boolProp name="CookieManager.clearEachIteration">true</boolProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/http_cookie_manager.jmx</stringProp></CookieManager> + <hashTree/> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Open Home Page" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/open_home_page.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="571386695"><title>Home page</title></stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Search" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="q" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">q</stringProp> + <stringProp name="Argument.value">${searchTerm}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol"/> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}catalogsearch/result/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/search/search_quick_filter.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion: Assert search result" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="56511661">Search results for: </stringProp> + <stringProp name="1533671447">Items <span class="toolbar-number">1</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + <XPathExtractor guiclass="XPathExtractorGui" testclass="XPathExtractor" testname="Extract number of attribute 1 options" enabled="true"> + <stringProp name="XPathExtractor.default">0</stringProp> + <stringProp name="XPathExtractor.refname">attribute_1_options_count</stringProp> + <stringProp name="XPathExtractor.xpathQuery">count((//div[@class="filter-options-content"])[1]//li[@class="item"])</stringProp> + <boolProp name="XPathExtractor.validate">false</boolProp> + <boolProp name="XPathExtractor.tolerant">true</boolProp> + <boolProp name="XPathExtractor.namespace">false</boolProp> + </XPathExtractor> + <hashTree/> + <XPathExtractor guiclass="XPathExtractorGui" testclass="XPathExtractor" testname="Extract number of attribute 2 options" enabled="true"> + <stringProp name="XPathExtractor.default">0</stringProp> + <stringProp name="XPathExtractor.refname">attribute_2_options_count</stringProp> + <stringProp name="XPathExtractor.xpathQuery">count((//div[@class="filter-options-content"])[2]//li[@class="item"])</stringProp> + <boolProp name="XPathExtractor.validate">false</boolProp> + <boolProp name="XPathExtractor.tolerant">true</boolProp> + <boolProp name="XPathExtractor.namespace">false</boolProp> + </XPathExtractor> + <hashTree/> + <XPathExtractor guiclass="XPathExtractorGui" testclass="XPathExtractor" testname="Extract filter link from layered navigation" enabled="true"> + <stringProp name="XPathExtractor.default"/> + <stringProp name="XPathExtractor.refname">attribute_1_filter_url</stringProp> + <stringProp name="XPathExtractor.xpathQuery">((//div[@class="filter-options-content"])[1]//li[@class="item"]//a)[1]/@href</stringProp> + <boolProp name="XPathExtractor.validate">false</boolProp> + <boolProp name="XPathExtractor.tolerant">true</boolProp> + <boolProp name="XPathExtractor.namespace">false</boolProp> + </XPathExtractor> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract product url keys" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">product_url_keys</stringProp> + <stringProp name="RegexExtractor.regex"><a class="product-item-link"(?s).+?href="http://${host}${base_path}(index.php/)?([^'"]+)${url_suffix}">(?s).</stringProp> + <stringProp name="RegexExtractor.template">$2$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">-1</stringProp> + </RegexExtractor> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Regular Expression Extractor: isPageCacheable" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">isPageCacheable</stringProp> + <stringProp name="RegexExtractor.regex">catalogsearch/searchTermsLog/save</stringProp> + <stringProp name="RegexExtractor.template">$0$</stringProp> + <stringProp name="RegexExtractor.default">0</stringProp> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + </hashTree> + + <IfController guiclass="IfControllerPanel" testclass="IfController" testname="If Controller" enabled="true"> + <stringProp name="IfController.condition">"${isPageCacheable}" != "0"</stringProp> + <boolProp name="IfController.evaluateAll">false</boolProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/search/if_page_cacheable_controller.jmx</stringProp></IfController> + <hashTree> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Search Terms Log" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="q" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">q</stringProp> + <stringProp name="Argument.value">${searchTerm}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}catalogsearch/searchTermsLog/save/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/search/search_terms_log_save.jmx</stringProp> + </HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion: Assert search result" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-547797305">"success":true</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + </hashTree> + + <IfController guiclass="IfControllerPanel" testclass="IfController" testname="If Attribute 1 present in layered navigation" enabled="true"> + <stringProp name="IfController.condition">${attribute_1_options_count} > 0</stringProp> + <boolProp name="IfController.evaluateAll">false</boolProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/search/search_quick_filter-first-attribute.jmx</stringProp></IfController> + <hashTree> + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Set Search Url 2" enabled="true"> + <stringProp name="BeanShellSampler.query">vars.put("search_url", vars.get("attribute_1_filter_url"));</stringProp> + <stringProp name="BeanShellSampler.filename"/> + <stringProp name="BeanShellSampler.parameters"/> + <boolProp name="BeanShellSampler.resetInterpreter">false</boolProp> + </BeanShellSampler> + <hashTree/> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Filter by Attribute 1" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol"/> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${attribute_1_filter_url}</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + </HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion: Assert search result" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="56511661">Search results for: </stringProp> + <stringProp name="1420634794"><span class="toolbar-number">[1-9]+</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + <XPathExtractor guiclass="XPathExtractorGui" testclass="XPathExtractor" testname="Extract number of attribute 2 options" enabled="true"> + <stringProp name="XPathExtractor.default">0</stringProp> + <stringProp name="XPathExtractor.refname">attribute_2_options_count</stringProp> + <stringProp name="XPathExtractor.xpathQuery">count((//div[@class="filter-options-content"])[2]//li[@class="item"])</stringProp> + <boolProp name="XPathExtractor.validate">false</boolProp> + <boolProp name="XPathExtractor.tolerant">true</boolProp> + <boolProp name="XPathExtractor.namespace">false</boolProp> + </XPathExtractor> + <hashTree/> + <XPathExtractor guiclass="XPathExtractorGui" testclass="XPathExtractor" testname="Extract filter link from layered navigation" enabled="true"> + <stringProp name="XPathExtractor.default"/> + <stringProp name="XPathExtractor.refname">attribute_2_filter_url</stringProp> + <stringProp name="XPathExtractor.xpathQuery">((//div[@class="filter-options-content"])[2]//li[@class="item"]//a)[1]/@href</stringProp> + <boolProp name="XPathExtractor.validate">false</boolProp> + <boolProp name="XPathExtractor.tolerant">true</boolProp> + <boolProp name="XPathExtractor.namespace">false</boolProp> + </XPathExtractor> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract product url keys" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">product_url_keys</stringProp> + <stringProp name="RegexExtractor.regex"><a class="product-item-link"(?s).+?href="http://${host}${base_path}(index.php/)?([^'"]+)${url_suffix}">(?s).</stringProp> + <stringProp name="RegexExtractor.template">$2$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">-1</stringProp> + </RegexExtractor> + <hashTree/> + </hashTree> + </hashTree> + + <IfController guiclass="IfControllerPanel" testclass="IfController" testname="If Attribute 2 present in layered navigation" enabled="true"> + <stringProp name="IfController.condition">${attribute_2_options_count} > 0</stringProp> + <boolProp name="IfController.evaluateAll">false</boolProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/search/search_quick_filter-second-attribute.jmx</stringProp></IfController> + <hashTree> + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Set Search Ul 3" enabled="true"> + <stringProp name="BeanShellSampler.query">vars.put("search_url", vars.get("attribute_2_filter_url"));</stringProp> + <stringProp name="BeanShellSampler.filename"/> + <stringProp name="BeanShellSampler.parameters"/> + <boolProp name="BeanShellSampler.resetInterpreter">false</boolProp> + </BeanShellSampler> + <hashTree/> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Filter by Attribute 2" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol"/> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${attribute_2_filter_url}</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + </HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion: Assert search result" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="56511661">Search results for: </stringProp> + <stringProp name="1420634794"><span class="toolbar-number">[1-9]+</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract product url keys" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">product_url_keys</stringProp> + <stringProp name="RegexExtractor.regex"><a class="product-item-link"(?s).+?href="http://${host}${base_path}(index.php/)?([^'"]+)${url_suffix}">(?s).</stringProp> + <stringProp name="RegexExtractor.template">$2$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">-1</stringProp> + </RegexExtractor> + <hashTree/> + </hashTree> + </hashTree> + + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Set Arguments" enabled="true"> + <stringProp name="BeanShellSampler.query"> +foundProducts = Integer.parseInt(vars.get("product_url_keys_matchNr")); + +if (foundProducts > 3) { + foundProducts = 3; +} + +vars.put("foundProducts", String.valueOf(foundProducts)); + </stringProp> + <stringProp name="BeanShellSampler.filename"/> + <stringProp name="BeanShellSampler.parameters"/> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/search/set_found_items.jmx</stringProp></BeanShellSampler> + <hashTree/> + + <LoopController guiclass="LoopControlPanel" testclass="LoopController" testname="View Searched Products" enabled="true"> + <boolProp name="LoopController.continue_forever">true</boolProp> + <stringProp name="LoopController.loops">${foundProducts}</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/loop_controller.jmx</stringProp></LoopController> + <hashTree> + <CounterConfig guiclass="CounterConfigGui" testclass="CounterConfig" testname="Counter" enabled="true"> + <stringProp name="CounterConfig.start">1</stringProp> + <stringProp name="CounterConfig.end"/> + <stringProp name="CounterConfig.incr">1</stringProp> + <stringProp name="CounterConfig.name">_counter</stringProp> + <stringProp name="CounterConfig.format"/> + <boolProp name="CounterConfig.per_user">true</boolProp> + <boolProp name="CounterConfig.reset_on_tg_iteration">true</boolProp> + </CounterConfig> + <hashTree/> + + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Prepare Product Data" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/search/searched_products_setup.jmx</stringProp> + <stringProp name="BeanShellSampler.query"> +number = vars.get("_counter"); +product = vars.get("product_url_keys_"+number); + +vars.put("product_url_key", product); + </stringProp> + <stringProp name="BeanShellSampler.filename"/> + <stringProp name="BeanShellSampler.parameters"/> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + </BeanShellSampler> + <hashTree/> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Product ${_counter} View" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${product_url_key}${url_suffix}</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/product_browsing_and_adding_items_to_the_cart/product_view.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-1787050162"><span>In stock</span></stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + </hashTree> + </hashTree> + + + <ThroughputController guiclass="ThroughputControllerGui" testclass="ThroughputController" testname="Advanced Search" enabled="true"> + <intProp name="ThroughputController.style">1</intProp> + <boolProp name="ThroughputController.perThread">false</boolProp> + <intProp name="ThroughputController.maxThroughput">1</intProp> + <stringProp name="ThroughputController.percentThroughput">${searchAdvancedPercentage}</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/_system/scenario_controller_tmpl.jmx</stringProp></ThroughputController> + <hashTree> + <JSR223PreProcessor guiclass="TestBeanGUI" testclass="JSR223PreProcessor" testname="Set Test Label" enabled="true"> + <stringProp name="script"> +var testLabel = "${testLabel}" ? " (${testLabel})" : ""; +if (testLabel + && sampler.getClass().getName() == 'org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy' +) { + if (sampler.getName().indexOf(testLabel) == -1) { + sampler.setName(sampler.getName() + testLabel); + } +} else if (sampler.getName().indexOf("SetUp - ") == -1) { + sampler.setName("SetUp - " + sampler.getName()); +} + </stringProp> + <stringProp name="scriptLanguage">javascript</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/_system/setup_label.jmx</stringProp></JSR223PreProcessor> + <hashTree/> + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Set Label" enabled="true"> + <stringProp name="BeanShellSampler.query"> + vars.put("testLabel", "Advanced Search"); + </stringProp> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + </BeanShellSampler> + <hashTree/> + + <CookieManager guiclass="CookiePanel" testclass="CookieManager" testname="HTTP Cookie Manager" enabled="true"> + <collectionProp name="CookieManager.cookies"> + <elementProp name="product_list_limit" elementType="Cookie" testname="product_list_limit"> + <stringProp name="Cookie.value">30</stringProp> + <stringProp name="Cookie.domain">${host}</stringProp> + <stringProp name="Cookie.path">/</stringProp> + <boolProp name="Cookie.secure">false</boolProp> + <longProp name="Cookie.expires">0</longProp> + <boolProp name="Cookie.path_specified">true</boolProp> + <boolProp name="Cookie.domain_specified">true</boolProp> + </elementProp> + <elementProp name="product_list_limit" elementType="Cookie" testname="form_key"> + <stringProp name="Cookie.value">${form_key}</stringProp> + <stringProp name="Cookie.domain">${host}</stringProp> + <stringProp name="Cookie.path">${base_path}</stringProp> + <boolProp name="Cookie.secure">false</boolProp> + <longProp name="Cookie.expires">0</longProp> + <boolProp name="Cookie.path_specified">true</boolProp> + <boolProp name="Cookie.domain_specified">true</boolProp> + </elementProp> + </collectionProp> + <boolProp name="CookieManager.clearEachIteration">true</boolProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/http_cookie_manager.jmx</stringProp></CookieManager> + <hashTree/> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Open Home Page" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/open_home_page.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="571386695"><title>Home page</title></stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Open Advanced Search" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol"/> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}catalogsearch/advanced/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/search/open_advanced_search_page.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="921122077"><title>Advanced Search</title></stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + <XPathExtractor guiclass="XPathExtractorGui" testclass="XPathExtractor" testname="Extract attribute name" enabled="true"> + <stringProp name="XPathExtractor.default"/> + <stringProp name="XPathExtractor.refname">attribute_name</stringProp> + <stringProp name="XPathExtractor.xpathQuery">(//select[@class="multiselect"])[last()]/@name</stringProp> + <boolProp name="XPathExtractor.validate">false</boolProp> + <boolProp name="XPathExtractor.tolerant">true</boolProp> + <boolProp name="XPathExtractor.namespace">false</boolProp> + </XPathExtractor> + <hashTree/> + <XPathExtractor guiclass="XPathExtractorGui" testclass="XPathExtractor" testname="Extract attribute options count" enabled="true"> + <stringProp name="XPathExtractor.default">0</stringProp> + <stringProp name="XPathExtractor.refname">attribute_options_count</stringProp> + <stringProp name="XPathExtractor.xpathQuery">count((//select[@class="multiselect"])[last()]/option)</stringProp> + <boolProp name="XPathExtractor.validate">false</boolProp> + <boolProp name="XPathExtractor.tolerant">true</boolProp> + <boolProp name="XPathExtractor.namespace">false</boolProp> + </XPathExtractor> + <hashTree/> + <XPathExtractor guiclass="XPathExtractorGui" testclass="XPathExtractor" testname="Extract attribute value" enabled="true"> + <stringProp name="XPathExtractor.default"/> + <stringProp name="XPathExtractor.refname">attribute_value</stringProp> + <stringProp name="XPathExtractor.xpathQuery">((//select[@class="multiselect"])[last()]/option)[1]/@value</stringProp> + <boolProp name="XPathExtractor.validate">false</boolProp> + <boolProp name="XPathExtractor.tolerant">true</boolProp> + <boolProp name="XPathExtractor.namespace">false</boolProp> + </XPathExtractor> + <hashTree/> + </hashTree> + + + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Search" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="name" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">name</stringProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="sku" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">sku</stringProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="description" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">description</stringProp> + <stringProp name="Argument.value">${searchTerm}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="short_description" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">short_description</stringProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="price%5Bfrom%5D" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">price%5Bfrom%5D</stringProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="price%5Bto%5D" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">price%5Bto%5D</stringProp> + <stringProp name="Argument.value">${priceTo}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <!-- Should be fixed in MAGETWO-80420 --> + <!--<elementProp name="${attribute_name}" elementType="HTTPArgument">--> + <!--<boolProp name="HTTPArgument.always_encode">true</boolProp>--> + <!--<stringProp name="Argument.value">${attribute_value}</stringProp>--> + <!--<stringProp name="Argument.metadata">=</stringProp>--> + <!--<boolProp name="HTTPArgument.use_equals">true</boolProp>--> + <!--<stringProp name="Argument.name">${attribute_name}</stringProp>--> + <!--</elementProp>--> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}catalogsearch/advanced/result/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/search/search_advanced.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion: Assert search result" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-1851531284">items</strong> were found using the following search criteria</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Regular Expression Extractor: Extract product url keys" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">product_url_keys</stringProp> + <stringProp name="RegexExtractor.regex"><a class="product-item-link"(?s).+?href="(?:http|https)://${host}${base_path}(index.php/)?([^'"]+)${url_suffix}">(?s).</stringProp> + <stringProp name="RegexExtractor.template">$2$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">-1</stringProp> + </RegexExtractor> + <hashTree/> + </hashTree> + + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Set Arguments" enabled="true"> + <stringProp name="BeanShellSampler.query"> +foundProducts = Integer.parseInt(vars.get("product_url_keys_matchNr")); + +if (foundProducts > 3) { + foundProducts = 3; +} + +vars.put("foundProducts", String.valueOf(foundProducts)); + </stringProp> + <stringProp name="BeanShellSampler.filename"/> + <stringProp name="BeanShellSampler.parameters"/> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/search/set_found_items.jmx</stringProp></BeanShellSampler> + <hashTree/> + + <LoopController guiclass="LoopControlPanel" testclass="LoopController" testname="View Searched Products" enabled="true"> + <boolProp name="LoopController.continue_forever">true</boolProp> + <stringProp name="LoopController.loops">${foundProducts}</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/loop_controller.jmx</stringProp></LoopController> + <hashTree> + <CounterConfig guiclass="CounterConfigGui" testclass="CounterConfig" testname="Counter" enabled="true"> + <stringProp name="CounterConfig.start">1</stringProp> + <stringProp name="CounterConfig.end"/> + <stringProp name="CounterConfig.incr">1</stringProp> + <stringProp name="CounterConfig.name">_counter</stringProp> + <stringProp name="CounterConfig.format"/> + <boolProp name="CounterConfig.per_user">true</boolProp> + <boolProp name="CounterConfig.reset_on_tg_iteration">true</boolProp> + </CounterConfig> + <hashTree/> + + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Prepare Product Data" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/search/searched_products_setup.jmx</stringProp> + <stringProp name="BeanShellSampler.query"> +number = vars.get("_counter"); +product = vars.get("product_url_keys_"+number); + +vars.put("product_url_key", product); + </stringProp> + <stringProp name="BeanShellSampler.filename"/> + <stringProp name="BeanShellSampler.parameters"/> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + </BeanShellSampler> + <hashTree/> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Product ${_counter} View" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${product_url_key}${url_suffix}</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/product_browsing_and_adding_items_to_the_cart/product_view.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-1787050162"><span>In stock</span></stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + </hashTree> + </hashTree> + + </hashTree> + + + <ThroughputController guiclass="ThroughputControllerGui" testclass="ThroughputController" testname="[C] Add To Cart By Guest" enabled="true"> + <intProp name="ThroughputController.style">1</intProp> + <boolProp name="ThroughputController.perThread">false</boolProp> + <intProp name="ThroughputController.maxThroughput">1</intProp> + <stringProp name="ThroughputController.percentThroughput">${cAddToCartByGuestPercentage}</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/_system/scenario_controller_tmpl.jmx</stringProp></ThroughputController> + <hashTree> + <JSR223PreProcessor guiclass="TestBeanGUI" testclass="JSR223PreProcessor" testname="Set Test Label" enabled="true"> + <stringProp name="script"> +var testLabel = "${testLabel}" ? " (${testLabel})" : ""; +if (testLabel + && sampler.getClass().getName() == 'org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy' +) { + if (sampler.getName().indexOf(testLabel) == -1) { + sampler.setName(sampler.getName() + testLabel); + } +} else if (sampler.getName().indexOf("SetUp - ") == -1) { + sampler.setName("SetUp - " + sampler.getName()); +} + </stringProp> + <stringProp name="scriptLanguage">javascript</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/_system/setup_label.jmx</stringProp></JSR223PreProcessor> + <hashTree/> + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Set Label" enabled="true"> + <stringProp name="BeanShellSampler.query"> + vars.put("testLabel", "[C] Add To Cart By Guest"); + </stringProp> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + </BeanShellSampler> + <hashTree/> + + <CookieManager guiclass="CookiePanel" testclass="CookieManager" testname="HTTP Cookie Manager" enabled="true"> + <collectionProp name="CookieManager.cookies"> + <elementProp name="product_list_limit" elementType="Cookie" testname="product_list_limit"> + <stringProp name="Cookie.value">30</stringProp> + <stringProp name="Cookie.domain">${host}</stringProp> + <stringProp name="Cookie.path">/</stringProp> + <boolProp name="Cookie.secure">false</boolProp> + <longProp name="Cookie.expires">0</longProp> + <boolProp name="Cookie.path_specified">true</boolProp> + <boolProp name="Cookie.domain_specified">true</boolProp> + </elementProp> + <elementProp name="product_list_limit" elementType="Cookie" testname="form_key"> + <stringProp name="Cookie.value">${form_key}</stringProp> + <stringProp name="Cookie.domain">${host}</stringProp> + <stringProp name="Cookie.path">${base_path}</stringProp> + <boolProp name="Cookie.secure">false</boolProp> + <longProp name="Cookie.expires">0</longProp> + <boolProp name="Cookie.path_specified">true</boolProp> + <boolProp name="Cookie.domain_specified">true</boolProp> + </elementProp> + </collectionProp> + <boolProp name="CookieManager.clearEachIteration">true</boolProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/http_cookie_manager.jmx</stringProp></CookieManager> + <hashTree/> + + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Init Random Generator" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/init_random_generator_setup.jmx</stringProp> + <stringProp name="BeanShellSampler.query"> +import java.util.Random; + +Random random = new Random(); +if (${seedForRandom} > 0) { + random.setSeed(${seedForRandom} + ${__threadNum}); +} + +vars.putObject("randomIntGenerator", random); + </stringProp> + <stringProp name="BeanShellSampler.filename"/> + <stringProp name="BeanShellSampler.parameters"/> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + </BeanShellSampler> + <hashTree/> + + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Init Total Products In Cart" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/init_total_products_in_cart_setup.jmx</stringProp> + <stringProp name="BeanShellSampler.query"> +vars.put("totalProductsAdded", "0"); + </stringProp> + <stringProp name="BeanShellSampler.filename"/> + <stringProp name="BeanShellSampler.parameters"/> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + </BeanShellSampler> + <hashTree/> + + <JSR223Sampler guiclass="TestBeanGUI" testclass="JSR223Sampler" testname="SetUp - Prepare Category Data" enabled="true"> + <stringProp name="scriptLanguage">javascript</stringProp> + <stringProp name="parameters"/> + <stringProp name="filename"/> + <stringProp name="cacheKey"/> + <stringProp name="script">random = vars.getObject("randomIntGenerator"); + +var categories = props.get("categories"); +number = random.nextInt(categories.length); + +vars.put("category_url_key", categories[number].url_key); +vars.put("category_name", categories[number].name); +vars.put("category_id", categories[number].id); +vars.putObject("category", categories[number]); + </stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/extract_category_setup.jmx</stringProp></JSR223Sampler> + <hashTree/> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Open Home Page" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/open_home_page.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="571386695"><title>Home page</title></stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Open Category" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${category_url_key}${url_suffix}</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/open_category.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="1210004667"><span class="base" data-ui-id="page-title">${category_name}</span></stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">6</intProp> + </ResponseAssertion> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Regular Expression Extractor: Extract category id" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">category_id</stringProp> + <stringProp name="RegexExtractor.regex"><li class="item category([^'"]+)">\s*<strong>${category_name}</strong>\s*</li></stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + <stringProp name="Scope.variable">simple_product_1_url_key</stringProp> + </RegexExtractor> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion: Assert category id" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="1191417111">^[0-9]+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">category_id</stringProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + + <LoopController guiclass="LoopControlPanel" testclass="LoopController" testname="Add Simple Products to Cart" enabled="true"> + <boolProp name="LoopController.continue_forever">true</boolProp> + <stringProp name="LoopController.loops">2</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/loop_controller.jmx</stringProp></LoopController> + <hashTree> + <CounterConfig guiclass="CounterConfigGui" testclass="CounterConfig" testname="Counter" enabled="true"> + <stringProp name="CounterConfig.start">1</stringProp> + <stringProp name="CounterConfig.end"/> + <stringProp name="CounterConfig.incr">1</stringProp> + <stringProp name="CounterConfig.name">_counter</stringProp> + <stringProp name="CounterConfig.format"/> + <boolProp name="CounterConfig.per_user">true</boolProp> + <boolProp name="CounterConfig.reset_on_tg_iteration">true</boolProp> + </CounterConfig> + <hashTree/> + + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Prepare Simple Product Data" enabled="true"> + <stringProp name="BeanShellSampler.query"> +import java.util.Random; + +Random random = vars.getObject("randomIntGenerator"); +number = random.nextInt(props.get("simple_products_list").size()); +product = props.get("simple_products_list").get(number); + +vars.put("product_url_key", product.get("url_key")); +vars.put("product_id", product.get("id")); +vars.put("product_name", product.get("title")); +vars.put("product_uenc", product.get("uenc")); +vars.put("product_sku", product.get("sku")); + </stringProp> + <stringProp name="BeanShellSampler.filename"/> + <stringProp name="BeanShellSampler.parameters"/> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/product_browsing_and_adding_items_to_the_cart/simple_products_setup.jmx</stringProp></BeanShellSampler> + <hashTree/> + + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Update Products Added Counter" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/loops/update_products_added_counter.jmx</stringProp> + <stringProp name="BeanShellSampler.query"> +productsAdded = Integer.parseInt(vars.get("totalProductsAdded")); +productsAdded = productsAdded + 1; + +vars.put("totalProductsAdded", String.valueOf(productsAdded)); + </stringProp> + <stringProp name="BeanShellSampler.filename"/> + <stringProp name="BeanShellSampler.parameters"/> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + </BeanShellSampler> + <hashTree/> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Simple Product ${_counter} View" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${product_url_key}${url_suffix}</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/product_browsing_and_adding_items_to_the_cart/product_view.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-1787050162"><span>In stock</span></stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Simple Product ${_counter} Add To Cart" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="product" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${product_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product</stringProp> + </elementProp> + <elementProp name="related_product" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">related_product</stringProp> + </elementProp> + <elementProp name="qty" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">qty</stringProp> + </elementProp> + <elementProp name="form_key" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${form_key}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">form_key</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}checkout/cart/add/</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/product_browsing_and_adding_items_to_the_cart/simple_product_add_to_cart.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Header Manager" enabled="true"> + <collectionProp name="HeaderManager.headers"> + <elementProp name="" elementType="Header"> + <stringProp name="Header.name">X-Requested-With</stringProp> + <stringProp name="Header.value">XMLHttpRequest</stringProp> + </elementProp> + </collectionProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/http_header_manager_ajax.jmx</stringProp></HeaderManager> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Load Cart Section ${_counter}" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="sections" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">cart,messages</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">sections</stringProp> + </elementProp> + <elementProp name="force_new_section_timestamp" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">true</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">force_new_section_timestamp</stringProp> + </elementProp> + <elementProp name="_" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${__time()}${__Random(1,1000000)}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">_</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}customer/section/load/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/load_cart_section.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="210217247">You added ${product_name} to your <a href="${base_path}checkout/cart/">shopping cart</a>.</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="2057973164">This product is out of stock.</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">6</intProp> + </ResponseAssertion> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-350323027">\"summary_count\":${totalProductsAdded}</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + + <HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Header Manager" enabled="true"> + <collectionProp name="HeaderManager.headers"> + <elementProp name="" elementType="Header"> + <stringProp name="Header.name">X-Requested-With</stringProp> + <stringProp name="Header.value">XMLHttpRequest</stringProp> + </elementProp> + </collectionProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/http_header_manager_ajax.jmx</stringProp></HeaderManager> + <hashTree/> + </hashTree> + </hashTree> + + <LoopController guiclass="LoopControlPanel" testclass="LoopController" testname="Add Configurable Products to Cart" enabled="true"> + <boolProp name="LoopController.continue_forever">true</boolProp> + <stringProp name="LoopController.loops">1</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/loop_controller.jmx</stringProp></LoopController> + <hashTree> + <CounterConfig guiclass="CounterConfigGui" testclass="CounterConfig" testname="Counter" enabled="true"> + <stringProp name="CounterConfig.start">1</stringProp> + <stringProp name="CounterConfig.end"/> + <stringProp name="CounterConfig.incr">1</stringProp> + <stringProp name="CounterConfig.name">_counter</stringProp> + <stringProp name="CounterConfig.format"/> + <boolProp name="CounterConfig.per_user">true</boolProp> + <boolProp name="CounterConfig.reset_on_tg_iteration">true</boolProp> + </CounterConfig> + <hashTree/> + + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Prepare Configurable Product Data" enabled="true"> + <stringProp name="BeanShellSampler.query"> +import java.util.Random; + +Random random = vars.getObject("randomIntGenerator"); +number = random.nextInt(props.get("configurable_products_list").size()); +product = props.get("configurable_products_list").get(number); + +vars.put("product_url_key", product.get("url_key")); +vars.put("product_id", product.get("id")); +vars.put("product_name", product.get("title")); +vars.put("product_uenc", product.get("uenc")); +vars.put("product_sku", product.get("sku")); + </stringProp> + <stringProp name="BeanShellSampler.filename"/> + <stringProp name="BeanShellSampler.parameters"/> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/product_browsing_and_adding_items_to_the_cart/configurable_products_setup.jmx</stringProp></BeanShellSampler> + <hashTree/> + + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Update Products Added Counter" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/loops/update_products_added_counter.jmx</stringProp> + <stringProp name="BeanShellSampler.query"> +productsAdded = Integer.parseInt(vars.get("totalProductsAdded")); +productsAdded = productsAdded + 1; + +vars.put("totalProductsAdded", String.valueOf(productsAdded)); + </stringProp> + <stringProp name="BeanShellSampler.filename"/> + <stringProp name="BeanShellSampler.parameters"/> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + </BeanShellSampler> + <hashTree/> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Configurable Product ${_counter} View" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${product_url_key}${url_suffix}</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/product_browsing_and_adding_items_to_the_cart/product_view.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-1787050162"><span>In stock</span></stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + + <LoopController guiclass="LoopControlPanel" testclass="LoopController" testname="SetUp - Get Configurable Product Options" enabled="true"> + <boolProp name="LoopController.continue_forever">true</boolProp> + <stringProp name="LoopController.loops">1</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/get_configurable_product_options.jmx</stringProp></LoopController> + <hashTree> + <HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Header Manager" enabled="true"> + <collectionProp name="HeaderManager.headers"> + <elementProp name="" elementType="Header"> + <stringProp name="Header.name">Content-Type</stringProp> + <stringProp name="Header.value">application/json</stringProp> + </elementProp> + <elementProp name="" elementType="Header"> + <stringProp name="Header.name">Accept</stringProp> + <stringProp name="Header.value">*/*</stringProp> + </elementProp> + </collectionProp> + </HeaderManager> + <hashTree/> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="SetUp - Admin Token Retrieval" enabled="true"> + <boolProp name="HTTPSampler.postBodyRaw">true</boolProp> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments"> + <collectionProp name="Arguments.arguments"> + <elementProp name="" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">false</boolProp> + <stringProp name="Argument.value">{"username":"${admin_user}","password":"${admin_password}"}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}rest/V1/integration/admin/token</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + </HTTPSamplerProxy> + <hashTree> + <com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor guiclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.gui.JSONPathExtractorGui" testclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor" testname="jp@gc - JSON Path Extractor" enabled="true"> + <stringProp name="VAR">admin_token</stringProp> + <stringProp name="JSONPATH">$</stringProp> + <stringProp name="DEFAULT"/> + <stringProp name="VARIABLE"/> + <stringProp name="SUBJECT">BODY</stringProp> + </com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert token not null" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="484395188">^[a-z0-9-]+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">admin_token</stringProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + <HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Header Manager" enabled="true"> + <collectionProp name="HeaderManager.headers"> + <elementProp name="" elementType="Header"> + <stringProp name="Header.name">Authorization</stringProp> + <stringProp name="Header.value">Bearer ${admin_token}</stringProp> + </elementProp> + </collectionProp> + </HeaderManager> + <hashTree/> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Get Configurable Product Options" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}rest/V1/configurable-products/${product_sku}/options/all</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + </HTTPSamplerProxy> + <hashTree> + <com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor guiclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.gui.JSONPathExtractorGui" testclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor" testname="JSON Path Extractor: Extract attribute_ids" enabled="true"> + <stringProp name="VAR">attribute_ids</stringProp> + <stringProp name="JSONPATH">$.[*].attribute_id</stringProp> + <stringProp name="DEFAULT">NO_VALUE</stringProp> + <stringProp name="VARIABLE"/> + <stringProp name="SUBJECT">BODY</stringProp> + </com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor> + <hashTree/> + <com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor guiclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.gui.JSONPathExtractorGui" testclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor" testname="JSON Path Extractor: Extract option_values" enabled="true"> + <stringProp name="VAR">option_values</stringProp> + <stringProp name="JSONPATH">$.[*].values[0].value_index</stringProp> + <stringProp name="DEFAULT">NO_VALUE</stringProp> + <stringProp name="VARIABLE"/> + <stringProp name="SUBJECT">BODY</stringProp> + </com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor> + <hashTree/> + </hashTree> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Configurable Product ${_counter} Add To Cart" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="product" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${product_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product</stringProp> + </elementProp> + <elementProp name="related_product" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">related_product</stringProp> + </elementProp> + <elementProp name="qty" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">qty</stringProp> + </elementProp> + <elementProp name="form_key" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${form_key}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">form_key</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}checkout/cart/add/</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/product_browsing_and_adding_items_to_the_cart/configurable_product_add_to_cart.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <BeanShellPreProcessor guiclass="TestBeanGUI" testclass="BeanShellPreProcessor" testname="BeanShell PreProcessor" enabled="true"> + <boolProp name="resetInterpreter">false</boolProp> + <stringProp name="parameters"/> + <stringProp name="filename"/> + <stringProp name="script"> + try { + attribute_ids = vars.get("attribute_ids"); + option_values = vars.get("option_values"); + attribute_ids = attribute_ids.replace("[","").replace("]","").replace("\"", ""); + option_values = option_values.replace("[","").replace("]","").replace("\"", ""); + attribute_ids_array = attribute_ids.split(","); + option_values_array = option_values.split(","); + args = ctx.getCurrentSampler().getArguments(); + it = args.iterator(); + while (it.hasNext()) { + argument = it.next(); + if (argument.getStringValue().contains("${")) { + args.removeArgument(argument.getName()); + } + } + for (int i = 0; i < attribute_ids_array.length; i++) { + ctx.getCurrentSampler().addArgument("super_attribute[" + attribute_ids_array[i] + "]", option_values_array[i]); + } + } catch (Exception e) { + log.error("eror…", e); + } + </stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/configurable_product_add_to_cart_preprocessor.jmx</stringProp></BeanShellPreProcessor> + <hashTree/> + + <HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Header Manager" enabled="true"> + <collectionProp name="HeaderManager.headers"> + <elementProp name="" elementType="Header"> + <stringProp name="Header.name">X-Requested-With</stringProp> + <stringProp name="Header.value">XMLHttpRequest</stringProp> + </elementProp> + </collectionProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/http_header_manager_ajax.jmx</stringProp></HeaderManager> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Load Cart Section ${_counter}" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="sections" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">cart,messages</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">sections</stringProp> + </elementProp> + <elementProp name="force_new_section_timestamp" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">true</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">force_new_section_timestamp</stringProp> + </elementProp> + <elementProp name="_" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${__time()}${__Random(1,1000000)}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">_</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}customer/section/load/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/load_cart_section.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="210217247">You added ${product_name} to your <a href="${base_path}checkout/cart/">shopping cart</a>.</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="2057973164">This product is out of stock.</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">6</intProp> + </ResponseAssertion> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-350323027">\"summary_count\":${totalProductsAdded}</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + + <HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Header Manager" enabled="true"> + <collectionProp name="HeaderManager.headers"> + <elementProp name="" elementType="Header"> + <stringProp name="Header.name">X-Requested-With</stringProp> + <stringProp name="Header.value">XMLHttpRequest</stringProp> + </elementProp> + </collectionProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/http_header_manager_ajax.jmx</stringProp></HeaderManager> + <hashTree/> + </hashTree> + </hashTree> + </hashTree> + + + <ThroughputController guiclass="ThroughputControllerGui" testclass="ThroughputController" testname="[C] Add to Wishlist" enabled="true"> + <intProp name="ThroughputController.style">1</intProp> + <boolProp name="ThroughputController.perThread">false</boolProp> + <intProp name="ThroughputController.maxThroughput">1</intProp> + <stringProp name="ThroughputController.percentThroughput">${cAddToWishlistPercentage}</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/_system/scenario_controller_tmpl.jmx</stringProp></ThroughputController> + <hashTree> + <JSR223PreProcessor guiclass="TestBeanGUI" testclass="JSR223PreProcessor" testname="Set Test Label" enabled="true"> + <stringProp name="script"> +var testLabel = "${testLabel}" ? " (${testLabel})" : ""; +if (testLabel + && sampler.getClass().getName() == 'org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy' +) { + if (sampler.getName().indexOf(testLabel) == -1) { + sampler.setName(sampler.getName() + testLabel); + } +} else if (sampler.getName().indexOf("SetUp - ") == -1) { + sampler.setName("SetUp - " + sampler.getName()); +} + </stringProp> + <stringProp name="scriptLanguage">javascript</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/_system/setup_label.jmx</stringProp></JSR223PreProcessor> + <hashTree/> + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Set Label" enabled="true"> + <stringProp name="BeanShellSampler.query"> + vars.put("testLabel", "[C] Add to Wishlist"); + </stringProp> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + </BeanShellSampler> + <hashTree/> + + <CookieManager guiclass="CookiePanel" testclass="CookieManager" testname="HTTP Cookie Manager" enabled="true"> + <collectionProp name="CookieManager.cookies"> + <elementProp name="product_list_limit" elementType="Cookie" testname="product_list_limit"> + <stringProp name="Cookie.value">30</stringProp> + <stringProp name="Cookie.domain">${host}</stringProp> + <stringProp name="Cookie.path">/</stringProp> + <boolProp name="Cookie.secure">false</boolProp> + <longProp name="Cookie.expires">0</longProp> + <boolProp name="Cookie.path_specified">true</boolProp> + <boolProp name="Cookie.domain_specified">true</boolProp> + </elementProp> + <elementProp name="product_list_limit" elementType="Cookie" testname="form_key"> + <stringProp name="Cookie.value">${form_key}</stringProp> + <stringProp name="Cookie.domain">${host}</stringProp> + <stringProp name="Cookie.path">${base_path}</stringProp> + <boolProp name="Cookie.secure">false</boolProp> + <longProp name="Cookie.expires">0</longProp> + <boolProp name="Cookie.path_specified">true</boolProp> + <boolProp name="Cookie.domain_specified">true</boolProp> + </elementProp> + </collectionProp> + <boolProp name="CookieManager.clearEachIteration">true</boolProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/http_cookie_manager.jmx</stringProp></CookieManager> + <hashTree/> + + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Init Random Generator" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/init_random_generator_setup.jmx</stringProp> + <stringProp name="BeanShellSampler.query"> +import java.util.Random; + +Random random = new Random(); +if (${seedForRandom} > 0) { + random.setSeed(${seedForRandom} + ${__threadNum}); +} + +vars.putObject("randomIntGenerator", random); + </stringProp> + <stringProp name="BeanShellSampler.filename"/> + <stringProp name="BeanShellSampler.parameters"/> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + </BeanShellSampler> + <hashTree/> + + <CriticalSectionController guiclass="CriticalSectionControllerGui" testclass="CriticalSectionController" testname="Get Customer Email" enabled="true"> + <stringProp name="CriticalSectionController.lockName">get-email</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/lock_controller.jmx</stringProp></CriticalSectionController> + <hashTree> + + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Get Customer Email" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/get_customer_email.jmx</stringProp> + <stringProp name="BeanShellSampler.query"> +customerUserList = props.get("customer_emails_list"); +customerUser = customerUserList.poll(); +if (customerUser == null) { + SampleResult.setResponseMessage("customernUser list is empty"); + SampleResult.setResponseData("customerUser list is empty","UTF-8"); + IsSuccess=false; + SampleResult.setSuccessful(false); + SampleResult.setStopThread(true); +} +vars.put("customer_email", customerUser); + </stringProp> + <stringProp name="BeanShellSampler.filename"/> + <stringProp name="BeanShellSampler.parameters"/> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + </BeanShellSampler> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Open Login Page" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}customer/account/login/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/open_login_page.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="637394530"><title>Customer Login</title></stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Login" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="form_key" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${form_key}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">form_key</stringProp> + </elementProp> + <elementProp name="login[username]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${customer_email}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">login[username]</stringProp> + </elementProp> + <elementProp name="login[password]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${customer_password}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">login[password]</stringProp> + </elementProp> + <elementProp name="send" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">send</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}customer/account/loginPost/</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/login.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="1312950388"><title>My Account</title></stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract Address" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">addressId</stringProp> + <stringProp name="RegexExtractor.regex">customer/address/edit/id/([^'"]+)/</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert addressId extracted" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="2845929">^.+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">addressId</stringProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Load Customer Private Data" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="sections" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">sections</stringProp> + </elementProp> + <elementProp name="force_new_section_timestamp" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">false</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">force_new_section_timestamp</stringProp> + </elementProp> + <elementProp name="_" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${__time()}${__Random(1,1000000)}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">_</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}customer/section/load/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + </HTTPSamplerProxy> + <hashTree/> + + <LoopController guiclass="LoopControlPanel" testclass="LoopController" testname="Add Produts to Wishlist" enabled="true"> + <boolProp name="LoopController.continue_forever">true</boolProp> + <stringProp name="LoopController.loops">5</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/loop_controller.jmx</stringProp></LoopController> + <hashTree> + <CounterConfig guiclass="CounterConfigGui" testclass="CounterConfig" testname="Counter" enabled="true"> + <stringProp name="CounterConfig.start">1</stringProp> + <stringProp name="CounterConfig.end"/> + <stringProp name="CounterConfig.incr">1</stringProp> + <stringProp name="CounterConfig.name">_counter</stringProp> + <stringProp name="CounterConfig.format"/> + <boolProp name="CounterConfig.per_user">true</boolProp> + <boolProp name="CounterConfig.reset_on_tg_iteration">true</boolProp> + </CounterConfig> + <hashTree/> + + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Prepare Simple Product Data" enabled="true"> + <stringProp name="BeanShellSampler.query"> +import java.util.Random; + +Random random = vars.getObject("randomIntGenerator"); +number = random.nextInt(props.get("simple_products_list").size()); +product = props.get("simple_products_list").get(number); + +vars.put("product_url_key", product.get("url_key")); +vars.put("product_id", product.get("id")); +vars.put("product_name", product.get("title")); +vars.put("product_uenc", product.get("uenc")); +vars.put("product_sku", product.get("sku")); + </stringProp> + <stringProp name="BeanShellSampler.filename"/> + <stringProp name="BeanShellSampler.parameters"/> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/product_browsing_and_adding_items_to_the_cart/simple_products_setup.jmx</stringProp></BeanShellSampler> + <hashTree/> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Simple Product ${_counter} View" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${product_url_key}${url_suffix}</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/product_browsing_and_adding_items_to_the_cart/product_view.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-1787050162"><span>In stock</span></stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Simple Product ${_counter} Add To Wishlist" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="form_key" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${form_key}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">form_key</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="uenc" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${product_uenc}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">uenc</stringProp> + </elementProp> + <elementProp name="product" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${product_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}wishlist/index/add/</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/wishlist/add_to_wishlist.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-1907714722"><title>My Wish List</title></stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">16</intProp> + </ResponseAssertion> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Regular Expression Extractor" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">wishListItems</stringProp> + <stringProp name="RegexExtractor.regex">data-post-remove='\{"action":"(.+)\/wishlist\\/index\\/remove\\/","data":\{"item":"([^"]+)"</stringProp> + <stringProp name="RegexExtractor.template">$2$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">-1</stringProp> + </RegexExtractor> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Load Wishlist Section ${_counter}" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="sections" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">wishlist,messages</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">sections</stringProp> + </elementProp> + <elementProp name="force_new_section_timestamp" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">false</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">force_new_section_timestamp</stringProp> + </elementProp> + <elementProp name="_" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${__time()}${__Random(1,1000000)}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">_</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}customer/section/load/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/wishlist/load_wishlist_section.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-1865430343">{"wishlist":{"counter":"</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">16</intProp> + </ResponseAssertion> + <hashTree/> + <ConstantTimer guiclass="ConstantTimerGui" testclass="ConstantTimer" testname="Constant Timer" enabled="true"> + <stringProp name="ConstantTimer.delay">${wishlistDelay}*1000</stringProp> + </ConstantTimer> + <hashTree/> + </hashTree> + </hashTree> + + <ForeachController guiclass="ForeachControlPanel" testclass="ForeachController" testname="ForEach Controller: Clear Wishlist" enabled="true"> + <stringProp name="ForeachController.inputVal">wishListItems</stringProp> + <stringProp name="ForeachController.returnVal">wishListItem</stringProp> + <boolProp name="ForeachController.useSeparator">true</boolProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/wishlist/clear_wishlist.jmx</stringProp></ForeachController> + <hashTree> + <CounterConfig guiclass="CounterConfigGui" testclass="CounterConfig" testname="Counter" enabled="true"> + <stringProp name="CounterConfig.start">1</stringProp> + <stringProp name="CounterConfig.end">5</stringProp> + <stringProp name="CounterConfig.incr">1</stringProp> + <stringProp name="CounterConfig.name">counter</stringProp> + <stringProp name="CounterConfig.format"/> + <boolProp name="CounterConfig.per_user">true</boolProp> + <boolProp name="CounterConfig.reset_on_tg_iteration">true</boolProp> + </CounterConfig> + <hashTree/> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Clear Wishlist ${counter}" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="form_key" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${form_key}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">form_key</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="item" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${wishListItem}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">item</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}wishlist/index/remove/</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + </HTTPSamplerProxy> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Logout" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}customer/account/logout/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/logout.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert success" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="1723813687">You are signed out.</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + + <BeanShellPostProcessor guiclass="TestBeanGUI" testclass="BeanShellPostProcessor" testname="Return Customer to Pool" enabled="true"> + <boolProp name="resetInterpreter">false</boolProp> + <stringProp name="parameters"/> + <stringProp name="filename"/> + <stringProp name="script"> +customerUserList = props.get("customer_emails_list"); +customerUserList.add(vars.get("customer_email")); + </stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/return_email_to_pool.jmx</stringProp></BeanShellPostProcessor> + <hashTree/> + </hashTree> + </hashTree> + + + <ThroughputController guiclass="ThroughputControllerGui" testclass="ThroughputController" testname="[C] Compare Products" enabled="true"> + <intProp name="ThroughputController.style">1</intProp> + <boolProp name="ThroughputController.perThread">false</boolProp> + <intProp name="ThroughputController.maxThroughput">1</intProp> + <stringProp name="ThroughputController.percentThroughput">${cCompareProductsPercentage}</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/_system/scenario_controller_tmpl.jmx</stringProp></ThroughputController> + <hashTree> + <JSR223PreProcessor guiclass="TestBeanGUI" testclass="JSR223PreProcessor" testname="Set Test Label" enabled="true"> + <stringProp name="script"> +var testLabel = "${testLabel}" ? " (${testLabel})" : ""; +if (testLabel + && sampler.getClass().getName() == 'org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy' +) { + if (sampler.getName().indexOf(testLabel) == -1) { + sampler.setName(sampler.getName() + testLabel); + } +} else if (sampler.getName().indexOf("SetUp - ") == -1) { + sampler.setName("SetUp - " + sampler.getName()); +} + </stringProp> + <stringProp name="scriptLanguage">javascript</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/_system/setup_label.jmx</stringProp></JSR223PreProcessor> + <hashTree/> + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Set Label" enabled="true"> + <stringProp name="BeanShellSampler.query"> + vars.put("testLabel", "[C] Compare Products"); + </stringProp> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + </BeanShellSampler> + <hashTree/> + + <CookieManager guiclass="CookiePanel" testclass="CookieManager" testname="HTTP Cookie Manager" enabled="true"> + <collectionProp name="CookieManager.cookies"> + <elementProp name="product_list_limit" elementType="Cookie" testname="product_list_limit"> + <stringProp name="Cookie.value">30</stringProp> + <stringProp name="Cookie.domain">${host}</stringProp> + <stringProp name="Cookie.path">/</stringProp> + <boolProp name="Cookie.secure">false</boolProp> + <longProp name="Cookie.expires">0</longProp> + <boolProp name="Cookie.path_specified">true</boolProp> + <boolProp name="Cookie.domain_specified">true</boolProp> + </elementProp> + <elementProp name="product_list_limit" elementType="Cookie" testname="form_key"> + <stringProp name="Cookie.value">${form_key}</stringProp> + <stringProp name="Cookie.domain">${host}</stringProp> + <stringProp name="Cookie.path">${base_path}</stringProp> + <boolProp name="Cookie.secure">false</boolProp> + <longProp name="Cookie.expires">0</longProp> + <boolProp name="Cookie.path_specified">true</boolProp> + <boolProp name="Cookie.domain_specified">true</boolProp> + </elementProp> + </collectionProp> + <boolProp name="CookieManager.clearEachIteration">true</boolProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/http_cookie_manager.jmx</stringProp></CookieManager> + <hashTree/> + + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Init Random Generator" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/init_random_generator_setup.jmx</stringProp> + <stringProp name="BeanShellSampler.query"> +import java.util.Random; + +Random random = new Random(); +if (${seedForRandom} > 0) { + random.setSeed(${seedForRandom} + ${__threadNum}); +} + +vars.putObject("randomIntGenerator", random); + </stringProp> + <stringProp name="BeanShellSampler.filename"/> + <stringProp name="BeanShellSampler.parameters"/> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + </BeanShellSampler> + <hashTree/> + + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Init Total Products In Cart" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/init_total_products_in_cart_setup.jmx</stringProp> + <stringProp name="BeanShellSampler.query"> +vars.put("totalProductsAdded", "0"); + </stringProp> + <stringProp name="BeanShellSampler.filename"/> + <stringProp name="BeanShellSampler.parameters"/> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + </BeanShellSampler> + <hashTree/> + + <JSR223Sampler guiclass="TestBeanGUI" testclass="JSR223Sampler" testname="SetUp - Prepare Category Data" enabled="true"> + <stringProp name="scriptLanguage">javascript</stringProp> + <stringProp name="parameters"/> + <stringProp name="filename"/> + <stringProp name="cacheKey"/> + <stringProp name="script">random = vars.getObject("randomIntGenerator"); + +var categories = props.get("categories"); +number = random.nextInt(categories.length); + +vars.put("category_url_key", categories[number].url_key); +vars.put("category_name", categories[number].name); +vars.put("category_id", categories[number].id); +vars.putObject("category", categories[number]); + </stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/extract_category_setup.jmx</stringProp></JSR223Sampler> + <hashTree/> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Open Category" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${category_url_key}${url_suffix}</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/product_compare/open_category.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="1210004667"><span class="base" data-ui-id="page-title">${category_name}</span></stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">6</intProp> + </ResponseAssertion> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract Random Product Id" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">random_product_compare_id</stringProp> + <stringProp name="RegexExtractor.regex">catalog\\/product_compare\\/add\\/\",\"data\":\{\"product\":\"([0-9]+)\"</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert Random Product Id" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="1191417111">^[0-9]+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">random_product_compare_id</stringProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + + <LoopController guiclass="LoopControlPanel" testclass="LoopController" testname="Add Simple Products to Compare" enabled="true"> + <boolProp name="LoopController.continue_forever">true</boolProp> + <stringProp name="LoopController.loops">2</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/loop_controller.jmx</stringProp></LoopController> + <hashTree> + <CounterConfig guiclass="CounterConfigGui" testclass="CounterConfig" testname="Counter" enabled="true"> + <stringProp name="CounterConfig.start">1</stringProp> + <stringProp name="CounterConfig.end"/> + <stringProp name="CounterConfig.incr">1</stringProp> + <stringProp name="CounterConfig.name">_counter</stringProp> + <stringProp name="CounterConfig.format"/> + <boolProp name="CounterConfig.per_user">true</boolProp> + <boolProp name="CounterConfig.reset_on_tg_iteration">true</boolProp> + </CounterConfig> + <hashTree/> + + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Prepare Simple Product Data" enabled="true"> + <stringProp name="BeanShellSampler.query"> +import java.util.Random; + +Random random = vars.getObject("randomIntGenerator"); +number = random.nextInt(props.get("simple_products_list").size()); +product = props.get("simple_products_list").get(number); + +vars.put("product_url_key", product.get("url_key")); +vars.put("product_id", product.get("id")); +vars.put("product_name", product.get("title")); +vars.put("product_uenc", product.get("uenc")); +vars.put("product_sku", product.get("sku")); + </stringProp> + <stringProp name="BeanShellSampler.filename"/> + <stringProp name="BeanShellSampler.parameters"/> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/product_browsing_and_adding_items_to_the_cart/simple_products_setup.jmx</stringProp></BeanShellSampler> + <hashTree/> + + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Update Products Added Counter" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/loops/update_products_added_counter.jmx</stringProp> + <stringProp name="BeanShellSampler.query"> +productsAdded = Integer.parseInt(vars.get("totalProductsAdded")); +productsAdded = productsAdded + 1; + +vars.put("totalProductsAdded", String.valueOf(productsAdded)); + </stringProp> + <stringProp name="BeanShellSampler.filename"/> + <stringProp name="BeanShellSampler.parameters"/> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + </BeanShellSampler> + <hashTree/> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Simple Product ${_counter} View" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${product_url_key}${url_suffix}</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/product_browsing_and_adding_items_to_the_cart/product_view.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-1787050162"><span>In stock</span></stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Simple Product ${_counter} Comparison Add" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="product" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${product_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product</stringProp> + </elementProp> + <elementProp name="form_key" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${form_key}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">form_key</stringProp> + </elementProp> + <elementProp name="uenc" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${product_uenc}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">uenc</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}catalog/product_compare/add/</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/product_compare/product_compare_add.jmx</stringProp></HTTPSamplerProxy> + <hashTree/> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Load Compare Product Section" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="sections" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">compare-products,messages</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">sections</stringProp> + </elementProp> + <elementProp name="force_new_section_timestamp" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">false</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">force_new_section_timestamp</stringProp> + </elementProp> + <elementProp name="_" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${__time()}${__Random(1,1000000)}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">_</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}customer/section/load/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/product_compare/customer_section_load_product_compare_add.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-350323027">\"compare-products\":{\"count\":${totalProductsAdded}</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + </hashTree> + + <LoopController guiclass="LoopControlPanel" testclass="LoopController" testname="Add Configurable Products to Compare" enabled="true"> + <boolProp name="LoopController.continue_forever">true</boolProp> + <stringProp name="LoopController.loops">1</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/loop_controller.jmx</stringProp></LoopController> + <hashTree> + <CounterConfig guiclass="CounterConfigGui" testclass="CounterConfig" testname="Counter" enabled="true"> + <stringProp name="CounterConfig.start">1</stringProp> + <stringProp name="CounterConfig.end"/> + <stringProp name="CounterConfig.incr">1</stringProp> + <stringProp name="CounterConfig.name">_counter</stringProp> + <stringProp name="CounterConfig.format"/> + <boolProp name="CounterConfig.per_user">true</boolProp> + <boolProp name="CounterConfig.reset_on_tg_iteration">true</boolProp> + </CounterConfig> + <hashTree/> + + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Prepare Configurable Product Data" enabled="true"> + <stringProp name="BeanShellSampler.query"> +import java.util.Random; + +Random random = vars.getObject("randomIntGenerator"); +number = random.nextInt(props.get("configurable_products_list").size()); +product = props.get("configurable_products_list").get(number); + +vars.put("product_url_key", product.get("url_key")); +vars.put("product_id", product.get("id")); +vars.put("product_name", product.get("title")); +vars.put("product_uenc", product.get("uenc")); +vars.put("product_sku", product.get("sku")); + </stringProp> + <stringProp name="BeanShellSampler.filename"/> + <stringProp name="BeanShellSampler.parameters"/> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/product_browsing_and_adding_items_to_the_cart/configurable_products_setup.jmx</stringProp></BeanShellSampler> + <hashTree/> + + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Update Products Added Counter" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/loops/update_products_added_counter.jmx</stringProp> + <stringProp name="BeanShellSampler.query"> +productsAdded = Integer.parseInt(vars.get("totalProductsAdded")); +productsAdded = productsAdded + 1; + +vars.put("totalProductsAdded", String.valueOf(productsAdded)); + </stringProp> + <stringProp name="BeanShellSampler.filename"/> + <stringProp name="BeanShellSampler.parameters"/> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + </BeanShellSampler> + <hashTree/> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Configurable Product ${_counter} View" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${product_url_key}${url_suffix}</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/product_browsing_and_adding_items_to_the_cart/product_view.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-1787050162"><span>In stock</span></stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Configurable Product ${_counter} Comparison Add" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="product" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${product_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product</stringProp> + </elementProp> + <elementProp name="form_key" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${form_key}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">form_key</stringProp> + </elementProp> + <elementProp name="uenc" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${product_uenc}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">uenc</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}catalog/product_compare/add/</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/product_compare/product_compare_add.jmx</stringProp></HTTPSamplerProxy> + <hashTree/> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Load Compare Product Section" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="sections" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">compare-products,messages</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">sections</stringProp> + </elementProp> + <elementProp name="force_new_section_timestamp" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">false</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">force_new_section_timestamp</stringProp> + </elementProp> + <elementProp name="_" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${__time()}${__Random(1,1000000)}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">_</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}customer/section/load/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/product_compare/customer_section_load_product_compare_add.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-350323027">\"compare-products\":{\"count\":${totalProductsAdded}</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Compare Products" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}catalog/product_compare/index/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/product_compare/compare_products.jmx</stringProp></HTTPSamplerProxy> + <hashTree/> + + <TestAction guiclass="TestActionGui" testclass="TestAction" testname="Product Compare - Pause" enabled="true"> + <intProp name="ActionProcessor.action">1</intProp> + <intProp name="ActionProcessor.target">0</intProp> + <stringProp name="ActionProcessor.duration">${__javaScript(Math.round(${productCompareDelay}*1000))}</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/product_compare/compare_products_pause.jmx</stringProp></TestAction> + <hashTree/> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Compare Products Clear" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="form_key" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${form_key}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">form_key</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}catalog/product_compare/clear</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/product_compare/compare_products_clear.jmx</stringProp></HTTPSamplerProxy> + <hashTree/> + </hashTree> + + + <ThroughputController guiclass="ThroughputControllerGui" testclass="ThroughputController" testname="[C] Checkout By Guest" enabled="true"> + <intProp name="ThroughputController.style">1</intProp> + <boolProp name="ThroughputController.perThread">false</boolProp> + <intProp name="ThroughputController.maxThroughput">1</intProp> + <stringProp name="ThroughputController.percentThroughput">${cCheckoutByGuestPercentage}</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/_system/scenario_controller_tmpl.jmx</stringProp></ThroughputController> + <hashTree> + <JSR223PreProcessor guiclass="TestBeanGUI" testclass="JSR223PreProcessor" testname="Set Test Label" enabled="true"> + <stringProp name="script"> +var testLabel = "${testLabel}" ? " (${testLabel})" : ""; +if (testLabel + && sampler.getClass().getName() == 'org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy' +) { + if (sampler.getName().indexOf(testLabel) == -1) { + sampler.setName(sampler.getName() + testLabel); + } +} else if (sampler.getName().indexOf("SetUp - ") == -1) { + sampler.setName("SetUp - " + sampler.getName()); +} + </stringProp> + <stringProp name="scriptLanguage">javascript</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/_system/setup_label.jmx</stringProp></JSR223PreProcessor> + <hashTree/> + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Set Label" enabled="true"> + <stringProp name="BeanShellSampler.query"> + vars.put("testLabel", "[C] Checkout By Guest"); + </stringProp> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + </BeanShellSampler> + <hashTree/> + + <CookieManager guiclass="CookiePanel" testclass="CookieManager" testname="HTTP Cookie Manager" enabled="true"> + <collectionProp name="CookieManager.cookies"> + <elementProp name="product_list_limit" elementType="Cookie" testname="product_list_limit"> + <stringProp name="Cookie.value">30</stringProp> + <stringProp name="Cookie.domain">${host}</stringProp> + <stringProp name="Cookie.path">/</stringProp> + <boolProp name="Cookie.secure">false</boolProp> + <longProp name="Cookie.expires">0</longProp> + <boolProp name="Cookie.path_specified">true</boolProp> + <boolProp name="Cookie.domain_specified">true</boolProp> + </elementProp> + <elementProp name="product_list_limit" elementType="Cookie" testname="form_key"> + <stringProp name="Cookie.value">${form_key}</stringProp> + <stringProp name="Cookie.domain">${host}</stringProp> + <stringProp name="Cookie.path">${base_path}</stringProp> + <boolProp name="Cookie.secure">false</boolProp> + <longProp name="Cookie.expires">0</longProp> + <boolProp name="Cookie.path_specified">true</boolProp> + <boolProp name="Cookie.domain_specified">true</boolProp> + </elementProp> + </collectionProp> + <boolProp name="CookieManager.clearEachIteration">true</boolProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/http_cookie_manager.jmx</stringProp></CookieManager> + <hashTree/> + + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Init Random Generator" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/init_random_generator_setup.jmx</stringProp> + <stringProp name="BeanShellSampler.query"> +import java.util.Random; + +Random random = new Random(); +if (${seedForRandom} > 0) { + random.setSeed(${seedForRandom} + ${__threadNum}); +} + +vars.putObject("randomIntGenerator", random); + </stringProp> + <stringProp name="BeanShellSampler.filename"/> + <stringProp name="BeanShellSampler.parameters"/> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + </BeanShellSampler> + <hashTree/> + + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Init Total Products In Cart" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/init_total_products_in_cart_setup.jmx</stringProp> + <stringProp name="BeanShellSampler.query"> +vars.put("totalProductsAdded", "0"); + </stringProp> + <stringProp name="BeanShellSampler.filename"/> + <stringProp name="BeanShellSampler.parameters"/> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + </BeanShellSampler> + <hashTree/> + + <JSR223Sampler guiclass="TestBeanGUI" testclass="JSR223Sampler" testname="SetUp - Prepare Category Data" enabled="true"> + <stringProp name="scriptLanguage">javascript</stringProp> + <stringProp name="parameters"/> + <stringProp name="filename"/> + <stringProp name="cacheKey"/> + <stringProp name="script">random = vars.getObject("randomIntGenerator"); + +var categories = props.get("categories"); +number = random.nextInt(categories.length); + +vars.put("category_url_key", categories[number].url_key); +vars.put("category_name", categories[number].name); +vars.put("category_id", categories[number].id); +vars.putObject("category", categories[number]); + </stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/extract_category_setup.jmx</stringProp></JSR223Sampler> + <hashTree/> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Open Home Page" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/open_home_page.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="571386695"><title>Home page</title></stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Open Category" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${category_url_key}${url_suffix}</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/open_category.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="1210004667"><span class="base" data-ui-id="page-title">${category_name}</span></stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">6</intProp> + </ResponseAssertion> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Regular Expression Extractor: Extract category id" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">category_id</stringProp> + <stringProp name="RegexExtractor.regex"><li class="item category([^'"]+)">\s*<strong>${category_name}</strong>\s*</li></stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + <stringProp name="Scope.variable">simple_product_1_url_key</stringProp> + </RegexExtractor> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion: Assert category id" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="1191417111">^[0-9]+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">category_id</stringProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + + <LoopController guiclass="LoopControlPanel" testclass="LoopController" testname="Add Simple Products to Cart" enabled="true"> + <boolProp name="LoopController.continue_forever">true</boolProp> + <stringProp name="LoopController.loops">2</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/loop_controller.jmx</stringProp></LoopController> + <hashTree> + <CounterConfig guiclass="CounterConfigGui" testclass="CounterConfig" testname="Counter" enabled="true"> + <stringProp name="CounterConfig.start">1</stringProp> + <stringProp name="CounterConfig.end"/> + <stringProp name="CounterConfig.incr">1</stringProp> + <stringProp name="CounterConfig.name">_counter</stringProp> + <stringProp name="CounterConfig.format"/> + <boolProp name="CounterConfig.per_user">true</boolProp> + <boolProp name="CounterConfig.reset_on_tg_iteration">true</boolProp> + </CounterConfig> + <hashTree/> + + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Prepare Simple Product Data" enabled="true"> + <stringProp name="BeanShellSampler.query"> +import java.util.Random; + +Random random = vars.getObject("randomIntGenerator"); +number = random.nextInt(props.get("simple_products_list").size()); +product = props.get("simple_products_list").get(number); + +vars.put("product_url_key", product.get("url_key")); +vars.put("product_id", product.get("id")); +vars.put("product_name", product.get("title")); +vars.put("product_uenc", product.get("uenc")); +vars.put("product_sku", product.get("sku")); + </stringProp> + <stringProp name="BeanShellSampler.filename"/> + <stringProp name="BeanShellSampler.parameters"/> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/product_browsing_and_adding_items_to_the_cart/simple_products_setup.jmx</stringProp></BeanShellSampler> + <hashTree/> + + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Update Products Added Counter" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/loops/update_products_added_counter.jmx</stringProp> + <stringProp name="BeanShellSampler.query"> +productsAdded = Integer.parseInt(vars.get("totalProductsAdded")); +productsAdded = productsAdded + 1; + +vars.put("totalProductsAdded", String.valueOf(productsAdded)); + </stringProp> + <stringProp name="BeanShellSampler.filename"/> + <stringProp name="BeanShellSampler.parameters"/> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + </BeanShellSampler> + <hashTree/> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Simple Product ${_counter} View" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${product_url_key}${url_suffix}</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/product_browsing_and_adding_items_to_the_cart/product_view.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-1787050162"><span>In stock</span></stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Simple Product ${_counter} Add To Cart" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="product" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${product_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product</stringProp> + </elementProp> + <elementProp name="related_product" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">related_product</stringProp> + </elementProp> + <elementProp name="qty" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">qty</stringProp> + </elementProp> + <elementProp name="form_key" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${form_key}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">form_key</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}checkout/cart/add/</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/product_browsing_and_adding_items_to_the_cart/simple_product_add_to_cart.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Header Manager" enabled="true"> + <collectionProp name="HeaderManager.headers"> + <elementProp name="" elementType="Header"> + <stringProp name="Header.name">X-Requested-With</stringProp> + <stringProp name="Header.value">XMLHttpRequest</stringProp> + </elementProp> + </collectionProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/http_header_manager_ajax.jmx</stringProp></HeaderManager> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Load Cart Section ${_counter}" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="sections" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">cart,messages</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">sections</stringProp> + </elementProp> + <elementProp name="force_new_section_timestamp" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">true</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">force_new_section_timestamp</stringProp> + </elementProp> + <elementProp name="_" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${__time()}${__Random(1,1000000)}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">_</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}customer/section/load/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/load_cart_section.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="210217247">You added ${product_name} to your <a href="${base_path}checkout/cart/">shopping cart</a>.</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="2057973164">This product is out of stock.</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">6</intProp> + </ResponseAssertion> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-350323027">\"summary_count\":${totalProductsAdded}</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + + <HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Header Manager" enabled="true"> + <collectionProp name="HeaderManager.headers"> + <elementProp name="" elementType="Header"> + <stringProp name="Header.name">X-Requested-With</stringProp> + <stringProp name="Header.value">XMLHttpRequest</stringProp> + </elementProp> + </collectionProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/http_header_manager_ajax.jmx</stringProp></HeaderManager> + <hashTree/> + </hashTree> + </hashTree> + + <LoopController guiclass="LoopControlPanel" testclass="LoopController" testname="Add Configurable Products to Cart" enabled="true"> + <boolProp name="LoopController.continue_forever">true</boolProp> + <stringProp name="LoopController.loops">1</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/loop_controller.jmx</stringProp></LoopController> + <hashTree> + <CounterConfig guiclass="CounterConfigGui" testclass="CounterConfig" testname="Counter" enabled="true"> + <stringProp name="CounterConfig.start">1</stringProp> + <stringProp name="CounterConfig.end"/> + <stringProp name="CounterConfig.incr">1</stringProp> + <stringProp name="CounterConfig.name">_counter</stringProp> + <stringProp name="CounterConfig.format"/> + <boolProp name="CounterConfig.per_user">true</boolProp> + <boolProp name="CounterConfig.reset_on_tg_iteration">true</boolProp> + </CounterConfig> + <hashTree/> + + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Prepare Configurable Product Data" enabled="true"> + <stringProp name="BeanShellSampler.query"> +import java.util.Random; + +Random random = vars.getObject("randomIntGenerator"); +number = random.nextInt(props.get("configurable_products_list").size()); +product = props.get("configurable_products_list").get(number); + +vars.put("product_url_key", product.get("url_key")); +vars.put("product_id", product.get("id")); +vars.put("product_name", product.get("title")); +vars.put("product_uenc", product.get("uenc")); +vars.put("product_sku", product.get("sku")); + </stringProp> + <stringProp name="BeanShellSampler.filename"/> + <stringProp name="BeanShellSampler.parameters"/> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/product_browsing_and_adding_items_to_the_cart/configurable_products_setup.jmx</stringProp></BeanShellSampler> + <hashTree/> + + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Update Products Added Counter" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/loops/update_products_added_counter.jmx</stringProp> + <stringProp name="BeanShellSampler.query"> +productsAdded = Integer.parseInt(vars.get("totalProductsAdded")); +productsAdded = productsAdded + 1; + +vars.put("totalProductsAdded", String.valueOf(productsAdded)); + </stringProp> + <stringProp name="BeanShellSampler.filename"/> + <stringProp name="BeanShellSampler.parameters"/> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + </BeanShellSampler> + <hashTree/> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Configurable Product ${_counter} View" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${product_url_key}${url_suffix}</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/product_browsing_and_adding_items_to_the_cart/product_view.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-1787050162"><span>In stock</span></stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + + <LoopController guiclass="LoopControlPanel" testclass="LoopController" testname="SetUp - Get Configurable Product Options" enabled="true"> + <boolProp name="LoopController.continue_forever">true</boolProp> + <stringProp name="LoopController.loops">1</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/get_configurable_product_options.jmx</stringProp></LoopController> + <hashTree> + <HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Header Manager" enabled="true"> + <collectionProp name="HeaderManager.headers"> + <elementProp name="" elementType="Header"> + <stringProp name="Header.name">Content-Type</stringProp> + <stringProp name="Header.value">application/json</stringProp> + </elementProp> + <elementProp name="" elementType="Header"> + <stringProp name="Header.name">Accept</stringProp> + <stringProp name="Header.value">*/*</stringProp> + </elementProp> + </collectionProp> + </HeaderManager> + <hashTree/> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="SetUp - Admin Token Retrieval" enabled="true"> + <boolProp name="HTTPSampler.postBodyRaw">true</boolProp> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments"> + <collectionProp name="Arguments.arguments"> + <elementProp name="" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">false</boolProp> + <stringProp name="Argument.value">{"username":"${admin_user}","password":"${admin_password}"}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}rest/V1/integration/admin/token</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + </HTTPSamplerProxy> + <hashTree> + <com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor guiclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.gui.JSONPathExtractorGui" testclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor" testname="jp@gc - JSON Path Extractor" enabled="true"> + <stringProp name="VAR">admin_token</stringProp> + <stringProp name="JSONPATH">$</stringProp> + <stringProp name="DEFAULT"/> + <stringProp name="VARIABLE"/> + <stringProp name="SUBJECT">BODY</stringProp> + </com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert token not null" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="484395188">^[a-z0-9-]+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">admin_token</stringProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + <HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Header Manager" enabled="true"> + <collectionProp name="HeaderManager.headers"> + <elementProp name="" elementType="Header"> + <stringProp name="Header.name">Authorization</stringProp> + <stringProp name="Header.value">Bearer ${admin_token}</stringProp> + </elementProp> + </collectionProp> + </HeaderManager> + <hashTree/> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Get Configurable Product Options" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}rest/V1/configurable-products/${product_sku}/options/all</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + </HTTPSamplerProxy> + <hashTree> + <com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor guiclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.gui.JSONPathExtractorGui" testclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor" testname="JSON Path Extractor: Extract attribute_ids" enabled="true"> + <stringProp name="VAR">attribute_ids</stringProp> + <stringProp name="JSONPATH">$.[*].attribute_id</stringProp> + <stringProp name="DEFAULT">NO_VALUE</stringProp> + <stringProp name="VARIABLE"/> + <stringProp name="SUBJECT">BODY</stringProp> + </com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor> + <hashTree/> + <com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor guiclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.gui.JSONPathExtractorGui" testclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor" testname="JSON Path Extractor: Extract option_values" enabled="true"> + <stringProp name="VAR">option_values</stringProp> + <stringProp name="JSONPATH">$.[*].values[0].value_index</stringProp> + <stringProp name="DEFAULT">NO_VALUE</stringProp> + <stringProp name="VARIABLE"/> + <stringProp name="SUBJECT">BODY</stringProp> + </com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor> + <hashTree/> + </hashTree> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Configurable Product ${_counter} Add To Cart" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="product" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${product_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product</stringProp> + </elementProp> + <elementProp name="related_product" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">related_product</stringProp> + </elementProp> + <elementProp name="qty" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">qty</stringProp> + </elementProp> + <elementProp name="form_key" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${form_key}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">form_key</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}checkout/cart/add/</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/product_browsing_and_adding_items_to_the_cart/configurable_product_add_to_cart.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <BeanShellPreProcessor guiclass="TestBeanGUI" testclass="BeanShellPreProcessor" testname="BeanShell PreProcessor" enabled="true"> + <boolProp name="resetInterpreter">false</boolProp> + <stringProp name="parameters"/> + <stringProp name="filename"/> + <stringProp name="script"> + try { + attribute_ids = vars.get("attribute_ids"); + option_values = vars.get("option_values"); + attribute_ids = attribute_ids.replace("[","").replace("]","").replace("\"", ""); + option_values = option_values.replace("[","").replace("]","").replace("\"", ""); + attribute_ids_array = attribute_ids.split(","); + option_values_array = option_values.split(","); + args = ctx.getCurrentSampler().getArguments(); + it = args.iterator(); + while (it.hasNext()) { + argument = it.next(); + if (argument.getStringValue().contains("${")) { + args.removeArgument(argument.getName()); + } + } + for (int i = 0; i < attribute_ids_array.length; i++) { + ctx.getCurrentSampler().addArgument("super_attribute[" + attribute_ids_array[i] + "]", option_values_array[i]); + } + } catch (Exception e) { + log.error("eror…", e); + } + </stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/configurable_product_add_to_cart_preprocessor.jmx</stringProp></BeanShellPreProcessor> + <hashTree/> + + <HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Header Manager" enabled="true"> + <collectionProp name="HeaderManager.headers"> + <elementProp name="" elementType="Header"> + <stringProp name="Header.name">X-Requested-With</stringProp> + <stringProp name="Header.value">XMLHttpRequest</stringProp> + </elementProp> + </collectionProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/http_header_manager_ajax.jmx</stringProp></HeaderManager> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Load Cart Section ${_counter}" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="sections" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">cart,messages</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">sections</stringProp> + </elementProp> + <elementProp name="force_new_section_timestamp" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">true</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">force_new_section_timestamp</stringProp> + </elementProp> + <elementProp name="_" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${__time()}${__Random(1,1000000)}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">_</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}customer/section/load/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/load_cart_section.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="210217247">You added ${product_name} to your <a href="${base_path}checkout/cart/">shopping cart</a>.</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="2057973164">This product is out of stock.</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">6</intProp> + </ResponseAssertion> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-350323027">\"summary_count\":${totalProductsAdded}</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + + <HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Header Manager" enabled="true"> + <collectionProp name="HeaderManager.headers"> + <elementProp name="" elementType="Header"> + <stringProp name="Header.name">X-Requested-With</stringProp> + <stringProp name="Header.value">XMLHttpRequest</stringProp> + </elementProp> + </collectionProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/http_header_manager_ajax.jmx</stringProp></HeaderManager> + <hashTree/> + </hashTree> + </hashTree> + + <GenericController guiclass="LogicControllerGui" testclass="GenericController" testname="Checkout" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/simple_controller.jmx</stringProp> +</GenericController> + <hashTree> + <JSR223PreProcessor guiclass="TestBeanGUI" testclass="JSR223PreProcessor" testname="Get region data" enabled="true"> + <stringProp name="scriptLanguage">javascript</stringProp> + <stringProp name="parameters"/> + <stringProp name="filename"/> + <stringProp name="cacheKey"/> + <stringProp name="script"> + vars.put("alabama_region_id", props.get("alabama_region_id")); + vars.put("california_region_id", props.get("california_region_id")); +</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/get_region_data.jmx</stringProp></JSR223PreProcessor> + <hashTree/> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Checkout start" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}checkout/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/guest_checkout/checkout_start.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-1403911775"><title>Checkout</title></stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-179817969"><title>Shopping Cart</title></stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">6</intProp> + </ResponseAssertion> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract Cart Id" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">cart_id</stringProp> + <stringProp name="RegexExtractor.regex">"quoteData":{"entity_id":"([^'"]+)",</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert Cart Id extracted" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="2845929">^.+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">cart_id</stringProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Checkout Email Available" enabled="true"> + <boolProp name="HTTPSampler.postBodyRaw">true</boolProp> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments"> + <collectionProp name="Arguments.arguments"> + <elementProp name="" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">false</boolProp> + <stringProp name="Argument.value">{"customerEmail":"test@example.com"}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}rest/default/V1/customers/isEmailAvailable</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/guest_checkout/checkout_email_available.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Header Manager" enabled="true"> + <collectionProp name="HeaderManager.headers"> + <elementProp name="" elementType="Header"> + <stringProp name="Header.name">Referer</stringProp> + <stringProp name="Header.value">${base_path}checkout/onepage/</stringProp> + </elementProp> + <elementProp name="Content-Type" elementType="Header"> + <stringProp name="Header.name">Content-Type</stringProp> + <stringProp name="Header.value">application/json; charset=UTF-8</stringProp> + </elementProp> + <elementProp name="X-Requested-With" elementType="Header"> + <stringProp name="Header.name">X-Requested-With</stringProp> + <stringProp name="Header.value">XMLHttpRequest</stringProp> + </elementProp> + <elementProp name="Accept" elementType="Header"> + <stringProp name="Header.name">Accept</stringProp> + <stringProp name="Header.value">application/json</stringProp> + </elementProp> + </collectionProp> + </HeaderManager> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="3569038">true</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">8</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Checkout Estimate Shipping Methods" enabled="true"> + <boolProp name="HTTPSampler.postBodyRaw">true</boolProp> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments"> + <collectionProp name="Arguments.arguments"> + <elementProp name="" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">false</boolProp> + <stringProp name="Argument.value">{"address":{"country_id":"US","postcode":"95630"}}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}rest/default/V1/guest-carts/${cart_id}/estimate-shipping-methods</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/guest_checkout/checkout_estimate_shipping_methods_with_postal_code.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Header Manager" enabled="true"> + <collectionProp name="HeaderManager.headers"> + <elementProp name="" elementType="Header"> + <stringProp name="Header.name">Referer</stringProp> + <stringProp name="Header.value">${base_path}checkout/onepage/</stringProp> + </elementProp> + <elementProp name="Content-Type" elementType="Header"> + <stringProp name="Header.name">Content-Type</stringProp> + <stringProp name="Header.value">application/json; charset=UTF-8</stringProp> + </elementProp> + <elementProp name="X-Requested-With" elementType="Header"> + <stringProp name="Header.name">X-Requested-With</stringProp> + <stringProp name="Header.value">XMLHttpRequest</stringProp> + </elementProp> + <elementProp name="Accept" elementType="Header"> + <stringProp name="Header.name">Accept</stringProp> + <stringProp name="Header.value">application/json</stringProp> + </elementProp> + </collectionProp> + </HeaderManager> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert success" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-1224567411">"available":true</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Checkout Billing/Shipping Information" enabled="true"> + <boolProp name="HTTPSampler.postBodyRaw">true</boolProp> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments"> + <collectionProp name="Arguments.arguments"> + <elementProp name="" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">false</boolProp> + <stringProp name="Argument.value">{"addressInformation":{"shipping_address":{"countryId":"US","regionId":"${california_region_id}","regionCode":"CA","region":"California","street":["10441 Jefferson Blvd ste 200"],"company":"","telephone":"3109450345","fax":"","postcode":"90232","city":"Culver City","firstname":"Name","lastname":"Lastname"},"shipping_method_code":"flatrate","shipping_carrier_code":"flatrate"}}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}rest/default/V1/guest-carts/${cart_id}/shipping-information</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/guest_checkout/checkout_billing_shipping_information.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Header Manager" enabled="true"> + <collectionProp name="HeaderManager.headers"> + <elementProp name="" elementType="Header"> + <stringProp name="Header.name">Referer</stringProp> + <stringProp name="Header.value">${base_path}checkout/onepage/</stringProp> + </elementProp> + <elementProp name="Content-Type" elementType="Header"> + <stringProp name="Header.name">Content-Type</stringProp> + <stringProp name="Header.value">application/json; charset=UTF-8</stringProp> + </elementProp> + <elementProp name="X-Requested-With" elementType="Header"> + <stringProp name="Header.name">X-Requested-With</stringProp> + <stringProp name="Header.value">XMLHttpRequest</stringProp> + </elementProp> + <elementProp name="Accept" elementType="Header"> + <stringProp name="Header.name">Accept</stringProp> + <stringProp name="Header.value">application/json</stringProp> + </elementProp> + </collectionProp> + </HeaderManager> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert success" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-1494218646">{"payment_methods":</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Checkout Payment Info/Place Order" enabled="true"> + <boolProp name="HTTPSampler.postBodyRaw">true</boolProp> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments"> + <collectionProp name="Arguments.arguments"> + <elementProp name="" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">false</boolProp> + <stringProp name="Argument.value">{"cartId":"${cart_id}","email":"test@example.com","paymentMethod":{"method":"checkmo","po_number":null,"additional_data":null},"billingAddress":{"countryId":"US","regionId":"${california_region_id}","regionCode":"CA","region":"California","street":["10441 Jefferson Blvd ste 200"],"company":"","telephone":"3109450345","fax":"","postcode":"90232","city":"Culver City","firstname":"Name","lastname":"Lastname"}}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}rest/default/V1/guest-carts/${cart_id}/payment-information</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/guest_checkout/checkout_payment_info_place_order.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Header Manager" enabled="true"> + <collectionProp name="HeaderManager.headers"> + <elementProp name="" elementType="Header"> + <stringProp name="Header.name">Referer</stringProp> + <stringProp name="Header.value">${base_path}checkout/onepage/</stringProp> + </elementProp> + <elementProp name="Content-Type" elementType="Header"> + <stringProp name="Header.name">Content-Type</stringProp> + <stringProp name="Header.value">application/json; charset=UTF-8</stringProp> + </elementProp> + <elementProp name="X-Requested-With" elementType="Header"> + <stringProp name="Header.name">X-Requested-With</stringProp> + <stringProp name="Header.value">XMLHttpRequest</stringProp> + </elementProp> + <elementProp name="Accept" elementType="Header"> + <stringProp name="Header.name">Accept</stringProp> + <stringProp name="Header.value">application/json</stringProp> + </elementProp> + </collectionProp> + </HeaderManager> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert success" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-297987887">"[0-9]+"</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + <com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor guiclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.gui.JSONPathExtractorGui" testclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor" testname="Extract cart id" enabled="true"> + <stringProp name="VAR">order_id</stringProp> + <stringProp name="JSONPATH">$</stringProp> + <stringProp name="DEFAULT"/> + <stringProp name="VARIABLE"/> + <stringProp name="SUBJECT">BODY</stringProp> + </com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="89649215">^\d+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">order_id</stringProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Checkout success" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}checkout/onepage/success/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/guest_checkout/checkout_success.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert success" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="494863233">Thank you for your purchase!</stringProp> + <stringProp name="1635682758">Your order # is</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + </hashTree> + </hashTree> + + + <ThroughputController guiclass="ThroughputControllerGui" testclass="ThroughputController" testname="[C] Checkout By Customer" enabled="true"> + <intProp name="ThroughputController.style">1</intProp> + <boolProp name="ThroughputController.perThread">false</boolProp> + <intProp name="ThroughputController.maxThroughput">1</intProp> + <stringProp name="ThroughputController.percentThroughput">${cCheckoutByCustomerPercentage}</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/_system/scenario_controller_tmpl.jmx</stringProp></ThroughputController> + <hashTree> + <JSR223PreProcessor guiclass="TestBeanGUI" testclass="JSR223PreProcessor" testname="Set Test Label" enabled="true"> + <stringProp name="script"> +var testLabel = "${testLabel}" ? " (${testLabel})" : ""; +if (testLabel + && sampler.getClass().getName() == 'org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy' +) { + if (sampler.getName().indexOf(testLabel) == -1) { + sampler.setName(sampler.getName() + testLabel); + } +} else if (sampler.getName().indexOf("SetUp - ") == -1) { + sampler.setName("SetUp - " + sampler.getName()); +} + </stringProp> + <stringProp name="scriptLanguage">javascript</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/_system/setup_label.jmx</stringProp></JSR223PreProcessor> + <hashTree/> + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Set Label" enabled="true"> + <stringProp name="BeanShellSampler.query"> + vars.put("testLabel", "[C] Checkout By Customer"); + </stringProp> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + </BeanShellSampler> + <hashTree/> + + <CookieManager guiclass="CookiePanel" testclass="CookieManager" testname="HTTP Cookie Manager" enabled="true"> + <collectionProp name="CookieManager.cookies"> + <elementProp name="product_list_limit" elementType="Cookie" testname="product_list_limit"> + <stringProp name="Cookie.value">30</stringProp> + <stringProp name="Cookie.domain">${host}</stringProp> + <stringProp name="Cookie.path">/</stringProp> + <boolProp name="Cookie.secure">false</boolProp> + <longProp name="Cookie.expires">0</longProp> + <boolProp name="Cookie.path_specified">true</boolProp> + <boolProp name="Cookie.domain_specified">true</boolProp> + </elementProp> + <elementProp name="product_list_limit" elementType="Cookie" testname="form_key"> + <stringProp name="Cookie.value">${form_key}</stringProp> + <stringProp name="Cookie.domain">${host}</stringProp> + <stringProp name="Cookie.path">${base_path}</stringProp> + <boolProp name="Cookie.secure">false</boolProp> + <longProp name="Cookie.expires">0</longProp> + <boolProp name="Cookie.path_specified">true</boolProp> + <boolProp name="Cookie.domain_specified">true</boolProp> + </elementProp> + </collectionProp> + <boolProp name="CookieManager.clearEachIteration">true</boolProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/http_cookie_manager.jmx</stringProp></CookieManager> + <hashTree/> + + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Init Random Generator" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/init_random_generator_setup.jmx</stringProp> + <stringProp name="BeanShellSampler.query"> +import java.util.Random; + +Random random = new Random(); +if (${seedForRandom} > 0) { + random.setSeed(${seedForRandom} + ${__threadNum}); +} + +vars.putObject("randomIntGenerator", random); + </stringProp> + <stringProp name="BeanShellSampler.filename"/> + <stringProp name="BeanShellSampler.parameters"/> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + </BeanShellSampler> + <hashTree/> + + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Init Total Products In Cart" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/init_total_products_in_cart_setup.jmx</stringProp> + <stringProp name="BeanShellSampler.query"> +vars.put("totalProductsAdded", "0"); + </stringProp> + <stringProp name="BeanShellSampler.filename"/> + <stringProp name="BeanShellSampler.parameters"/> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + </BeanShellSampler> + <hashTree/> + + <JSR223Sampler guiclass="TestBeanGUI" testclass="JSR223Sampler" testname="SetUp - Prepare Category Data" enabled="true"> + <stringProp name="scriptLanguage">javascript</stringProp> + <stringProp name="parameters"/> + <stringProp name="filename"/> + <stringProp name="cacheKey"/> + <stringProp name="script">random = vars.getObject("randomIntGenerator"); + +var categories = props.get("categories"); +number = random.nextInt(categories.length); + +vars.put("category_url_key", categories[number].url_key); +vars.put("category_name", categories[number].name); +vars.put("category_id", categories[number].id); +vars.putObject("category", categories[number]); + </stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/extract_category_setup.jmx</stringProp></JSR223Sampler> + <hashTree/> + + <CriticalSectionController guiclass="CriticalSectionControllerGui" testclass="CriticalSectionController" testname="Get Customer Email" enabled="true"> + <stringProp name="CriticalSectionController.lockName">get-email</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/lock_controller.jmx</stringProp></CriticalSectionController> + <hashTree> + + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Get Customer Email" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/get_customer_email.jmx</stringProp> + <stringProp name="BeanShellSampler.query"> +customerUserList = props.get("customer_emails_list"); +customerUser = customerUserList.poll(); +if (customerUser == null) { + SampleResult.setResponseMessage("customernUser list is empty"); + SampleResult.setResponseData("customerUser list is empty","UTF-8"); + IsSuccess=false; + SampleResult.setSuccessful(false); + SampleResult.setStopThread(true); +} +vars.put("customer_email", customerUser); + </stringProp> + <stringProp name="BeanShellSampler.filename"/> + <stringProp name="BeanShellSampler.parameters"/> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + </BeanShellSampler> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Open Home Page" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/open_home_page.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="571386695"><title>Home page</title></stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Open Login Page" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}customer/account/login/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/open_login_page.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="637394530"><title>Customer Login</title></stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Login" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="form_key" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${form_key}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">form_key</stringProp> + </elementProp> + <elementProp name="login[username]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${customer_email}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">login[username]</stringProp> + </elementProp> + <elementProp name="login[password]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${customer_password}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">login[password]</stringProp> + </elementProp> + <elementProp name="send" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">send</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}customer/account/loginPost/</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/login.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="1312950388"><title>My Account</title></stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract Address" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">addressId</stringProp> + <stringProp name="RegexExtractor.regex">customer/address/edit/id/([^'"]+)/</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert addressId extracted" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="2845929">^.+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">addressId</stringProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Load Customer Private Data" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="sections" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">sections</stringProp> + </elementProp> + <elementProp name="force_new_section_timestamp" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">false</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">force_new_section_timestamp</stringProp> + </elementProp> + <elementProp name="_" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${__time()}${__Random(1,1000000)}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">_</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}customer/section/load/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + </HTTPSamplerProxy> + <hashTree/> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Open Category" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${category_url_key}${url_suffix}</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/open_category.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="1210004667"><span class="base" data-ui-id="page-title">${category_name}</span></stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">6</intProp> + </ResponseAssertion> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Regular Expression Extractor: Extract category id" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">category_id</stringProp> + <stringProp name="RegexExtractor.regex"><li class="item category([^'"]+)">\s*<strong>${category_name}</strong>\s*</li></stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + <stringProp name="Scope.variable">simple_product_1_url_key</stringProp> + </RegexExtractor> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion: Assert category id" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="1191417111">^[0-9]+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">category_id</stringProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + + <LoopController guiclass="LoopControlPanel" testclass="LoopController" testname="Add Simple Products to Cart" enabled="true"> + <boolProp name="LoopController.continue_forever">true</boolProp> + <stringProp name="LoopController.loops">2</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/loop_controller.jmx</stringProp></LoopController> + <hashTree> + <CounterConfig guiclass="CounterConfigGui" testclass="CounterConfig" testname="Counter" enabled="true"> + <stringProp name="CounterConfig.start">1</stringProp> + <stringProp name="CounterConfig.end"/> + <stringProp name="CounterConfig.incr">1</stringProp> + <stringProp name="CounterConfig.name">_counter</stringProp> + <stringProp name="CounterConfig.format"/> + <boolProp name="CounterConfig.per_user">true</boolProp> + <boolProp name="CounterConfig.reset_on_tg_iteration">true</boolProp> + </CounterConfig> + <hashTree/> + + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Prepare Simple Product Data" enabled="true"> + <stringProp name="BeanShellSampler.query"> +import java.util.Random; + +Random random = vars.getObject("randomIntGenerator"); +number = random.nextInt(props.get("simple_products_list").size()); +product = props.get("simple_products_list").get(number); + +vars.put("product_url_key", product.get("url_key")); +vars.put("product_id", product.get("id")); +vars.put("product_name", product.get("title")); +vars.put("product_uenc", product.get("uenc")); +vars.put("product_sku", product.get("sku")); + </stringProp> + <stringProp name="BeanShellSampler.filename"/> + <stringProp name="BeanShellSampler.parameters"/> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/product_browsing_and_adding_items_to_the_cart/simple_products_setup.jmx</stringProp></BeanShellSampler> + <hashTree/> + + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Update Products Added Counter" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/loops/update_products_added_counter.jmx</stringProp> + <stringProp name="BeanShellSampler.query"> +productsAdded = Integer.parseInt(vars.get("totalProductsAdded")); +productsAdded = productsAdded + 1; + +vars.put("totalProductsAdded", String.valueOf(productsAdded)); + </stringProp> + <stringProp name="BeanShellSampler.filename"/> + <stringProp name="BeanShellSampler.parameters"/> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + </BeanShellSampler> + <hashTree/> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Simple Product ${_counter} View" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${product_url_key}${url_suffix}</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/product_browsing_and_adding_items_to_the_cart/product_view.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-1787050162"><span>In stock</span></stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Simple Product ${_counter} Add To Cart" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="product" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${product_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product</stringProp> + </elementProp> + <elementProp name="related_product" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">related_product</stringProp> + </elementProp> + <elementProp name="qty" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">qty</stringProp> + </elementProp> + <elementProp name="form_key" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${form_key}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">form_key</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}checkout/cart/add/</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/product_browsing_and_adding_items_to_the_cart/simple_product_add_to_cart.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Header Manager" enabled="true"> + <collectionProp name="HeaderManager.headers"> + <elementProp name="" elementType="Header"> + <stringProp name="Header.name">X-Requested-With</stringProp> + <stringProp name="Header.value">XMLHttpRequest</stringProp> + </elementProp> + </collectionProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/http_header_manager_ajax.jmx</stringProp></HeaderManager> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Load Cart Section ${_counter}" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="sections" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">cart,messages</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">sections</stringProp> + </elementProp> + <elementProp name="force_new_section_timestamp" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">true</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">force_new_section_timestamp</stringProp> + </elementProp> + <elementProp name="_" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${__time()}${__Random(1,1000000)}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">_</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}customer/section/load/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/load_cart_section.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="210217247">You added ${product_name} to your <a href="${base_path}checkout/cart/">shopping cart</a>.</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="2057973164">This product is out of stock.</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">6</intProp> + </ResponseAssertion> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-350323027">\"summary_count\":${totalProductsAdded}</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + + <HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Header Manager" enabled="true"> + <collectionProp name="HeaderManager.headers"> + <elementProp name="" elementType="Header"> + <stringProp name="Header.name">X-Requested-With</stringProp> + <stringProp name="Header.value">XMLHttpRequest</stringProp> + </elementProp> + </collectionProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/http_header_manager_ajax.jmx</stringProp></HeaderManager> + <hashTree/> + </hashTree> + </hashTree> + + <LoopController guiclass="LoopControlPanel" testclass="LoopController" testname="Add Configurable Products to Cart" enabled="true"> + <boolProp name="LoopController.continue_forever">true</boolProp> + <stringProp name="LoopController.loops">1</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/loop_controller.jmx</stringProp></LoopController> + <hashTree> + <CounterConfig guiclass="CounterConfigGui" testclass="CounterConfig" testname="Counter" enabled="true"> + <stringProp name="CounterConfig.start">1</stringProp> + <stringProp name="CounterConfig.end"/> + <stringProp name="CounterConfig.incr">1</stringProp> + <stringProp name="CounterConfig.name">_counter</stringProp> + <stringProp name="CounterConfig.format"/> + <boolProp name="CounterConfig.per_user">true</boolProp> + <boolProp name="CounterConfig.reset_on_tg_iteration">true</boolProp> + </CounterConfig> + <hashTree/> + + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Prepare Configurable Product Data" enabled="true"> + <stringProp name="BeanShellSampler.query"> +import java.util.Random; + +Random random = vars.getObject("randomIntGenerator"); +number = random.nextInt(props.get("configurable_products_list").size()); +product = props.get("configurable_products_list").get(number); + +vars.put("product_url_key", product.get("url_key")); +vars.put("product_id", product.get("id")); +vars.put("product_name", product.get("title")); +vars.put("product_uenc", product.get("uenc")); +vars.put("product_sku", product.get("sku")); + </stringProp> + <stringProp name="BeanShellSampler.filename"/> + <stringProp name="BeanShellSampler.parameters"/> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/product_browsing_and_adding_items_to_the_cart/configurable_products_setup.jmx</stringProp></BeanShellSampler> + <hashTree/> + + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Update Products Added Counter" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/loops/update_products_added_counter.jmx</stringProp> + <stringProp name="BeanShellSampler.query"> +productsAdded = Integer.parseInt(vars.get("totalProductsAdded")); +productsAdded = productsAdded + 1; + +vars.put("totalProductsAdded", String.valueOf(productsAdded)); + </stringProp> + <stringProp name="BeanShellSampler.filename"/> + <stringProp name="BeanShellSampler.parameters"/> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + </BeanShellSampler> + <hashTree/> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Configurable Product ${_counter} View" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${product_url_key}${url_suffix}</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/product_browsing_and_adding_items_to_the_cart/product_view.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-1787050162"><span>In stock</span></stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + + <LoopController guiclass="LoopControlPanel" testclass="LoopController" testname="SetUp - Get Configurable Product Options" enabled="true"> + <boolProp name="LoopController.continue_forever">true</boolProp> + <stringProp name="LoopController.loops">1</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/get_configurable_product_options.jmx</stringProp></LoopController> + <hashTree> + <HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Header Manager" enabled="true"> + <collectionProp name="HeaderManager.headers"> + <elementProp name="" elementType="Header"> + <stringProp name="Header.name">Content-Type</stringProp> + <stringProp name="Header.value">application/json</stringProp> + </elementProp> + <elementProp name="" elementType="Header"> + <stringProp name="Header.name">Accept</stringProp> + <stringProp name="Header.value">*/*</stringProp> + </elementProp> + </collectionProp> + </HeaderManager> + <hashTree/> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="SetUp - Admin Token Retrieval" enabled="true"> + <boolProp name="HTTPSampler.postBodyRaw">true</boolProp> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments"> + <collectionProp name="Arguments.arguments"> + <elementProp name="" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">false</boolProp> + <stringProp name="Argument.value">{"username":"${admin_user}","password":"${admin_password}"}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}rest/V1/integration/admin/token</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + </HTTPSamplerProxy> + <hashTree> + <com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor guiclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.gui.JSONPathExtractorGui" testclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor" testname="jp@gc - JSON Path Extractor" enabled="true"> + <stringProp name="VAR">admin_token</stringProp> + <stringProp name="JSONPATH">$</stringProp> + <stringProp name="DEFAULT"/> + <stringProp name="VARIABLE"/> + <stringProp name="SUBJECT">BODY</stringProp> + </com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert token not null" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="484395188">^[a-z0-9-]+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">admin_token</stringProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + <HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Header Manager" enabled="true"> + <collectionProp name="HeaderManager.headers"> + <elementProp name="" elementType="Header"> + <stringProp name="Header.name">Authorization</stringProp> + <stringProp name="Header.value">Bearer ${admin_token}</stringProp> + </elementProp> + </collectionProp> + </HeaderManager> + <hashTree/> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Get Configurable Product Options" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}rest/V1/configurable-products/${product_sku}/options/all</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + </HTTPSamplerProxy> + <hashTree> + <com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor guiclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.gui.JSONPathExtractorGui" testclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor" testname="JSON Path Extractor: Extract attribute_ids" enabled="true"> + <stringProp name="VAR">attribute_ids</stringProp> + <stringProp name="JSONPATH">$.[*].attribute_id</stringProp> + <stringProp name="DEFAULT">NO_VALUE</stringProp> + <stringProp name="VARIABLE"/> + <stringProp name="SUBJECT">BODY</stringProp> + </com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor> + <hashTree/> + <com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor guiclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.gui.JSONPathExtractorGui" testclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor" testname="JSON Path Extractor: Extract option_values" enabled="true"> + <stringProp name="VAR">option_values</stringProp> + <stringProp name="JSONPATH">$.[*].values[0].value_index</stringProp> + <stringProp name="DEFAULT">NO_VALUE</stringProp> + <stringProp name="VARIABLE"/> + <stringProp name="SUBJECT">BODY</stringProp> + </com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor> + <hashTree/> + </hashTree> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Configurable Product ${_counter} Add To Cart" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="product" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${product_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product</stringProp> + </elementProp> + <elementProp name="related_product" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">related_product</stringProp> + </elementProp> + <elementProp name="qty" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">qty</stringProp> + </elementProp> + <elementProp name="form_key" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${form_key}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">form_key</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}checkout/cart/add/</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/product_browsing_and_adding_items_to_the_cart/configurable_product_add_to_cart.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <BeanShellPreProcessor guiclass="TestBeanGUI" testclass="BeanShellPreProcessor" testname="BeanShell PreProcessor" enabled="true"> + <boolProp name="resetInterpreter">false</boolProp> + <stringProp name="parameters"/> + <stringProp name="filename"/> + <stringProp name="script"> + try { + attribute_ids = vars.get("attribute_ids"); + option_values = vars.get("option_values"); + attribute_ids = attribute_ids.replace("[","").replace("]","").replace("\"", ""); + option_values = option_values.replace("[","").replace("]","").replace("\"", ""); + attribute_ids_array = attribute_ids.split(","); + option_values_array = option_values.split(","); + args = ctx.getCurrentSampler().getArguments(); + it = args.iterator(); + while (it.hasNext()) { + argument = it.next(); + if (argument.getStringValue().contains("${")) { + args.removeArgument(argument.getName()); + } + } + for (int i = 0; i < attribute_ids_array.length; i++) { + ctx.getCurrentSampler().addArgument("super_attribute[" + attribute_ids_array[i] + "]", option_values_array[i]); + } + } catch (Exception e) { + log.error("eror…", e); + } + </stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/configurable_product_add_to_cart_preprocessor.jmx</stringProp></BeanShellPreProcessor> + <hashTree/> + + <HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Header Manager" enabled="true"> + <collectionProp name="HeaderManager.headers"> + <elementProp name="" elementType="Header"> + <stringProp name="Header.name">X-Requested-With</stringProp> + <stringProp name="Header.value">XMLHttpRequest</stringProp> + </elementProp> + </collectionProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/http_header_manager_ajax.jmx</stringProp></HeaderManager> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Load Cart Section ${_counter}" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="sections" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">cart,messages</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">sections</stringProp> + </elementProp> + <elementProp name="force_new_section_timestamp" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">true</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">force_new_section_timestamp</stringProp> + </elementProp> + <elementProp name="_" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${__time()}${__Random(1,1000000)}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">_</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}customer/section/load/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/load_cart_section.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="210217247">You added ${product_name} to your <a href="${base_path}checkout/cart/">shopping cart</a>.</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="2057973164">This product is out of stock.</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">6</intProp> + </ResponseAssertion> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-350323027">\"summary_count\":${totalProductsAdded}</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + + <HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Header Manager" enabled="true"> + <collectionProp name="HeaderManager.headers"> + <elementProp name="" elementType="Header"> + <stringProp name="Header.name">X-Requested-With</stringProp> + <stringProp name="Header.value">XMLHttpRequest</stringProp> + </elementProp> + </collectionProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/http_header_manager_ajax.jmx</stringProp></HeaderManager> + <hashTree/> + </hashTree> + </hashTree> + + <GenericController guiclass="LogicControllerGui" testclass="GenericController" testname="Checkout" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/simple_controller.jmx</stringProp> +</GenericController> + <hashTree> + <JSR223PreProcessor guiclass="TestBeanGUI" testclass="JSR223PreProcessor" testname="Get region data" enabled="true"> + <stringProp name="scriptLanguage">javascript</stringProp> + <stringProp name="parameters"/> + <stringProp name="filename"/> + <stringProp name="cacheKey"/> + <stringProp name="script"> + vars.put("alabama_region_id", props.get("alabama_region_id")); + vars.put("california_region_id", props.get("california_region_id")); +</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/get_region_data.jmx</stringProp></JSR223PreProcessor> + <hashTree/> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Checkout start" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}checkout/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/customer_checkout/checkout_start.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-1403911775"><title>Checkout</title></stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-179817969"><title>Shopping Cart</title></stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">6</intProp> + </ResponseAssertion> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract Cart Id" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">cart_id</stringProp> + <stringProp name="RegexExtractor.regex">"quoteData":{"entity_id":"([^'"]+)",</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract Address Id" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">address_id</stringProp> + <stringProp name="RegexExtractor.regex">"default_billing":"([^'"]+)",</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract Customer Id" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">customer_id</stringProp> + <stringProp name="RegexExtractor.regex">"customer_id":([^'",]+),</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert Cart Id extracted" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="2845929">^.+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">cart_id</stringProp> + </ResponseAssertion> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert Address Id extracted" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="576002869">[0-9]+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">address_id</stringProp> + </ResponseAssertion> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert Customer Id extracted" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="576002869">[0-9]+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">customer_id</stringProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Checkout Estimate Shipping Methods" enabled="true"> + <boolProp name="HTTPSampler.postBodyRaw">true</boolProp> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments"> + <collectionProp name="Arguments.arguments"> + <elementProp name="" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">false</boolProp> + <stringProp name="Argument.value">{"addressId":"${addressId}"}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}rest/default/V1/carts/mine/estimate-shipping-methods-by-address-id</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/customer_checkout/checkout_estimate_shipping_methods.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Header Manager" enabled="true"> + <collectionProp name="HeaderManager.headers"> + <elementProp name="" elementType="Header"> + <stringProp name="Header.name">Referer</stringProp> + <stringProp name="Header.value">${base_path}checkout/onepage/</stringProp> + </elementProp> + <elementProp name="Content-Type" elementType="Header"> + <stringProp name="Header.name">Content-Type</stringProp> + <stringProp name="Header.value">application/json; charset=UTF-8</stringProp> + </elementProp> + <elementProp name="X-Requested-With" elementType="Header"> + <stringProp name="Header.name">X-Requested-With</stringProp> + <stringProp name="Header.value">XMLHttpRequest</stringProp> + </elementProp> + <elementProp name="Accept" elementType="Header"> + <stringProp name="Header.name">Accept</stringProp> + <stringProp name="Header.value">application/json</stringProp> + </elementProp> + </collectionProp> + </HeaderManager> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert success" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-1224567411">"available":true</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Checkout Billing/Shipping Information" enabled="true"> + <boolProp name="HTTPSampler.postBodyRaw">true</boolProp> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments"> + <collectionProp name="Arguments.arguments"> + <elementProp name="" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">false</boolProp> + <stringProp name="Argument.value">{"addressInformation":{"shipping_address":{"customerAddressId":"${address_id}","countryId":"US","regionId":"${alabama_region_id}","regionCode":"AL","region":"Alabama","customerId":"${customer_id}","street":["123 Freedom Blvd. #123"],"telephone":"022-333-4455","postcode":"123123","city":"Fayetteville","firstname":"Anthony","lastname":"Nealy"},"shipping_method_code":"flatrate","shipping_carrier_code":"flatrate"}}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}rest/default/V1/carts/mine/shipping-information</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/customer_checkout/checkout_billing_shipping_information.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Header Manager" enabled="true"> + <collectionProp name="HeaderManager.headers"> + <elementProp name="" elementType="Header"> + <stringProp name="Header.name">Referer</stringProp> + <stringProp name="Header.value">${host}${base_path}checkout/onepage</stringProp> + </elementProp> + <elementProp name="Content-Type" elementType="Header"> + <stringProp name="Header.name">Content-Type</stringProp> + <stringProp name="Header.value">application/json; charset=UTF-8</stringProp> + </elementProp> + <elementProp name="X-Requested-With" elementType="Header"> + <stringProp name="Header.name">X-Requested-With</stringProp> + <stringProp name="Header.value">XMLHttpRequest</stringProp> + </elementProp> + <elementProp name="Accept" elementType="Header"> + <stringProp name="Header.name">Accept</stringProp> + <stringProp name="Header.value">application/json</stringProp> + </elementProp> + </collectionProp> + </HeaderManager> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert success" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-740937264">{"payment_methods"</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Checkout Payment Info/Place Order" enabled="true"> + <boolProp name="HTTPSampler.postBodyRaw">true</boolProp> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments"> + <collectionProp name="Arguments.arguments"> + <elementProp name="" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">false</boolProp> + <stringProp name="Argument.value">{"cartId":"${cart_id}","paymentMethod":{"method":"checkmo","po_number":null,"additional_data":null},"billingAddress":{"customerAddressId":"${address_id}","countryId":"US","regionId":"${alabama_region_id}","regionCode":"AL","region":"Alabama","customerId":"${customer_id}","street":["123 Freedom Blvd. #123"],"telephone":"022-333-4455","postcode":"123123","city":"Fayetteville","firstname":"Anthony","lastname":"Nealy"}}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}rest/default/V1/carts/mine/payment-information</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/customer_checkout/checkout_payment_info_place_order.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Header Manager" enabled="true"> + <collectionProp name="HeaderManager.headers"> + <elementProp name="" elementType="Header"> + <stringProp name="Header.name">Referer</stringProp> + <stringProp name="Header.value">${host}${base_path}checkout/onepage</stringProp> + </elementProp> + <elementProp name="" elementType="Header"> + <stringProp name="Header.name">Content-Type</stringProp> + <stringProp name="Header.value">application/json; charset=UTF-8 </stringProp> + </elementProp> + <elementProp name="" elementType="Header"> + <stringProp name="Header.name">Accept</stringProp> + <stringProp name="Header.value">application/json</stringProp> + </elementProp> + <elementProp name="" elementType="Header"> + <stringProp name="Header.name">X-Requested-With</stringProp> + <stringProp name="Header.value">XMLHttpRequest</stringProp> + </elementProp> + </collectionProp> + </HeaderManager> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert order number" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-297987887">"[0-9]+"</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Checkout success" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}checkout/onepage/success/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/customer_checkout/checkout_success.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert success" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="494863233">Thank you for your purchase!</stringProp> + <stringProp name="-1590086334">Your order number is</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Logout" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}customer/account/logout/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/logout.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert success" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="1723813687">You are signed out.</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + + <BeanShellPostProcessor guiclass="TestBeanGUI" testclass="BeanShellPostProcessor" testname="Clear Cookie" enabled="true"> + <boolProp name="resetInterpreter">false</boolProp> + <stringProp name="parameters"/> + <stringProp name="filename"/> + <stringProp name="script">curSampler = ctx.getCurrentSampler(); +if(curSampler.getName().contains("Checkout success")) { + manager = curSampler.getCookieManager(); + manager.clear(); +} +</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/customer_checkout/checkout_clear_cookie.jmx</stringProp></BeanShellPostProcessor> + <hashTree/> + + <BeanShellPostProcessor guiclass="TestBeanGUI" testclass="BeanShellPostProcessor" testname="Return Customer to Pool" enabled="true"> + <boolProp name="resetInterpreter">false</boolProp> + <stringProp name="parameters"/> + <stringProp name="filename"/> + <stringProp name="script"> +customerUserList = props.get("customer_emails_list"); +customerUserList.add(vars.get("customer_email")); + </stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/return_email_to_pool.jmx</stringProp></BeanShellPostProcessor> + <hashTree/> + </hashTree> + </hashTree> + + + <ThroughputController guiclass="ThroughputControllerGui" testclass="ThroughputController" testname="[C] Account management" enabled="true"> + <intProp name="ThroughputController.style">1</intProp> + <boolProp name="ThroughputController.perThread">false</boolProp> + <intProp name="ThroughputController.maxThroughput">1</intProp> + <stringProp name="ThroughputController.percentThroughput">${cAccountManagementPercentage}</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/_system/scenario_controller_tmpl.jmx</stringProp></ThroughputController> + <hashTree> + <JSR223PreProcessor guiclass="TestBeanGUI" testclass="JSR223PreProcessor" testname="Set Test Label" enabled="true"> + <stringProp name="script"> +var testLabel = "${testLabel}" ? " (${testLabel})" : ""; +if (testLabel + && sampler.getClass().getName() == 'org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy' +) { + if (sampler.getName().indexOf(testLabel) == -1) { + sampler.setName(sampler.getName() + testLabel); + } +} else if (sampler.getName().indexOf("SetUp - ") == -1) { + sampler.setName("SetUp - " + sampler.getName()); +} + </stringProp> + <stringProp name="scriptLanguage">javascript</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/_system/setup_label.jmx</stringProp></JSR223PreProcessor> + <hashTree/> + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Set Label" enabled="true"> + <stringProp name="BeanShellSampler.query"> + vars.put("testLabel", "[C] Account management"); + </stringProp> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + </BeanShellSampler> + <hashTree/> + + <CookieManager guiclass="CookiePanel" testclass="CookieManager" testname="HTTP Cookie Manager" enabled="true"> + <collectionProp name="CookieManager.cookies"> + <elementProp name="product_list_limit" elementType="Cookie" testname="product_list_limit"> + <stringProp name="Cookie.value">30</stringProp> + <stringProp name="Cookie.domain">${host}</stringProp> + <stringProp name="Cookie.path">/</stringProp> + <boolProp name="Cookie.secure">false</boolProp> + <longProp name="Cookie.expires">0</longProp> + <boolProp name="Cookie.path_specified">true</boolProp> + <boolProp name="Cookie.domain_specified">true</boolProp> + </elementProp> + <elementProp name="product_list_limit" elementType="Cookie" testname="form_key"> + <stringProp name="Cookie.value">${form_key}</stringProp> + <stringProp name="Cookie.domain">${host}</stringProp> + <stringProp name="Cookie.path">${base_path}</stringProp> + <boolProp name="Cookie.secure">false</boolProp> + <longProp name="Cookie.expires">0</longProp> + <boolProp name="Cookie.path_specified">true</boolProp> + <boolProp name="Cookie.domain_specified">true</boolProp> + </elementProp> + </collectionProp> + <boolProp name="CookieManager.clearEachIteration">true</boolProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/http_cookie_manager.jmx</stringProp></CookieManager> + <hashTree/> + + <CriticalSectionController guiclass="CriticalSectionControllerGui" testclass="CriticalSectionController" testname="Get Customer Email" enabled="true"> + <stringProp name="CriticalSectionController.lockName">get-email</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/lock_controller.jmx</stringProp></CriticalSectionController> + <hashTree> + + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Get Customer Email" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/get_customer_email.jmx</stringProp> + <stringProp name="BeanShellSampler.query"> +customerUserList = props.get("customer_emails_list"); +customerUser = customerUserList.poll(); +if (customerUser == null) { + SampleResult.setResponseMessage("customernUser list is empty"); + SampleResult.setResponseData("customerUser list is empty","UTF-8"); + IsSuccess=false; + SampleResult.setSuccessful(false); + SampleResult.setStopThread(true); +} +vars.put("customer_email", customerUser); + </stringProp> + <stringProp name="BeanShellSampler.filename"/> + <stringProp name="BeanShellSampler.parameters"/> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + </BeanShellSampler> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Open Home Page" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/open_home_page.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="571386695"><title>Home page</title></stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Open Login Page" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}customer/account/login/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/open_login_page.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="637394530"><title>Customer Login</title></stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Login" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="form_key" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${form_key}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">form_key</stringProp> + </elementProp> + <elementProp name="login[username]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${customer_email}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">login[username]</stringProp> + </elementProp> + <elementProp name="login[password]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${customer_password}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">login[password]</stringProp> + </elementProp> + <elementProp name="send" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">send</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}customer/account/loginPost/</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/login.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="1312950388"><title>My Account</title></stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract Address" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">addressId</stringProp> + <stringProp name="RegexExtractor.regex">customer/address/edit/id/([^'"]+)/</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert addressId extracted" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="2845929">^.+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">addressId</stringProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Load Customer Private Data" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="sections" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">sections</stringProp> + </elementProp> + <elementProp name="force_new_section_timestamp" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">false</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">force_new_section_timestamp</stringProp> + </elementProp> + <elementProp name="_" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${__time()}${__Random(1,1000000)}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">_</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}customer/section/load/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + </HTTPSamplerProxy> + <hashTree/> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="My Orders" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}sales/order/history/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/account_management/my_orders.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="220295440"><title>My Orders</title></stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract orderId" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">orderId</stringProp> + <stringProp name="RegexExtractor.regex">sales/order/view/order_id/(\d+)/</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default">NOT_FOUND</stringProp> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + </hashTree> + + <IfController guiclass="IfControllerPanel" testclass="IfController" testname="If Orders Controller" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/account_management/if_orders.jmx</stringProp> + <stringProp name="IfController.condition">"${orderId}" != "NOT_FOUND"</stringProp> + <boolProp name="IfController.evaluateAll">false</boolProp> + </IfController> + <hashTree> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="View Order" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}sales/order/view/order_id/${orderId}</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + </HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="1956770127"><title>Order #</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract shipment tab" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">shipment_tab</stringProp> + <stringProp name="RegexExtractor.regex">sales/order/shipment/order_id/(\d+)..Order Shipments</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default">NOT_FOUND</stringProp> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + </hashTree> + <IfController guiclass="IfControllerPanel" testclass="IfController" testname="If Shipments Controller" enabled="true"> + <stringProp name="TestPlan.comments">May not have shipped</stringProp> + <stringProp name="IfController.condition">"${shipment_tab}" != "NOT_FOUND"</stringProp> + <boolProp name="IfController.evaluateAll">false</boolProp> + </IfController> + <hashTree> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="View Order Shipments" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}sales/order/shipment/order_id/${orderId}</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + </HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="120578727">Track this shipment</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract popup link" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">popupLink</stringProp> + <stringProp name="RegexExtractor.regex">popupWindow": {"windowURL":"([^'"]+)",</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + </hashTree> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Track Shipment" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${popupLink}</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + </HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-760430210"><title>Tracking Information</title></stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + </hashTree> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="My Downloadable Products" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}downloadable/customer/products</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/account_management/my_downloadable_products.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="358050505"><title>My Downloadable Products</title></stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract orderId" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">orderId</stringProp> + <stringProp name="RegexExtractor.regex">sales/order/view/order_id/(\d+)/</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default">NOT_FOUND</stringProp> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract linkId" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">linkId</stringProp> + <stringProp name="RegexExtractor.regex">downloadable/download/link/id/(\d+)/</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + </hashTree> + + <IfController guiclass="IfControllerPanel" testclass="IfController" testname="If Downloadables Controller" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/account_management/if_downloadables.jmx</stringProp> + <stringProp name="IfController.condition">"${orderId}" != "NOT_FOUND"</stringProp> + <boolProp name="IfController.evaluateAll">false</boolProp> + </IfController> + <hashTree> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="View Downloadable Product" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}sales/order/view/order_id/${orderId}</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/account_management/view_downloadable_products.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="1956770127"><title>Order #</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Download Product" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}downloadable/download/link/id/${linkId}</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/account_management/download_product.jmx</stringProp></HTTPSamplerProxy> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="My Wish List" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}wishlist</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + </HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-1907714722"><title>My Wish List</title></stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract wishlistId" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">wishlistId</stringProp> + <stringProp name="RegexExtractor.regex">wishlist/index/update/wishlist_id/([^'"]+)/</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/account_management/my_wish_list.jmx</stringProp> + </RegexExtractor> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Verify that there are items in the wishlist" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">buttonTitle</stringProp> + <stringProp name="RegexExtractor.regex">Update Wish List</stringProp> + <stringProp name="RegexExtractor.template">FOUND</stringProp> + <stringProp name="RegexExtractor.default">NOT_FOUND</stringProp> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + </hashTree> + + <IfController guiclass="IfControllerPanel" testclass="IfController" testname="If Wish List Controller" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/account_management/if_wishlist.jmx</stringProp> + <stringProp name="IfController.condition">"${buttonTitle}" === "FOUND"</stringProp> + <boolProp name="IfController.evaluateAll">false</boolProp> + </IfController> + <hashTree> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Share Wish List" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}wishlist/index/share/wishlist_id/${wishlistId}/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/account_management/share_wish_list.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="1257102154"><title>Wish List Sharing</title></stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Send Wish List" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="form_key" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${form_key}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">form_key</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="emails" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${customer_email}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">emails</stringProp> + </elementProp> + <elementProp name="message" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">[TEST] See my wishlist!!!</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">message</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}wishlist/index/send/wishlist_id/${wishlistId}/</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/account_management/send_wish_list.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-1907714722"><title>My Wish List</title></stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Logout" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}customer/account/logout/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/logout.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert success" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="1723813687">You are signed out.</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + + <BeanShellPostProcessor guiclass="TestBeanGUI" testclass="BeanShellPostProcessor" testname="Return Customer to Pool" enabled="true"> + <boolProp name="resetInterpreter">false</boolProp> + <stringProp name="parameters"/> + <stringProp name="filename"/> + <stringProp name="script"> +customerUserList = props.get("customer_emails_list"); +customerUserList.add(vars.get("customer_email")); + </stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/return_email_to_pool.jmx</stringProp></BeanShellPostProcessor> + <hashTree/> + </hashTree> + </hashTree> + + + <ThroughputController guiclass="ThroughputControllerGui" testclass="ThroughputController" testname="[C] Admin CMS Management" enabled="true"> + <intProp name="ThroughputController.style">1</intProp> + <boolProp name="ThroughputController.perThread">false</boolProp> + <intProp name="ThroughputController.maxThroughput">1</intProp> + <stringProp name="ThroughputController.percentThroughput">${cAdminCMSManagementPercentage}</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/_system/scenario_controller_tmpl.jmx</stringProp></ThroughputController> + <hashTree> + <JSR223PreProcessor guiclass="TestBeanGUI" testclass="JSR223PreProcessor" testname="Set Test Label" enabled="true"> + <stringProp name="script"> +var testLabel = "${testLabel}" ? " (${testLabel})" : ""; +if (testLabel + && sampler.getClass().getName() == 'org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy' +) { + if (sampler.getName().indexOf(testLabel) == -1) { + sampler.setName(sampler.getName() + testLabel); + } +} else if (sampler.getName().indexOf("SetUp - ") == -1) { + sampler.setName("SetUp - " + sampler.getName()); +} + </stringProp> + <stringProp name="scriptLanguage">javascript</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/_system/setup_label.jmx</stringProp></JSR223PreProcessor> + <hashTree/> + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Set Label" enabled="true"> + <stringProp name="BeanShellSampler.query"> + vars.put("testLabel", "[C] Admin CMS Management"); + </stringProp> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + </BeanShellSampler> + <hashTree/> + + <JSR223PostProcessor guiclass="TestBeanGUI" testclass="JSR223PostProcessor" testname="Get admin form key PostProcessor" enabled="true"> + <stringProp name="script"> + function getFormKeyFromResponse() + { + var url = prev.getUrlAsString(), + responseCode = prev.getResponseCode(), + formKey = null; + searchPattern = /var FORM_KEY = '(.+)'/; + if (responseCode == "200" && url) { + response = prev.getResponseDataAsString(); + formKey = response && response.match(searchPattern) ? response.match(searchPattern)[1] : null; + } + return formKey; + } + + formKey = vars.get("form_key_storage"); + + currentFormKey = getFormKeyFromResponse(); + + if (currentFormKey != null && currentFormKey != formKey) { + vars.put("form_key_storage", currentFormKey); + } + </stringProp> + <stringProp name="scriptLanguage">javascript</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin/handle_admin_form_key.jmx</stringProp></JSR223PostProcessor> + <hashTree/> + <JSR223PreProcessor guiclass="TestBeanGUI" testclass="JSR223PreProcessor" testname="Set admin form key PreProcessor" enabled="true"> + <stringProp name="script"> + formKey = vars.get("form_key_storage"); + if (formKey + && sampler.getClass().getName() == 'org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy' + && sampler.getMethod() == "POST") + { + arguments = sampler.getArguments(); + for (i=0; i<arguments.getArgumentCount(); i++) + { + argument = arguments.getArgument(i); + if (argument.getName() == 'form_key' && argument.getValue() != formKey) { + log.info("admin form key updated: " + argument.getValue() + " => " + formKey); + argument.setValue(formKey); + } + } + } + </stringProp> + <stringProp name="scriptLanguage">javascript</stringProp> + </JSR223PreProcessor> + <hashTree/> + + <CookieManager guiclass="CookiePanel" testclass="CookieManager" testname="HTTP Cookie Manager" enabled="true"> + <collectionProp name="CookieManager.cookies"/> + <boolProp name="CookieManager.clearEachIteration">false</boolProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/http_cookie_manager_without_clear_each_iteration.jmx</stringProp></CookieManager> + <hashTree/> + + <GenericController guiclass="LogicControllerGui" testclass="GenericController" testname="Admin Login" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/simple_controller.jmx</stringProp> +</GenericController> + <hashTree> + <CriticalSectionController guiclass="CriticalSectionControllerGui" testclass="CriticalSectionController" testname="Admin Login Lock" enabled="true"> + <stringProp name="CriticalSectionController.lockName">get-admin-email</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/lock_controller.jmx</stringProp></CriticalSectionController> + <hashTree> + + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Get Admin Email" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/get_admin_email.jmx</stringProp> + <stringProp name="BeanShellSampler.query"> +adminUserList = props.get("adminUserList"); +adminUserListIterator = props.get("adminUserListIterator"); +adminUsersDistribution = Integer.parseInt(vars.get("admin_users_distribution_per_admin_pool")); + +if (adminUsersDistribution == 1) { + adminUser = adminUserList.poll(); +} else { + if (!adminUserListIterator.hasNext()) { + adminUserListIterator = adminUserList.descendingIterator(); + } + + adminUser = adminUserListIterator.next(); +} + +if (adminUser == null) { + SampleResult.setResponseMessage("adminUser list is empty"); + SampleResult.setResponseData("adminUser list is empty","UTF-8"); + IsSuccess=false; + SampleResult.setSuccessful(false); + SampleResult.setStopThread(true); +} +vars.put("admin_user", adminUser); + </stringProp> + <stringProp name="BeanShellSampler.filename"/> + <stringProp name="BeanShellSampler.parameters"/> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + </BeanShellSampler> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="SetUp - Login" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_login/admin_login.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert login form shown" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-1397214398">Welcome</stringProp> + <stringProp name="-515240035"><title>Magento Admin</title></stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract form key" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">admin_form_key</stringProp> + <stringProp name="RegexExtractor.regex"><input name="form_key" type="hidden" value="([^'"]+)" /></stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert form_key extracted" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="2845929">^.+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">admin_form_key</stringProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="SetUp - Login Submit Form" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="dummy" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">dummy</stringProp> + </elementProp> + <elementProp name="form_key" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_form_key}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">form_key</stringProp> + </elementProp> + <elementProp name="login[password]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_password}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">login[password]</stringProp> + </elementProp> + <elementProp name="login[username]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_user}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">login[username]</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/dashboard/</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <stringProp name="HTTPSampler.implementation">Java</stringProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_login/admin_login_submit_form.jmx</stringProp> + </HTTPSamplerProxy> + <hashTree> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract form key" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">admin_form_key</stringProp> + <stringProp name="RegexExtractor.regex"><input name="form_key" type="hidden" value="([^'"]+)" /></stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_login/admin_retrieve_form_key.jmx</stringProp></RegexExtractor> + <hashTree/> + </hashTree> + </hashTree> + + <GenericController guiclass="LogicControllerGui" testclass="GenericController" testname="Simple Controller" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/simple_controller.jmx</stringProp> +</GenericController> + <hashTree> + <TestFragmentController guiclass="TestFragmentControllerGui" testclass="TestFragmentController" testname="Admin CMS Management" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_cms_management/admin_cms_management.jmx</stringProp> +</TestFragmentController> + <hashTree> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Landing Page" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/cms/page/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + </HTTPSamplerProxy> + <hashTree/> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Create New" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/cms/page/new</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + </HTTPSamplerProxy> + <hashTree/> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Save" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="content" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"><p>CMS Content ${__time(YMDHMS)}-${__threadNum}-${__Random(1,1000000)}</p></stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">content</stringProp> + </elementProp> + <elementProp name="content_heading" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">content_heading</stringProp> + </elementProp> + <elementProp name="form_key" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_form_key}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">form_key</stringProp> + </elementProp> + <elementProp name="identifier" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">identifier</stringProp> + </elementProp> + <elementProp name="is_active" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">is_active</stringProp> + </elementProp> + <elementProp name="layout_update_xml" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">layout_update_xml</stringProp> + </elementProp> + <elementProp name="meta_description" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">meta_description</stringProp> + </elementProp> + <elementProp name="meta_keywords" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">meta_keywords</stringProp> + </elementProp> + <elementProp name="meta_title" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">meta_title</stringProp> + </elementProp> + <elementProp name="nodes_data" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">false</boolProp> + <stringProp name="Argument.value">{}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">nodes_data</stringProp> + </elementProp> + <elementProp name="node_ids" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">node_ids</stringProp> + </elementProp> + <elementProp name="page_id" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">page_id</stringProp> + </elementProp> + <elementProp name="page_layout" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1column</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">page_layout</stringProp> + </elementProp> + <elementProp name="store_id[0]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">store_id[0]</stringProp> + </elementProp> + <elementProp name="title" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">CMS Title ${__time(YMDHMS)}-${__threadNum}-${__Random(1,1000000)}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">title</stringProp> + </elementProp> + <elementProp name="website_root" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">website_root</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/cms/page/save/</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + </HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-398886250">You saved the page.</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">16</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + <TestAction guiclass="TestActionGui" testclass="TestAction" testname="Pause" enabled="true"> + <intProp name="ActionProcessor.action">1</intProp> + <intProp name="ActionProcessor.target">0</intProp> + <stringProp name="ActionProcessor.duration">${__javaScript(Math.round(${adminCMSManagementDelay}*1000))}</stringProp> + </TestAction> + <hashTree/> + </hashTree> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Logout" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/auth/logout/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/setup/admin_logout.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + + <BeanShellPostProcessor guiclass="TestBeanGUI" testclass="BeanShellPostProcessor" testname="Return Admin to Pool" enabled="true"> + <boolProp name="resetInterpreter">false</boolProp> + <stringProp name="parameters"/> + <stringProp name="filename"/> + <stringProp name="script"> + adminUsersDistribution = Integer.parseInt(vars.get("admin_users_distribution_per_admin_pool")); + if (adminUsersDistribution == 1) { + adminUserList = props.get("adminUserList"); + adminUserList.add(vars.get("admin_user")); + } + </stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/return_admin_email_to_pool.jmx</stringProp></BeanShellPostProcessor> + <hashTree/> + </hashTree> + </hashTree> + + + <ThroughputController guiclass="ThroughputControllerGui" testclass="ThroughputController" testname="[C] Admin Browse Product Grid" enabled="true"> + <intProp name="ThroughputController.style">1</intProp> + <boolProp name="ThroughputController.perThread">false</boolProp> + <intProp name="ThroughputController.maxThroughput">1</intProp> + <stringProp name="ThroughputController.percentThroughput">${cAdminBrowseProductGridPercentage}</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/_system/scenario_controller_tmpl.jmx</stringProp></ThroughputController> + <hashTree> + <JSR223PreProcessor guiclass="TestBeanGUI" testclass="JSR223PreProcessor" testname="Set Test Label" enabled="true"> + <stringProp name="script"> +var testLabel = "${testLabel}" ? " (${testLabel})" : ""; +if (testLabel + && sampler.getClass().getName() == 'org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy' +) { + if (sampler.getName().indexOf(testLabel) == -1) { + sampler.setName(sampler.getName() + testLabel); + } +} else if (sampler.getName().indexOf("SetUp - ") == -1) { + sampler.setName("SetUp - " + sampler.getName()); +} + </stringProp> + <stringProp name="scriptLanguage">javascript</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/_system/setup_label.jmx</stringProp></JSR223PreProcessor> + <hashTree/> + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Set Label" enabled="true"> + <stringProp name="BeanShellSampler.query"> + vars.put("testLabel", "[C] Admin Browse Product Grid"); + </stringProp> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + </BeanShellSampler> + <hashTree/> + + <JSR223PostProcessor guiclass="TestBeanGUI" testclass="JSR223PostProcessor" testname="Get admin form key PostProcessor" enabled="true"> + <stringProp name="script"> + function getFormKeyFromResponse() + { + var url = prev.getUrlAsString(), + responseCode = prev.getResponseCode(), + formKey = null; + searchPattern = /var FORM_KEY = '(.+)'/; + if (responseCode == "200" && url) { + response = prev.getResponseDataAsString(); + formKey = response && response.match(searchPattern) ? response.match(searchPattern)[1] : null; + } + return formKey; + } + + formKey = vars.get("form_key_storage"); + + currentFormKey = getFormKeyFromResponse(); + + if (currentFormKey != null && currentFormKey != formKey) { + vars.put("form_key_storage", currentFormKey); + } + </stringProp> + <stringProp name="scriptLanguage">javascript</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin/handle_admin_form_key.jmx</stringProp></JSR223PostProcessor> + <hashTree/> + <JSR223PreProcessor guiclass="TestBeanGUI" testclass="JSR223PreProcessor" testname="Set admin form key PreProcessor" enabled="true"> + <stringProp name="script"> + formKey = vars.get("form_key_storage"); + if (formKey + && sampler.getClass().getName() == 'org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy' + && sampler.getMethod() == "POST") + { + arguments = sampler.getArguments(); + for (i=0; i<arguments.getArgumentCount(); i++) + { + argument = arguments.getArgument(i); + if (argument.getName() == 'form_key' && argument.getValue() != formKey) { + log.info("admin form key updated: " + argument.getValue() + " => " + formKey); + argument.setValue(formKey); + } + } + } + </stringProp> + <stringProp name="scriptLanguage">javascript</stringProp> + </JSR223PreProcessor> + <hashTree/> + + <CookieManager guiclass="CookiePanel" testclass="CookieManager" testname="HTTP Cookie Manager" enabled="true"> + <collectionProp name="CookieManager.cookies"/> + <boolProp name="CookieManager.clearEachIteration">false</boolProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/http_cookie_manager_without_clear_each_iteration.jmx</stringProp></CookieManager> + <hashTree/> + + <GenericController guiclass="LogicControllerGui" testclass="GenericController" testname="Admin Login" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/simple_controller.jmx</stringProp> +</GenericController> + <hashTree> + <CriticalSectionController guiclass="CriticalSectionControllerGui" testclass="CriticalSectionController" testname="Admin Login Lock" enabled="true"> + <stringProp name="CriticalSectionController.lockName">get-admin-email</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/lock_controller.jmx</stringProp></CriticalSectionController> + <hashTree> + + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Get Admin Email" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/get_admin_email.jmx</stringProp> + <stringProp name="BeanShellSampler.query"> +adminUserList = props.get("adminUserList"); +adminUserListIterator = props.get("adminUserListIterator"); +adminUsersDistribution = Integer.parseInt(vars.get("admin_users_distribution_per_admin_pool")); + +if (adminUsersDistribution == 1) { + adminUser = adminUserList.poll(); +} else { + if (!adminUserListIterator.hasNext()) { + adminUserListIterator = adminUserList.descendingIterator(); + } + + adminUser = adminUserListIterator.next(); +} + +if (adminUser == null) { + SampleResult.setResponseMessage("adminUser list is empty"); + SampleResult.setResponseData("adminUser list is empty","UTF-8"); + IsSuccess=false; + SampleResult.setSuccessful(false); + SampleResult.setStopThread(true); +} +vars.put("admin_user", adminUser); + </stringProp> + <stringProp name="BeanShellSampler.filename"/> + <stringProp name="BeanShellSampler.parameters"/> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + </BeanShellSampler> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="SetUp - Login" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_login/admin_login.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert login form shown" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-1397214398">Welcome</stringProp> + <stringProp name="-515240035"><title>Magento Admin</title></stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract form key" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">admin_form_key</stringProp> + <stringProp name="RegexExtractor.regex"><input name="form_key" type="hidden" value="([^'"]+)" /></stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert form_key extracted" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="2845929">^.+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">admin_form_key</stringProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="SetUp - Login Submit Form" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="dummy" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">dummy</stringProp> + </elementProp> + <elementProp name="form_key" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_form_key}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">form_key</stringProp> + </elementProp> + <elementProp name="login[password]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_password}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">login[password]</stringProp> + </elementProp> + <elementProp name="login[username]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_user}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">login[username]</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/dashboard/</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <stringProp name="HTTPSampler.implementation">Java</stringProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_login/admin_login_submit_form.jmx</stringProp> + </HTTPSamplerProxy> + <hashTree> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract form key" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">admin_form_key</stringProp> + <stringProp name="RegexExtractor.regex"><input name="form_key" type="hidden" value="([^'"]+)" /></stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_login/admin_retrieve_form_key.jmx</stringProp></RegexExtractor> + <hashTree/> + </hashTree> + </hashTree> + + <JSR223PostProcessor guiclass="TestBeanGUI" testclass="JSR223PreProcessor" testname="SetUp - Set Arguments" enabled="true"> + <stringProp name="script"> + vars.put("gridEntityType" , "Product"); + + pagesCount = parseInt(vars.get("products_page_size")) || 20; + vars.put("grid_entity_page_size" , pagesCount); + vars.put("grid_namespace" , "product_listing"); + vars.put("grid_admin_browse_filter_text" , vars.get("admin_browse_product_filter_text")); + vars.put("grid_filter_field", "name"); + + // set sort fields and sort directions + vars.put("grid_sort_field_1", "name"); + vars.put("grid_sort_field_2", "price"); + vars.put("grid_sort_field_3", "attribute_set_id"); + vars.put("grid_sort_order_1", "asc"); + vars.put("grid_sort_order_2", "desc"); + </stringProp> + <stringProp name="scriptLanguage">javascript</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_browse_products_grid/setup.jmx</stringProp></JSR223PostProcessor> + <hashTree/> + + <GenericController guiclass="LogicControllerGui" testclass="GenericController" testname="Simple Controller" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/simple_controller.jmx</stringProp> +</GenericController> + <hashTree> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="SetUp - Set ${gridEntityType} Pages Count" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="namespace" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${grid_namespace}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">namespace</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="search" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">search</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="filters[placeholder]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">true</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">filters[placeholder]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="paging[pageSize]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${grid_entity_page_size}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">paging[pageSize]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="paging[current]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">paging[current]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="sorting[field]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">entity_id</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">sorting[field]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="sorting[direction]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">asc</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">sorting[direction]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="isAjax" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">true</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">isAjax</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/mui/index/render/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_grid_browsing/set_pages_count.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <com.atlantbh.jmeter.plugins.jsonutils.jsonpathassertion.JSONPathAssertion guiclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathassertion.gui.JSONPathAssertionGui" testclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathassertion.JSONPathAssertion" testname="Assert total records is not 0" enabled="true"> + <stringProp name="JSON_PATH">$.totalRecords</stringProp> + <stringProp name="EXPECTED_VALUE">0</stringProp> + <boolProp name="JSONVALIDATION">true</boolProp> + <boolProp name="EXPECT_NULL">false</boolProp> + <boolProp name="INVERT">true</boolProp> + </com.atlantbh.jmeter.plugins.jsonutils.jsonpathassertion.JSONPathAssertion> + <hashTree/> + <com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor guiclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.gui.JSONPathExtractorGui" testclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor" testname="Extract total records" enabled="true"> + <stringProp name="VAR">entity_total_records</stringProp> + <stringProp name="JSONPATH">$.totalRecords</stringProp> + <stringProp name="DEFAULT"/> + <stringProp name="VARIABLE"/> + <stringProp name="SUBJECT">BODY</stringProp> + </com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor> + <hashTree/> + <JSR223PostProcessor guiclass="TestBeanGUI" testclass="JSR223PreProcessor" testname="SetUp - Calculate ${gridEntityType} pages count" enabled="true"> + <stringProp name="cacheKey"/> + <stringProp name="filename"/> + <stringProp name="parameters"/> + <stringProp name="script"> + var pageSize = parseInt(vars.get("grid_entity_page_size")) || 20; + var totalsRecord = parseInt(vars.get("entity_total_records")); + var pageCount = Math.round(totalsRecord/pageSize); + + vars.put("grid_pages_count", pageCount); + </stringProp> + <stringProp name="scriptLanguage">javascript</stringProp> + </JSR223PostProcessor> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="SetUp - Set ${gridEntityType} Filtered Pages Count" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="namespace" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${grid_namespace}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">namespace</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="search" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">search</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="filters[placeholder]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${grid_admin_browse_filter_text}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">filters[placeholder]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="paging[pageSize]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${grid_entity_page_size}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">paging[pageSize]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="paging[current]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">paging[current]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="sorting[field]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">entity_id</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">sorting[field]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="sorting[direction]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">asc</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">sorting[direction]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="isAjax" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">true</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">isAjax</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/mui/index/render/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_grid_browsing/set_filtered_pages_count.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <com.atlantbh.jmeter.plugins.jsonutils.jsonpathassertion.JSONPathAssertion guiclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathassertion.gui.JSONPathAssertionGui" testclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathassertion.JSONPathAssertion" testname="Assert total records is not 0" enabled="true"> + <stringProp name="JSON_PATH">$.totalRecords</stringProp> + <stringProp name="EXPECTED_VALUE">0</stringProp> + <boolProp name="JSONVALIDATION">true</boolProp> + <boolProp name="EXPECT_NULL">false</boolProp> + <boolProp name="INVERT">true</boolProp> + <boolProp name="ISREGEX">true</boolProp> + </com.atlantbh.jmeter.plugins.jsonutils.jsonpathassertion.JSONPathAssertion> + <hashTree/> + <com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor guiclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.gui.JSONPathExtractorGui" testclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor" testname="Extract total records" enabled="true"> + <stringProp name="VAR">entity_total_records</stringProp> + <stringProp name="JSONPATH">$.totalRecords</stringProp> + <stringProp name="DEFAULT"/> + <stringProp name="VARIABLE"/> + <stringProp name="SUBJECT">BODY</stringProp> + </com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor> + <hashTree/> + <JSR223PostProcessor guiclass="TestBeanGUI" testclass="JSR223PreProcessor" testname="SetUp - Calculate ${gridEntityType} filtered pages count" enabled="true"> + <stringProp name="parameters"/> + <stringProp name="filename"/> + <stringProp name="script"> + var pageSize = parseInt(vars.get("grid_entity_page_size")) || 20; +var totalsRecord = parseInt(vars.get("entity_total_records")); +var pageCount = Math.round(totalsRecord/pageSize); + +vars.put("grid_pages_count_filtered", pageCount); + </stringProp> + <stringProp name="scriptLanguage">javascript</stringProp> + </JSR223PostProcessor> + <hashTree/> + </hashTree> + + <CounterConfig guiclass="CounterConfigGui" testclass="CounterConfig" testname="SetUp - Select ${gridEntityType} Page Number" enabled="true"> + <stringProp name="CounterConfig.start">1</stringProp> + <stringProp name="CounterConfig.end">${grid_pages_count}</stringProp> + <stringProp name="CounterConfig.incr">1</stringProp> + <stringProp name="CounterConfig.name">page_number</stringProp> + <stringProp name="CounterConfig.format"/> + <boolProp name="CounterConfig.per_user">true</boolProp> + <boolProp name="CounterConfig.reset_on_tg_iteration">false</boolProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_grid_browsing/select_page_number.jmx</stringProp></CounterConfig> + <hashTree/> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="View ${gridEntityType} page" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="namespace" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${grid_namespace}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">namespace</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="search" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">search</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="filters[placeholder]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">true</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">filters[placeholder]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="paging[pageSize]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${grid_entity_page_size}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">paging[pageSize]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="paging[current]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${page_number}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">paging[current]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="sorting[field]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">entity_id</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">sorting[field]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="sorting[direction]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">asc</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">sorting[direction]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="isAjax" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">true</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">isAjax</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/mui/index/render/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_grid_browsing/admin_browse_grid.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="1637639774">\"totalRecords\":[^0]\d*</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + + <CounterConfig guiclass="CounterConfigGui" testclass="CounterConfig" testname="SetUp - Select Filtered ${gridEntityType} Page Number" enabled="true"> + <stringProp name="CounterConfig.start">1</stringProp> + <stringProp name="CounterConfig.end">${grid_pages_count_filtered}</stringProp> + <stringProp name="CounterConfig.incr">1</stringProp> + <stringProp name="CounterConfig.name">page_number</stringProp> + <stringProp name="CounterConfig.format"/> + <boolProp name="CounterConfig.per_user">true</boolProp> + <boolProp name="CounterConfig.reset_on_tg_iteration">false</boolProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_grid_browsing/select_filtered_page_number.jmx</stringProp></CounterConfig> + <hashTree/> + + <TestFragmentController guiclass="TestFragmentControllerGui" testclass="TestFragmentController" testname="View ${gridEntityType} page - Filtering + Sorting" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_grid_browsing/admin_browse_grid_sort_and_filter.jmx</stringProp> +</TestFragmentController> + <hashTree> + <ForeachController guiclass="ForeachControlPanel" testclass="ForeachController" testname="ForEach Sort Field Defined" enabled="true"> + <stringProp name="ForeachController.inputVal">grid_sort_field</stringProp> + <stringProp name="ForeachController.returnVal">grid_sort_field</stringProp> + <boolProp name="ForeachController.useSeparator">true</boolProp> + <stringProp name="ForeachController.startIndex">0</stringProp> + <stringProp name="ForeachController.endIndex">3</stringProp> + </ForeachController> + <hashTree> + <ForeachController guiclass="ForeachControlPanel" testclass="ForeachController" testname="ForEach Sort Order Defined" enabled="true"> + <stringProp name="ForeachController.inputVal">grid_sort_order</stringProp> + <stringProp name="ForeachController.returnVal">grid_sort_order</stringProp> + <boolProp name="ForeachController.useSeparator">true</boolProp> + <stringProp name="ForeachController.startIndex">0</stringProp> + <stringProp name="ForeachController.endIndex">2</stringProp> + </ForeachController> + <hashTree> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="View ${gridEntityType} page - Filtering + Sort By ${grid_sort_field} ${grid_sort_order}" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="namespace" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${grid_namespace}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">namespace</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="filters[${grid_filter_field}]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${grid_admin_browse_filter_text}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">filters[${grid_filter_field}]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="filters[placeholder]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">true</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">filters[placeholder]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="paging[pageSize]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${grid_entity_page_size}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">paging[pageSize]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="paging[current]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${page_number}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">paging[current]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="sorting[field]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${grid_sort_field}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">sorting[field]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="sorting[direction]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${grid_sort_order}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">sorting[direction]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="isAjax" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">true</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">isAjax</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/mui/index/render/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + </HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="1637639774">\"totalRecords\":[^0]\d*</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + </hashTree> + </hashTree> + </hashTree> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Logout" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/auth/logout/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/setup/admin_logout.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + + <BeanShellPostProcessor guiclass="TestBeanGUI" testclass="BeanShellPostProcessor" testname="Return Admin to Pool" enabled="true"> + <boolProp name="resetInterpreter">false</boolProp> + <stringProp name="parameters"/> + <stringProp name="filename"/> + <stringProp name="script"> + adminUsersDistribution = Integer.parseInt(vars.get("admin_users_distribution_per_admin_pool")); + if (adminUsersDistribution == 1) { + adminUserList = props.get("adminUserList"); + adminUserList.add(vars.get("admin_user")); + } + </stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/return_admin_email_to_pool.jmx</stringProp></BeanShellPostProcessor> + <hashTree/> + </hashTree> + </hashTree> + + + <ThroughputController guiclass="ThroughputControllerGui" testclass="ThroughputController" testname="[C] Admin Browse Order Grid" enabled="true"> + <intProp name="ThroughputController.style">1</intProp> + <boolProp name="ThroughputController.perThread">false</boolProp> + <intProp name="ThroughputController.maxThroughput">1</intProp> + <stringProp name="ThroughputController.percentThroughput">${cAdminBrowseOrderGridPercentage}</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/_system/scenario_controller_tmpl.jmx</stringProp></ThroughputController> + <hashTree> + <JSR223PreProcessor guiclass="TestBeanGUI" testclass="JSR223PreProcessor" testname="Set Test Label" enabled="true"> + <stringProp name="script"> +var testLabel = "${testLabel}" ? " (${testLabel})" : ""; +if (testLabel + && sampler.getClass().getName() == 'org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy' +) { + if (sampler.getName().indexOf(testLabel) == -1) { + sampler.setName(sampler.getName() + testLabel); + } +} else if (sampler.getName().indexOf("SetUp - ") == -1) { + sampler.setName("SetUp - " + sampler.getName()); +} + </stringProp> + <stringProp name="scriptLanguage">javascript</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/_system/setup_label.jmx</stringProp></JSR223PreProcessor> + <hashTree/> + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Set Label" enabled="true"> + <stringProp name="BeanShellSampler.query"> + vars.put("testLabel", "[C] Admin Browse Order Grid"); + </stringProp> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + </BeanShellSampler> + <hashTree/> + + <JSR223PostProcessor guiclass="TestBeanGUI" testclass="JSR223PostProcessor" testname="Get admin form key PostProcessor" enabled="true"> + <stringProp name="script"> + function getFormKeyFromResponse() + { + var url = prev.getUrlAsString(), + responseCode = prev.getResponseCode(), + formKey = null; + searchPattern = /var FORM_KEY = '(.+)'/; + if (responseCode == "200" && url) { + response = prev.getResponseDataAsString(); + formKey = response && response.match(searchPattern) ? response.match(searchPattern)[1] : null; + } + return formKey; + } + + formKey = vars.get("form_key_storage"); + + currentFormKey = getFormKeyFromResponse(); + + if (currentFormKey != null && currentFormKey != formKey) { + vars.put("form_key_storage", currentFormKey); + } + </stringProp> + <stringProp name="scriptLanguage">javascript</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin/handle_admin_form_key.jmx</stringProp></JSR223PostProcessor> + <hashTree/> + <JSR223PreProcessor guiclass="TestBeanGUI" testclass="JSR223PreProcessor" testname="Set admin form key PreProcessor" enabled="true"> + <stringProp name="script"> + formKey = vars.get("form_key_storage"); + if (formKey + && sampler.getClass().getName() == 'org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy' + && sampler.getMethod() == "POST") + { + arguments = sampler.getArguments(); + for (i=0; i<arguments.getArgumentCount(); i++) + { + argument = arguments.getArgument(i); + if (argument.getName() == 'form_key' && argument.getValue() != formKey) { + log.info("admin form key updated: " + argument.getValue() + " => " + formKey); + argument.setValue(formKey); + } + } + } + </stringProp> + <stringProp name="scriptLanguage">javascript</stringProp> + </JSR223PreProcessor> + <hashTree/> + + <CookieManager guiclass="CookiePanel" testclass="CookieManager" testname="HTTP Cookie Manager" enabled="true"> + <collectionProp name="CookieManager.cookies"/> + <boolProp name="CookieManager.clearEachIteration">false</boolProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/http_cookie_manager_without_clear_each_iteration.jmx</stringProp></CookieManager> + <hashTree/> + + <GenericController guiclass="LogicControllerGui" testclass="GenericController" testname="Admin Login" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/simple_controller.jmx</stringProp> +</GenericController> + <hashTree> + <CriticalSectionController guiclass="CriticalSectionControllerGui" testclass="CriticalSectionController" testname="Admin Login Lock" enabled="true"> + <stringProp name="CriticalSectionController.lockName">get-admin-email</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/lock_controller.jmx</stringProp></CriticalSectionController> + <hashTree> + + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Get Admin Email" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/get_admin_email.jmx</stringProp> + <stringProp name="BeanShellSampler.query"> +adminUserList = props.get("adminUserList"); +adminUserListIterator = props.get("adminUserListIterator"); +adminUsersDistribution = Integer.parseInt(vars.get("admin_users_distribution_per_admin_pool")); + +if (adminUsersDistribution == 1) { + adminUser = adminUserList.poll(); +} else { + if (!adminUserListIterator.hasNext()) { + adminUserListIterator = adminUserList.descendingIterator(); + } + + adminUser = adminUserListIterator.next(); +} + +if (adminUser == null) { + SampleResult.setResponseMessage("adminUser list is empty"); + SampleResult.setResponseData("adminUser list is empty","UTF-8"); + IsSuccess=false; + SampleResult.setSuccessful(false); + SampleResult.setStopThread(true); +} +vars.put("admin_user", adminUser); + </stringProp> + <stringProp name="BeanShellSampler.filename"/> + <stringProp name="BeanShellSampler.parameters"/> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + </BeanShellSampler> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="SetUp - Login" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_login/admin_login.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert login form shown" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-1397214398">Welcome</stringProp> + <stringProp name="-515240035"><title>Magento Admin</title></stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract form key" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">admin_form_key</stringProp> + <stringProp name="RegexExtractor.regex"><input name="form_key" type="hidden" value="([^'"]+)" /></stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert form_key extracted" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="2845929">^.+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">admin_form_key</stringProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="SetUp - Login Submit Form" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="dummy" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">dummy</stringProp> + </elementProp> + <elementProp name="form_key" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_form_key}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">form_key</stringProp> + </elementProp> + <elementProp name="login[password]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_password}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">login[password]</stringProp> + </elementProp> + <elementProp name="login[username]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_user}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">login[username]</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/dashboard/</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <stringProp name="HTTPSampler.implementation">Java</stringProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_login/admin_login_submit_form.jmx</stringProp> + </HTTPSamplerProxy> + <hashTree> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract form key" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">admin_form_key</stringProp> + <stringProp name="RegexExtractor.regex"><input name="form_key" type="hidden" value="([^'"]+)" /></stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_login/admin_retrieve_form_key.jmx</stringProp></RegexExtractor> + <hashTree/> + </hashTree> + </hashTree> + + <JSR223PostProcessor guiclass="TestBeanGUI" testclass="JSR223PreProcessor" testname="SetUp - Set Arguments" enabled="true"> + <stringProp name="script"> + vars.put("gridEntityType" , "Order"); + + pagesCount = parseInt(vars.get("orders_page_size")) || 20; + vars.put("grid_entity_page_size" , pagesCount); + vars.put("grid_namespace" , "sales_order_grid"); + vars.put("grid_admin_browse_filter_text" , vars.get("admin_browse_orders_filter_text")); + vars.put("grid_filter_field", "status"); + + // set sort fields and sort directions + vars.put("grid_sort_field_1", "increment_id"); + vars.put("grid_sort_field_2", "created_at"); + vars.put("grid_sort_field_3", "billing_name"); + vars.put("grid_sort_order_1", "asc"); + vars.put("grid_sort_order_2", "desc"); + </stringProp> + <stringProp name="scriptLanguage">javascript</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_browse_orders_grid/setup.jmx</stringProp></JSR223PostProcessor> + <hashTree/> + + <GenericController guiclass="LogicControllerGui" testclass="GenericController" testname="Simple Controller" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/simple_controller.jmx</stringProp> +</GenericController> + <hashTree> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="SetUp - Set ${gridEntityType} Pages Count" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="namespace" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${grid_namespace}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">namespace</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="search" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">search</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="filters[placeholder]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">true</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">filters[placeholder]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="paging[pageSize]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${grid_entity_page_size}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">paging[pageSize]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="paging[current]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">paging[current]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="sorting[field]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">entity_id</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">sorting[field]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="sorting[direction]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">asc</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">sorting[direction]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="isAjax" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">true</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">isAjax</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/mui/index/render/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_grid_browsing/set_pages_count.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <com.atlantbh.jmeter.plugins.jsonutils.jsonpathassertion.JSONPathAssertion guiclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathassertion.gui.JSONPathAssertionGui" testclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathassertion.JSONPathAssertion" testname="Assert total records is not 0" enabled="true"> + <stringProp name="JSON_PATH">$.totalRecords</stringProp> + <stringProp name="EXPECTED_VALUE">0</stringProp> + <boolProp name="JSONVALIDATION">true</boolProp> + <boolProp name="EXPECT_NULL">false</boolProp> + <boolProp name="INVERT">true</boolProp> + </com.atlantbh.jmeter.plugins.jsonutils.jsonpathassertion.JSONPathAssertion> + <hashTree/> + <com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor guiclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.gui.JSONPathExtractorGui" testclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor" testname="Extract total records" enabled="true"> + <stringProp name="VAR">entity_total_records</stringProp> + <stringProp name="JSONPATH">$.totalRecords</stringProp> + <stringProp name="DEFAULT"/> + <stringProp name="VARIABLE"/> + <stringProp name="SUBJECT">BODY</stringProp> + </com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor> + <hashTree/> + <JSR223PostProcessor guiclass="TestBeanGUI" testclass="JSR223PreProcessor" testname="SetUp - Calculate ${gridEntityType} pages count" enabled="true"> + <stringProp name="cacheKey"/> + <stringProp name="filename"/> + <stringProp name="parameters"/> + <stringProp name="script"> + var pageSize = parseInt(vars.get("grid_entity_page_size")) || 20; + var totalsRecord = parseInt(vars.get("entity_total_records")); + var pageCount = Math.round(totalsRecord/pageSize); + + vars.put("grid_pages_count", pageCount); + </stringProp> + <stringProp name="scriptLanguage">javascript</stringProp> + </JSR223PostProcessor> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="SetUp - Set ${gridEntityType} Filtered Pages Count" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="namespace" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${grid_namespace}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">namespace</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="search" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">search</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="filters[placeholder]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${grid_admin_browse_filter_text}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">filters[placeholder]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="paging[pageSize]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${grid_entity_page_size}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">paging[pageSize]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="paging[current]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">paging[current]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="sorting[field]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">entity_id</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">sorting[field]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="sorting[direction]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">asc</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">sorting[direction]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="isAjax" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">true</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">isAjax</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/mui/index/render/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_grid_browsing/set_filtered_pages_count.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <com.atlantbh.jmeter.plugins.jsonutils.jsonpathassertion.JSONPathAssertion guiclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathassertion.gui.JSONPathAssertionGui" testclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathassertion.JSONPathAssertion" testname="Assert total records is not 0" enabled="true"> + <stringProp name="JSON_PATH">$.totalRecords</stringProp> + <stringProp name="EXPECTED_VALUE">0</stringProp> + <boolProp name="JSONVALIDATION">true</boolProp> + <boolProp name="EXPECT_NULL">false</boolProp> + <boolProp name="INVERT">true</boolProp> + <boolProp name="ISREGEX">true</boolProp> + </com.atlantbh.jmeter.plugins.jsonutils.jsonpathassertion.JSONPathAssertion> + <hashTree/> + <com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor guiclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.gui.JSONPathExtractorGui" testclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor" testname="Extract total records" enabled="true"> + <stringProp name="VAR">entity_total_records</stringProp> + <stringProp name="JSONPATH">$.totalRecords</stringProp> + <stringProp name="DEFAULT"/> + <stringProp name="VARIABLE"/> + <stringProp name="SUBJECT">BODY</stringProp> + </com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor> + <hashTree/> + <JSR223PostProcessor guiclass="TestBeanGUI" testclass="JSR223PreProcessor" testname="SetUp - Calculate ${gridEntityType} filtered pages count" enabled="true"> + <stringProp name="parameters"/> + <stringProp name="filename"/> + <stringProp name="script"> + var pageSize = parseInt(vars.get("grid_entity_page_size")) || 20; +var totalsRecord = parseInt(vars.get("entity_total_records")); +var pageCount = Math.round(totalsRecord/pageSize); + +vars.put("grid_pages_count_filtered", pageCount); + </stringProp> + <stringProp name="scriptLanguage">javascript</stringProp> + </JSR223PostProcessor> + <hashTree/> + </hashTree> + + <CounterConfig guiclass="CounterConfigGui" testclass="CounterConfig" testname="SetUp - Select ${gridEntityType} Page Number" enabled="true"> + <stringProp name="CounterConfig.start">1</stringProp> + <stringProp name="CounterConfig.end">${grid_pages_count}</stringProp> + <stringProp name="CounterConfig.incr">1</stringProp> + <stringProp name="CounterConfig.name">page_number</stringProp> + <stringProp name="CounterConfig.format"/> + <boolProp name="CounterConfig.per_user">true</boolProp> + <boolProp name="CounterConfig.reset_on_tg_iteration">false</boolProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_grid_browsing/select_page_number.jmx</stringProp></CounterConfig> + <hashTree/> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="View ${gridEntityType} page" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="namespace" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${grid_namespace}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">namespace</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="search" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">search</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="filters[placeholder]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">true</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">filters[placeholder]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="paging[pageSize]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${grid_entity_page_size}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">paging[pageSize]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="paging[current]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${page_number}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">paging[current]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="sorting[field]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">entity_id</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">sorting[field]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="sorting[direction]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">asc</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">sorting[direction]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="isAjax" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">true</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">isAjax</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/mui/index/render/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_grid_browsing/admin_browse_grid.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="1637639774">\"totalRecords\":[^0]\d*</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + + <CounterConfig guiclass="CounterConfigGui" testclass="CounterConfig" testname="SetUp - Select Filtered ${gridEntityType} Page Number" enabled="true"> + <stringProp name="CounterConfig.start">1</stringProp> + <stringProp name="CounterConfig.end">${grid_pages_count_filtered}</stringProp> + <stringProp name="CounterConfig.incr">1</stringProp> + <stringProp name="CounterConfig.name">page_number</stringProp> + <stringProp name="CounterConfig.format"/> + <boolProp name="CounterConfig.per_user">true</boolProp> + <boolProp name="CounterConfig.reset_on_tg_iteration">false</boolProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_grid_browsing/select_filtered_page_number.jmx</stringProp></CounterConfig> + <hashTree/> + + <TestFragmentController guiclass="TestFragmentControllerGui" testclass="TestFragmentController" testname="View ${gridEntityType} page - Filtering + Sorting" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_grid_browsing/admin_browse_grid_sort_and_filter.jmx</stringProp> +</TestFragmentController> + <hashTree> + <ForeachController guiclass="ForeachControlPanel" testclass="ForeachController" testname="ForEach Sort Field Defined" enabled="true"> + <stringProp name="ForeachController.inputVal">grid_sort_field</stringProp> + <stringProp name="ForeachController.returnVal">grid_sort_field</stringProp> + <boolProp name="ForeachController.useSeparator">true</boolProp> + <stringProp name="ForeachController.startIndex">0</stringProp> + <stringProp name="ForeachController.endIndex">3</stringProp> + </ForeachController> + <hashTree> + <ForeachController guiclass="ForeachControlPanel" testclass="ForeachController" testname="ForEach Sort Order Defined" enabled="true"> + <stringProp name="ForeachController.inputVal">grid_sort_order</stringProp> + <stringProp name="ForeachController.returnVal">grid_sort_order</stringProp> + <boolProp name="ForeachController.useSeparator">true</boolProp> + <stringProp name="ForeachController.startIndex">0</stringProp> + <stringProp name="ForeachController.endIndex">2</stringProp> + </ForeachController> + <hashTree> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="View ${gridEntityType} page - Filtering + Sort By ${grid_sort_field} ${grid_sort_order}" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="namespace" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${grid_namespace}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">namespace</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="filters[${grid_filter_field}]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${grid_admin_browse_filter_text}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">filters[${grid_filter_field}]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="filters[placeholder]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">true</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">filters[placeholder]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="paging[pageSize]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${grid_entity_page_size}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">paging[pageSize]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="paging[current]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${page_number}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">paging[current]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="sorting[field]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${grid_sort_field}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">sorting[field]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="sorting[direction]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${grid_sort_order}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">sorting[direction]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="isAjax" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">true</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">isAjax</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/mui/index/render/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + </HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="1637639774">\"totalRecords\":[^0]\d*</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + </hashTree> + </hashTree> + </hashTree> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Logout" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/auth/logout/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/setup/admin_logout.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + + <BeanShellPostProcessor guiclass="TestBeanGUI" testclass="BeanShellPostProcessor" testname="Return Admin to Pool" enabled="true"> + <boolProp name="resetInterpreter">false</boolProp> + <stringProp name="parameters"/> + <stringProp name="filename"/> + <stringProp name="script"> + adminUsersDistribution = Integer.parseInt(vars.get("admin_users_distribution_per_admin_pool")); + if (adminUsersDistribution == 1) { + adminUserList = props.get("adminUserList"); + adminUserList.add(vars.get("admin_user")); + } + </stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/return_admin_email_to_pool.jmx</stringProp></BeanShellPostProcessor> + <hashTree/> + </hashTree> + </hashTree> + + + <ThroughputController guiclass="ThroughputControllerGui" testclass="ThroughputController" testname="[C] Admin Create Product" enabled="true"> + <intProp name="ThroughputController.style">1</intProp> + <boolProp name="ThroughputController.perThread">false</boolProp> + <intProp name="ThroughputController.maxThroughput">1</intProp> + <stringProp name="ThroughputController.percentThroughput">${cAdminProductCreationPercentage}</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/_system/scenario_controller_tmpl.jmx</stringProp></ThroughputController> + <hashTree> + <JSR223PreProcessor guiclass="TestBeanGUI" testclass="JSR223PreProcessor" testname="Set Test Label" enabled="true"> + <stringProp name="script"> +var testLabel = "${testLabel}" ? " (${testLabel})" : ""; +if (testLabel + && sampler.getClass().getName() == 'org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy' +) { + if (sampler.getName().indexOf(testLabel) == -1) { + sampler.setName(sampler.getName() + testLabel); + } +} else if (sampler.getName().indexOf("SetUp - ") == -1) { + sampler.setName("SetUp - " + sampler.getName()); +} + </stringProp> + <stringProp name="scriptLanguage">javascript</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/_system/setup_label.jmx</stringProp></JSR223PreProcessor> + <hashTree/> + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Set Label" enabled="true"> + <stringProp name="BeanShellSampler.query"> + vars.put("testLabel", "[C] Admin Create Product"); + </stringProp> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + </BeanShellSampler> + <hashTree/> + + <JSR223PostProcessor guiclass="TestBeanGUI" testclass="JSR223PostProcessor" testname="Get admin form key PostProcessor" enabled="true"> + <stringProp name="script"> + function getFormKeyFromResponse() + { + var url = prev.getUrlAsString(), + responseCode = prev.getResponseCode(), + formKey = null; + searchPattern = /var FORM_KEY = '(.+)'/; + if (responseCode == "200" && url) { + response = prev.getResponseDataAsString(); + formKey = response && response.match(searchPattern) ? response.match(searchPattern)[1] : null; + } + return formKey; + } + + formKey = vars.get("form_key_storage"); + + currentFormKey = getFormKeyFromResponse(); + + if (currentFormKey != null && currentFormKey != formKey) { + vars.put("form_key_storage", currentFormKey); + } + </stringProp> + <stringProp name="scriptLanguage">javascript</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin/handle_admin_form_key.jmx</stringProp></JSR223PostProcessor> + <hashTree/> + <JSR223PreProcessor guiclass="TestBeanGUI" testclass="JSR223PreProcessor" testname="Set admin form key PreProcessor" enabled="true"> + <stringProp name="script"> + formKey = vars.get("form_key_storage"); + if (formKey + && sampler.getClass().getName() == 'org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy' + && sampler.getMethod() == "POST") + { + arguments = sampler.getArguments(); + for (i=0; i<arguments.getArgumentCount(); i++) + { + argument = arguments.getArgument(i); + if (argument.getName() == 'form_key' && argument.getValue() != formKey) { + log.info("admin form key updated: " + argument.getValue() + " => " + formKey); + argument.setValue(formKey); + } + } + } + </stringProp> + <stringProp name="scriptLanguage">javascript</stringProp> + </JSR223PreProcessor> + <hashTree/> + + <CookieManager guiclass="CookiePanel" testclass="CookieManager" testname="HTTP Cookie Manager" enabled="true"> + <collectionProp name="CookieManager.cookies"/> + <boolProp name="CookieManager.clearEachIteration">false</boolProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/http_cookie_manager_without_clear_each_iteration.jmx</stringProp></CookieManager> + <hashTree/> + + <GenericController guiclass="LogicControllerGui" testclass="GenericController" testname="Admin Login" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/simple_controller.jmx</stringProp> +</GenericController> + <hashTree> + <CriticalSectionController guiclass="CriticalSectionControllerGui" testclass="CriticalSectionController" testname="Admin Login Lock" enabled="true"> + <stringProp name="CriticalSectionController.lockName">get-admin-email</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/lock_controller.jmx</stringProp></CriticalSectionController> + <hashTree> + + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Get Admin Email" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/get_admin_email.jmx</stringProp> + <stringProp name="BeanShellSampler.query"> +adminUserList = props.get("adminUserList"); +adminUserListIterator = props.get("adminUserListIterator"); +adminUsersDistribution = Integer.parseInt(vars.get("admin_users_distribution_per_admin_pool")); + +if (adminUsersDistribution == 1) { + adminUser = adminUserList.poll(); +} else { + if (!adminUserListIterator.hasNext()) { + adminUserListIterator = adminUserList.descendingIterator(); + } + + adminUser = adminUserListIterator.next(); +} + +if (adminUser == null) { + SampleResult.setResponseMessage("adminUser list is empty"); + SampleResult.setResponseData("adminUser list is empty","UTF-8"); + IsSuccess=false; + SampleResult.setSuccessful(false); + SampleResult.setStopThread(true); +} +vars.put("admin_user", adminUser); + </stringProp> + <stringProp name="BeanShellSampler.filename"/> + <stringProp name="BeanShellSampler.parameters"/> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + </BeanShellSampler> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="SetUp - Login" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_login/admin_login.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert login form shown" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-1397214398">Welcome</stringProp> + <stringProp name="-515240035"><title>Magento Admin</title></stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract form key" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">admin_form_key</stringProp> + <stringProp name="RegexExtractor.regex"><input name="form_key" type="hidden" value="([^'"]+)" /></stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert form_key extracted" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="2845929">^.+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">admin_form_key</stringProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="SetUp - Login Submit Form" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="dummy" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">dummy</stringProp> + </elementProp> + <elementProp name="form_key" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_form_key}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">form_key</stringProp> + </elementProp> + <elementProp name="login[password]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_password}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">login[password]</stringProp> + </elementProp> + <elementProp name="login[username]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_user}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">login[username]</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/dashboard/</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <stringProp name="HTTPSampler.implementation">Java</stringProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_login/admin_login_submit_form.jmx</stringProp> + </HTTPSamplerProxy> + <hashTree> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract form key" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">admin_form_key</stringProp> + <stringProp name="RegexExtractor.regex"><input name="form_key" type="hidden" value="([^'"]+)" /></stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_login/admin_retrieve_form_key.jmx</stringProp></RegexExtractor> + <hashTree/> + </hashTree> + </hashTree> + + <OnceOnlyController guiclass="OnceOnlyControllerGui" testclass="OnceOnlyController" testname="Once Only Controller" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/once_only_controller.jmx</stringProp> +</OnceOnlyController> + <hashTree> + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Get Related Product Id" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_create_product/get_related_product_id.jmx</stringProp> + <stringProp name="BeanShellSampler.query">import org.apache.jmeter.samplers.SampleResult; +import java.util.Random; +Random random = new Random(); +if (${seedForRandom} > 0) { + random.setSeed(${seedForRandom}); +} +relatedIndex = random.nextInt(props.get("simple_products_list").size()); +vars.put("related_product_id", props.get("simple_products_list").get(relatedIndex).get("id"));</stringProp> + <stringProp name="BeanShellSampler.filename"/> + <stringProp name="BeanShellSampler.parameters"/> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + </BeanShellSampler> + <hashTree/> + + <GenericController guiclass="LogicControllerGui" testclass="GenericController" testname="SetUp - Get Product Attributes" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/simple_controller.jmx</stringProp> +</GenericController> + <hashTree> + <HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Header Manager" enabled="true"> + <collectionProp name="HeaderManager.headers"> + <elementProp name="" elementType="Header"> + <stringProp name="Header.name">Content-Type</stringProp> + <stringProp name="Header.value">application/json</stringProp> + </elementProp> + <elementProp name="" elementType="Header"> + <stringProp name="Header.name">Accept</stringProp> + <stringProp name="Header.value">*/*</stringProp> + </elementProp> + </collectionProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/api/header_manager_before_token.jmx</stringProp></HeaderManager> + <hashTree/> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="SetUp - Admin Token Retrieval" enabled="true"> + <boolProp name="HTTPSampler.postBodyRaw">true</boolProp> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments"> + <collectionProp name="Arguments.arguments"> + <elementProp name="" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">false</boolProp> + <stringProp name="Argument.value">{"username":"${admin_user}","password":"${admin_password}"}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}rest/V1/integration/admin/token</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/api/admin_token_retrieval.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor guiclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.gui.JSONPathExtractorGui" testclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor" testname="jp@gc - JSON Path Extractor" enabled="true"> + <stringProp name="VAR">admin_token</stringProp> + <stringProp name="JSONPATH">$</stringProp> + <stringProp name="DEFAULT"/> + <stringProp name="VARIABLE"/> + <stringProp name="SUBJECT">BODY</stringProp> + </com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert token not null" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="484395188">^[a-z0-9-]+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">admin_token</stringProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + + <HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Header Manager" enabled="true"> + <collectionProp name="HeaderManager.headers"> + <elementProp name="" elementType="Header"> + <stringProp name="Header.name">Authorization</stringProp> + <stringProp name="Header.value">Bearer ${admin_token}</stringProp> + </elementProp> + </collectionProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/api/header_manager.jmx</stringProp></HeaderManager> + <hashTree/> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="SetUp - API Get product attributes" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="searchCriteria[filterGroups][0][filters][0][value]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">false</boolProp> + <stringProp name="Argument.value">mycolor</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">searchCriteria[filterGroups][0][filters][0][value]</stringProp> + </elementProp> + <elementProp name="searchCriteria[filterGroups][0][filters][0][field]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">false</boolProp> + <stringProp name="Argument.value">attribute_code</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">searchCriteria[filterGroups][0][filters][0][field]</stringProp> + </elementProp> + <elementProp name="searchCriteria[filterGroups][0][filters][1][value]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">false</boolProp> + <stringProp name="Argument.value">mysize</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">searchCriteria[filterGroups][0][filters][1][value]</stringProp> + </elementProp> + <elementProp name="searchCriteria[filterGroups][0][filters][1][field]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">false</boolProp> + <stringProp name="Argument.value">attribute_code</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">searchCriteria[filterGroups][0][filters][1][field]</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}rest/default/V1/products/attributes</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_create_product/get_product_attributes.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor guiclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.gui.JSONPathExtractorGui" testclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor" testname="Extract product attributes" enabled="true"> + <stringProp name="VAR">product_attributes</stringProp> + <stringProp name="JSONPATH">$.items</stringProp> + <stringProp name="DEFAULT"/> + <stringProp name="VARIABLE"/> + <stringProp name="SUBJECT">BODY</stringProp> + </com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor> + <hashTree/> + <JSR223PostProcessor guiclass="TestBeanGUI" testclass="JSR223PostProcessor" testname="SetUp - Prepare product attributes" enabled="true"> + <stringProp name="scriptLanguage">javascript</stringProp> + <stringProp name="parameters"/> + <stringProp name="filename"/> + <stringProp name="cacheKey"/> + <stringProp name="script"> +var attributesData = JSON.parse(vars.get("product_attributes")), +maxOptions = 2; + +attributes = []; +for (i in attributesData) { + if (i >= 2) { + break; + } + var data = attributesData[i], + attribute = { + "id": data.attribute_id, + "code": data.attribute_code, + "label": data.default_frontend_label, + "options": [] + }; + + var processedOptions = 0; + for (optionN in data.options) { + var option = data.options[optionN]; + if (parseInt(option.value) > 0 && processedOptions < maxOptions) { + processedOptions++; + attribute.options.push(option); + } + } + attributes.push(attribute); +} + +vars.putObject("product_attributes", attributes); +</stringProp> + </JSR223PostProcessor> + <hashTree/> + </hashTree> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="SetUp - Get Attribute Set Id" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/catalog/product_set/index/filter/${attribute_set_filter}</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_create_product/configurable_setup_attribute_set.jmx</stringProp> + </HTTPSamplerProxy> + <hashTree> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Regular Expression Extractor" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">attribute_set_id</stringProp> + <stringProp name="RegexExtractor.regex">catalog\/product_set\/edit\/id\/([\d]+)\/"[\D\d]*Attribute Set 1</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + <BeanShellPreProcessor guiclass="TestBeanGUI" testclass="BeanShellPreProcessor" testname="SetUp - Set Attribute Set Filter" enabled="true"> + <boolProp name="resetInterpreter">false</boolProp> + <stringProp name="parameters"/> + <stringProp name="filename"/> + <stringProp name="script">import org.apache.commons.codec.binary.Base64; + +byte[] encodedBytes = Base64.encodeBase64("set_name=Attribute Set 1".getBytes()); +vars.put("attribute_set_filter", new String(encodedBytes)); +</stringProp> + </BeanShellPreProcessor> + <hashTree/> + </hashTree> + </hashTree> + + <GenericController guiclass="LogicControllerGui" testclass="GenericController" testname="Simple Controller" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/simple_controller.jmx</stringProp> +</GenericController> + <hashTree> + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Set Arguments" enabled="true"> + <stringProp name="BeanShellSampler.query">import org.apache.jmeter.samplers.SampleResult; +import java.util.Random; +Random random = new Random(); +if (${seedForRandom} > 0) { + random.setSeed(${seedForRandom}); +} +number = random.nextInt(props.get("simple_products_list_for_edit").size()); +simpleList = props.get("simple_products_list_for_edit").get(number); +vars.put("simple_product_1_id", simpleList.get("id")); +vars.put("simple_product_1_name", simpleList.get("title")); + +do { + number1 = random.nextInt(props.get("simple_products_list_for_edit").size()); +} while(number == number1); +simpleList = props.get("simple_products_list_for_edit").get(number1); +vars.put("simple_product_2_id", simpleList.get("id")); +vars.put("simple_product_2_name", simpleList.get("title")); + +number2 = random.nextInt(props.get("configurable_products_list").size()); +configurableList = props.get("configurable_products_list").get(number2); +vars.put("configurable_product_1_id", configurableList.get("id")); +vars.put("configurable_product_1_url_key", configurableList.get("url_key")); +vars.put("configurable_product_1_name", configurableList.get("title")); + +//Additional category to be added +//int categoryId = Integer.parseInt(vars.get("simple_product_category_id")); +//vars.put("category_additional", (categoryId+1).toString()); +//New price +vars.put("price_new", "9999"); +//New special price +vars.put("special_price_new", "8888"); +//New quantity +vars.put("quantity_new", "100600"); +vars.put("configurable_sku", "Configurable Product - ${__time(YMD)}-${__threadNum}-${__Random(1,1000000)}"); + + </stringProp> + <stringProp name="BeanShellSampler.filename"/> + <stringProp name="BeanShellSampler.parameters"/> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_create_product/setup.jmx</stringProp></BeanShellSampler> + <hashTree/> + + <TestFragmentController guiclass="TestFragmentControllerGui" testclass="TestFragmentController" testname="Admin Create Bundle Product" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_create_product/create_bundle_product.jmx</stringProp> +</TestFragmentController> + <hashTree> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Catalog Product" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/catalog/product/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + </HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="1509986340">records found</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="New Bundle Product" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/catalog/product/new/set/4/type/bundle/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + </HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-144461265">New Product</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="New Bundle Product Validate" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="ajax" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">true</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">ajax</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="isAjax" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">true</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">isAjax</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="form_key" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_form_key}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">form_key</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[name]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">Bundle Product ${__time(YMDHMS)}-${__threadNum}-${__Random(1,1000000)}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[name]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[sku]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">Bundle Product ${__time(YMDHMS)}-${__threadNum}-${__Random(1,1000000)}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[sku]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[price]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">42</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[price]</stringProp> + </elementProp> + <elementProp name="product[tax_class_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">2</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[tax_class_id]</stringProp> + </elementProp> + <elementProp name="product[quantity_and_stock_status][qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">111</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[quantity_and_stock_status][qty]</stringProp> + </elementProp> + <elementProp name="product[quantity_and_stock_status][is_in_stock]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[quantity_and_stock_status][is_in_stock]</stringProp> + </elementProp> + <elementProp name="product[weight]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1.0000</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[weight]</stringProp> + </elementProp> + <elementProp name="product[product_has_weight]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[product_has_weight]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[category_ids][]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">2</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[category_ids][]</stringProp> + </elementProp> + <elementProp name="product[description]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"><p>Full bundle product description ${__time(YMDHMS)}-${__threadNum}-${__Random(1,1000000)}</p></stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[description]</stringProp> + </elementProp> + <elementProp name="product[short_description]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"><p>Short bundle product description ${__time(YMDHMS)}-${__threadNum}-${__Random(1,1000000)}</p></stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[short_description]</stringProp> + </elementProp> + <elementProp name="product[status]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[status]</stringProp> + </elementProp> + <elementProp name="product[configurable_variations]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[configurable_variations]</stringProp> + </elementProp> + <elementProp name="affect_configurable_product_attributes" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">affect_configurable_product_attributes</stringProp> + </elementProp> + <elementProp name="product[image]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[image]</stringProp> + </elementProp> + <elementProp name="product[small_image]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[small_image]</stringProp> + </elementProp> + <elementProp name="product[thumbnail]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[thumbnail]</stringProp> + </elementProp> + <elementProp name="product[url_key]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">bundle-product-${__time(YMDHMS)}-${__threadNum}-${__Random(1,1000000)}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[url_key]</stringProp> + </elementProp> + <elementProp name="product[meta_title]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">Bundle Product ${__time(YMDHMS)}-${__threadNum}-${__Random(1,1000000)} Meta Title</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[meta_title]</stringProp> + </elementProp> + <elementProp name="product[meta_keyword]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">Bundle Product ${__time(YMDHMS)}-${__threadNum}-${__Random(1,1000000)} Meta Keyword</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[meta_keyword]</stringProp> + </elementProp> + <elementProp name="product[meta_description]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">Bundle Product ${__time(YMDHMS)}-${__threadNum}-${__Random(1,1000000)} Meta Description</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[meta_description]</stringProp> + </elementProp> + <elementProp name="product[website_ids][]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[website_ids][]</stringProp> + </elementProp> + <elementProp name="product[special_price]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">99</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[special_price]</stringProp> + </elementProp> + <elementProp name="product[stock_data][notify_stock_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][notify_stock_qty]</stringProp> + </elementProp> + <elementProp name="product[special_from_date]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[special_from_date]</stringProp> + </elementProp> + <elementProp name="product[special_to_date]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[special_to_date]</stringProp> + </elementProp> + <elementProp name="product[cost]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[cost]</stringProp> + </elementProp> + <elementProp name="product[tier_price][0][website_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[tier_price][0][website_id]</stringProp> + </elementProp> + <elementProp name="product[tier_price][0][cust_group]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">32000</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[tier_price][0][cust_group]</stringProp> + </elementProp> + <elementProp name="product[tier_price][0][price_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">100</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[tier_price][0][price_qty]</stringProp> + </elementProp> + <elementProp name="product[tier_price][0][price]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">90</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[tier_price][0][price]</stringProp> + </elementProp> + <elementProp name="product[tier_price][0][delete]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[tier_price][0][delete]</stringProp> + </elementProp> + <elementProp name="product[tier_price][1][website_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[tier_price][1][website_id]</stringProp> + </elementProp> + <elementProp name="product[tier_price][1][cust_group]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[tier_price][1][cust_group]</stringProp> + </elementProp> + <elementProp name="product[tier_price][1][price_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">101</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[tier_price][1][price_qty]</stringProp> + </elementProp> + <elementProp name="product[tier_price][1][price]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">99</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[tier_price][1][price]</stringProp> + </elementProp> + <elementProp name="product[tier_price][1][delete]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[tier_price][1][delete]</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_manage_stock]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_manage_stock]</stringProp> + </elementProp> + <elementProp name="product[stock_data][original_inventory_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">100500</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][original_inventory_qty]</stringProp> + </elementProp> + <elementProp name="product[stock_data][qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">100500</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][qty]</stringProp> + </elementProp> + <elementProp name="product[stock_data][min_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][min_qty]</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_min_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_min_qty]</stringProp> + </elementProp> + <elementProp name="product[stock_data][min_sale_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][min_sale_qty]</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_min_sale_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_min_sale_qty]</stringProp> + </elementProp> + <elementProp name="product[stock_data][max_sale_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">10000</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][max_sale_qty]</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_max_sale_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_max_sale_qty]</stringProp> + </elementProp> + <elementProp name="product[stock_data][is_qty_decimal]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][is_qty_decimal]</stringProp> + </elementProp> + <elementProp name="product[stock_data][is_decimal_divided]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][is_decimal_divided]</stringProp> + </elementProp> + <elementProp name="product[stock_data][backorders]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][backorders]</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_backorders]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_backorders]</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_notify_stock_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_notify_stock_qty]</stringProp> + </elementProp> + <elementProp name="product[stock_data][enable_qty_increments]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][enable_qty_increments]</stringProp> + </elementProp> + <elementProp name="product[stock_data][qty_increments]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][qty_increments]</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_qty_increments]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_qty_increments]</stringProp> + </elementProp> + <elementProp name="product[stock_data][is_in_stock]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][is_in_stock]</stringProp> + </elementProp> + <elementProp name="product[custom_design]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[custom_design]</stringProp> + </elementProp> + <elementProp name="product[custom_design_from]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[custom_design_from]</stringProp> + </elementProp> + <elementProp name="product[custom_design_to]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[custom_design_to]</stringProp> + </elementProp> + <elementProp name="product[custom_layout_update]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[custom_layout_update]</stringProp> + </elementProp> + <elementProp name="product[page_layout]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[page_layout]</stringProp> + </elementProp> + <elementProp name="product[options_container]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">container2</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[options_container]</stringProp> + </elementProp> + <elementProp name="new-variations-attribute-set-id" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">new-variations-attribute-set-id</stringProp> + </elementProp> + <elementProp name="product[shipment_type]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[shipment_type]</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][0][title]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">option title one</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][0][title]</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][0][option_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][0][option_id]</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][0][delete]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][0][delete]</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][0][type]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">select</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][0][type]</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][0][required]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][0][required]</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][0][position]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][0][position]</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][0][bundle_selections][0][selection_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][0][bundle_selections][0][selection_id]</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][0][bundle_selections][0][option_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][0][bundle_selections][0][option_id]</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][0][bundle_selections][0][product_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${simple_product_1_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][0][bundle_selections][0][product_id]</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][0][bundle_selections][0][delete]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][0][bundle_selections][0][delete]</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][0][bundle_selections][0][selection_price_value]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">25</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][0][bundle_selections][0][selection_price_value]</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][0][bundle_selections][0][selection_price_type]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][0][bundle_selections][0][selection_price_type]</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][0][bundle_selections][0][selection_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][0][bundle_selections][0][selection_qty]</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][0][bundle_selections][0][selection_can_change_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][0][bundle_selections][0][selection_can_change_qty]</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][0][bundle_selections][0][position]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][0][bundle_selections][0][position]</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][0][bundle_selections][1][selection_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][0][bundle_selections][1][selection_id]</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][0][bundle_selections][1][option_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][0][bundle_selections][1][option_id]</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][0][bundle_selections][1][product_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${simple_product_2_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][0][bundle_selections][1][product_id]</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][0][bundle_selections][1][delete]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][0][bundle_selections][1][delete]</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][0][bundle_selections][1][selection_price_value]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">10.99</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][0][bundle_selections][1][selection_price_value]</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][0][bundle_selections][1][selection_price_type]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][0][bundle_selections][1][selection_price_type]</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][0][bundle_selections][1][selection_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][0][bundle_selections][1][selection_qty]</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][0][bundle_selections][1][selection_can_change_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][0][bundle_selections][1][selection_can_change_qty]</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][0][bundle_selections][1][position]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][0][bundle_selections][1][position]</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][1][title]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">option title two</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][1][title]</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][1][option_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][1][option_id]</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][1][delete]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][1][delete]</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][1][type]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">select</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][1][type]</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][1][required]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][1][required]</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][1][position]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][1][position]</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][1][bundle_selections][0][selection_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][1][bundle_selections][0][selection_id]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][1][bundle_selections][0][option_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][1][bundle_selections][0][option_id]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][1][bundle_selections][0][product_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${simple_product_1_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][1][bundle_selections][0][product_id]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][1][bundle_selections][0][delete]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][1][bundle_selections][0][delete]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][1][bundle_selections][0][selection_price_value]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">5.00</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][1][bundle_selections][0][selection_price_value]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][1][bundle_selections][0][selection_price_type]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][1][bundle_selections][0][selection_price_type]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][1][bundle_selections][0][selection_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][1][bundle_selections][0][selection_qty]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][1][bundle_selections][0][selection_can_change_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][1][bundle_selections][0][selection_can_change_qty]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][1][bundle_selections][0][position]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][1][bundle_selections][0][position]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][1][bundle_selections][1][selection_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][1][bundle_selections][1][selection_id]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][1][bundle_selections][1][option_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][1][bundle_selections][1][option_id]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][1][bundle_selections][1][product_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${simple_product_2_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][1][bundle_selections][1][product_id]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][1][bundle_selections][1][delete]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][1][bundle_selections][1][delete]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][1][bundle_selections][1][selection_price_value]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">7.00</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][1][bundle_selections][1][selection_price_value]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][1][bundle_selections][1][selection_price_type]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][1][bundle_selections][1][selection_price_type]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][1][bundle_selections][1][selection_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][1][bundle_selections][1][selection_qty]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][1][bundle_selections][1][selection_can_change_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][1][bundle_selections][1][selection_can_change_qty]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][1][bundle_selections][1][position]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][1][bundle_selections][1][position]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="affect_bundle_product_selections" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">2</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">affect_bundle_product_selections</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="links[related][0][id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${related_product_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">links[related][0][id]</stringProp> + </elementProp> + <elementProp name="links[related][0][position]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">links[related][0][position]</stringProp> + </elementProp> + <elementProp name="links[upsell][0][id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${related_product_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">links[upsell][0][id]</stringProp> + </elementProp> + <elementProp name="links[upsell][0][position]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">links[upsell][0][position]</stringProp> + </elementProp> + <elementProp name="links[crosssell][0][id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${related_product_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">links[crosssell][0][id]</stringProp> + </elementProp> + <elementProp name="links[crosssell][0][position]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">links[crosssell][0][position]</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/catalog/product/validate/set/4/</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + </HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="1853918323">{"error":false}</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="New Bundle Product Save" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="ajax" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">true</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">ajax</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="isAjax" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">true</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">isAjax</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="form_key" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_form_key}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">form_key</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[name]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">Bundle Product ${__time(YMDHMS)}-${__threadNum}-${__Random(1,1000000)}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[name]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[sku]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">Bundle Product ${__time(YMDHMS)}-${__threadNum}-${__Random(1,1000000)}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[sku]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[price]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">42</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[price]</stringProp> + </elementProp> + <elementProp name="product[tax_class_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">2</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[tax_class_id]</stringProp> + </elementProp> + <elementProp name="product[quantity_and_stock_status][qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">111</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[quantity_and_stock_status][qty]</stringProp> + </elementProp> + <elementProp name="product[quantity_and_stock_status][is_in_stock]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[quantity_and_stock_status][is_in_stock]</stringProp> + </elementProp> + <elementProp name="product[weight]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1.0000</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[weight]</stringProp> + </elementProp> + <elementProp name="product[product_has_weight]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[product_has_weight]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[category_ids][]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">2</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[category_ids][]</stringProp> + </elementProp> + <elementProp name="product[description]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"><p>Full bundle product Description ${__time(YMDHMS)}-${__threadNum}-${__Random(1,1000000)}</p></stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[description]</stringProp> + </elementProp> + <elementProp name="product[short_description]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"><p>Short bundle product description ${__time(YMDHMS)}-${__threadNum}-${__Random(1,1000000)}</p></stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[short_description]</stringProp> + </elementProp> + <elementProp name="product[status]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[status]</stringProp> + </elementProp> + <elementProp name="product[configurable_variations]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[configurable_variations]</stringProp> + </elementProp> + <elementProp name="affect_configurable_product_attributes" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">affect_configurable_product_attributes</stringProp> + </elementProp> + <elementProp name="product[image]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[image]</stringProp> + </elementProp> + <elementProp name="product[small_image]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[small_image]</stringProp> + </elementProp> + <elementProp name="product[thumbnail]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[thumbnail]</stringProp> + </elementProp> + <elementProp name="product[url_key]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">bundle-product-${__time(YMDHMS)}-${__threadNum}-${__Random(1,1000000)}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[url_key]</stringProp> + </elementProp> + <elementProp name="product[meta_title]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">Bundle Product ${__time(YMDHMS)}-${__threadNum}-${__Random(1,1000000)} Meta Title</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[meta_title]</stringProp> + </elementProp> + <elementProp name="product[meta_keyword]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">Bundle Product ${__time(YMDHMS)}-${__threadNum}-${__Random(1,1000000)} Meta Keyword</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[meta_keyword]</stringProp> + </elementProp> + <elementProp name="product[meta_description]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">Bundle Product ${__time(YMDHMS)}-${__threadNum}-${__Random(1,1000000)} Meta Description</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[meta_description]</stringProp> + </elementProp> + <elementProp name="product[website_ids][]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[website_ids][]</stringProp> + </elementProp> + <elementProp name="product[special_price]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">99</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[special_price]</stringProp> + </elementProp> + <elementProp name="product[special_from_date]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[special_from_date]</stringProp> + </elementProp> + <elementProp name="product[special_to_date]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[special_to_date]</stringProp> + </elementProp> + <elementProp name="product[cost]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[cost]</stringProp> + </elementProp> + <elementProp name="product[tier_price][0][website_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[tier_price][0][website_id]</stringProp> + </elementProp> + <elementProp name="product[tier_price][0][cust_group]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">32000</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[tier_price][0][cust_group]</stringProp> + </elementProp> + <elementProp name="product[tier_price][0][price_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">100</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[tier_price][0][price_qty]</stringProp> + </elementProp> + <elementProp name="product[tier_price][0][price]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">90</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[tier_price][0][price]</stringProp> + </elementProp> + <elementProp name="product[tier_price][0][delete]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[tier_price][0][delete]</stringProp> + </elementProp> + <elementProp name="product[tier_price][1][website_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[tier_price][1][website_id]</stringProp> + </elementProp> + <elementProp name="product[tier_price][1][cust_group]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[tier_price][1][cust_group]</stringProp> + </elementProp> + <elementProp name="product[tier_price][1][price_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">101</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[tier_price][1][price_qty]</stringProp> + </elementProp> + <elementProp name="product[tier_price][1][price]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">99</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[tier_price][1][price]</stringProp> + </elementProp> + <elementProp name="product[tier_price][1][delete]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[tier_price][1][delete]</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_manage_stock]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_manage_stock]</stringProp> + </elementProp> + <elementProp name="product[stock_data][original_inventory_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">100500</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][original_inventory_qty]</stringProp> + </elementProp> + <elementProp name="product[stock_data][qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">100500</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][qty]</stringProp> + </elementProp> + <elementProp name="product[stock_data][min_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][min_qty]</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_min_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_min_qty]</stringProp> + </elementProp> + <elementProp name="product[stock_data][min_sale_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][min_sale_qty]</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_min_sale_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_min_sale_qty]</stringProp> + </elementProp> + <elementProp name="product[stock_data][max_sale_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">10000</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][max_sale_qty]</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_max_sale_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_max_sale_qty]</stringProp> + </elementProp> + <elementProp name="product[stock_data][is_qty_decimal]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][is_qty_decimal]</stringProp> + </elementProp> + <elementProp name="product[stock_data][is_decimal_divided]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][is_decimal_divided]</stringProp> + </elementProp> + <elementProp name="product[stock_data][backorders]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][backorders]</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_backorders]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_backorders]</stringProp> + </elementProp> + <elementProp name="product[stock_data][notify_stock_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][notify_stock_qty]</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_notify_stock_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_notify_stock_qty]</stringProp> + </elementProp> + <elementProp name="product[stock_data][enable_qty_increments]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][enable_qty_increments]</stringProp> + </elementProp> + <elementProp name="product[stock_data][qty_increments]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][qty_increments]</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_qty_increments]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_qty_increments]</stringProp> + </elementProp> + <elementProp name="product[stock_data][is_in_stock]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][is_in_stock]</stringProp> + </elementProp> + <elementProp name="product[custom_design]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[custom_design]</stringProp> + </elementProp> + <elementProp name="product[custom_design_from]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[custom_design_from]</stringProp> + </elementProp> + <elementProp name="product[custom_design_to]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[custom_design_to]</stringProp> + </elementProp> + <elementProp name="product[custom_layout_update]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[custom_layout_update]</stringProp> + </elementProp> + <elementProp name="product[page_layout]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[page_layout]</stringProp> + </elementProp> + <elementProp name="product[options_container]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">container2</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[options_container]</stringProp> + </elementProp> + <elementProp name="new-variations-attribute-set-id" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">new-variations-attribute-set-id</stringProp> + </elementProp> + <elementProp name="product[shipment_type]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[shipment_type]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][0][title]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">option title one</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][0][title]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][0][option_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][0][option_id]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][0][delete]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][0][delete]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][0][type]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">select</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][0][type]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][0][required]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][0][required]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][0][position]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][0][position]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][0][bundle_selections][0][selection_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][0][bundle_selections][0][selection_id]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][0][bundle_selections][0][option_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][0][bundle_selections][0][option_id]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][0][bundle_selections][0][product_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${simple_product_1_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][0][bundle_selections][0][product_id]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][0][bundle_selections][0][delete]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][0][bundle_selections][0][delete]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][0][bundle_selections][0][selection_price_value]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">25</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][0][bundle_selections][0][selection_price_value]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][0][bundle_selections][0][selection_price_type]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][0][bundle_selections][0][selection_price_type]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][0][bundle_selections][0][selection_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][0][bundle_selections][0][selection_qty]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][0][bundle_selections][0][selection_can_change_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][0][bundle_selections][0][selection_can_change_qty]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][0][bundle_selections][0][position]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][0][bundle_selections][0][position]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][0][bundle_selections][1][selection_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][0][bundle_selections][1][selection_id]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][0][bundle_selections][1][option_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][0][bundle_selections][1][option_id]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][0][bundle_selections][1][product_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${simple_product_2_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][0][bundle_selections][1][product_id]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][0][bundle_selections][1][delete]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][0][bundle_selections][1][delete]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][0][bundle_selections][1][selection_price_value]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">10.99</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][0][bundle_selections][1][selection_price_value]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][0][bundle_selections][1][selection_price_type]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][0][bundle_selections][1][selection_price_type]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][0][bundle_selections][1][selection_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][0][bundle_selections][1][selection_qty]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][0][bundle_selections][1][selection_can_change_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][0][bundle_selections][1][selection_can_change_qty]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][0][bundle_selections][1][position]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][0][bundle_selections][1][position]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][1][title]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">option title two</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][1][title]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][1][option_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][1][option_id]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][1][delete]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][1][delete]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][1][type]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">select</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][1][type]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][1][required]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][1][required]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][1][position]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][1][position]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][1][bundle_selections][0][selection_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][1][bundle_selections][0][selection_id]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][1][bundle_selections][0][option_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][1][bundle_selections][0][option_id]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][1][bundle_selections][0][product_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${simple_product_1_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][1][bundle_selections][0][product_id]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][1][bundle_selections][0][delete]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][1][bundle_selections][0][delete]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][1][bundle_selections][0][selection_price_value]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">5.00</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][1][bundle_selections][0][selection_price_value]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][1][bundle_selections][0][selection_price_type]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][1][bundle_selections][0][selection_price_type]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][1][bundle_selections][0][selection_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][1][bundle_selections][0][selection_qty]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][1][bundle_selections][0][selection_can_change_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][1][bundle_selections][0][selection_can_change_qty]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][1][bundle_selections][0][position]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][1][bundle_selections][0][position]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][1][bundle_selections][1][selection_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][1][bundle_selections][1][selection_id]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][1][bundle_selections][1][option_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][1][bundle_selections][1][option_id]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][1][bundle_selections][1][product_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${simple_product_2_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][1][bundle_selections][1][product_id]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][1][bundle_selections][1][delete]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][1][bundle_selections][1][delete]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][1][bundle_selections][1][selection_price_value]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">7.00</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][1][bundle_selections][1][selection_price_value]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][1][bundle_selections][1][selection_price_type]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][1][bundle_selections][1][selection_price_type]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][1][bundle_selections][1][selection_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][1][bundle_selections][1][selection_qty]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][1][bundle_selections][1][selection_can_change_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][1][bundle_selections][1][selection_can_change_qty]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="bundle_options[bundle_options][1][bundle_selections][1][position]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">bundle_options[bundle_options][1][bundle_selections][1][position]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="affect_bundle_product_selections" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">2</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">affect_bundle_product_selections</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="links[related][0][id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${related_product_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">links[related][0][id]</stringProp> + </elementProp> + <elementProp name="links[related][0][position]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">links[related][0][position]</stringProp> + </elementProp> + <elementProp name="links[upsell][0][id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${related_product_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">links[upsell][0][id]</stringProp> + </elementProp> + <elementProp name="links[upsell][0][position]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">links[upsell][0][position]</stringProp> + </elementProp> + <elementProp name="links[crosssell][0][id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${related_product_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">links[crosssell][0][id]</stringProp> + </elementProp> + <elementProp name="links[crosssell][0][position]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">links[crosssell][0][position]</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/catalog/product/save/set/4/type/bundle/back/edit/active_tab/product-details/</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + </HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-583471546">You saved the product</stringProp> + <stringProp name="-1534079309">option title one</stringProp> + <stringProp name="-1534074215">option title two</stringProp> + <stringProp name="1304788671">${simple_product_2_name}</stringProp> + <stringProp name="417284990">${simple_product_1_name}</stringProp> + </collectionProp> + + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + </hashTree> + + <GenericController guiclass="LogicControllerGui" testclass="GenericController" testname="Admin Create Configurable Product" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/simple_controller.jmx</stringProp> +</GenericController> + <hashTree> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Catalog Product" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/catalog/product/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_create_product/open_catalog_grid.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="1509986340">records found</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="New Configurable Product" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/catalog/product/new/set/${attribute_set_id}/type/configurable/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_create_product/new_configurable.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-144461265">New Product</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="New Configurable Product Validate" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="ajax" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">true</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">ajax</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="isAjax" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">true</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">isAjax</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="affect_configurable_product_attributes" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">affect_configurable_product_attributes</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="form_key" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_form_key}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">form_key</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="new-variations-attribute-set-id" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${attribute_set_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">new-variations-attribute-set-id</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[affect_product_custom_options]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[affect_product_custom_options]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[attribute_set_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${attribute_set_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[attribute_set_id]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[category_ids][0]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">4</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[category_ids][0]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[custom_layout_update]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[custom_layout_update]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[description]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[description]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[gift_message_available]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[gift_message_available]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[gift_wrapping_available]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[gift_wrapping_available]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[gift_wrapping_price]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[gift_wrapping_price]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[image]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[image]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[is_returnable]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">2</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[is_returnable]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[meta_description]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${configurable_sku} - Meta Description</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[meta_description]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[meta_keyword]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${configurable_sku} - Meta Keyword</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[meta_keyword]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[meta_title]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${configurable_sku} - Meta Title</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[meta_title]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[name]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${configurable_sku}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[name]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[options_container]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">container2</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[options_container]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[price]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${price_new}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[price]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[product_has_weight]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[product_has_weight]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[quantity_and_stock_status][is_in_stock]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[quantity_and_stock_status][is_in_stock]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[quantity_and_stock_status][qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1000</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[quantity_and_stock_status][qty]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[short_description]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[short_description]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[sku]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${configurable_sku}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[sku]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[small_image]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[small_image]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[special_price]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${special_price_new}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[special_price]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[status]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[status]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[stock_data][backorders]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][backorders]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[stock_data][deferred_stock_update]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][deferred_stock_update]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[stock_data][enable_qty_increments]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][enable_qty_increments]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[stock_data][is_decimal_divided]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][is_decimal_divided]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[stock_data][is_qty_decimal]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][is_qty_decimal]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[stock_data][manage_stock]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][manage_stock]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[stock_data][max_sale_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">10000</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][max_sale_qty]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[stock_data][min_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][min_qty]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[stock_data][min_sale_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][min_sale_qty]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[stock_data][notify_stock_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][notify_stock_qty]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[stock_data][qty_increments]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][qty_increments]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_backorders]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_backorders]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_deferred_stock_update]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_deferred_stock_update]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_enable_qty_increments]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_enable_qty_increments]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_manage_stock]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_manage_stock]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_max_sale_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_max_sale_qty]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_min_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_min_qty]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_min_sale_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_min_sale_qty]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_notify_stock_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_notify_stock_qty]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_qty_increments]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_qty_increments]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[tax_class_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">2</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[tax_class_id]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[thumbnail]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[thumbnail]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[url_key]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[url_key]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[use_config_gift_message_available]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[use_config_gift_message_available]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[use_config_gift_wrapping_available]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[use_config_gift_wrapping_available]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[use_config_is_returnable]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[use_config_is_returnable]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[visibility]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">4</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[visibility]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[website_ids][1]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[website_ids][1]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="links[related][0][id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${related_product_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">links[related][0][id]</stringProp> + </elementProp> + <elementProp name="links[related][0][position]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">links[related][0][position]</stringProp> + </elementProp> + <elementProp name="links[upsell][0][id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${related_product_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">links[upsell][0][id]</stringProp> + </elementProp> + <elementProp name="links[upsell][0][position]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">links[upsell][0][position]</stringProp> + </elementProp> + <elementProp name="links[crosssell][0][id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${related_product_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">links[crosssell][0][id]</stringProp> + </elementProp> + <elementProp name="links[crosssell][0][position]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">links[crosssell][0][position]</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/catalog/product/validate/set/${attribute_set_id}/</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_create_product/configurable_validate.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="1853918323">{"error":false}</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + + <JSR223PreProcessor guiclass="TestBeanGUI" testclass="JSR223PreProcessor" testname="Prepare Configurable Data" enabled="true"> + <stringProp name="scriptLanguage">javascript</stringProp> + <stringProp name="parameters"/> + <stringProp name="filename"/> + <stringProp name="cacheKey"/> + <stringProp name="script"> +attributes = vars.getObject("product_attributes"); + +for (i in attributes) { + var attribute = attributes[i]; + sampler.addArgument("attribute_codes[" + i + "]", attribute.code); + sampler.addArgument("attributes[" + i + "]", attribute.id); + sampler.addArgument("product[" + attribute.code + "]", attribute.options[0].value); + addConfigurableAttributeData(attribute); +} + +addConfigurableMatrix(attributes); + +function addConfigurableAttributeData(attribute) { + var attributeId = attribute.id; + + sampler.addArgument("product[configurable_attributes_data][" + attributeId + "][attribute_id]", attributeId); + sampler.addArgument("product[configurable_attributes_data][" + attributeId + "][code]", attribute.code); + sampler.addArgument("product[configurable_attributes_data][" + attributeId + "][label]", attribute.label); + sampler.addArgument("product[configurable_attributes_data][" + attributeId + "][position]", 0); + attribute.options.forEach(function (option, index) { + sampler.addArgument("product[configurable_attributes_data][" + attributeId + "][values][" + option.value + "][include]", index); + sampler.addArgument("product[configurable_attributes_data][" + attributeId + "][values][" + option.value + "][value_index]", option.value); + }); +} + +/** + * Build 4 simple products for Configurable + */ +function addConfigurableMatrix(attributes) { + + var attribute1 = attributes[0], + attribute2 = attributes[1], + productIndex = 1, + products = []; + var variationNames = []; + attribute1.options.forEach(function (option1) { + attribute2.options.forEach(function (option2) { + var productAttributes = {}, + namePart = option1.label + "+" + option2.label, + variationKey = option1.value + "-" + option2.value; + productAttributes[attribute1.code] = option1.value; + productAttributes[attribute2.code] = option2.value; + + variationNames.push(namePart + " - " + vars.get("configurable_sku")); + var product = { + "id": null, + "name": namePart + " - " + vars.get("configurable_sku"), + "sku": namePart + " - " + vars.get("configurable_sku"), + "status": 1, + "price": "100", + "price_currency": "$", + "price_string": "$100", + "weight": "6", + "qty": "50", + "variationKey": variationKey, + "configurable_attribute": JSON.stringify(productAttributes), + "thumbnail_image": "", + "media_gallery": {"images": {}}, + "image": [], + "was_changed": true, + "canEdit": 1, + "newProduct": 1, + "record_id": productIndex + }; + productIndex++; + products.push(product); + }); + }); + + sampler.addArgument("configurable-matrix-serialized", JSON.stringify(products)); + vars.putObject("configurable_variations_assertion", variationNames); +} + </stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_create_product/configurable_prepare_data.jmx</stringProp></JSR223PreProcessor> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="New Configurable Product Save" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="ajax" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">true</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">ajax</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="isAjax" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">true</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">isAjax</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="affect_configurable_product_attributes" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">affect_configurable_product_attributes</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="form_key" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_form_key}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">form_key</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="new-variations-attribute-set-id" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${attribute_set_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">new-variations-attribute-set-id</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[affect_product_custom_options]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[affect_product_custom_options]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[attribute_set_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${attribute_set_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[attribute_set_id]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[category_ids][0]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">2</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[category_ids][0]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[custom_layout_update]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[custom_layout_update]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[description]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[description]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[gift_message_available]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[gift_message_available]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[gift_wrapping_available]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[gift_wrapping_available]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[gift_wrapping_price]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[gift_wrapping_price]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[image]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[image]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[is_returnable]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">2</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[is_returnable]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[meta_description]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${configurable_sku} - Meta Description</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[meta_description]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[meta_keyword]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${configurable_sku} - Meta Keyword</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[meta_keyword]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[meta_title]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${configurable_sku} - Meta Title</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[meta_title]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[name]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${configurable_sku}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[name]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[options_container]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">container2</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[options_container]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[price]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${price_new}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[price]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[product_has_weight]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[product_has_weight]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[quantity_and_stock_status][is_in_stock]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[quantity_and_stock_status][is_in_stock]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[quantity_and_stock_status][qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1000</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[quantity_and_stock_status][qty]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[short_description]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[short_description]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[sku]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${configurable_sku}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[sku]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[small_image]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[small_image]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[special_price]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${special_price_new}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[special_price]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[status]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[status]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[stock_data][backorders]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][backorders]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[stock_data][deferred_stock_update]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][deferred_stock_update]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[stock_data][enable_qty_increments]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][enable_qty_increments]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[stock_data][is_decimal_divided]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][is_decimal_divided]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[stock_data][is_qty_decimal]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][is_qty_decimal]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[stock_data][manage_stock]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][manage_stock]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[stock_data][max_sale_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">10000</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][max_sale_qty]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[stock_data][min_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][min_qty]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[stock_data][min_sale_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][min_sale_qty]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[stock_data][notify_stock_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][notify_stock_qty]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[stock_data][qty_increments]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][qty_increments]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_backorders]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_backorders]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_deferred_stock_update]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_deferred_stock_update]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_enable_qty_increments]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_enable_qty_increments]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_manage_stock]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_manage_stock]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_max_sale_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_max_sale_qty]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_min_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_min_qty]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_min_sale_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_min_sale_qty]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_notify_stock_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_notify_stock_qty]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_qty_increments]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_qty_increments]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[tax_class_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">2</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[tax_class_id]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[thumbnail]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[thumbnail]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[url_key]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[url_key]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[use_config_gift_message_available]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[use_config_gift_message_available]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[use_config_gift_wrapping_available]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[use_config_gift_wrapping_available]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[use_config_is_returnable]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[use_config_is_returnable]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[visibility]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">4</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[visibility]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[website_ids][1]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[website_ids][1]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="links[related][0][id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${related_product_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">links[related][0][id]</stringProp> + </elementProp> + <elementProp name="links[related][0][position]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">links[related][0][position]</stringProp> + </elementProp> + <elementProp name="links[upsell][0][id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${related_product_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">links[upsell][0][id]</stringProp> + </elementProp> + <elementProp name="links[upsell][0][position]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">links[upsell][0][position]</stringProp> + </elementProp> + <elementProp name="links[crosssell][0][id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${related_product_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">links[crosssell][0][id]</stringProp> + </elementProp> + <elementProp name="links[crosssell][0][position]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">links[crosssell][0][position]</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/catalog/product/save/set/${attribute_set_id}/type/configurable/back/edit/active_tab/product-details/</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_create_product/configurable_save.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-583471546">You saved the product</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + <JSR223Assertion guiclass="TestBeanGUI" testclass="JSR223Assertion" testname="Assert Variation" enabled="true"> + <stringProp name="scriptLanguage">javascript</stringProp> + <stringProp name="parameters"/> + <stringProp name="filename"/> + <stringProp name="cacheKey"/> + <stringProp name="script"> +var configurableVariations = vars.getObject("configurable_variations_assertion"), +response = SampleResult.getResponseDataAsString(); + +configurableVariations.forEach(function (variation) { + if (response.indexOf(variation) == -1) { + AssertionResult.setFailureMessage("Cannot find variation \"" + variation + "\""); + AssertionResult.setFailure(true); + } +}); +</stringProp> + </JSR223Assertion> + <hashTree/> + + <JSR223PreProcessor guiclass="TestBeanGUI" testclass="JSR223PreProcessor" testname="Prepare Configurable Data" enabled="true"> + <stringProp name="scriptLanguage">javascript</stringProp> + <stringProp name="parameters"/> + <stringProp name="filename"/> + <stringProp name="cacheKey"/> + <stringProp name="script"> +attributes = vars.getObject("product_attributes"); + +for (i in attributes) { + var attribute = attributes[i]; + sampler.addArgument("attribute_codes[" + i + "]", attribute.code); + sampler.addArgument("attributes[" + i + "]", attribute.id); + sampler.addArgument("product[" + attribute.code + "]", attribute.options[0].value); + addConfigurableAttributeData(attribute); +} + +addConfigurableMatrix(attributes); + +function addConfigurableAttributeData(attribute) { + var attributeId = attribute.id; + + sampler.addArgument("product[configurable_attributes_data][" + attributeId + "][attribute_id]", attributeId); + sampler.addArgument("product[configurable_attributes_data][" + attributeId + "][code]", attribute.code); + sampler.addArgument("product[configurable_attributes_data][" + attributeId + "][label]", attribute.label); + sampler.addArgument("product[configurable_attributes_data][" + attributeId + "][position]", 0); + attribute.options.forEach(function (option, index) { + sampler.addArgument("product[configurable_attributes_data][" + attributeId + "][values][" + option.value + "][include]", index); + sampler.addArgument("product[configurable_attributes_data][" + attributeId + "][values][" + option.value + "][value_index]", option.value); + }); +} + +/** + * Build 4 simple products for Configurable + */ +function addConfigurableMatrix(attributes) { + + var attribute1 = attributes[0], + attribute2 = attributes[1], + productIndex = 1, + products = []; + var variationNames = []; + attribute1.options.forEach(function (option1) { + attribute2.options.forEach(function (option2) { + var productAttributes = {}, + namePart = option1.label + "+" + option2.label, + variationKey = option1.value + "-" + option2.value; + productAttributes[attribute1.code] = option1.value; + productAttributes[attribute2.code] = option2.value; + + variationNames.push(namePart + " - " + vars.get("configurable_sku")); + var product = { + "id": null, + "name": namePart + " - " + vars.get("configurable_sku"), + "sku": namePart + " - " + vars.get("configurable_sku"), + "status": 1, + "price": "100", + "price_currency": "$", + "price_string": "$100", + "weight": "6", + "qty": "50", + "variationKey": variationKey, + "configurable_attribute": JSON.stringify(productAttributes), + "thumbnail_image": "", + "media_gallery": {"images": {}}, + "image": [], + "was_changed": true, + "canEdit": 1, + "newProduct": 1, + "record_id": productIndex + }; + productIndex++; + products.push(product); + }); + }); + + sampler.addArgument("configurable-matrix-serialized", JSON.stringify(products)); + vars.putObject("configurable_variations_assertion", variationNames); +} + </stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_create_product/configurable_prepare_data.jmx</stringProp></JSR223PreProcessor> + <hashTree/> + </hashTree> + </hashTree> + + <TestFragmentController guiclass="TestFragmentControllerGui" testclass="TestFragmentController" testname="Admin Create Downloadable Product" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_create_product/create_downloadable_product.jmx</stringProp> +</TestFragmentController> + <hashTree> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Catalog Product" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/catalog/product/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + </HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="1509986340">records found</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="New Downloadable Product" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/catalog/product/new/set/4/type/downloadable/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + </HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-144461265">New Product</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="New Downloadable Upload Original File" enabled="true"> + <elementProp name="HTTPsampler.Files" elementType="HTTPFileArgs"> + <collectionProp name="HTTPFileArgs.files"> + <elementProp name="${files_folder}downloadable_original.txt" elementType="HTTPFileArg"> + <stringProp name="File.path">${files_folder}downloadable_original.txt</stringProp> + <stringProp name="File.paramname">links</stringProp> + <stringProp name="File.mimetype">text/plain</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="form_key" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_form_key}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">form_key</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/downloadable_file/upload/type/links/?isAjax=true</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">false</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">true</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + </HTTPSamplerProxy> + <hashTree> + <com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor guiclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.gui.JSONPathExtractorGui" testclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor" testname="Extract original file" enabled="true"> + <stringProp name="VAR">original_file</stringProp> + <stringProp name="JSONPATH">$.file</stringProp> + <stringProp name="DEFAULT"/> + <stringProp name="VARIABLE"/> + <stringProp name="SUBJECT">BODY</stringProp> + </com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor> + <hashTree/> + </hashTree> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="New Downloadable Upload Sample File" enabled="true"> + <elementProp name="HTTPsampler.Files" elementType="HTTPFileArgs"> + <collectionProp name="HTTPFileArgs.files"> + <elementProp name="${files_folder}downloadable_sample.txt" elementType="HTTPFileArg"> + <stringProp name="File.path">${files_folder}downloadable_sample.txt</stringProp> + <stringProp name="File.paramname">samples</stringProp> + <stringProp name="File.mimetype">text/plain</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="form_key" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_form_key}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">form_key</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/downloadable_file/upload/type/samples/?isAjax=true</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">false</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">true</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + </HTTPSamplerProxy> + <hashTree> + <com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor guiclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.gui.JSONPathExtractorGui" testclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor" testname="Extract sample file" enabled="true"> + <stringProp name="VAR">sample_file</stringProp> + <stringProp name="JSONPATH">$.file</stringProp> + <stringProp name="DEFAULT"/> + <stringProp name="VARIABLE"/> + <stringProp name="SUBJECT">BODY</stringProp> + </com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor> + <hashTree/> + </hashTree> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="New Downloadable Product Validate" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="ajax" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">true</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">ajax</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="isAjax" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">true</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">isAjax</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="form_key" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_form_key}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">form_key</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[name]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">Downloadable Product ${__time(YMDHMS)}-${__threadNum}-${__Random(1,1000000)}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[name]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[sku]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">SKU ${__time(YMDHMS)}-${__threadNum}-${__Random(1,1000000)}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[sku]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[price]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">123</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[price]</stringProp> + </elementProp> + <elementProp name="product[tax_class_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">2</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[tax_class_id]</stringProp> + </elementProp> + <elementProp name="product[quantity_and_stock_status][qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">111</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[quantity_and_stock_status][qty]</stringProp> + </elementProp> + <elementProp name="product[quantity_and_stock_status][is_in_stock]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[quantity_and_stock_status][is_in_stock]</stringProp> + </elementProp> + <elementProp name="product[weight]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1.0000</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[weight]</stringProp> + </elementProp> + <elementProp name="product[category_ids][]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">2</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[category_ids][]</stringProp> + </elementProp> + <elementProp name="product[description]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"><p>Full downloadable product description ${__time(YMDHMS)}-${__threadNum}-${__Random(1,1000000)}</p></stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[description]</stringProp> + </elementProp> + <elementProp name="product[short_description]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"><p>Short downloadable product description ${__time(YMDHMS)}-${__threadNum}-${__Random(1,1000000)}</p></stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[short_description]</stringProp> + </elementProp> + <elementProp name="product[status]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[status]</stringProp> + </elementProp> + <elementProp name="product[image]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[image]</stringProp> + </elementProp> + <elementProp name="product[small_image]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[small_image]</stringProp> + </elementProp> + <elementProp name="product[thumbnail]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[thumbnail]</stringProp> + </elementProp> + <elementProp name="product[url_key]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">downloadable-product-${__time(YMDHMS)}-${__threadNum}-${__Random(1,1000000)}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[url_key]</stringProp> + </elementProp> + <elementProp name="product[meta_title]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">Downloadable Product ${__time(YMDHMS)}-${__threadNum}-${__Random(1,1000000)} Meta Title</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[meta_title]</stringProp> + </elementProp> + <elementProp name="product[meta_keyword]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">Downloadable Product ${__time(YMDHMS)}-${__threadNum}-${__Random(1,1000000)} Meta Keyword</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[meta_keyword]</stringProp> + </elementProp> + <elementProp name="product[meta_description]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">Downloadable Product ${__time(YMDHMS)}-${__threadNum}-${__Random(1,1000000)} Meta Description</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[meta_description]</stringProp> + </elementProp> + <elementProp name="product[website_ids][]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[website_ids][]</stringProp> + </elementProp> + <elementProp name="product[special_price]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">99</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[special_price]</stringProp> + </elementProp> + <elementProp name="product[stock_data][notify_stock_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][notify_stock_qty]</stringProp> + </elementProp> + <elementProp name="product[special_from_date]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[special_from_date]</stringProp> + </elementProp> + <elementProp name="product[special_to_date]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[special_to_date]</stringProp> + </elementProp> + <elementProp name="product[cost]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[cost]</stringProp> + </elementProp> + <elementProp name="product[tier_price][0][website_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[tier_price][0][website_id]</stringProp> + </elementProp> + <elementProp name="product[tier_price][0][cust_group]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">32000</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[tier_price][0][cust_group]</stringProp> + </elementProp> + <elementProp name="product[tier_price][0][price_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">100</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[tier_price][0][price_qty]</stringProp> + </elementProp> + <elementProp name="product[tier_price][0][price]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">90</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[tier_price][0][price]</stringProp> + </elementProp> + <elementProp name="product[tier_price][0][delete]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[tier_price][0][delete]</stringProp> + </elementProp> + <elementProp name="product[tier_price][1][website_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[tier_price][1][website_id]</stringProp> + </elementProp> + <elementProp name="product[tier_price][1][cust_group]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[tier_price][1][cust_group]</stringProp> + </elementProp> + <elementProp name="product[tier_price][1][price_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">101</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[tier_price][1][price_qty]</stringProp> + </elementProp> + <elementProp name="product[tier_price][1][price]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">99</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[tier_price][1][price]</stringProp> + </elementProp> + <elementProp name="product[tier_price][1][delete]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[tier_price][1][delete]</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_manage_stock]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_manage_stock]</stringProp> + </elementProp> + <elementProp name="product[stock_data][original_inventory_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">100500</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][original_inventory_qty]</stringProp> + </elementProp> + <elementProp name="product[stock_data][qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">100500</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][qty]</stringProp> + </elementProp> + <elementProp name="product[stock_data][min_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][min_qty]</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_min_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_min_qty]</stringProp> + </elementProp> + <elementProp name="product[stock_data][min_sale_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][min_sale_qty]</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_min_sale_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_min_sale_qty]</stringProp> + </elementProp> + <elementProp name="product[stock_data][max_sale_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">10000</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][max_sale_qty]</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_max_sale_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_max_sale_qty]</stringProp> + </elementProp> + <elementProp name="product[stock_data][is_qty_decimal]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][is_qty_decimal]</stringProp> + </elementProp> + <elementProp name="product[stock_data][is_decimal_divided]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][is_decimal_divided]</stringProp> + </elementProp> + <elementProp name="product[stock_data][backorders]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][backorders]</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_backorders]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_backorders]</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_notify_stock_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_notify_stock_qty]</stringProp> + </elementProp> + <elementProp name="product[stock_data][enable_qty_increments]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][enable_qty_increments]</stringProp> + </elementProp> + <elementProp name="product[stock_data][qty_increments]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][qty_increments]</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_qty_increments]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_qty_increments]</stringProp> + </elementProp> + <elementProp name="product[stock_data][is_in_stock]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][is_in_stock]</stringProp> + </elementProp> + <elementProp name="product[custom_design]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[custom_design]</stringProp> + </elementProp> + <elementProp name="product[custom_design_from]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[custom_design_from]</stringProp> + </elementProp> + <elementProp name="product[custom_design_to]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[custom_design_to]</stringProp> + </elementProp> + <elementProp name="product[custom_layout_update]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[custom_layout_update]</stringProp> + </elementProp> + <elementProp name="product[page_layout]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[page_layout]</stringProp> + </elementProp> + <elementProp name="product[options_container]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">container2</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[options_container]</stringProp> + </elementProp> + <elementProp name="is_downloadable" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">on</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">is_downloadable</stringProp> + </elementProp> + <elementProp name="product[links_title]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">Links</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[links_title]</stringProp> + </elementProp> + <elementProp name="product[links_purchased_separately]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[links_purchased_separately]</stringProp> + </elementProp> + <elementProp name="downloadable[link][0][file][0][file]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${original_file}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">downloadable[link][0][file][0][file]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="downloadable[link][0][file][0][name]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">downloadable_original.txt</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">downloadable[link][0][file][0][name]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="downloadable[link][0][file][0][size]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">13</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">downloadable[link][0][file][0][size]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="downloadable[link][0][file][0][status]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">new</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">downloadable[link][0][file][0][status]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="downloadable[link][0][is_shareable]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">downloadable[link][0][is_shareable]</stringProp> + </elementProp> + <elementProp name="downloadable[link][0][is_unlimited]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">downloadable[link][0][is_unlimited]</stringProp> + </elementProp> + <elementProp name="downloadable[link][0][link_url]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">downloadable[link][0][link_url]</stringProp> + </elementProp> + <elementProp name="downloadable[link][0][number_of_downloads]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">downloadable[link][0][number_of_downloads]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="downloadable[link][0][price]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">120</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">downloadable[link][0][price]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="downloadable[link][0][record_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">downloadable[link][0][record_id]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="downloadable[link][0][sample][type]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">file</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">downloadable[link][0][sample][type]</stringProp> + </elementProp> + <elementProp name="downloadable[link][0][sample][url]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">downloadable[link][0][sample][url]</stringProp> + </elementProp> + <elementProp name="downloadable[link][0][sort_order]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">downloadable[link][0][sort_order]</stringProp> + </elementProp> + <elementProp name="downloadable[link][0][title]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">Original Link</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">downloadable[link][0][title]</stringProp> + </elementProp> + <elementProp name="downloadable[link][0][type]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">file</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">downloadable[link][0][type]</stringProp> + </elementProp> + <elementProp name="downloadable[sample][0][file][0][file]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${sample_file}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">downloadable[sample][0][file][0][file]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="downloadable[sample][0][file][0][name]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">downloadable_sample.txt</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">downloadable[sample][0][file][0][name]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="downloadable[sample][0][file][0][size]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">14</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">downloadable[sample][0][file][0][size]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="downloadable[sample][0][file][0][status]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">new</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">downloadable[sample][0][file][0][status]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="downloadable[sample][0][record_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">downloadable[sample][0][record_id]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="downloadable[sample][0][sample_url]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">downloadable[sample][0][sample_url]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="downloadable[sample][0][sort_order]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">downloadable[sample][0][sort_order]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="downloadable[sample][0][title]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">Sample Link</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">downloadable[sample][0][title]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="downloadable[sample][0][type]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">file</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">downloadable[sample][0][type]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="affect_configurable_product_attributes" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">affect_configurable_product_attributes</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="new-variations-attribute-set-id" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">4</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">new-variations-attribute-set-id</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[configurable_variation]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[configurable_variation]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="links[related][0][id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${related_product_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">links[related][0][id]</stringProp> + </elementProp> + <elementProp name="links[related][0][position]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">links[related][0][position]</stringProp> + </elementProp> + <elementProp name="links[upsell][0][id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${related_product_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">links[upsell][0][id]</stringProp> + </elementProp> + <elementProp name="links[upsell][0][position]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">links[upsell][0][position]</stringProp> + </elementProp> + <elementProp name="links[crosssell][0][id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${related_product_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">links[crosssell][0][id]</stringProp> + </elementProp> + <elementProp name="links[crosssell][0][position]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">links[crosssell][0][position]</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/catalog/product/validate/set/4/type/downloadable/</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + </HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="1853918323">{"error":false}</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="New Downloadable Product Save" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="ajax" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">true</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">ajax</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="isAjax" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">true</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">isAjax</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="form_key" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_form_key}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">form_key</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[name]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">Downloadable Product ${__time(YMDHMS)}-${__threadNum}-${__Random(1,1000000)}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[name]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[sku]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">SKU ${__time(YMDHMS)}-${__threadNum}-${__Random(1,1000000)}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[sku]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[price]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">123</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[price]</stringProp> + </elementProp> + <elementProp name="product[tax_class_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">2</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[tax_class_id]</stringProp> + </elementProp> + <elementProp name="product[quantity_and_stock_status][qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">111</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[quantity_and_stock_status][qty]</stringProp> + </elementProp> + <elementProp name="product[quantity_and_stock_status][is_in_stock]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[quantity_and_stock_status][is_in_stock]</stringProp> + </elementProp> + <elementProp name="product[weight]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1.0000</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[weight]</stringProp> + </elementProp> + <elementProp name="product[category_ids][]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">2</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[category_ids][]</stringProp> + </elementProp> + <elementProp name="product[description]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"><p>Full downloadable product description ${__time(YMDHMS)}-${__threadNum}-${__Random(1,1000000)}</p></stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[description]</stringProp> + </elementProp> + <elementProp name="product[short_description]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"><p>Short downloadable product description ${__time(YMDHMS)}-${__threadNum}-${__Random(1,1000000)}</p></stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[short_description]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[status]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[status]</stringProp> + </elementProp> + <elementProp name="product[image]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[image]</stringProp> + </elementProp> + <elementProp name="product[small_image]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[small_image]</stringProp> + </elementProp> + <elementProp name="product[thumbnail]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[thumbnail]</stringProp> + </elementProp> + <elementProp name="product[url_key]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">downloadable-product-${__time(YMDHMS)}-${__threadNum}-${__Random(1,1000000)}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[url_key]</stringProp> + </elementProp> + <elementProp name="product[meta_title]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">Downloadable Product ${__time(YMDHMS)}-${__threadNum}-${__Random(1,1000000)} Meta Title</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[meta_title]</stringProp> + </elementProp> + <elementProp name="product[meta_keyword]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">Downloadable Product ${__time(YMDHMS)}-${__threadNum}-${__Random(1,1000000)} Meta Keyword</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[meta_keyword]</stringProp> + </elementProp> + <elementProp name="product[meta_description]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">Downloadable Product ${__time(YMDHMS)}-${__threadNum}-${__Random(1,1000000)} Meta Description</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[meta_description]</stringProp> + </elementProp> + <elementProp name="product[website_ids][]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[website_ids][]</stringProp> + </elementProp> + <elementProp name="product[special_price]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">99</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[special_price]</stringProp> + </elementProp> + <elementProp name="product[special_from_date]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[special_from_date]</stringProp> + </elementProp> + <elementProp name="product[special_to_date]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[special_to_date]</stringProp> + </elementProp> + <elementProp name="product[cost]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[cost]</stringProp> + </elementProp> + <elementProp name="product[tier_price][0][website_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[tier_price][0][website_id]</stringProp> + </elementProp> + <elementProp name="product[tier_price][0][cust_group]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">32000</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[tier_price][0][cust_group]</stringProp> + </elementProp> + <elementProp name="product[tier_price][0][price_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">100</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[tier_price][0][price_qty]</stringProp> + </elementProp> + <elementProp name="product[tier_price][0][price]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">90</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[tier_price][0][price]</stringProp> + </elementProp> + <elementProp name="product[tier_price][0][delete]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[tier_price][0][delete]</stringProp> + </elementProp> + <elementProp name="product[tier_price][1][website_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[tier_price][1][website_id]</stringProp> + </elementProp> + <elementProp name="product[tier_price][1][cust_group]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[tier_price][1][cust_group]</stringProp> + </elementProp> + <elementProp name="product[tier_price][1][price_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">101</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[tier_price][1][price_qty]</stringProp> + </elementProp> + <elementProp name="product[tier_price][1][price]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">99</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[tier_price][1][price]</stringProp> + </elementProp> + <elementProp name="product[tier_price][1][delete]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[tier_price][1][delete]</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_manage_stock]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_manage_stock]</stringProp> + </elementProp> + <elementProp name="product[stock_data][original_inventory_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">100500</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][original_inventory_qty]</stringProp> + </elementProp> + <elementProp name="product[stock_data][qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">100500</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][qty]</stringProp> + </elementProp> + <elementProp name="product[stock_data][min_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][min_qty]</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_min_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_min_qty]</stringProp> + </elementProp> + <elementProp name="product[stock_data][min_sale_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][min_sale_qty]</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_min_sale_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_min_sale_qty]</stringProp> + </elementProp> + <elementProp name="product[stock_data][max_sale_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">10000</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][max_sale_qty]</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_max_sale_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_max_sale_qty]</stringProp> + </elementProp> + <elementProp name="product[stock_data][is_qty_decimal]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][is_qty_decimal]</stringProp> + </elementProp> + <elementProp name="product[stock_data][is_decimal_divided]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][is_decimal_divided]</stringProp> + </elementProp> + <elementProp name="product[stock_data][backorders]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][backorders]</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_backorders]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_backorders]</stringProp> + </elementProp> + <elementProp name="product[stock_data][notify_stock_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][notify_stock_qty]</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_notify_stock_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_notify_stock_qty]</stringProp> + </elementProp> + <elementProp name="product[stock_data][enable_qty_increments]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][enable_qty_increments]</stringProp> + </elementProp> + <elementProp name="product[stock_data][qty_increments]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][qty_increments]</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_qty_increments]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_qty_increments]</stringProp> + </elementProp> + <elementProp name="product[stock_data][is_in_stock]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][is_in_stock]</stringProp> + </elementProp> + <elementProp name="product[custom_design]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[custom_design]</stringProp> + </elementProp> + <elementProp name="product[custom_design_from]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[custom_design_from]</stringProp> + </elementProp> + <elementProp name="product[custom_design_to]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[custom_design_to]</stringProp> + </elementProp> + <elementProp name="product[custom_layout_update]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[custom_layout_update]</stringProp> + </elementProp> + <elementProp name="product[page_layout]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[page_layout]</stringProp> + </elementProp> + <elementProp name="product[options_container]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">container2</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[options_container]</stringProp> + </elementProp> + <elementProp name="downloadable[link][0][file][0][file]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${original_file}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">downloadable[link][0][file][0][file]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="downloadable[link][0][file][0][name]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">downloadable_original.txt</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">downloadable[link][0][file][0][name]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="downloadable[link][0][file][0][size]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">13</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">downloadable[link][0][file][0][size]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="downloadable[link][0][file][0][status]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">new</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">downloadable[link][0][file][0][status]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="downloadable[link][0][is_shareable]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">downloadable[link][0][is_shareable]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="downloadable[link][0][is_unlimited]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">downloadable[link][0][is_unlimited]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="downloadable[link][0][link_url]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">downloadable[link][0][link_url]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="downloadable[link][0][number_of_downloads]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">downloadable[link][0][number_of_downloads]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="downloadable[link][0][price]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">120</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">downloadable[link][0][price]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="downloadable[link][0][record_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">downloadable[link][0][record_id]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="downloadable[link][0][sample][type]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">file</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">downloadable[link][0][sample][type]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="downloadable[link][0][sample][url]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">downloadable[link][0][sample][url]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="downloadable[link][0][sort_order]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">downloadable[link][0][sort_order]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="downloadable[link][0][title]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">Original Link</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">downloadable[link][0][title]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="downloadable[link][0][type]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">file</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">downloadable[link][0][type]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="downloadable[sample][0][file][0][file]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${sample_file}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">downloadable[sample][0][file][0][file]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="downloadable[sample][0][file][0][name]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">downloadable_sample.txt</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">downloadable[sample][0][file][0][name]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="downloadable[sample][0][file][0][size]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">14</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">downloadable[sample][0][file][0][size]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="downloadable[sample][0][file][0][status]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">new</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">downloadable[sample][0][file][0][status]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="downloadable[sample][0][record_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">downloadable[sample][0][record_id]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="downloadable[sample][0][sample_url]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">downloadable[sample][0][sample_url]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="downloadable[sample][0][sort_order]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">downloadable[sample][0][sort_order]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="downloadable[sample][0][title]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">Sample Link</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">downloadable[sample][0][title]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="downloadable[sample][0][type]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">file</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">downloadable[sample][0][type]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="affect_configurable_product_attributes" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">affect_configurable_product_attributes</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="new-variations-attribute-set-id" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">4</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">new-variations-attribute-set-id</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[configurable_variation]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[configurable_variation]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="links[related][0][id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${related_product_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">links[related][0][id]</stringProp> + </elementProp> + <elementProp name="links[related][0][position]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">links[related][0][position]</stringProp> + </elementProp> + <elementProp name="links[upsell][0][id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${related_product_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">links[upsell][0][id]</stringProp> + </elementProp> + <elementProp name="links[upsell][0][position]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">links[upsell][0][position]</stringProp> + </elementProp> + <elementProp name="links[crosssell][0][id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${related_product_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">links[crosssell][0][id]</stringProp> + </elementProp> + <elementProp name="links[crosssell][0][position]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">links[crosssell][0][position]</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/catalog/product/save/set/4/type/downloadable/back/edit/active_tab/product-details/</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + </HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-583471546">You saved the product</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-1600986843">violation</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">6</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + </hashTree> + + <TestFragmentController guiclass="TestFragmentControllerGui" testclass="TestFragmentController" testname="Admin Create Simple Product" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_create_product/create_simple_product.jmx</stringProp> +</TestFragmentController> + <hashTree> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Catalog Product" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/catalog/product/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + </HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="1509986340">records found</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="New Simple Product" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/catalog/product/new/set/4/type/simple/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + </HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-144461265">New Product</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="New Simple Product Validate" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="ajax" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">true</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">ajax</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="isAjax" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">true</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">isAjax</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="form_key" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_form_key}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">form_key</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[name]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">Simple Product ${__time(YMDHMS)}-${__threadNum}-${__Random(1,1000000)}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[name]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[sku]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">SKU ${__time(YMDHMS)}-${__threadNum}-${__Random(1,1000000)}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[sku]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[price]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">123</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[price]</stringProp> + </elementProp> + <elementProp name="product[tax_class_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">2</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[tax_class_id]</stringProp> + </elementProp> + <elementProp name="product[quantity_and_stock_status][qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">111</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[quantity_and_stock_status][qty]</stringProp> + </elementProp> + <elementProp name="product[quantity_and_stock_status][is_in_stock]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[quantity_and_stock_status][is_in_stock]</stringProp> + </elementProp> + <elementProp name="product[weight]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1.0000</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[weight]</stringProp> + </elementProp> + <elementProp name="product[product_has_weight]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[product_has_weight]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[category_ids][]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">2</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[category_ids][]</stringProp> + </elementProp> + <elementProp name="product[description]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"><p>Full simple product description ${__time(YMDHMS)}-${__threadNum}-${__Random(1,1000000)}</p></stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[description]</stringProp> + </elementProp> + <elementProp name="product[short_description]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"><p>Short simple product description ${__time(YMDHMS)}-${__threadNum}-${__Random(1,1000000)}</p></stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[short_description]</stringProp> + </elementProp> + <elementProp name="product[status]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[status]</stringProp> + </elementProp> + <elementProp name="product[image]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[image]</stringProp> + </elementProp> + <elementProp name="product[small_image]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[small_image]</stringProp> + </elementProp> + <elementProp name="product[thumbnail]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[thumbnail]</stringProp> + </elementProp> + <elementProp name="product[url_key]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">simple-product-${__time(YMDHMS)}-${__threadNum}-${__Random(1,1000000)}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[url_key]</stringProp> + </elementProp> + <elementProp name="product[meta_title]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">Simple Product ${__time(YMDHMS)}-${__threadNum}-${__Random(1,1000000)} Meta Title</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[meta_title]</stringProp> + </elementProp> + <elementProp name="product[meta_keyword]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">Simple Product ${__time(YMDHMS)}-${__threadNum}-${__Random(1,1000000)} Meta Keyword</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[meta_keyword]</stringProp> + </elementProp> + <elementProp name="product[meta_description]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">Simple Product ${__time(YMDHMS)}-${__threadNum}-${__Random(1,1000000)} Meta Description</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[meta_description]</stringProp> + </elementProp> + <elementProp name="product[website_ids][]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[website_ids][]</stringProp> + </elementProp> + <elementProp name="product[special_price]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">99</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[special_price]</stringProp> + </elementProp> + <elementProp name="product[stock_data][notify_stock_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][notify_stock_qty]</stringProp> + </elementProp> + <elementProp name="product[special_from_date]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[special_from_date]</stringProp> + </elementProp> + <elementProp name="product[special_to_date]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[special_to_date]</stringProp> + </elementProp> + <elementProp name="product[cost]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[cost]</stringProp> + </elementProp> + <elementProp name="product[tier_price][0][website_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[tier_price][0][website_id]</stringProp> + </elementProp> + <elementProp name="product[tier_price][0][cust_group]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">32000</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[tier_price][0][cust_group]</stringProp> + </elementProp> + <elementProp name="product[tier_price][0][price_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">100</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[tier_price][0][price_qty]</stringProp> + </elementProp> + <elementProp name="product[tier_price][0][price]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">90</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[tier_price][0][price]</stringProp> + </elementProp> + <elementProp name="product[tier_price][0][delete]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[tier_price][0][delete]</stringProp> + </elementProp> + <elementProp name="product[tier_price][1][website_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[tier_price][1][website_id]</stringProp> + </elementProp> + <elementProp name="product[tier_price][1][cust_group]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[tier_price][1][cust_group]</stringProp> + </elementProp> + <elementProp name="product[tier_price][1][price_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">101</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[tier_price][1][price_qty]</stringProp> + </elementProp> + <elementProp name="product[tier_price][1][price]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">99</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[tier_price][1][price]</stringProp> + </elementProp> + <elementProp name="product[tier_price][1][delete]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[tier_price][1][delete]</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_manage_stock]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_manage_stock]</stringProp> + </elementProp> + <elementProp name="product[stock_data][original_inventory_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">100500</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][original_inventory_qty]</stringProp> + </elementProp> + <elementProp name="product[stock_data][qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">100500</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][qty]</stringProp> + </elementProp> + <elementProp name="product[stock_data][min_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][min_qty]</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_min_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_min_qty]</stringProp> + </elementProp> + <elementProp name="product[stock_data][min_sale_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][min_sale_qty]</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_min_sale_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_min_sale_qty]</stringProp> + </elementProp> + <elementProp name="product[stock_data][max_sale_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">10000</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][max_sale_qty]</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_max_sale_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_max_sale_qty]</stringProp> + </elementProp> + <elementProp name="product[stock_data][is_qty_decimal]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][is_qty_decimal]</stringProp> + </elementProp> + <elementProp name="product[stock_data][is_decimal_divided]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][is_decimal_divided]</stringProp> + </elementProp> + <elementProp name="product[stock_data][backorders]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][backorders]</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_backorders]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_backorders]</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_notify_stock_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_notify_stock_qty]</stringProp> + </elementProp> + <elementProp name="product[stock_data][enable_qty_increments]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][enable_qty_increments]</stringProp> + </elementProp> + <elementProp name="product[stock_data][qty_increments]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][qty_increments]</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_qty_increments]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_qty_increments]</stringProp> + </elementProp> + <elementProp name="product[stock_data][is_in_stock]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][is_in_stock]</stringProp> + </elementProp> + <elementProp name="product[custom_design]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[custom_design]</stringProp> + </elementProp> + <elementProp name="product[custom_design_from]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[custom_design_from]</stringProp> + </elementProp> + <elementProp name="product[custom_design_to]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[custom_design_to]</stringProp> + </elementProp> + <elementProp name="product[custom_layout_update]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[custom_layout_update]</stringProp> + </elementProp> + <elementProp name="product[page_layout]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[page_layout]</stringProp> + </elementProp> + <elementProp name="product[options_container]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">container2</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[options_container]</stringProp> + </elementProp> + <elementProp name="product[options][1][is_delete]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[options][1][is_delete]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[options][1][is_require]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[options][1][is_require]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[options][1][previous_group]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">select</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[options][1][previous_group]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[options][1][previous_type]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">drop_down</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[options][1][previous_type]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[options][1][sort_order]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[options][1][sort_order]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[options][1][title]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">Product Option Title One</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[options][1][title]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[options][1][type]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">drop_down</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[options][1][type]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[options][1][values][1][is_delete]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[options][1][values][1][is_delete]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[options][1][values][1][price]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">200</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[options][1][values][1][price]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[options][1][values][1][price_type]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">fixed</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[options][1][values][1][price_type]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[options][1][values][1][sku]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">sku-one</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[options][1][values][1][sku]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[options][1][values][1][sort_order]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[options][1][values][1][sort_order]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[options][1][values][1][title]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">Row Title</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[options][1][values][1][title]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[options][2][is_delete]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[options][2][is_delete]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[options][2][is_require]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[options][2][is_require]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[options][2][max_characters]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">250</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[options][2][max_characters]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[options][2][previous_group]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">text</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[options][2][previous_group]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[options][2][previous_type]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">field</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[options][2][previous_type]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[options][2][price]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">500</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[options][2][price]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[options][2][price_type]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">fixed</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[options][2][price_type]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[options][2][sku]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">sku-two</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[options][2][sku]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[options][2][sort_order]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[options][2][sort_order]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[options][2][title]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">Field Title</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[options][2][title]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[options][2][type]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">field</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[options][2][type]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="affect_configurable_product_attributes" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">affect_configurable_product_attributes</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="new-variations-attribute-set-id" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">4</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">new-variations-attribute-set-id</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[configurable_variation]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[configurable_variation]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="links[related][0][id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${related_product_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">links[related][0][id]</stringProp> + </elementProp> + <elementProp name="links[related][0][position]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">links[related][0][position]</stringProp> + </elementProp> + <elementProp name="links[upsell][0][id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${related_product_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">links[upsell][0][id]</stringProp> + </elementProp> + <elementProp name="links[upsell][0][position]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">links[upsell][0][position]</stringProp> + </elementProp> + <elementProp name="links[crosssell][0][id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${related_product_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">links[crosssell][0][id]</stringProp> + </elementProp> + <elementProp name="links[crosssell][0][position]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">links[crosssell][0][position]</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/catalog/product/validate/set/4/</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + </HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="1853918323">{"error":false}</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="New Simple Product Save" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="ajax" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">true</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">ajax</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="isAjax" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">true</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">isAjax</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="form_key" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_form_key}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">form_key</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[name]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">Simple Product ${__time(YMDHMS)}-${__threadNum}-${__Random(1,1000000)}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[name]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[sku]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">SKU ${__time(YMDHMS)}-${__threadNum}-${__Random(1,1000000)}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[sku]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[price]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">123</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[price]</stringProp> + </elementProp> + <elementProp name="product[tax_class_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">2</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[tax_class_id]</stringProp> + </elementProp> + <elementProp name="product[quantity_and_stock_status][qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">111</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[quantity_and_stock_status][qty]</stringProp> + </elementProp> + <elementProp name="product[quantity_and_stock_status][is_in_stock]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[quantity_and_stock_status][is_in_stock]</stringProp> + </elementProp> + <elementProp name="product[weight]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1.0000</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[weight]</stringProp> + </elementProp> + <elementProp name="product[product_has_weight]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[product_has_weight]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[category_ids][]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">2</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[category_ids][]</stringProp> + </elementProp> + <elementProp name="product[description]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"><p>Full simple product Description ${__time(YMDHMS)}-${__threadNum}-${__Random(1,1000000)}</p></stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[description]</stringProp> + </elementProp> + <elementProp name="product[short_description]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"><p>Short simple product description ${__time(YMDHMS)}-${__threadNum}-${__Random(1,1000000)}</p></stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[short_description]</stringProp> + </elementProp> + <elementProp name="product[status]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[status]</stringProp> + </elementProp> + <elementProp name="product[image]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[image]</stringProp> + </elementProp> + <elementProp name="product[small_image]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[small_image]</stringProp> + </elementProp> + <elementProp name="product[thumbnail]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[thumbnail]</stringProp> + </elementProp> + <elementProp name="product[url_key]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">simple-product-${__time(YMDHMS)}-${__threadNum}-${__Random(1,1000000)}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[url_key]</stringProp> + </elementProp> + <elementProp name="product[meta_title]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">Simple Product ${__time(YMDHMS)}-${__threadNum}-${__Random(1,1000000)} Meta Title</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[meta_title]</stringProp> + </elementProp> + <elementProp name="product[meta_keyword]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">Simple Product ${__time(YMDHMS)}-${__threadNum}-${__Random(1,1000000)} Meta Keyword</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[meta_keyword]</stringProp> + </elementProp> + <elementProp name="product[meta_description]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">Simple Product ${__time(YMDHMS)}-${__threadNum}-${__Random(1,1000000)} Meta Description</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[meta_description]</stringProp> + </elementProp> + <elementProp name="product[website_ids][]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[website_ids][]</stringProp> + </elementProp> + <elementProp name="product[special_price]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">99</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[special_price]</stringProp> + </elementProp> + <elementProp name="product[special_from_date]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[special_from_date]</stringProp> + </elementProp> + <elementProp name="product[special_to_date]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[special_to_date]</stringProp> + </elementProp> + <elementProp name="product[cost]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[cost]</stringProp> + </elementProp> + <elementProp name="product[tier_price][0][website_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[tier_price][0][website_id]</stringProp> + </elementProp> + <elementProp name="product[tier_price][0][cust_group]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">32000</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[tier_price][0][cust_group]</stringProp> + </elementProp> + <elementProp name="product[tier_price][0][price_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">100</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[tier_price][0][price_qty]</stringProp> + </elementProp> + <elementProp name="product[tier_price][0][price]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">90</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[tier_price][0][price]</stringProp> + </elementProp> + <elementProp name="product[tier_price][0][delete]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[tier_price][0][delete]</stringProp> + </elementProp> + <elementProp name="product[tier_price][1][website_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[tier_price][1][website_id]</stringProp> + </elementProp> + <elementProp name="product[tier_price][1][cust_group]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[tier_price][1][cust_group]</stringProp> + </elementProp> + <elementProp name="product[tier_price][1][price_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">101</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[tier_price][1][price_qty]</stringProp> + </elementProp> + <elementProp name="product[tier_price][1][price]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">99</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[tier_price][1][price]</stringProp> + </elementProp> + <elementProp name="product[tier_price][1][delete]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[tier_price][1][delete]</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_manage_stock]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_manage_stock]</stringProp> + </elementProp> + <elementProp name="product[stock_data][original_inventory_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">100500</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][original_inventory_qty]</stringProp> + </elementProp> + <elementProp name="product[stock_data][qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">100500</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][qty]</stringProp> + </elementProp> + <elementProp name="product[stock_data][min_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][min_qty]</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_min_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_min_qty]</stringProp> + </elementProp> + <elementProp name="product[stock_data][min_sale_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][min_sale_qty]</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_min_sale_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_min_sale_qty]</stringProp> + </elementProp> + <elementProp name="product[stock_data][max_sale_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">10000</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][max_sale_qty]</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_max_sale_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_max_sale_qty]</stringProp> + </elementProp> + <elementProp name="product[stock_data][is_qty_decimal]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][is_qty_decimal]</stringProp> + </elementProp> + <elementProp name="product[stock_data][is_decimal_divided]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][is_decimal_divided]</stringProp> + </elementProp> + <elementProp name="product[stock_data][backorders]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][backorders]</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_backorders]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_backorders]</stringProp> + </elementProp> + <elementProp name="product[stock_data][notify_stock_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][notify_stock_qty]</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_notify_stock_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_notify_stock_qty]</stringProp> + </elementProp> + <elementProp name="product[stock_data][enable_qty_increments]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][enable_qty_increments]</stringProp> + </elementProp> + <elementProp name="product[stock_data][qty_increments]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][qty_increments]</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_qty_increments]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_qty_increments]</stringProp> + </elementProp> + <elementProp name="product[stock_data][is_in_stock]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][is_in_stock]</stringProp> + </elementProp> + <elementProp name="product[custom_design]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[custom_design]</stringProp> + </elementProp> + <elementProp name="product[custom_design_from]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[custom_design_from]</stringProp> + </elementProp> + <elementProp name="product[custom_design_to]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[custom_design_to]</stringProp> + </elementProp> + <elementProp name="product[custom_layout_update]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[custom_layout_update]</stringProp> + </elementProp> + <elementProp name="product[page_layout]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[page_layout]</stringProp> + </elementProp> + <elementProp name="product[options_container]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">container2</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[options_container]</stringProp> + </elementProp> + <elementProp name="product[options][1][is_delete]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[options][1][is_delete]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[options][1][is_require]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[options][1][is_require]</stringProp> + </elementProp> + <elementProp name="product[options][1][previous_group]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">select</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[options][1][previous_group]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[options][1][previous_type]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">drop_down</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[options][1][previous_type]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[options][1][sort_order]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[options][1][sort_order]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[options][1][title]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">Product Option Title One</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[options][1][title]</stringProp> + </elementProp> + <elementProp name="product[options][1][type]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">drop_down</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[options][1][type]</stringProp> + </elementProp> + <elementProp name="product[options][1][values][1][is_delete]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[options][1][values][1][is_delete]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[options][1][values][1][price]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">200</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[options][1][values][1][price]</stringProp> + </elementProp> + <elementProp name="product[options][1][values][1][price_type]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">fixed</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[options][1][values][1][price_type]</stringProp> + </elementProp> + <elementProp name="product[options][1][values][1][sku]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">sku-one</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[options][1][values][1][sku]</stringProp> + </elementProp> + <elementProp name="product[options][1][values][1][sort_order]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[options][1][values][1][sort_order]</stringProp> + </elementProp> + <elementProp name="product[options][1][values][1][title]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">Row Title</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[options][1][values][1][title]</stringProp> + </elementProp> + <elementProp name="product[options][2][is_delete]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[options][2][is_delete]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[options][2][is_require]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[options][2][is_require]</stringProp> + </elementProp> + <elementProp name="product[options][2][max_characters]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">250</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[options][2][max_characters]</stringProp> + </elementProp> + <elementProp name="product[options][2][previous_group]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">text</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[options][2][previous_group]</stringProp> + </elementProp> + <elementProp name="product[options][2][previous_type]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">field</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[options][2][previous_type]</stringProp> + </elementProp> + <elementProp name="product[options][2][price]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">500</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[options][2][price]</stringProp> + </elementProp> + <elementProp name="product[options][2][price_type]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">fixed</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[options][2][price_type]</stringProp> + </elementProp> + <elementProp name="product[options][2][sku]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">sku-two</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[options][2][sku]</stringProp> + </elementProp> + <elementProp name="product[options][2][sort_order]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[options][2][sort_order]</stringProp> + </elementProp> + <elementProp name="product[options][2][title]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">Field Title</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[options][2][title]</stringProp> + </elementProp> + <elementProp name="product[options][2][type]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">field</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[options][2][type]</stringProp> + </elementProp> + <elementProp name="affect_configurable_product_attributes" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">affect_configurable_product_attributes</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="new-variations-attribute-set-id" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">4</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">new-variations-attribute-set-id</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[configurable_variation]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[configurable_variation]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="links[related][0][id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${related_product_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">links[related][0][id]</stringProp> + </elementProp> + <elementProp name="links[related][0][position]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">links[related][0][position]</stringProp> + </elementProp> + <elementProp name="links[upsell][0][id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${related_product_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">links[upsell][0][id]</stringProp> + </elementProp> + <elementProp name="links[upsell][0][position]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">links[upsell][0][position]</stringProp> + </elementProp> + <elementProp name="links[crosssell][0][id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${related_product_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">links[crosssell][0][id]</stringProp> + </elementProp> + <elementProp name="links[crosssell][0][position]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">links[crosssell][0][position]</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/catalog/product/save/set/4/type/simple/back/edit/active_tab/product-details/</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + </HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-583471546">You saved the product</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-1600986843">violation</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">6</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + </hashTree> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Logout" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/auth/logout/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/setup/admin_logout.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + + <BeanShellPostProcessor guiclass="TestBeanGUI" testclass="BeanShellPostProcessor" testname="Return Admin to Pool" enabled="true"> + <boolProp name="resetInterpreter">false</boolProp> + <stringProp name="parameters"/> + <stringProp name="filename"/> + <stringProp name="script"> + adminUsersDistribution = Integer.parseInt(vars.get("admin_users_distribution_per_admin_pool")); + if (adminUsersDistribution == 1) { + adminUserList = props.get("adminUserList"); + adminUserList.add(vars.get("admin_user")); + } + </stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/return_admin_email_to_pool.jmx</stringProp></BeanShellPostProcessor> + <hashTree/> + </hashTree> + </hashTree> + + + <ThroughputController guiclass="ThroughputControllerGui" testclass="ThroughputController" testname="[C] Admin Edit Product" enabled="true"> + <intProp name="ThroughputController.style">1</intProp> + <boolProp name="ThroughputController.perThread">false</boolProp> + <intProp name="ThroughputController.maxThroughput">1</intProp> + <stringProp name="ThroughputController.percentThroughput">${cAdminProductEditingPercentage}</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/_system/scenario_controller_tmpl.jmx</stringProp></ThroughputController> + <hashTree> + <JSR223PreProcessor guiclass="TestBeanGUI" testclass="JSR223PreProcessor" testname="Set Test Label" enabled="true"> + <stringProp name="script"> +var testLabel = "${testLabel}" ? " (${testLabel})" : ""; +if (testLabel + && sampler.getClass().getName() == 'org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy' +) { + if (sampler.getName().indexOf(testLabel) == -1) { + sampler.setName(sampler.getName() + testLabel); + } +} else if (sampler.getName().indexOf("SetUp - ") == -1) { + sampler.setName("SetUp - " + sampler.getName()); +} + </stringProp> + <stringProp name="scriptLanguage">javascript</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/_system/setup_label.jmx</stringProp></JSR223PreProcessor> + <hashTree/> + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Set Label" enabled="true"> + <stringProp name="BeanShellSampler.query"> + vars.put("testLabel", "[C] Admin Edit Product"); + </stringProp> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + </BeanShellSampler> + <hashTree/> + + <JSR223PostProcessor guiclass="TestBeanGUI" testclass="JSR223PostProcessor" testname="Get admin form key PostProcessor" enabled="true"> + <stringProp name="script"> + function getFormKeyFromResponse() + { + var url = prev.getUrlAsString(), + responseCode = prev.getResponseCode(), + formKey = null; + searchPattern = /var FORM_KEY = '(.+)'/; + if (responseCode == "200" && url) { + response = prev.getResponseDataAsString(); + formKey = response && response.match(searchPattern) ? response.match(searchPattern)[1] : null; + } + return formKey; + } + + formKey = vars.get("form_key_storage"); + + currentFormKey = getFormKeyFromResponse(); + + if (currentFormKey != null && currentFormKey != formKey) { + vars.put("form_key_storage", currentFormKey); + } + </stringProp> + <stringProp name="scriptLanguage">javascript</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin/handle_admin_form_key.jmx</stringProp></JSR223PostProcessor> + <hashTree/> + <JSR223PreProcessor guiclass="TestBeanGUI" testclass="JSR223PreProcessor" testname="Set admin form key PreProcessor" enabled="true"> + <stringProp name="script"> + formKey = vars.get("form_key_storage"); + if (formKey + && sampler.getClass().getName() == 'org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy' + && sampler.getMethod() == "POST") + { + arguments = sampler.getArguments(); + for (i=0; i<arguments.getArgumentCount(); i++) + { + argument = arguments.getArgument(i); + if (argument.getName() == 'form_key' && argument.getValue() != formKey) { + log.info("admin form key updated: " + argument.getValue() + " => " + formKey); + argument.setValue(formKey); + } + } + } + </stringProp> + <stringProp name="scriptLanguage">javascript</stringProp> + </JSR223PreProcessor> + <hashTree/> + + <CookieManager guiclass="CookiePanel" testclass="CookieManager" testname="HTTP Cookie Manager" enabled="true"> + <collectionProp name="CookieManager.cookies"/> + <boolProp name="CookieManager.clearEachIteration">false</boolProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/http_cookie_manager_without_clear_each_iteration.jmx</stringProp></CookieManager> + <hashTree/> + + <GenericController guiclass="LogicControllerGui" testclass="GenericController" testname="Admin Login" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/simple_controller.jmx</stringProp> +</GenericController> + <hashTree> + <CriticalSectionController guiclass="CriticalSectionControllerGui" testclass="CriticalSectionController" testname="Admin Login Lock" enabled="true"> + <stringProp name="CriticalSectionController.lockName">get-admin-email</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/lock_controller.jmx</stringProp></CriticalSectionController> + <hashTree> + + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Get Admin Email" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/get_admin_email.jmx</stringProp> + <stringProp name="BeanShellSampler.query"> +adminUserList = props.get("adminUserList"); +adminUserListIterator = props.get("adminUserListIterator"); +adminUsersDistribution = Integer.parseInt(vars.get("admin_users_distribution_per_admin_pool")); + +if (adminUsersDistribution == 1) { + adminUser = adminUserList.poll(); +} else { + if (!adminUserListIterator.hasNext()) { + adminUserListIterator = adminUserList.descendingIterator(); + } + + adminUser = adminUserListIterator.next(); +} + +if (adminUser == null) { + SampleResult.setResponseMessage("adminUser list is empty"); + SampleResult.setResponseData("adminUser list is empty","UTF-8"); + IsSuccess=false; + SampleResult.setSuccessful(false); + SampleResult.setStopThread(true); +} +vars.put("admin_user", adminUser); + </stringProp> + <stringProp name="BeanShellSampler.filename"/> + <stringProp name="BeanShellSampler.parameters"/> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + </BeanShellSampler> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="SetUp - Login" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_login/admin_login.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert login form shown" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-1397214398">Welcome</stringProp> + <stringProp name="-515240035"><title>Magento Admin</title></stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract form key" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">admin_form_key</stringProp> + <stringProp name="RegexExtractor.regex"><input name="form_key" type="hidden" value="([^'"]+)" /></stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert form_key extracted" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="2845929">^.+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">admin_form_key</stringProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="SetUp - Login Submit Form" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="dummy" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">dummy</stringProp> + </elementProp> + <elementProp name="form_key" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_form_key}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">form_key</stringProp> + </elementProp> + <elementProp name="login[password]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_password}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">login[password]</stringProp> + </elementProp> + <elementProp name="login[username]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_user}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">login[username]</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/dashboard/</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <stringProp name="HTTPSampler.implementation">Java</stringProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_login/admin_login_submit_form.jmx</stringProp> + </HTTPSamplerProxy> + <hashTree> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract form key" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">admin_form_key</stringProp> + <stringProp name="RegexExtractor.regex"><input name="form_key" type="hidden" value="([^'"]+)" /></stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_login/admin_retrieve_form_key.jmx</stringProp></RegexExtractor> + <hashTree/> + </hashTree> + </hashTree> + + <GenericController guiclass="LogicControllerGui" testclass="GenericController" testname="Simple Controller" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/simple_controller.jmx</stringProp> +</GenericController> + <hashTree> + <TestFragmentController guiclass="TestFragmentControllerGui" testclass="TestFragmentController" testname="Admin Edit Product" enabled="true"/> + <hashTree> + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Generate Unique Ids for each Thread" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_edit_product/admin_edit_product_updated.jmx</stringProp> + <stringProp name="BeanShellSampler.query">import java.util.ArrayList; + import java.util.HashMap; + import java.util.Random; + + try { + Random random = new Random(); + if (${seedForRandom} > 0) { + random.setSeed(${seedForRandom} + ${__threadNum}); + } + simpleCount = props.get("simple_products_list_for_edit").size(); + configCount = props.get("configurable_products_list_for_edit").size(); + productCount = 0; + if (simpleCount > configCount) { + productCount = configCount; + } else { + productCount = simpleCount; + } + int threadsNumber = ctx.getThreadGroup().getNumThreads(); + if (threadsNumber == 0) { + threadsNumber = 1; + } + //Current thread number starts from 0 + currentThreadNum = ctx.getThreadNum(); + + String siterator = vars.get("threadIterator_" + currentThreadNum.toString()); + iterator = 0; + if(siterator == null){ + vars.put("threadIterator_" + currentThreadNum.toString() , "0"); + } else { + iterator = Integer.parseInt(siterator); + iterator ++; + vars.put("threadIterator_" + currentThreadNum.toString() , iterator.toString()); + } + + //Number of products for one thread + productClusterLength = productCount / threadsNumber; + + if (iterator >= productClusterLength) { + vars.put("threadIterator_" + currentThreadNum.toString(), "0"); + iterator = 0; + } + + //Index of the current product from the cluster + i = productClusterLength * currentThreadNum + iterator; + + //ids of simple and configurable products to edit + vars.put("simple_product_id", props.get("simple_products_list_for_edit").get(i).get("id")); + vars.put("configurable_product_id", props.get("configurable_products_list_for_edit").get(i).get("id")); + + //id of related product + do { + relatedIndex = random.nextInt(props.get("simple_products_list_for_edit").size()); + } while(i == relatedIndex); + vars.put("related_product_id", props.get("simple_products_list_for_edit").get(relatedIndex).get("id")); + } catch (Exception ex) { + log.info("Script execution failed", ex); +}</stringProp> + <stringProp name="BeanShellSampler.filename"/> + <stringProp name="BeanShellSampler.parameters"/> + <boolProp name="BeanShellSampler.resetInterpreter">false</boolProp> + </BeanShellSampler> + <hashTree/> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Edit Simple Product" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/catalog/product/edit/id/${simple_product_id}/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + </HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="1355179215">Product</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">16</intProp> + </ResponseAssertion> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract name" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">simple_product_name</stringProp> + <stringProp name="RegexExtractor.regex">,"name":"([^'"]+)",</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract sku" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">simple_product_sku</stringProp> + <stringProp name="RegexExtractor.regex">,"sku":"([^'"]+)",</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract category" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">simple_product_category_id</stringProp> + <stringProp name="RegexExtractor.regex">,"category_ids":."(\d+)".</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + </hashTree> + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Set updated values" enabled="true"> + <stringProp name="TestPlan.comments">Passing arguments between threads</stringProp> + <stringProp name="BeanShellSampler.query">//Additional category to be added + import java.util.Random; + + Random randomGenerator = new Random(); + if (${seedForRandom} > 0) { + randomGenerator.setSeed(${seedForRandom} + ${__threadNum}); + } + + int categoryId = Integer.parseInt(vars.get("simple_product_category_id")); + categoryList = props.get("admin_category_ids_list"); + + if (categoryList.size() > 1) { + do { + int index = randomGenerator.nextInt(categoryList.size()); + newCategoryId = categoryList.get(index); + } while (categoryId == newCategoryId); + + vars.put("category_additional", newCategoryId.toString()); + } + + //New price + vars.put("price_new", "9999"); + //New special price + vars.put("special_price_new", "8888"); + //New quantity + vars.put("quantity_new", "100600"); + </stringProp> + <stringProp name="BeanShellSampler.filename"/> + <stringProp name="BeanShellSampler.parameters"/> + <boolProp name="BeanShellSampler.resetInterpreter">false</boolProp> + </BeanShellSampler> + <hashTree/> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Edit Simple Product Validate" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="ajax" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">true</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">ajax</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="isAjax" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">true</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">isAjax</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="form_key" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_form_key}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">form_key</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[name]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${simple_product_name}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[name]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[sku]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${simple_product_sku}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[sku]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[price]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${price_new}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[price]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[tax_class_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">2</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[tax_class_id]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[quantity_and_stock_status][qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${quantity_new}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[quantity_and_stock_status][qty]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[quantity_and_stock_status][is_in_stock]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[quantity_and_stock_status][is_in_stock]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[weight]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1.0000</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[weight]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[product_has_weight]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[product_has_weight]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[category_ids][]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${simple_product_category_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[category_ids][]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[description]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"><p>Full simple product Description ${simple_product_id} Edited</p></stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[description]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[status]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[status]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[configurable_variations]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[configurable_variations]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="affect_configurable_product_attributes" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">affect_configurable_product_attributes</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[image]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[image]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[small_image]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[small_image]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[thumbnail]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[thumbnail]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[url_key]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${simple_product_name}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[url_key]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[meta_title]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${simple_product_name} Meta Title Edited</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[meta_title]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[meta_keyword]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${simple_product_name} Meta Keyword Edited</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[meta_keyword]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[meta_description]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${simple_product_name} Meta Description Edited</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[meta_description]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[website_ids][]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[website_ids][]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[special_price]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${special_price_new}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[special_price]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[special_from_date]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[special_from_date]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[special_to_date]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[special_to_date]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[cost]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[cost]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_manage_stock]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_manage_stock]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[stock_data][original_inventory_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${quantity_new}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][original_inventory_qty]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[stock_data][qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${quantity_new}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][qty]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[stock_data][min_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][min_qty]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_min_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_min_qty]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[stock_data][min_sale_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][min_sale_qty]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_min_sale_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_min_sale_qty]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[stock_data][max_sale_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">10000</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][max_sale_qty]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_max_sale_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_max_sale_qty]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[stock_data][is_qty_decimal]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][is_qty_decimal]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[stock_data][is_decimal_divided]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][is_decimal_divided]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[stock_data][backorders]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][backorders]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_backorders]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_backorders]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[stock_data][notify_stock_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][notify_stock_qty]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_notify_stock_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_notify_stock_qty]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[stock_data][enable_qty_increments]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][enable_qty_increments]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[stock_data][qty_increments]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][qty_increments]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_qty_increments]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_qty_increments]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[stock_data][is_in_stock]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][is_in_stock]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[custom_design]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[custom_design]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[custom_design_from]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[custom_design_from]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[custom_design_to]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[custom_design_to]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[custom_layout_update]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[custom_layout_update]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[page_layout]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[page_layout]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[options_container]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">container2</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[options_container]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="new-variations-attribute-set-id" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">new-variations-attribute-set-id</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/catalog/product/validate/id/${simple_product_id}/?isAjax=true</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + </HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="1853918323">{"error":false}</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Edit Simple Product Save" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="ajax" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">true</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">ajax</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="isAjax" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">true</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">isAjax</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="form_key" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_form_key}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">form_key</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[name]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${simple_product_name}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[name]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[sku]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${simple_product_sku}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[sku]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[price]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${price_new}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[price]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[tax_class_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">2</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[tax_class_id]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[quantity_and_stock_status][qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${quantity_new}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[quantity_and_stock_status][qty]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[quantity_and_stock_status][is_in_stock]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[quantity_and_stock_status][is_in_stock]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[weight]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1.0000</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[weight]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[product_has_weight]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[product_has_weight]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[category_ids][]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${simple_product_category_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[category_ids][]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[category_ids][]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${category_additional}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[category_ids][]</stringProp> + </elementProp> + <elementProp name="product[description]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"><p>Full simple product Description ${simple_product_id} Edited</p></stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[description]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[status]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[status]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[configurable_variations]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[configurable_variations]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="affect_configurable_product_attributes" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">affect_configurable_product_attributes</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[image]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[image]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[small_image]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[small_image]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[thumbnail]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[thumbnail]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[url_key]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${simple_product_name}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[url_key]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[meta_title]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${simple_product_name} Meta Title Edited</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[meta_title]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[meta_keyword]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${simple_product_name} Meta Keyword Edited</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[meta_keyword]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[meta_description]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${simple_product_name} Meta Description Edited</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[meta_description]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[website_ids][]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[website_ids][]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[special_price]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${special_price_new}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[special_price]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[special_from_date]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[special_from_date]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[special_to_date]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[special_to_date]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[cost]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[cost]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_manage_stock]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_manage_stock]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[stock_data][original_inventory_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${quantity_new}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][original_inventory_qty]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[stock_data][qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${quantity_new}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][qty]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[stock_data][min_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][min_qty]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_min_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_min_qty]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[stock_data][min_sale_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][min_sale_qty]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_min_sale_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_min_sale_qty]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[stock_data][max_sale_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">10000</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][max_sale_qty]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_max_sale_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_max_sale_qty]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[stock_data][is_qty_decimal]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][is_qty_decimal]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[stock_data][is_decimal_divided]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][is_decimal_divided]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[stock_data][backorders]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][backorders]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_backorders]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_backorders]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[stock_data][notify_stock_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][notify_stock_qty]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_notify_stock_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_notify_stock_qty]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[stock_data][enable_qty_increments]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][enable_qty_increments]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[stock_data][qty_increments]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][qty_increments]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_qty_increments]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_qty_increments]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[stock_data][is_in_stock]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][is_in_stock]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[custom_design]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[custom_design]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[custom_design_from]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[custom_design_from]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[custom_design_to]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[custom_design_to]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[custom_layout_update]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[custom_layout_update]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[page_layout]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[page_layout]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[options_container]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">container2</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[options_container]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="new-variations-attribute-set-id" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">new-variations-attribute-set-id</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/catalog/product/save/id/${simple_product_id}/back/edit/active_tab/product-details/</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + </HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-583471546">You saved the product</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Edit Configurable Product" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/catalog/product/edit/id/${configurable_product_id}/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + </HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="1355179215">Product</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">16</intProp> + </ResponseAssertion> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract name" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">configurable_product_name</stringProp> + <stringProp name="RegexExtractor.regex">,"name":"([^'"]+)",</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract sku" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">configurable_product_sku</stringProp> + <stringProp name="RegexExtractor.regex">,"sku":"([^'"]+)",</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract category" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">configurable_product_category_id</stringProp> + <stringProp name="RegexExtractor.regex">,"category_ids":."(\d+)"</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract configurable attribute id" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">configurable_attribute_id</stringProp> + <stringProp name="RegexExtractor.regex">,"configurable_variation":"([^'"]+)",</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + <boolProp name="RegexExtractor.default_empty_value">true</boolProp> + </RegexExtractor> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract configurable matrix" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">configurable_matrix</stringProp> + <stringProp name="RegexExtractor.regex">"configurable-matrix":(\[.*?\])</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + <boolProp name="RegexExtractor.default_empty_value">true</boolProp> + </RegexExtractor> + <hashTree/> + <com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor guiclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.gui.JSONPathExtractorGui" testclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor" testname="Extract associated products ids" enabled="true"> + <stringProp name="VAR">associated_products_ids</stringProp> + <stringProp name="JSONPATH">$.[*].id</stringProp> + <stringProp name="DEFAULT"/> + <stringProp name="VARIABLE">configurable_matrix</stringProp> + <stringProp name="SUBJECT">VAR</stringProp> + </com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract configurable product json" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">configurable_product_data</stringProp> + <stringProp name="RegexExtractor.regex">(\{"product":.*?configurable_attributes_data.*?\})\s*<</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + <com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor guiclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.gui.JSONPathExtractorGui" testclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor" testname="Extract configurable attributes data" enabled="true"> + <stringProp name="VAR">configurable_attributes_data</stringProp> + <stringProp name="JSONPATH">$.product.configurable_attributes_data</stringProp> + <stringProp name="DEFAULT"/> + <stringProp name="VARIABLE">configurable_product_data</stringProp> + <stringProp name="SUBJECT">VAR</stringProp> + </com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract attribute ids" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">configurable_attribute_ids</stringProp> + <stringProp name="RegexExtractor.regex">"attribute_id":"(\d+)"</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">-1</stringProp> + <stringProp name="Sample.scope">variable</stringProp> + <stringProp name="Scope.variable">configurable_attributes_data</stringProp> + </RegexExtractor> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract attribute codes" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">configurable_attribute_codes</stringProp> + <stringProp name="RegexExtractor.regex">"code":"(\w+)"</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">-1</stringProp> + <stringProp name="Sample.scope">variable</stringProp> + <stringProp name="Scope.variable">configurable_attributes_data</stringProp> + </RegexExtractor> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract attribute labels" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">configurable_attribute_labels</stringProp> + <stringProp name="RegexExtractor.regex">"label":"(.*?)"</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">-1</stringProp> + <stringProp name="Sample.scope">variable</stringProp> + <stringProp name="Scope.variable">configurable_attributes_data</stringProp> + </RegexExtractor> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract attribute values" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">configurable_attribute_values</stringProp> + <stringProp name="RegexExtractor.regex">"values":(\{(?:\}|.*?\}\}))</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">-1</stringProp> + <stringProp name="Sample.scope">variable</stringProp> + <stringProp name="Scope.variable">configurable_attributes_data</stringProp> + </RegexExtractor> + <hashTree/> + </hashTree> + <ForeachController guiclass="ForeachControlPanel" testclass="ForeachController" testname="ForEach configurable attribute id" enabled="true"> + <stringProp name="ForeachController.inputVal">configurable_attribute_ids</stringProp> + <stringProp name="ForeachController.returnVal">configurable_attribute_id</stringProp> + <boolProp name="ForeachController.useSeparator">true</boolProp> + </ForeachController> + <hashTree> + <CounterConfig guiclass="CounterConfigGui" testclass="CounterConfig" testname="Counter" enabled="true"> + <stringProp name="CounterConfig.start">1</stringProp> + <stringProp name="CounterConfig.end">${configurable_attribute_ids_matchNr}</stringProp> + <stringProp name="CounterConfig.incr">1</stringProp> + <stringProp name="CounterConfig.name">attribute_counter</stringProp> + <stringProp name="CounterConfig.format"/> + <boolProp name="CounterConfig.per_user">true</boolProp> + <boolProp name="CounterConfig.reset_on_tg_iteration">true</boolProp> + </CounterConfig> + <hashTree/> + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="Process configurable attribute values" enabled="true"> + <stringProp name="BeanShellSampler.query">return vars.get("configurable_attribute_values_" + vars.get("attribute_counter"));</stringProp> + <stringProp name="BeanShellSampler.filename"/> + <stringProp name="BeanShellSampler.parameters"/> + <boolProp name="BeanShellSampler.resetInterpreter">false</boolProp> + </BeanShellSampler> + <hashTree> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Exctract attribute values" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">attribute_${configurable_attribute_id}_values</stringProp> + <stringProp name="RegexExtractor.regex">"value_index":"(\d+)"</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">-1</stringProp> + <stringProp name="Scope.variable">configurable_attribute_values_${attribute_counter}</stringProp> + </RegexExtractor> + <hashTree/> + </hashTree> + </hashTree> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Edit Configurable Product Validate" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="isAjax" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">true</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">isAjax</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="form_key" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_form_key}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">form_key</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[name]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${configurable_product_name}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[name]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[sku]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${configurable_product_sku}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[sku]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[price]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${price_new}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[price]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[tax_class_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">2</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[tax_class_id]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[quantity_and_stock_status][is_in_stock]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[quantity_and_stock_status][is_in_stock]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[weight]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">3</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[weight]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[category_ids][]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${configurable_product_category_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[category_ids][]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[category_ids][]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${category_additional}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[category_ids][]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[description]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"><p>Configurable product description ${configurable_product_id} Edited</p></stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[description]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[status]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[status]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[meta_title]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${configurable_product_name} Meta Title Edited</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[meta_title]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[meta_keyword]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${configurable_product_name} Meta Keyword Edited</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[meta_keyword]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[meta_description]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${configurable_product_name} Meta Description Edited</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[meta_description]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[website_ids][]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[website_ids][]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[special_price]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${special_price_new}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[special_price]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[special_from_date]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[special_from_date]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[special_to_date]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[special_to_date]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[cost]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[cost]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_manage_stock]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_manage_stock]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[stock_data][min_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][min_qty]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_min_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_min_qty]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[stock_data][min_sale_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][min_sale_qty]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_min_sale_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_min_sale_qty]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_max_sale_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_max_sale_qty]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[stock_data][is_qty_decimal]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][is_qty_decimal]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[stock_data][is_decimal_divided]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][is_decimal_divided]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[stock_data][backorders]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][backorders]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_backorders]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_backorders]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[stock_data][notify_stock_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][notify_stock_qty]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_notify_stock_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_notify_stock_qty]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[stock_data][enable_qty_increments]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][enable_qty_increments]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[stock_data][qty_increments]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][qty_increments]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_qty_increments]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_qty_increments]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[stock_data][is_in_stock]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][is_in_stock]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[custom_design]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[custom_design]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[custom_design_from]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[custom_design_from]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[custom_design_to]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[custom_design_to]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[custom_layout_update]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[custom_layout_update]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[page_layout]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[page_layout]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[options_container]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">container2</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[options_container]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[configurable_variation]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${configurable_attribute_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[configurable_variation]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[url_key]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${configurable_product_name}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[url_key]</stringProp> + </elementProp> + <elementProp name="product[use_config_gift_message_available]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[use_config_gift_message_available]</stringProp> + </elementProp> + <elementProp name="product[use_config_gift_wrapping_available]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[use_config_gift_wrapping_available]</stringProp> + </elementProp> + <elementProp name="product[visibility]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">4</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[visibility]</stringProp> + </elementProp> + <elementProp name="product[product_has_weight]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[product_has_weight]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[stock_data][qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">50</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][qty]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[stock_data][type_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">configurable</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][type_id]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/catalog/product/validate/id/${configurable_product_id}/</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + </HTTPSamplerProxy> + <hashTree> + <BeanShellPreProcessor guiclass="TestBeanGUI" testclass="BeanShellPreProcessor" testname="Configure product options" enabled="true"> + <boolProp name="resetInterpreter">false</boolProp> + <stringProp name="parameters"/> + <stringProp name="filename"/> + <stringProp name="script">try { + int attributesCount = Integer.parseInt(vars.get("configurable_attribute_ids_matchNr")); + for (int i = 1; i <= attributesCount; i++) { + attributeId = vars.get("configurable_attribute_ids_" + i.toString()); + attributeCode = vars.get("configurable_attribute_codes_" + i.toString()); + attributeLabel = vars.get("configurable_attribute_labels_" + i.toString()); + ctx.getCurrentSampler().addArgument("attributes[" + (i - 1).toString() + "]", attributeId); + ctx.getCurrentSampler().addArgument("attribute_codes[" + (i - 1).toString() + "]", attributeCode); + ctx.getCurrentSampler().addArgument("product[" + attributeCode + "]", attributeId); + ctx.getCurrentSampler().addArgument("product[configurable_attributes_data][" + attributeId + "][attribute_id]", attributeId); + ctx.getCurrentSampler().addArgument("product[configurable_attributes_data][" + attributeId + "][position]", (i - 1).toString()); + ctx.getCurrentSampler().addArgument("product[configurable_attributes_data][" + attributeId + "][code]", attributeCode); + ctx.getCurrentSampler().addArgument("product[configurable_attributes_data][" + attributeId + "][label]", attributeLabel); + + int valuesCount = Integer.parseInt(vars.get("attribute_" + attributeId + "_values_matchNr")); + for (int j = 1; j <= valuesCount; j++) { + attributeValue = vars.get("attribute_" + attributeId + "_values_" + j.toString()); + ctx.getCurrentSampler().addArgument( + "product[configurable_attributes_data][" + attributeId + "][values][" + attributeValue + "][include]", + "1" + ); + ctx.getCurrentSampler().addArgument( + "product[configurable_attributes_data][" + attributeId + "][values][" + attributeValue + "][value_index]", + attributeValue + ); + } + } + ctx.getCurrentSampler().addArgument("associated_product_ids_serialized", vars.get("associated_products_ids").toString()); + } catch (Exception e) { + log.error("error???", e); + }</stringProp> + </BeanShellPreProcessor> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="1853918323">{"error":false}</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Edit Configurable Product Save" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="ajax" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">true</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">ajax</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="isAjax" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">true</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">isAjax</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="form_key" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_form_key}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">form_key</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[name]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${configurable_product_name}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[name]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[sku]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${configurable_product_sku}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[sku]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[price]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${price_new}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[price]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[tax_class_id]admin" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">2</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[tax_class_id]admin</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[quantity_and_stock_status][is_in_stock]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[quantity_and_stock_status][is_in_stock]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[weight]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">3</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[weight]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[category_ids][]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${configurable_product_category_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[category_ids][]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[category_ids][]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${category_additional}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[category_ids][]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[description]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"><p>Configurable product description ${configurable_product_id} Edited</p></stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[description]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[status]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[status]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[meta_title]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${configurable_product_name} Meta Title Edited</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[meta_title]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[meta_keyword]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${configurable_product_name} Meta Keyword Edited</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[meta_keyword]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[meta_description]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${configurable_product_name} Meta Description Edited</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[meta_description]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[website_ids][]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[website_ids][]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[special_price]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${special_price_new}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[special_price]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[special_from_date]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[special_from_date]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[special_to_date]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[special_to_date]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[cost]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[cost]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_manage_stock]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_manage_stock]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[stock_data][min_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][min_qty]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_min_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_min_qty]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[stock_data][min_sale_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][min_sale_qty]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_min_sale_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_min_sale_qty]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_max_sale_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_max_sale_qty]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[stock_data][is_qty_decimal]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][is_qty_decimal]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[stock_data][is_decimal_divided]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][is_decimal_divided]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[stock_data][backorders]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][backorders]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_backorders]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_backorders]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[stock_data][notify_stock_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][notify_stock_qty]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_notify_stock_qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_notify_stock_qty]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[stock_data][enable_qty_increments]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][enable_qty_increments]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[stock_data][qty_increments]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][qty_increments]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[stock_data][use_config_qty_increments]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][use_config_qty_increments]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[stock_data][is_in_stock]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][is_in_stock]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[custom_design]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[custom_design]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[custom_design_from]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[custom_design_from]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[custom_design_to]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[custom_design_to]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[custom_layout_update]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[custom_layout_update]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[page_layout]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[page_layout]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[options_container]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">container2</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[options_container]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[configurable_variation]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${configurable_attribute_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[configurable_variation]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[url_key]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${configurable_product_name}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[url_key]</stringProp> + </elementProp> + <elementProp name="product[use_config_gift_message_available]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[use_config_gift_message_available]</stringProp> + </elementProp> + <elementProp name="product[use_config_gift_wrapping_available]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[use_config_gift_wrapping_available]</stringProp> + </elementProp> + <elementProp name="product[visibility]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">4</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[visibility]</stringProp> + </elementProp> + <elementProp name="product[product_has_weight]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[product_has_weight]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="product[stock_data][qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">50</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][qty]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="product[stock_data][type_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">configurable</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">product[stock_data][type_id]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/catalog/product/save/id/${configurable_product_id}/back/edit/active_tab/product-details/</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + </HTTPSamplerProxy> + <hashTree> + <BeanShellPreProcessor guiclass="TestBeanGUI" testclass="BeanShellPreProcessor" testname="Configure product options" enabled="true"> + <boolProp name="resetInterpreter">false</boolProp> + <stringProp name="parameters"/> + <stringProp name="filename"/> + <stringProp name="script">try { + int attributesCount = Integer.parseInt(vars.get("configurable_attribute_ids_matchNr")); + for (int i = 1; i <= attributesCount; i++) { + attributeId = vars.get("configurable_attribute_ids_" + i.toString()); + attributeCode = vars.get("configurable_attribute_codes_" + i.toString()); + attributeLabel = vars.get("configurable_attribute_labels_" + i.toString()); + ctx.getCurrentSampler().addArgument("attributes[" + (i - 1).toString() + "]", attributeId); + ctx.getCurrentSampler().addArgument("attribute_codes[" + (i - 1).toString() + "]", attributeCode); + ctx.getCurrentSampler().addArgument("product[" + attributeCode + "]", attributeId); + ctx.getCurrentSampler().addArgument("product[configurable_attributes_data][" + attributeId + "][attribute_id]", attributeId); + ctx.getCurrentSampler().addArgument("product[configurable_attributes_data][" + attributeId + "][position]", (i - 1).toString()); + ctx.getCurrentSampler().addArgument("product[configurable_attributes_data][" + attributeId + "][code]", attributeCode); + ctx.getCurrentSampler().addArgument("product[configurable_attributes_data][" + attributeId + "][label]", attributeLabel); + + int valuesCount = Integer.parseInt(vars.get("attribute_" + attributeId + "_values_matchNr")); + for (int j = 1; j <= valuesCount; j++) { + attributeValue = vars.get("attribute_" + attributeId + "_values_" + j.toString()); + ctx.getCurrentSampler().addArgument( + "product[configurable_attributes_data][" + attributeId + "][values][" + attributeValue + "][include]", + "1" + ); + ctx.getCurrentSampler().addArgument( + "product[configurable_attributes_data][" + attributeId + "][values][" + attributeValue + "][value_index]", + attributeValue + ); + } + } + ctx.getCurrentSampler().addArgument("associated_product_ids_serialized", vars.get("associated_products_ids").toString()); + } catch (Exception e) { + log.error("error???", e); + }</stringProp> + </BeanShellPreProcessor> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-583471546">You saved the product</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + <stringProp name="TestPlan.comments"> if have trouble see messages-message-error </stringProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + </hashTree> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Logout" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/auth/logout/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/setup/admin_logout.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + + <BeanShellPostProcessor guiclass="TestBeanGUI" testclass="BeanShellPostProcessor" testname="Return Admin to Pool" enabled="true"> + <boolProp name="resetInterpreter">false</boolProp> + <stringProp name="parameters"/> + <stringProp name="filename"/> + <stringProp name="script"> + adminUsersDistribution = Integer.parseInt(vars.get("admin_users_distribution_per_admin_pool")); + if (adminUsersDistribution == 1) { + adminUserList = props.get("adminUserList"); + adminUserList.add(vars.get("admin_user")); + } + </stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/return_admin_email_to_pool.jmx</stringProp></BeanShellPostProcessor> + <hashTree/> + </hashTree> + </hashTree> + + + <ThroughputController guiclass="ThroughputControllerGui" testclass="ThroughputController" testname="[C] Admin Returns Management" enabled="true"> + <intProp name="ThroughputController.style">1</intProp> + <boolProp name="ThroughputController.perThread">false</boolProp> + <intProp name="ThroughputController.maxThroughput">1</intProp> + <stringProp name="ThroughputController.percentThroughput">${cAdminReturnsManagementPercentage}</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/_system/scenario_controller_tmpl.jmx</stringProp></ThroughputController> + <hashTree> + <JSR223PreProcessor guiclass="TestBeanGUI" testclass="JSR223PreProcessor" testname="Set Test Label" enabled="true"> + <stringProp name="script"> +var testLabel = "${testLabel}" ? " (${testLabel})" : ""; +if (testLabel + && sampler.getClass().getName() == 'org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy' +) { + if (sampler.getName().indexOf(testLabel) == -1) { + sampler.setName(sampler.getName() + testLabel); + } +} else if (sampler.getName().indexOf("SetUp - ") == -1) { + sampler.setName("SetUp - " + sampler.getName()); +} + </stringProp> + <stringProp name="scriptLanguage">javascript</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/_system/setup_label.jmx</stringProp></JSR223PreProcessor> + <hashTree/> + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Set Label" enabled="true"> + <stringProp name="BeanShellSampler.query"> + vars.put("testLabel", "[C] Admin Returns Management"); + </stringProp> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + </BeanShellSampler> + <hashTree/> + + <JSR223PostProcessor guiclass="TestBeanGUI" testclass="JSR223PostProcessor" testname="Get admin form key PostProcessor" enabled="true"> + <stringProp name="script"> + function getFormKeyFromResponse() + { + var url = prev.getUrlAsString(), + responseCode = prev.getResponseCode(), + formKey = null; + searchPattern = /var FORM_KEY = '(.+)'/; + if (responseCode == "200" && url) { + response = prev.getResponseDataAsString(); + formKey = response && response.match(searchPattern) ? response.match(searchPattern)[1] : null; + } + return formKey; + } + + formKey = vars.get("form_key_storage"); + + currentFormKey = getFormKeyFromResponse(); + + if (currentFormKey != null && currentFormKey != formKey) { + vars.put("form_key_storage", currentFormKey); + } + </stringProp> + <stringProp name="scriptLanguage">javascript</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin/handle_admin_form_key.jmx</stringProp></JSR223PostProcessor> + <hashTree/> + <JSR223PreProcessor guiclass="TestBeanGUI" testclass="JSR223PreProcessor" testname="Set admin form key PreProcessor" enabled="true"> + <stringProp name="script"> + formKey = vars.get("form_key_storage"); + if (formKey + && sampler.getClass().getName() == 'org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy' + && sampler.getMethod() == "POST") + { + arguments = sampler.getArguments(); + for (i=0; i<arguments.getArgumentCount(); i++) + { + argument = arguments.getArgument(i); + if (argument.getName() == 'form_key' && argument.getValue() != formKey) { + log.info("admin form key updated: " + argument.getValue() + " => " + formKey); + argument.setValue(formKey); + } + } + } + </stringProp> + <stringProp name="scriptLanguage">javascript</stringProp> + </JSR223PreProcessor> + <hashTree/> + + <CookieManager guiclass="CookiePanel" testclass="CookieManager" testname="HTTP Cookie Manager" enabled="true"> + <collectionProp name="CookieManager.cookies"/> + <boolProp name="CookieManager.clearEachIteration">false</boolProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/http_cookie_manager_without_clear_each_iteration.jmx</stringProp></CookieManager> + <hashTree/> + + <GenericController guiclass="LogicControllerGui" testclass="GenericController" testname="Admin Login" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/simple_controller.jmx</stringProp> +</GenericController> + <hashTree> + <CriticalSectionController guiclass="CriticalSectionControllerGui" testclass="CriticalSectionController" testname="Admin Login Lock" enabled="true"> + <stringProp name="CriticalSectionController.lockName">get-admin-email</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/lock_controller.jmx</stringProp></CriticalSectionController> + <hashTree> + + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Get Admin Email" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/get_admin_email.jmx</stringProp> + <stringProp name="BeanShellSampler.query"> +adminUserList = props.get("adminUserList"); +adminUserListIterator = props.get("adminUserListIterator"); +adminUsersDistribution = Integer.parseInt(vars.get("admin_users_distribution_per_admin_pool")); + +if (adminUsersDistribution == 1) { + adminUser = adminUserList.poll(); +} else { + if (!adminUserListIterator.hasNext()) { + adminUserListIterator = adminUserList.descendingIterator(); + } + + adminUser = adminUserListIterator.next(); +} + +if (adminUser == null) { + SampleResult.setResponseMessage("adminUser list is empty"); + SampleResult.setResponseData("adminUser list is empty","UTF-8"); + IsSuccess=false; + SampleResult.setSuccessful(false); + SampleResult.setStopThread(true); +} +vars.put("admin_user", adminUser); + </stringProp> + <stringProp name="BeanShellSampler.filename"/> + <stringProp name="BeanShellSampler.parameters"/> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + </BeanShellSampler> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="SetUp - Login" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_login/admin_login.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert login form shown" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-1397214398">Welcome</stringProp> + <stringProp name="-515240035"><title>Magento Admin</title></stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract form key" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">admin_form_key</stringProp> + <stringProp name="RegexExtractor.regex"><input name="form_key" type="hidden" value="([^'"]+)" /></stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert form_key extracted" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="2845929">^.+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">admin_form_key</stringProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="SetUp - Login Submit Form" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="dummy" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">dummy</stringProp> + </elementProp> + <elementProp name="form_key" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_form_key}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">form_key</stringProp> + </elementProp> + <elementProp name="login[password]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_password}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">login[password]</stringProp> + </elementProp> + <elementProp name="login[username]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_user}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">login[username]</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/dashboard/</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <stringProp name="HTTPSampler.implementation">Java</stringProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_login/admin_login_submit_form.jmx</stringProp> + </HTTPSamplerProxy> + <hashTree> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract form key" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">admin_form_key</stringProp> + <stringProp name="RegexExtractor.regex"><input name="form_key" type="hidden" value="([^'"]+)" /></stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_login/admin_retrieve_form_key.jmx</stringProp></RegexExtractor> + <hashTree/> + </hashTree> + </hashTree> + + <GenericController guiclass="LogicControllerGui" testclass="GenericController" testname="Simple Controller" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/simple_controller.jmx</stringProp> +</GenericController> + <hashTree> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Orders page" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/sales/order/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_create_process_returns/orders_page.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="1204796042">Create New Order</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Open Orders" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="namespace" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">sales_order_grid</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">namespace</stringProp> + </elementProp> + <elementProp name="search" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">search</stringProp> + </elementProp> + <elementProp name="filters[placeholder]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">true</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">filters[placeholder]</stringProp> + </elementProp> + <elementProp name="paging[pageSize]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">200</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">paging[pageSize]</stringProp> + </elementProp> + <elementProp name="paging[current]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">paging[current]</stringProp> + </elementProp> + <elementProp name="sorting[field]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">increment_id</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">sorting[field]</stringProp> + </elementProp> + <elementProp name="sorting[direction]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">desc</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">sorting[direction]</stringProp> + </elementProp> + <elementProp name="isAjax" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">true</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">isAjax</stringProp> + </elementProp> + <elementProp name="form_key" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_form_key}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">form_key</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="filters[status]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">pending</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">filters[status]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="_" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${__time()}${__Random(1,1000000)}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">_</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/mui/index/render/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_create_process_returns/open_orders.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="1637639774">totalRecords</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Search Pending Orders" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="form_key" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_form_key}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">form_key</stringProp> + </elementProp> + <elementProp name="namespace" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">sales_order_grid</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">namespace</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="search" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">search</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="filters[placeholder]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">true</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">filters[placeholder]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="paging[pageSize]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">200</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">paging[pageSize]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="paging[current]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">paging[current]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="sorting[field]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">increment_id</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">sorting[field]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="sorting[direction]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">asc</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">sorting[direction]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="isAjax" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">true</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">isAjax</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="filters[status]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">pending</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">filters[status]</stringProp> + </elementProp> + <elementProp name="_" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${__time()}${__Random(1,1000000)}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">_</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/mui/index/render/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_create_process_returns/search_orders.jmx</stringProp></HTTPSamplerProxy> +<hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="1637639774">totalRecords</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract order numbers" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">order_numbers</stringProp> + <stringProp name="RegexExtractor.regex">\"increment_id\":\"(\d+)\"\,</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">-1</stringProp> + <stringProp name="Scope.variable">simple_products</stringProp> + </RegexExtractor> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract order ids" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">order_ids</stringProp> + <stringProp name="RegexExtractor.regex">\"entity_id\":\"(\d+)\"\,</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">-1</stringProp> + <stringProp name="Scope.variable">simple_products</stringProp> + </RegexExtractor> + <hashTree/> + </hashTree> + + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Generate Unique Ids for each Thread" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_create_process_returns/setup.jmx</stringProp> + <stringProp name="BeanShellSampler.query"> + import java.util.ArrayList; + import java.util.HashMap; + import org.apache.jmeter.protocol.http.util.Base64Encoder; + import java.util.Random; + + // get count of "order_numbers" variable defined in "Search Pending Orders Limit" + int ordersCount = Integer.parseInt(vars.get("order_numbers_matchNr")); + + + int clusterLength; + int threadsNumber = ctx.getThreadGroup().getNumThreads(); + if (threadsNumber == 0) { + //Number of orders for one thread + clusterLength = ordersCount; + } else { + clusterLength = Math.round(ordersCount / threadsNumber); + if (clusterLength == 0) { + clusterLength = 1; + } + } + + //Current thread number starts from 0 + int currentThreadNum = ctx.getThreadNum(); + + //Index of the current product from the cluster + Random random = new Random(); + if (${seedForRandom} > 0) { + random.setSeed(${seedForRandom} + ${__threadNum}); + } + int iterator = random.nextInt(clusterLength); + if (iterator == 0) { + iterator = 1; + } + + int i = clusterLength * currentThreadNum + iterator; + + orderNumber = vars.get("order_numbers_" + i.toString()); + orderId = vars.get("order_ids_" + i.toString()); + vars.put("order_number", orderNumber); + vars.put("order_id", orderId); + + </stringProp> + <stringProp name="BeanShellSampler.filename"/> + <stringProp name="BeanShellSampler.parameters"/> + <boolProp name="BeanShellSampler.resetInterpreter">false</boolProp> + </BeanShellSampler> + <hashTree/> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Open Order" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/sales/order/view/order_id/${order_id}/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_create_process_returns/open_order.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="2103620713">#${order_number}</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract order status" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">order_status</stringProp> + <stringProp name="RegexExtractor.regex"><span id="order_status">([^<]+)</span></stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + <stringProp name="Scope.variable">simple_products</stringProp> + </RegexExtractor> + <hashTree/> + </hashTree> + + <IfController guiclass="IfControllerPanel" testclass="IfController" testname="If Controller" enabled="true"> + <stringProp name="IfController.condition">"${order_status}" == "Pending"</stringProp> + <boolProp name="IfController.evaluateAll">false</boolProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_edit_order/if_controller.jmx</stringProp></IfController> + <hashTree> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Invoice Start" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/sales/order_invoice/start/order_id/${order_id}/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_create_process_returns/invoice_start.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-1233850814">Invoice Totals</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract ordered items ids" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">item_ids</stringProp> + <stringProp name="RegexExtractor.regex"><div id="order_item_(\d+)_title"\s*class="product-title"></stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">-1</stringProp> + <stringProp name="Scope.variable">simple_products</stringProp> + </RegexExtractor> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Invoice Submit" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="form_key" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_form_key}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">form_key</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="invoice[items][${item_ids_1}]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">invoice[items][${item_ids_1}]</stringProp> + </elementProp> + <elementProp name="invoice[items][${item_ids_2}]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">invoice[items][${item_ids_2}]</stringProp> + </elementProp> + <elementProp name="invoice[comment_text]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">Invoiced</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">invoice[comment_text]</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/sales/order_invoice/save/order_id/${order_id}/</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_create_process_returns/invoice_submit.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="1740524604">The invoice has been created</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Credit Memo Start" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/sales/order_creditmemo/start/order_id/${order_id}/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_create_process_returns/credit_memo_start.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="1382627322">New Memo</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Credit Memo Submit - Full Refund" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="form_key" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_form_key}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">form_key</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="creditmemo[items][${item_ids_1}][qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">creditmemo[items][${item_ids_1}][qty]</stringProp> + </elementProp> + <elementProp name="creditmemo[items][${item_ids_2}][qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">creditmemo[items][${item_ids_2}][qty]</stringProp> + </elementProp> + <elementProp name="creditmemo[do_offline]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">creditmemo[do_offline]</stringProp> + </elementProp> + <elementProp name="creditmemo[comment_text]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">Credit Memo added</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">creditmemo[comment_text]</stringProp> + </elementProp> + <elementProp name="creditmemo[shipping_amount]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">10</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">creditmemo[shipping_amount]</stringProp> + </elementProp> + <elementProp name="creditmemo[adjustment_positive]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">creditmemo[adjustment_positive]</stringProp> + </elementProp> + <elementProp name="creditmemo[adjustment_negative]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">creditmemo[adjustment_negative]</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/sales/order_creditmemo/save/order_id/${order_id}/</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_create_process_returns/credit_memo_full_refund.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-515117447">You created the credit memo</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + + <TestAction guiclass="TestActionGui" testclass="TestAction" testname="Create/Process Returns - Pause" enabled="true"> + <intProp name="ActionProcessor.action">1</intProp> + <intProp name="ActionProcessor.target">0</intProp> + <stringProp name="ActionProcessor.duration">${__javaScript(Math.round(${adminCreateProcessReturnsDelay}*1000))}</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_create_process_returns/pause.jmx</stringProp></TestAction> + <hashTree/> + </hashTree> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Logout" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/auth/logout/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/setup/admin_logout.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + + <BeanShellPostProcessor guiclass="TestBeanGUI" testclass="BeanShellPostProcessor" testname="Return Admin to Pool" enabled="true"> + <boolProp name="resetInterpreter">false</boolProp> + <stringProp name="parameters"/> + <stringProp name="filename"/> + <stringProp name="script"> + adminUsersDistribution = Integer.parseInt(vars.get("admin_users_distribution_per_admin_pool")); + if (adminUsersDistribution == 1) { + adminUserList = props.get("adminUserList"); + adminUserList.add(vars.get("admin_user")); + } + </stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/return_admin_email_to_pool.jmx</stringProp></BeanShellPostProcessor> + <hashTree/> + </hashTree> + </hashTree> + + + <ThroughputController guiclass="ThroughputControllerGui" testclass="ThroughputController" testname="[C] Admin Browse Customer Grid" enabled="true"> + <intProp name="ThroughputController.style">1</intProp> + <boolProp name="ThroughputController.perThread">false</boolProp> + <intProp name="ThroughputController.maxThroughput">1</intProp> + <stringProp name="ThroughputController.percentThroughput">${cAdminBrowseCustomerGridPercentage}</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/_system/scenario_controller_tmpl.jmx</stringProp></ThroughputController> + <hashTree> + <JSR223PreProcessor guiclass="TestBeanGUI" testclass="JSR223PreProcessor" testname="Set Test Label" enabled="true"> + <stringProp name="script"> +var testLabel = "${testLabel}" ? " (${testLabel})" : ""; +if (testLabel + && sampler.getClass().getName() == 'org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy' +) { + if (sampler.getName().indexOf(testLabel) == -1) { + sampler.setName(sampler.getName() + testLabel); + } +} else if (sampler.getName().indexOf("SetUp - ") == -1) { + sampler.setName("SetUp - " + sampler.getName()); +} + </stringProp> + <stringProp name="scriptLanguage">javascript</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/_system/setup_label.jmx</stringProp></JSR223PreProcessor> + <hashTree/> + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Set Label" enabled="true"> + <stringProp name="BeanShellSampler.query"> + vars.put("testLabel", "[C] Admin Browse Customer Grid"); + </stringProp> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + </BeanShellSampler> + <hashTree/> + + <JSR223PostProcessor guiclass="TestBeanGUI" testclass="JSR223PostProcessor" testname="Get admin form key PostProcessor" enabled="true"> + <stringProp name="script"> + function getFormKeyFromResponse() + { + var url = prev.getUrlAsString(), + responseCode = prev.getResponseCode(), + formKey = null; + searchPattern = /var FORM_KEY = '(.+)'/; + if (responseCode == "200" && url) { + response = prev.getResponseDataAsString(); + formKey = response && response.match(searchPattern) ? response.match(searchPattern)[1] : null; + } + return formKey; + } + + formKey = vars.get("form_key_storage"); + + currentFormKey = getFormKeyFromResponse(); + + if (currentFormKey != null && currentFormKey != formKey) { + vars.put("form_key_storage", currentFormKey); + } + </stringProp> + <stringProp name="scriptLanguage">javascript</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin/handle_admin_form_key.jmx</stringProp></JSR223PostProcessor> + <hashTree/> + <JSR223PreProcessor guiclass="TestBeanGUI" testclass="JSR223PreProcessor" testname="Set admin form key PreProcessor" enabled="true"> + <stringProp name="script"> + formKey = vars.get("form_key_storage"); + if (formKey + && sampler.getClass().getName() == 'org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy' + && sampler.getMethod() == "POST") + { + arguments = sampler.getArguments(); + for (i=0; i<arguments.getArgumentCount(); i++) + { + argument = arguments.getArgument(i); + if (argument.getName() == 'form_key' && argument.getValue() != formKey) { + log.info("admin form key updated: " + argument.getValue() + " => " + formKey); + argument.setValue(formKey); + } + } + } + </stringProp> + <stringProp name="scriptLanguage">javascript</stringProp> + </JSR223PreProcessor> + <hashTree/> + + <CookieManager guiclass="CookiePanel" testclass="CookieManager" testname="HTTP Cookie Manager" enabled="true"> + <collectionProp name="CookieManager.cookies"/> + <boolProp name="CookieManager.clearEachIteration">false</boolProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/http_cookie_manager_without_clear_each_iteration.jmx</stringProp></CookieManager> + <hashTree/> + + <GenericController guiclass="LogicControllerGui" testclass="GenericController" testname="Admin Login" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/simple_controller.jmx</stringProp> +</GenericController> + <hashTree> + <CriticalSectionController guiclass="CriticalSectionControllerGui" testclass="CriticalSectionController" testname="Admin Login Lock" enabled="true"> + <stringProp name="CriticalSectionController.lockName">get-admin-email</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/lock_controller.jmx</stringProp></CriticalSectionController> + <hashTree> + + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Get Admin Email" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/get_admin_email.jmx</stringProp> + <stringProp name="BeanShellSampler.query"> +adminUserList = props.get("adminUserList"); +adminUserListIterator = props.get("adminUserListIterator"); +adminUsersDistribution = Integer.parseInt(vars.get("admin_users_distribution_per_admin_pool")); + +if (adminUsersDistribution == 1) { + adminUser = adminUserList.poll(); +} else { + if (!adminUserListIterator.hasNext()) { + adminUserListIterator = adminUserList.descendingIterator(); + } + + adminUser = adminUserListIterator.next(); +} + +if (adminUser == null) { + SampleResult.setResponseMessage("adminUser list is empty"); + SampleResult.setResponseData("adminUser list is empty","UTF-8"); + IsSuccess=false; + SampleResult.setSuccessful(false); + SampleResult.setStopThread(true); +} +vars.put("admin_user", adminUser); + </stringProp> + <stringProp name="BeanShellSampler.filename"/> + <stringProp name="BeanShellSampler.parameters"/> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + </BeanShellSampler> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="SetUp - Login" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_login/admin_login.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert login form shown" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-1397214398">Welcome</stringProp> + <stringProp name="-515240035"><title>Magento Admin</title></stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract form key" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">admin_form_key</stringProp> + <stringProp name="RegexExtractor.regex"><input name="form_key" type="hidden" value="([^'"]+)" /></stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert form_key extracted" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="2845929">^.+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">admin_form_key</stringProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="SetUp - Login Submit Form" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="dummy" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">dummy</stringProp> + </elementProp> + <elementProp name="form_key" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_form_key}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">form_key</stringProp> + </elementProp> + <elementProp name="login[password]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_password}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">login[password]</stringProp> + </elementProp> + <elementProp name="login[username]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_user}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">login[username]</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/dashboard/</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <stringProp name="HTTPSampler.implementation">Java</stringProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_login/admin_login_submit_form.jmx</stringProp> + </HTTPSamplerProxy> + <hashTree> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract form key" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">admin_form_key</stringProp> + <stringProp name="RegexExtractor.regex"><input name="form_key" type="hidden" value="([^'"]+)" /></stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_login/admin_retrieve_form_key.jmx</stringProp></RegexExtractor> + <hashTree/> + </hashTree> + </hashTree> + + <JSR223PostProcessor guiclass="TestBeanGUI" testclass="JSR223PreProcessor" testname="SetUp - Set Arguments" enabled="true"> + <stringProp name="script"> + vars.put("gridEntityType" , "Customer"); + + pagesCount = parseInt(vars.get("customers_page_size")) || 20; + vars.put("grid_entity_page_size" , pagesCount); + vars.put("grid_namespace" , "customer_listing"); + vars.put("grid_admin_browse_filter_text" , vars.get("admin_browse_customer_filter_text")); + vars.put("grid_filter_field", "name"); + + // set sort fields and sort directions + vars.put("grid_sort_field_1", "name"); + vars.put("grid_sort_field_2", "group_id"); + vars.put("grid_sort_field_3", "billing_country_id"); + vars.put("grid_sort_order_1", "asc"); + vars.put("grid_sort_order_2", "desc"); + </stringProp> + <stringProp name="scriptLanguage">javascript</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_browse_customers_grid/setup.jmx</stringProp></JSR223PostProcessor> + <hashTree/> + + <GenericController guiclass="LogicControllerGui" testclass="GenericController" testname="Simple Controller" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/simple_controller.jmx</stringProp> +</GenericController> + <hashTree> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="SetUp - Set ${gridEntityType} Pages Count" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="namespace" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${grid_namespace}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">namespace</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="search" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">search</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="filters[placeholder]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">true</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">filters[placeholder]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="paging[pageSize]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${grid_entity_page_size}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">paging[pageSize]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="paging[current]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">paging[current]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="sorting[field]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">entity_id</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">sorting[field]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="sorting[direction]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">asc</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">sorting[direction]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="isAjax" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">true</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">isAjax</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/mui/index/render/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_grid_browsing/set_pages_count.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <com.atlantbh.jmeter.plugins.jsonutils.jsonpathassertion.JSONPathAssertion guiclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathassertion.gui.JSONPathAssertionGui" testclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathassertion.JSONPathAssertion" testname="Assert total records is not 0" enabled="true"> + <stringProp name="JSON_PATH">$.totalRecords</stringProp> + <stringProp name="EXPECTED_VALUE">0</stringProp> + <boolProp name="JSONVALIDATION">true</boolProp> + <boolProp name="EXPECT_NULL">false</boolProp> + <boolProp name="INVERT">true</boolProp> + </com.atlantbh.jmeter.plugins.jsonutils.jsonpathassertion.JSONPathAssertion> + <hashTree/> + <com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor guiclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.gui.JSONPathExtractorGui" testclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor" testname="Extract total records" enabled="true"> + <stringProp name="VAR">entity_total_records</stringProp> + <stringProp name="JSONPATH">$.totalRecords</stringProp> + <stringProp name="DEFAULT"/> + <stringProp name="VARIABLE"/> + <stringProp name="SUBJECT">BODY</stringProp> + </com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor> + <hashTree/> + <JSR223PostProcessor guiclass="TestBeanGUI" testclass="JSR223PreProcessor" testname="SetUp - Calculate ${gridEntityType} pages count" enabled="true"> + <stringProp name="cacheKey"/> + <stringProp name="filename"/> + <stringProp name="parameters"/> + <stringProp name="script"> + var pageSize = parseInt(vars.get("grid_entity_page_size")) || 20; + var totalsRecord = parseInt(vars.get("entity_total_records")); + var pageCount = Math.round(totalsRecord/pageSize); + + vars.put("grid_pages_count", pageCount); + </stringProp> + <stringProp name="scriptLanguage">javascript</stringProp> + </JSR223PostProcessor> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="SetUp - Set ${gridEntityType} Filtered Pages Count" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="namespace" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${grid_namespace}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">namespace</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="search" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">search</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="filters[placeholder]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${grid_admin_browse_filter_text}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">filters[placeholder]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="paging[pageSize]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${grid_entity_page_size}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">paging[pageSize]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="paging[current]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">paging[current]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="sorting[field]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">entity_id</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">sorting[field]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="sorting[direction]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">asc</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">sorting[direction]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="isAjax" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">true</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">isAjax</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/mui/index/render/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_grid_browsing/set_filtered_pages_count.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <com.atlantbh.jmeter.plugins.jsonutils.jsonpathassertion.JSONPathAssertion guiclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathassertion.gui.JSONPathAssertionGui" testclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathassertion.JSONPathAssertion" testname="Assert total records is not 0" enabled="true"> + <stringProp name="JSON_PATH">$.totalRecords</stringProp> + <stringProp name="EXPECTED_VALUE">0</stringProp> + <boolProp name="JSONVALIDATION">true</boolProp> + <boolProp name="EXPECT_NULL">false</boolProp> + <boolProp name="INVERT">true</boolProp> + <boolProp name="ISREGEX">true</boolProp> + </com.atlantbh.jmeter.plugins.jsonutils.jsonpathassertion.JSONPathAssertion> + <hashTree/> + <com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor guiclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.gui.JSONPathExtractorGui" testclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor" testname="Extract total records" enabled="true"> + <stringProp name="VAR">entity_total_records</stringProp> + <stringProp name="JSONPATH">$.totalRecords</stringProp> + <stringProp name="DEFAULT"/> + <stringProp name="VARIABLE"/> + <stringProp name="SUBJECT">BODY</stringProp> + </com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor> + <hashTree/> + <JSR223PostProcessor guiclass="TestBeanGUI" testclass="JSR223PreProcessor" testname="SetUp - Calculate ${gridEntityType} filtered pages count" enabled="true"> + <stringProp name="parameters"/> + <stringProp name="filename"/> + <stringProp name="script"> + var pageSize = parseInt(vars.get("grid_entity_page_size")) || 20; +var totalsRecord = parseInt(vars.get("entity_total_records")); +var pageCount = Math.round(totalsRecord/pageSize); + +vars.put("grid_pages_count_filtered", pageCount); + </stringProp> + <stringProp name="scriptLanguage">javascript</stringProp> + </JSR223PostProcessor> + <hashTree/> + </hashTree> + + <CounterConfig guiclass="CounterConfigGui" testclass="CounterConfig" testname="SetUp - Select ${gridEntityType} Page Number" enabled="true"> + <stringProp name="CounterConfig.start">1</stringProp> + <stringProp name="CounterConfig.end">${grid_pages_count}</stringProp> + <stringProp name="CounterConfig.incr">1</stringProp> + <stringProp name="CounterConfig.name">page_number</stringProp> + <stringProp name="CounterConfig.format"/> + <boolProp name="CounterConfig.per_user">true</boolProp> + <boolProp name="CounterConfig.reset_on_tg_iteration">false</boolProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_grid_browsing/select_page_number.jmx</stringProp></CounterConfig> + <hashTree/> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="View ${gridEntityType} page" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="namespace" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${grid_namespace}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">namespace</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="search" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">search</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="filters[placeholder]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">true</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">filters[placeholder]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="paging[pageSize]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${grid_entity_page_size}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">paging[pageSize]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="paging[current]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${page_number}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">paging[current]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="sorting[field]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">entity_id</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">sorting[field]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="sorting[direction]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">asc</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">sorting[direction]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="isAjax" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">true</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">isAjax</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/mui/index/render/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_grid_browsing/admin_browse_grid.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="1637639774">\"totalRecords\":[^0]\d*</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + + <CounterConfig guiclass="CounterConfigGui" testclass="CounterConfig" testname="SetUp - Select Filtered ${gridEntityType} Page Number" enabled="true"> + <stringProp name="CounterConfig.start">1</stringProp> + <stringProp name="CounterConfig.end">${grid_pages_count_filtered}</stringProp> + <stringProp name="CounterConfig.incr">1</stringProp> + <stringProp name="CounterConfig.name">page_number</stringProp> + <stringProp name="CounterConfig.format"/> + <boolProp name="CounterConfig.per_user">true</boolProp> + <boolProp name="CounterConfig.reset_on_tg_iteration">false</boolProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_grid_browsing/select_filtered_page_number.jmx</stringProp></CounterConfig> + <hashTree/> + + <TestFragmentController guiclass="TestFragmentControllerGui" testclass="TestFragmentController" testname="View ${gridEntityType} page - Filtering + Sorting" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_grid_browsing/admin_browse_grid_sort_and_filter.jmx</stringProp> +</TestFragmentController> + <hashTree> + <ForeachController guiclass="ForeachControlPanel" testclass="ForeachController" testname="ForEach Sort Field Defined" enabled="true"> + <stringProp name="ForeachController.inputVal">grid_sort_field</stringProp> + <stringProp name="ForeachController.returnVal">grid_sort_field</stringProp> + <boolProp name="ForeachController.useSeparator">true</boolProp> + <stringProp name="ForeachController.startIndex">0</stringProp> + <stringProp name="ForeachController.endIndex">3</stringProp> + </ForeachController> + <hashTree> + <ForeachController guiclass="ForeachControlPanel" testclass="ForeachController" testname="ForEach Sort Order Defined" enabled="true"> + <stringProp name="ForeachController.inputVal">grid_sort_order</stringProp> + <stringProp name="ForeachController.returnVal">grid_sort_order</stringProp> + <boolProp name="ForeachController.useSeparator">true</boolProp> + <stringProp name="ForeachController.startIndex">0</stringProp> + <stringProp name="ForeachController.endIndex">2</stringProp> + </ForeachController> + <hashTree> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="View ${gridEntityType} page - Filtering + Sort By ${grid_sort_field} ${grid_sort_order}" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="namespace" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${grid_namespace}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">namespace</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="filters[${grid_filter_field}]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${grid_admin_browse_filter_text}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">filters[${grid_filter_field}]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="filters[placeholder]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">true</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">filters[placeholder]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="paging[pageSize]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${grid_entity_page_size}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">paging[pageSize]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="paging[current]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${page_number}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">paging[current]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="sorting[field]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${grid_sort_field}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">sorting[field]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="sorting[direction]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${grid_sort_order}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">sorting[direction]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="isAjax" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">true</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">isAjax</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/mui/index/render/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + </HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="1637639774">\"totalRecords\":[^0]\d*</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + </hashTree> + </hashTree> + </hashTree> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Logout" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/auth/logout/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/setup/admin_logout.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + + <BeanShellPostProcessor guiclass="TestBeanGUI" testclass="BeanShellPostProcessor" testname="Return Admin to Pool" enabled="true"> + <boolProp name="resetInterpreter">false</boolProp> + <stringProp name="parameters"/> + <stringProp name="filename"/> + <stringProp name="script"> + adminUsersDistribution = Integer.parseInt(vars.get("admin_users_distribution_per_admin_pool")); + if (adminUsersDistribution == 1) { + adminUserList = props.get("adminUserList"); + adminUserList.add(vars.get("admin_user")); + } + </stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/return_admin_email_to_pool.jmx</stringProp></BeanShellPostProcessor> + <hashTree/> + </hashTree> + </hashTree> + + + <ThroughputController guiclass="ThroughputControllerGui" testclass="ThroughputController" testname="[C] Admin Create Order" enabled="true"> + <intProp name="ThroughputController.style">1</intProp> + <boolProp name="ThroughputController.perThread">false</boolProp> + <intProp name="ThroughputController.maxThroughput">1</intProp> + <stringProp name="ThroughputController.percentThroughput">${cAdminCreateOrderPercentage}</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/_system/scenario_controller_tmpl.jmx</stringProp></ThroughputController> + <hashTree> + <JSR223PreProcessor guiclass="TestBeanGUI" testclass="JSR223PreProcessor" testname="Set Test Label" enabled="true"> + <stringProp name="script"> +var testLabel = "${testLabel}" ? " (${testLabel})" : ""; +if (testLabel + && sampler.getClass().getName() == 'org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy' +) { + if (sampler.getName().indexOf(testLabel) == -1) { + sampler.setName(sampler.getName() + testLabel); + } +} else if (sampler.getName().indexOf("SetUp - ") == -1) { + sampler.setName("SetUp - " + sampler.getName()); +} + </stringProp> + <stringProp name="scriptLanguage">javascript</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/_system/setup_label.jmx</stringProp></JSR223PreProcessor> + <hashTree/> + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Set Label" enabled="true"> + <stringProp name="BeanShellSampler.query"> + vars.put("testLabel", "[C] Admin Create Order"); + </stringProp> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + </BeanShellSampler> + <hashTree/> + + <JSR223PostProcessor guiclass="TestBeanGUI" testclass="JSR223PostProcessor" testname="Get admin form key PostProcessor" enabled="true"> + <stringProp name="script"> + function getFormKeyFromResponse() + { + var url = prev.getUrlAsString(), + responseCode = prev.getResponseCode(), + formKey = null; + searchPattern = /var FORM_KEY = '(.+)'/; + if (responseCode == "200" && url) { + response = prev.getResponseDataAsString(); + formKey = response && response.match(searchPattern) ? response.match(searchPattern)[1] : null; + } + return formKey; + } + + formKey = vars.get("form_key_storage"); + + currentFormKey = getFormKeyFromResponse(); + + if (currentFormKey != null && currentFormKey != formKey) { + vars.put("form_key_storage", currentFormKey); + } + </stringProp> + <stringProp name="scriptLanguage">javascript</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin/handle_admin_form_key.jmx</stringProp></JSR223PostProcessor> + <hashTree/> + <JSR223PreProcessor guiclass="TestBeanGUI" testclass="JSR223PreProcessor" testname="Set admin form key PreProcessor" enabled="true"> + <stringProp name="script"> + formKey = vars.get("form_key_storage"); + if (formKey + && sampler.getClass().getName() == 'org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy' + && sampler.getMethod() == "POST") + { + arguments = sampler.getArguments(); + for (i=0; i<arguments.getArgumentCount(); i++) + { + argument = arguments.getArgument(i); + if (argument.getName() == 'form_key' && argument.getValue() != formKey) { + log.info("admin form key updated: " + argument.getValue() + " => " + formKey); + argument.setValue(formKey); + } + } + } + </stringProp> + <stringProp name="scriptLanguage">javascript</stringProp> + </JSR223PreProcessor> + <hashTree/> + + <CookieManager guiclass="CookiePanel" testclass="CookieManager" testname="HTTP Cookie Manager" enabled="true"> + <collectionProp name="CookieManager.cookies"/> + <boolProp name="CookieManager.clearEachIteration">false</boolProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/http_cookie_manager_without_clear_each_iteration.jmx</stringProp></CookieManager> + <hashTree/> + + <GenericController guiclass="LogicControllerGui" testclass="GenericController" testname="Admin Login" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/simple_controller.jmx</stringProp> +</GenericController> + <hashTree> + <CriticalSectionController guiclass="CriticalSectionControllerGui" testclass="CriticalSectionController" testname="Admin Login Lock" enabled="true"> + <stringProp name="CriticalSectionController.lockName">get-admin-email</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/lock_controller.jmx</stringProp></CriticalSectionController> + <hashTree> + + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Get Admin Email" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/get_admin_email.jmx</stringProp> + <stringProp name="BeanShellSampler.query"> +adminUserList = props.get("adminUserList"); +adminUserListIterator = props.get("adminUserListIterator"); +adminUsersDistribution = Integer.parseInt(vars.get("admin_users_distribution_per_admin_pool")); + +if (adminUsersDistribution == 1) { + adminUser = adminUserList.poll(); +} else { + if (!adminUserListIterator.hasNext()) { + adminUserListIterator = adminUserList.descendingIterator(); + } + + adminUser = adminUserListIterator.next(); +} + +if (adminUser == null) { + SampleResult.setResponseMessage("adminUser list is empty"); + SampleResult.setResponseData("adminUser list is empty","UTF-8"); + IsSuccess=false; + SampleResult.setSuccessful(false); + SampleResult.setStopThread(true); +} +vars.put("admin_user", adminUser); + </stringProp> + <stringProp name="BeanShellSampler.filename"/> + <stringProp name="BeanShellSampler.parameters"/> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + </BeanShellSampler> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="SetUp - Login" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_login/admin_login.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert login form shown" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-1397214398">Welcome</stringProp> + <stringProp name="-515240035"><title>Magento Admin</title></stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract form key" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">admin_form_key</stringProp> + <stringProp name="RegexExtractor.regex"><input name="form_key" type="hidden" value="([^'"]+)" /></stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert form_key extracted" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="2845929">^.+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">admin_form_key</stringProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="SetUp - Login Submit Form" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="dummy" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">dummy</stringProp> + </elementProp> + <elementProp name="form_key" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_form_key}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">form_key</stringProp> + </elementProp> + <elementProp name="login[password]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_password}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">login[password]</stringProp> + </elementProp> + <elementProp name="login[username]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_user}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">login[username]</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/dashboard/</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <stringProp name="HTTPSampler.implementation">Java</stringProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_login/admin_login_submit_form.jmx</stringProp> + </HTTPSamplerProxy> + <hashTree> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract form key" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">admin_form_key</stringProp> + <stringProp name="RegexExtractor.regex"><input name="form_key" type="hidden" value="([^'"]+)" /></stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_login/admin_retrieve_form_key.jmx</stringProp></RegexExtractor> + <hashTree/> + </hashTree> + </hashTree> + + <GenericController guiclass="LogicControllerGui" testclass="GenericController" testname="Simple Controller" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/simple_controller.jmx</stringProp> +</GenericController> + <hashTree> + <JSR223PreProcessor guiclass="TestBeanGUI" testclass="JSR223PreProcessor" testname="Get region data" enabled="true"> + <stringProp name="scriptLanguage">javascript</stringProp> + <stringProp name="parameters"/> + <stringProp name="filename"/> + <stringProp name="cacheKey"/> + <stringProp name="script"> + vars.put("alabama_region_id", props.get("alabama_region_id")); + vars.put("california_region_id", props.get("california_region_id")); +</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/get_region_data.jmx</stringProp></JSR223PreProcessor> + <hashTree/> + + <TestFragmentController guiclass="TestFragmentControllerGui" testclass="TestFragmentController" testname="Admin Create Order" enabled="true"/> + <hashTree> + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Set Arguments" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_create_order/admin_create_order.jmx</stringProp> + <stringProp name="BeanShellSampler.query">import org.apache.jmeter.samplers.SampleResult; +import java.util.Random; +Random random = new Random(); +if (${seedForRandom} > 0) { + random.setSeed(${seedForRandom}); +} +number = random.nextInt(props.get("simple_products_list").size()); +simpleList = props.get("simple_products_list").get(number); +vars.put("simple_product_1_url_key", simpleList.get("url_key")); +vars.put("simple_product_1_name", simpleList.get("title")); +vars.put("simple_product_1_id", simpleList.get("id")); + +do { + number1 = random.nextInt(props.get("simple_products_list").size()); +} while(number == number1); +simpleList = props.get("simple_products_list").get(number1); +vars.put("simple_product_2_url_key", simpleList.get("url_key")); +vars.put("simple_product_2_name", simpleList.get("title")); +vars.put("simple_product_2_id", simpleList.get("id")); + +number = random.nextInt(props.get("configurable_products_list").size()); +configurableList = props.get("configurable_products_list").get(number); +vars.put("configurable_product_1_url_key", configurableList.get("url_key")); +vars.put("configurable_product_1_name", configurableList.get("title")); +vars.put("configurable_product_1_id", configurableList.get("id")); +vars.put("configurable_product_1_sku", configurableList.get("sku")); +vars.put("configurable_attribute_id", configurableList.get("attribute_id")); +vars.put("configurable_option_id", configurableList.get("attribute_option_id")); + + +customers_index = 0; +if (!props.containsKey("customer_ids_index")) { + props.put("customer_ids_index", customers_index); +} + +try { + customers_index = props.get("customer_ids_index"); + customers_list = props.get("customer_ids_list"); + + if (customers_index == customers_list.size()) { + customers_index=0; + } + vars.put("customer_id", customers_list.get(customers_index)); + props.put("customer_ids_index", ++customers_index); +} +catch (java.lang.Exception e) { + log.error("Caught Exception in 'Admin Create Order' thread."); + SampleResult.setStopThread(true); +}</stringProp> + <stringProp name="BeanShellSampler.filename"/> + <stringProp name="BeanShellSampler.parameters"/> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + </BeanShellSampler> + <hashTree/> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Start Order" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/sales/order_create/start/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">Detected the start of a redirect chain</stringProp> + </HTTPSamplerProxy> + <hashTree/> + <GenericController guiclass="LogicControllerGui" testclass="GenericController" testname="SetUp - Get Configurable Product Options" enabled="true"/> + <hashTree> + <HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Header Manager" enabled="true"> + <collectionProp name="HeaderManager.headers"> + <elementProp name="" elementType="Header"> + <stringProp name="Header.name">Content-Type</stringProp> + <stringProp name="Header.value">application/json</stringProp> + </elementProp> + <elementProp name="" elementType="Header"> + <stringProp name="Header.name">Accept</stringProp> + <stringProp name="Header.value">*/*</stringProp> + </elementProp> + </collectionProp> + </HeaderManager> + <hashTree/> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="SetUp - Admin Token Retrieval" enabled="true"> + <boolProp name="HTTPSampler.postBodyRaw">true</boolProp> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments"> + <collectionProp name="Arguments.arguments"> + <elementProp name="" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">false</boolProp> + <stringProp name="Argument.value">{"username":"${admin_user}","password":"${admin_password}"}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}rest/V1/integration/admin/token</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + </HTTPSamplerProxy> + <hashTree> + <com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor guiclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.gui.JSONPathExtractorGui" testclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor" testname="jp@gc - JSON Path Extractor" enabled="true"> + <stringProp name="VAR">admin_token</stringProp> + <stringProp name="JSONPATH">$</stringProp> + <stringProp name="DEFAULT"/> + <stringProp name="VARIABLE"/> + <stringProp name="SUBJECT">BODY</stringProp> + </com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert token not null" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="484395188">^[a-z0-9-]+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">admin_token</stringProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + <HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Header Manager" enabled="true"> + <collectionProp name="HeaderManager.headers"> + <elementProp name="" elementType="Header"> + <stringProp name="Header.name">Authorization</stringProp> + <stringProp name="Header.value">Bearer ${admin_token}</stringProp> + </elementProp> + </collectionProp> + </HeaderManager> + <hashTree/> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="SetUp - Get Configurable Product Options" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}rest/V1/configurable-products/${configurable_product_1_sku}/options/all</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + </HTTPSamplerProxy> + <hashTree> + <com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor guiclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.gui.JSONPathExtractorGui" testclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor" testname="JSON Path Extractor: Extract attribute_ids" enabled="true"> + <stringProp name="VAR">attribute_ids</stringProp> + <stringProp name="JSONPATH">$.[*].attribute_id</stringProp> + <stringProp name="DEFAULT">NO_VALUE</stringProp> + <stringProp name="VARIABLE"/> + <stringProp name="SUBJECT">BODY</stringProp> + </com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor> + <hashTree/> + <com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor guiclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.gui.JSONPathExtractorGui" testclass="com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor" testname="JSON Path Extractor: Extract option_values" enabled="true"> + <stringProp name="VAR">option_values</stringProp> + <stringProp name="JSONPATH">$.[*].values[0].value_index</stringProp> + <stringProp name="DEFAULT">NO_VALUE</stringProp> + <stringProp name="VARIABLE"/> + <stringProp name="SUBJECT">BODY</stringProp> + </com.atlantbh.jmeter.plugins.jsonutils.jsonpathextractor.JSONPathExtractor> + <hashTree/> + </hashTree> + </hashTree> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Add Products" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="item[${simple_product_1_id}][qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">item[${simple_product_1_id}][qty]</stringProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="item[${simple_product_2_id}][qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">item[${simple_product_2_id}][qty]</stringProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="item[${configurable_product_1_id}][qty]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">item[${configurable_product_1_id}][qty]</stringProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="customer_id" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">customer_id</stringProp> + <stringProp name="Argument.value">${customer_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="store_id" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">store_id</stringProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="currency_id" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">currency_id</stringProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="form_key" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">form_key</stringProp> + <stringProp name="Argument.value">${admin_form_key}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="payment[method]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">payment[method]</stringProp> + <stringProp name="Argument.value">checkmo</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="reset_shipping" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">reset_shipping</stringProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="json" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">json</stringProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="as_js_varname" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">as_js_varname</stringProp> + <stringProp name="Argument.value">iFrameResponse</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="form_key" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">form_key</stringProp> + <stringProp name="Argument.value">${admin_form_key}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/sales/order_create/loadBlock/block/search,items,shipping_method,totals,giftmessage,billing_method?isAjax=true</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">Detected the start of a redirect chain</stringProp> + </HTTPSamplerProxy> + <hashTree> + <BeanShellPreProcessor guiclass="TestBeanGUI" testclass="BeanShellPreProcessor" testname="Configure product options" enabled="true"> + <boolProp name="resetInterpreter">false</boolProp> + <stringProp name="parameters"/> + <stringProp name="filename"/> + <stringProp name="script">try { + attribute_ids = vars.get("attribute_ids"); + option_values = vars.get("option_values"); + attribute_ids = attribute_ids.replace("[","").replace("]","").replace("\"", ""); + option_values = option_values.replace("[","").replace("]","").replace("\"", ""); + attribute_ids_array = attribute_ids.split(","); + option_values_array = option_values.split(","); + args = ctx.getCurrentSampler().getArguments(); + it = args.iterator(); + while (it.hasNext()) { + argument = it.next(); + if (argument.getStringValue().contains("${")) { + args.removeArgument(argument.getName()); + } + } + for (int i = 0; i < attribute_ids_array.length; i++) { + + ctx.getCurrentSampler().addArgument("item[" + vars.get("configurable_product_1_id") + "][super_attribute][" + attribute_ids_array[i] + "]", option_values_array[i]); + } +} catch (Exception e) { + log.error("error???", e); +}</stringProp> + </BeanShellPreProcessor> + <hashTree/> + </hashTree> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Collect Shipping Rates" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="collect_shipping_rates" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">collect_shipping_rates</stringProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="customer_id" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">customer_id</stringProp> + <stringProp name="Argument.value">${customer_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="store_id" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">store_id</stringProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="currency_id" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">currency_id</stringProp> + <stringProp name="Argument.value">false</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="form_key" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">form_key</stringProp> + <stringProp name="Argument.value">${admin_form_key}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="payment[method]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">payment[method]</stringProp> + <stringProp name="Argument.value">checkmo</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="json" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">json</stringProp> + <stringProp name="Argument.value">true</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/sales/order_create/loadBlock/block/shipping_method,totals?isAjax=true</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + </HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert Shipping Method" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-1987784558">shipping_method</stringProp> + <stringProp name="818779431">Flat Rate</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Filled Order Page" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/sales/order_create/index/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">Detected the start of a redirect chain</stringProp> + </HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert Filled Order Page" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-37823069">Select from existing customer addresses</stringProp> + <stringProp name="-13185722">Submit Order</stringProp> + <stringProp name="-209419315">Items Ordered</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Save Order" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="form_key" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">form_key</stringProp> + <stringProp name="Argument.value">${admin_form_key}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="limit" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">limit</stringProp> + <stringProp name="Argument.value">20</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="entity_id" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">entity_id</stringProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="name" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">name</stringProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="email" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">email</stringProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="Telephone" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">Telephone</stringProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="billing_postcode" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">billing_postcode</stringProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="billing_country_id" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">billing_country_id</stringProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="billing_regione" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">billing_regione</stringProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="store_name" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">store_name</stringProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="page" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">page</stringProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="order[currency]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">order[currency]</stringProp> + <stringProp name="Argument.value">USD</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="sku" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">sku</stringProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="qty" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">qty</stringProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="limit" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">limit</stringProp> + <stringProp name="Argument.value">20</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="entity_id" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">entity_id</stringProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="name" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">name</stringProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="sku" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">sku</stringProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="price[from]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">price[from]</stringProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="price[to]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">price[to]</stringProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="in_products" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">in_products</stringProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="page" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">page</stringProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="coupon_code" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">coupon_code</stringProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="order[account][group_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">order[account][group_id]</stringProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="order[account][email]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">order[account][email]</stringProp> + <stringProp name="Argument.value">user_${customer_id}@example.com</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="order[billing_address][customer_address_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">order[billing_address][customer_address_id]</stringProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="order[billing_address][prefix]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">order[billing_address][prefix]</stringProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="order[billing_address][firstname]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">order[billing_address][firstname]</stringProp> + <stringProp name="Argument.value">Anthony</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="order[billing_address][middlename]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">order[billing_address][middlename]</stringProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="order[billing_address][lastname]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">order[billing_address][lastname]</stringProp> + <stringProp name="Argument.value">Nealy</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="order[billing_address][suffix]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">order[billing_address][suffix]</stringProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="order[billing_address][company]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">order[billing_address][company]</stringProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="order[billing_address][street][0]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">order[billing_address][street][0]</stringProp> + <stringProp name="Argument.value">123 Freedom Blvd. #123</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="order[billing_address][street][1]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">order[billing_address][street][1]</stringProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="order[billing_address][city]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">order[billing_address][city]</stringProp> + <stringProp name="Argument.value">Fayetteville</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="order[billing_address][country_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">order[billing_address][country_id]</stringProp> + <stringProp name="Argument.value">US</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="order[billing_address][region]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">order[billing_address][region]</stringProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="order[billing_address][region_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">order[billing_address][region_id]</stringProp> + <stringProp name="Argument.value">${alabama_region_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="order[billing_address][postcode]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">order[billing_address][postcode]</stringProp> + <stringProp name="Argument.value">123123</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="order[billing_address][telephone]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">order[billing_address][telephone]</stringProp> + <stringProp name="Argument.value">022-333-4455</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="order[billing_address][fax]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">order[billing_address][fax]</stringProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="order[billing_address][vat_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">order[billing_address][vat_id]</stringProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="shipping_same_as_billing" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">shipping_same_as_billing</stringProp> + <stringProp name="Argument.value">on</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="payment[method]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">payment[method]</stringProp> + <stringProp name="Argument.value">checkmo</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="order[shipping_method]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">order[shipping_method]</stringProp> + <stringProp name="Argument.value">flatrate_flatrate</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="order[comment][customer_note]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">order[comment][customer_note]</stringProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="order[comment][customer_note_notify]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">order[comment][customer_note_notify]</stringProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="order[send_confirmation]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">order[send_confirmation]</stringProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/sales/order_create/save/</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">true</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">Detected the start of a redirect chain</stringProp> + </HTTPSamplerProxy> + <hashTree> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract Order Id" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">order_id</stringProp> + <stringProp name="RegexExtractor.regex">${host}${base_path}${admin_path}/sales/order/index/order_id/(\d+)/</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract Order Item 1" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">order_item_1</stringProp> + <stringProp name="RegexExtractor.regex">order_item_(\d+)_title</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract Order Item 2" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">order_item_2</stringProp> + <stringProp name="RegexExtractor.regex">order_item_(\d+)_title</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">2</stringProp> + </RegexExtractor> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract Order Item 3" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">order_item_3</stringProp> + <stringProp name="RegexExtractor.regex">order_item_(\d+)_title</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">3</stringProp> + </RegexExtractor> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert Order Id" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="89649215">^\d+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">order_id</stringProp> + </ResponseAssertion> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert Order Item 1" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="89649215">^\d+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">order_item_1</stringProp> + </ResponseAssertion> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert Order Item 2" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="89649215">^\d+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">order_item_2</stringProp> + </ResponseAssertion> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert Order Item 3" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="89649215">^\d+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">order_item_3</stringProp> + </ResponseAssertion> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert Order Created" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="563107624">You created the order.</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Save Invoice" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="form_key" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">form_key</stringProp> + <stringProp name="Argument.value">${admin_form_key}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="invoice[items][${order_item_1}]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">invoice[items][${order_item_1}]</stringProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="invoice[items][${order_item_2}]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">invoice[items][${order_item_2}]</stringProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="invoice[items][${order_item_3}]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">invoice[items][${order_item_3}]</stringProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="invoice[comment_text]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">invoice[comment_text]</stringProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/sales/order_invoice/save/order_id/${order_id}/</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">Detected the start of a redirect chain</stringProp> + </HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert Invoice Created" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-1878312078">The invoice has been created.</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Save Shipment" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="form_key" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">form_key</stringProp> + <stringProp name="Argument.value">${admin_form_key}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="shipment[items][${order_item_1}]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">shipment[items][${order_item_1}]</stringProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="shipment[items][${order_item_2}]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">shipment[items][${order_item_2}]</stringProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="shipment[items][${order_item_3}]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">shipment[items][${order_item_3}]</stringProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + <elementProp name="shipment[comment_text]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.name">shipment[comment_text]</stringProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/order_shipment/save/order_id/${order_id}/</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">Detected the start of a redirect chain</stringProp> + </HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert Shipment Created" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-348539683">The shipment has been created.</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + </hashTree> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Logout" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/auth/logout/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/setup/admin_logout.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + + <BeanShellPostProcessor guiclass="TestBeanGUI" testclass="BeanShellPostProcessor" testname="Return Admin to Pool" enabled="true"> + <boolProp name="resetInterpreter">false</boolProp> + <stringProp name="parameters"/> + <stringProp name="filename"/> + <stringProp name="script"> + adminUsersDistribution = Integer.parseInt(vars.get("admin_users_distribution_per_admin_pool")); + if (adminUsersDistribution == 1) { + adminUserList = props.get("adminUserList"); + adminUserList.add(vars.get("admin_user")); + } + </stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/return_admin_email_to_pool.jmx</stringProp></BeanShellPostProcessor> + <hashTree/> + </hashTree> + </hashTree> + + + <ThroughputController guiclass="ThroughputControllerGui" testclass="ThroughputController" testname="[C] Admin Category Management" enabled="true"> + <intProp name="ThroughputController.style">1</intProp> + <boolProp name="ThroughputController.perThread">false</boolProp> + <intProp name="ThroughputController.maxThroughput">1</intProp> + <stringProp name="ThroughputController.percentThroughput">${cAdminCategoryManagementPercentage}</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/_system/scenario_controller_tmpl.jmx</stringProp></ThroughputController> + <hashTree> + <JSR223PreProcessor guiclass="TestBeanGUI" testclass="JSR223PreProcessor" testname="Set Test Label" enabled="true"> + <stringProp name="script"> +var testLabel = "${testLabel}" ? " (${testLabel})" : ""; +if (testLabel + && sampler.getClass().getName() == 'org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy' +) { + if (sampler.getName().indexOf(testLabel) == -1) { + sampler.setName(sampler.getName() + testLabel); + } +} else if (sampler.getName().indexOf("SetUp - ") == -1) { + sampler.setName("SetUp - " + sampler.getName()); +} + </stringProp> + <stringProp name="scriptLanguage">javascript</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/_system/setup_label.jmx</stringProp></JSR223PreProcessor> + <hashTree/> + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Set Label" enabled="true"> + <stringProp name="BeanShellSampler.query"> + vars.put("testLabel", "[C] Admin Category Management"); + </stringProp> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + </BeanShellSampler> + <hashTree/> + + <JSR223PostProcessor guiclass="TestBeanGUI" testclass="JSR223PostProcessor" testname="Get admin form key PostProcessor" enabled="true"> + <stringProp name="script"> + function getFormKeyFromResponse() + { + var url = prev.getUrlAsString(), + responseCode = prev.getResponseCode(), + formKey = null; + searchPattern = /var FORM_KEY = '(.+)'/; + if (responseCode == "200" && url) { + response = prev.getResponseDataAsString(); + formKey = response && response.match(searchPattern) ? response.match(searchPattern)[1] : null; + } + return formKey; + } + + formKey = vars.get("form_key_storage"); + + currentFormKey = getFormKeyFromResponse(); + + if (currentFormKey != null && currentFormKey != formKey) { + vars.put("form_key_storage", currentFormKey); + } + </stringProp> + <stringProp name="scriptLanguage">javascript</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin/handle_admin_form_key.jmx</stringProp></JSR223PostProcessor> + <hashTree/> + <JSR223PreProcessor guiclass="TestBeanGUI" testclass="JSR223PreProcessor" testname="Set admin form key PreProcessor" enabled="true"> + <stringProp name="script"> + formKey = vars.get("form_key_storage"); + if (formKey + && sampler.getClass().getName() == 'org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy' + && sampler.getMethod() == "POST") + { + arguments = sampler.getArguments(); + for (i=0; i<arguments.getArgumentCount(); i++) + { + argument = arguments.getArgument(i); + if (argument.getName() == 'form_key' && argument.getValue() != formKey) { + log.info("admin form key updated: " + argument.getValue() + " => " + formKey); + argument.setValue(formKey); + } + } + } + </stringProp> + <stringProp name="scriptLanguage">javascript</stringProp> + </JSR223PreProcessor> + <hashTree/> + + <CookieManager guiclass="CookiePanel" testclass="CookieManager" testname="HTTP Cookie Manager" enabled="true"> + <collectionProp name="CookieManager.cookies"/> + <boolProp name="CookieManager.clearEachIteration">false</boolProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/http_cookie_manager_without_clear_each_iteration.jmx</stringProp></CookieManager> + <hashTree/> + + <GenericController guiclass="LogicControllerGui" testclass="GenericController" testname="Admin Login" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/simple_controller.jmx</stringProp> +</GenericController> + <hashTree> + <CriticalSectionController guiclass="CriticalSectionControllerGui" testclass="CriticalSectionController" testname="Admin Login Lock" enabled="true"> + <stringProp name="CriticalSectionController.lockName">get-admin-email</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/lock_controller.jmx</stringProp></CriticalSectionController> + <hashTree> + + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Get Admin Email" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/get_admin_email.jmx</stringProp> + <stringProp name="BeanShellSampler.query"> +adminUserList = props.get("adminUserList"); +adminUserListIterator = props.get("adminUserListIterator"); +adminUsersDistribution = Integer.parseInt(vars.get("admin_users_distribution_per_admin_pool")); + +if (adminUsersDistribution == 1) { + adminUser = adminUserList.poll(); +} else { + if (!adminUserListIterator.hasNext()) { + adminUserListIterator = adminUserList.descendingIterator(); + } + + adminUser = adminUserListIterator.next(); +} + +if (adminUser == null) { + SampleResult.setResponseMessage("adminUser list is empty"); + SampleResult.setResponseData("adminUser list is empty","UTF-8"); + IsSuccess=false; + SampleResult.setSuccessful(false); + SampleResult.setStopThread(true); +} +vars.put("admin_user", adminUser); + </stringProp> + <stringProp name="BeanShellSampler.filename"/> + <stringProp name="BeanShellSampler.parameters"/> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + </BeanShellSampler> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="SetUp - Login" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_login/admin_login.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert login form shown" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-1397214398">Welcome</stringProp> + <stringProp name="-515240035"><title>Magento Admin</title></stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract form key" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">admin_form_key</stringProp> + <stringProp name="RegexExtractor.regex"><input name="form_key" type="hidden" value="([^'"]+)" /></stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert form_key extracted" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="2845929">^.+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">admin_form_key</stringProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="SetUp - Login Submit Form" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="dummy" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">dummy</stringProp> + </elementProp> + <elementProp name="form_key" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_form_key}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">form_key</stringProp> + </elementProp> + <elementProp name="login[password]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_password}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">login[password]</stringProp> + </elementProp> + <elementProp name="login[username]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_user}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">login[username]</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/dashboard/</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <stringProp name="HTTPSampler.implementation">Java</stringProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_login/admin_login_submit_form.jmx</stringProp> + </HTTPSamplerProxy> + <hashTree> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract form key" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">admin_form_key</stringProp> + <stringProp name="RegexExtractor.regex"><input name="form_key" type="hidden" value="([^'"]+)" /></stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_login/admin_retrieve_form_key.jmx</stringProp></RegexExtractor> + <hashTree/> + </hashTree> + </hashTree> + + <GenericController guiclass="LogicControllerGui" testclass="GenericController" testname="Simple Controller" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/simple_controller.jmx</stringProp> +</GenericController> + <hashTree> + <TestFragmentController guiclass="TestFragmentControllerGui" testclass="TestFragmentController" testname="Admin Category Management" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_category_management/admin_category_management.jmx</stringProp> +</TestFragmentController> + <hashTree> + <JSR223Sampler guiclass="TestBeanGUI" testclass="JSR223Sampler" testname="SetUp - Set Arguments" enabled="true"> + <stringProp name="scriptLanguage">javascript</stringProp> + <stringProp name="parameters"/> + <stringProp name="filename"/> + <stringProp name="cacheKey"/> + <stringProp name="script">random = new java.util.Random(); +if (${seedForRandom} > 0) { +random.setSeed(${seedForRandom} + ${__threadNum}); +} + +/** + * Get unique ids for fix concurrent category saving + */ +function getNextProductNumber(i) { + number = productsVariationsSize * ${__threadNum} - i; + if (number >= productsSize) { + log.info("${testLabel}: capacity of product list is not enough for support all ${adminPoolUsers} threads"); + return random.nextInt(productsSize); + } + return productsVariationsSize * ${__threadNum} - i; +} + +var productsVariationsSize = 5, + productsSize = props.get("simple_products_list_for_edit").size(); + + +for (i = 1; i<= productsVariationsSize; i++) { + var productVariablePrefix = "simple_product_" + i + "_"; + number = getNextProductNumber(i); + simpleList = props.get("simple_products_list_for_edit").get(number); + + vars.put(productVariablePrefix + "url_key", simpleList.get("url_key")); + vars.put(productVariablePrefix + "id", simpleList.get("id")); + vars.put(productVariablePrefix + "name", simpleList.get("title")); +} + +categoryIndex = random.nextInt(props.get("admin_category_ids_list").size()); +vars.put("parent_category_id", props.get("admin_category_ids_list").get(categoryIndex)); +do { +categoryIndexNew = random.nextInt(props.get("admin_category_ids_list").size()); +} while(categoryIndex == categoryIndexNew); +vars.put("new_parent_category_id", props.get("admin_category_ids_list").get(categoryIndexNew));</stringProp> + </JSR223Sampler> + <hashTree/> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Landing Page" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/catalog/category/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + </HTTPSamplerProxy> + <hashTree> + <HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Header Manager" enabled="true"> + <collectionProp name="HeaderManager.headers"> + <elementProp name="Accept-Language" elementType="Header"> + <stringProp name="Header.name">Accept-Language</stringProp> + <stringProp name="Header.value">en-US,en;q=0.5</stringProp> + </elementProp> + <elementProp name="Accept" elementType="Header"> + <stringProp name="Header.name">Accept</stringProp> + <stringProp name="Header.value">text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8</stringProp> + </elementProp> + <elementProp name="User-Agent" elementType="Header"> + <stringProp name="Header.name">User-Agent</stringProp> + <stringProp name="Header.value">Mozilla/5.0 (Windows NT 6.1; WOW64; rv:27.0) Gecko/20100101 Firefox/27.0</stringProp> + </elementProp> + <elementProp name="Accept-Encoding" elementType="Header"> + <stringProp name="Header.name">Accept-Encoding</stringProp> + <stringProp name="Header.value">gzip, deflate</stringProp> + </elementProp> + </collectionProp> + </HeaderManager> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract form key" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">admin_form_key</stringProp> + <stringProp name="RegexExtractor.regex"><input name="form_key" type="hidden" value="([^'"]+)" /></stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + </hashTree> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Select parent category" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/catalog/category/edit/id/${parent_category_id}/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + </HTTPSamplerProxy> + <hashTree> + <HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Header Manager" enabled="true"> + <collectionProp name="HeaderManager.headers"> + <elementProp name="Accept-Language" elementType="Header"> + <stringProp name="Header.name">Accept-Language</stringProp> + <stringProp name="Header.value">en-US,en;q=0.5</stringProp> + </elementProp> + <elementProp name="Accept" elementType="Header"> + <stringProp name="Header.name">Accept</stringProp> + <stringProp name="Header.value">text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8</stringProp> + </elementProp> + <elementProp name="User-Agent" elementType="Header"> + <stringProp name="Header.name">User-Agent</stringProp> + <stringProp name="Header.value">Mozilla/5.0 (Windows NT 6.1; WOW64; rv:27.0) Gecko/20100101 Firefox/27.0</stringProp> + </elementProp> + <elementProp name="Accept-Encoding" elementType="Header"> + <stringProp name="Header.name">Accept-Encoding</stringProp> + <stringProp name="Header.value">gzip, deflate</stringProp> + </elementProp> + </collectionProp> + </HeaderManager> + <hashTree/> + </hashTree> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Open new category page" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/catalog/category/add/store/0/parent/${parent_category_id}</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + </HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-1903925024"><title>New Category</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Create category" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="id" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">id</stringProp> + </elementProp> + <elementProp name="parent" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${parent_category_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">parent</stringProp> + </elementProp> + <elementProp name="path" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">path</stringProp> + </elementProp> + <elementProp name="store_id" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">store_id</stringProp> + </elementProp> + <elementProp name="is_active" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">is_active</stringProp> + </elementProp> + <elementProp name="include_in_menu" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">include_in_menu</stringProp> + </elementProp> + <elementProp name="is_anchor" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">is_anchor</stringProp> + </elementProp> + <elementProp name="use_config[available_sort_by]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">true</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">use_config[available_sort_by]</stringProp> + </elementProp> + <elementProp name="use_config[default_sort_by]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">true</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">use_config[default_sort_by]</stringProp> + </elementProp> + <elementProp name="use_config[filter_price_range]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">true</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">use_config[filter_price_range]</stringProp> + </elementProp> + <elementProp name="use_default[url_key]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">false</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">use_default[url_key]</stringProp> + </elementProp> + <elementProp name="url_key_create_redirect" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">url_key_create_redirect</stringProp> + </elementProp> + <elementProp name="custom_use_parent_settings" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">custom_use_parent_settings</stringProp> + </elementProp> + <elementProp name="custom_apply_to_products" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">custom_apply_to_products</stringProp> + </elementProp> + <elementProp name="name" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">Admin Category Management ${__time(YMDHMS)}-${__threadNum}-${__Random(1,1000000)}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">name</stringProp> + </elementProp> + <elementProp name="url_key" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">admin-category-management-${__time(YMDHMS)}-${__threadNum}-${__Random(1,1000000)}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">url_key</stringProp> + </elementProp> + <elementProp name="meta_title" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">meta_title</stringProp> + </elementProp> + <elementProp name="description" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">description</stringProp> + </elementProp> + <elementProp name="display_mode" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">PRODUCTS</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">display_mode</stringProp> + </elementProp> + <elementProp name="default_sort_by" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">position</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">default_sort_by</stringProp> + </elementProp> + <elementProp name="meta_keywords" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">meta_keywords</stringProp> + </elementProp> + <elementProp name="meta_description" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">meta_description</stringProp> + </elementProp> + <elementProp name="custom_layout_update" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">custom_layout_update</stringProp> + </elementProp> + <elementProp name="category_products" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">false</boolProp> + <stringProp name="Argument.value">{"${simple_product_1_id}":"","${simple_product_2_id}":"","${simple_product_3_id}":"","${simple_product_4_id}":"","${simple_product_5_id}":""}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">category_products</stringProp> + </elementProp> + <elementProp name="form_key" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_form_key}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">form_key</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/catalog/category/save/</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + </HTTPSamplerProxy> + <hashTree> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract category id" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">URL</stringProp> + <stringProp name="RegexExtractor.refname">admin_category_id</stringProp> + <stringProp name="RegexExtractor.regex">/catalog/category/edit/id/(\d+)/</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert category id" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="89649215">^\d+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">admin_category_id</stringProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Select created category" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/catalog/category/edit/id/${admin_category_id}/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + </HTTPSamplerProxy> + <hashTree> + <HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Header Manager" enabled="true"> + <collectionProp name="HeaderManager.headers"> + <elementProp name="Accept-Language" elementType="Header"> + <stringProp name="Header.name">Accept-Language</stringProp> + <stringProp name="Header.value">en-US,en;q=0.5</stringProp> + </elementProp> + <elementProp name="Accept" elementType="Header"> + <stringProp name="Header.name">Accept</stringProp> + <stringProp name="Header.value">text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8</stringProp> + </elementProp> + <elementProp name="User-Agent" elementType="Header"> + <stringProp name="Header.name">User-Agent</stringProp> + <stringProp name="Header.value">Mozilla/5.0 (Windows NT 6.1; WOW64; rv:27.0) Gecko/20100101 Firefox/27.0</stringProp> + </elementProp> + <elementProp name="Accept-Encoding" elementType="Header"> + <stringProp name="Header.name">Accept-Encoding</stringProp> + <stringProp name="Header.value">gzip, deflate</stringProp> + </elementProp> + </collectionProp> + </HeaderManager> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract category row id" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">admin_category_entity_id</stringProp> + <stringProp name="RegexExtractor.regex">"entity_id":"([^"]+)"</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract category attribute set id" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">admin_category_attribute_set_id</stringProp> + <stringProp name="RegexExtractor.regex">"attribute_set_id":"([^"]+)"</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract category parent Id" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">admin_category_parent_id</stringProp> + <stringProp name="RegexExtractor.regex">"parent_id":"([^"]+)"</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract category created at" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">admin_category_created_at</stringProp> + <stringProp name="RegexExtractor.regex">"created_at":"([^"]+)"</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract category updated at" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">admin_category_updated_at</stringProp> + <stringProp name="RegexExtractor.regex">"updated_at":"([^"]+)"</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract category path" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">admin_category_path</stringProp> + <stringProp name="RegexExtractor.regex">"entity_id":(.+)"path":"([^\"]+)"</stringProp> + <stringProp name="RegexExtractor.template">$2$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract category level" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">admin_category_level</stringProp> + <stringProp name="RegexExtractor.regex">"level":"([^"]+)"</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract category name" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">admin_category_name</stringProp> + <stringProp name="RegexExtractor.regex">"entity_id":(.+)"name":"([^"]+)"</stringProp> + <stringProp name="RegexExtractor.template">$2$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract category url key" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">admin_category_url_key</stringProp> + <stringProp name="RegexExtractor.regex">"url_key":"([^"]+)"</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract category url path" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">admin_category_url_path</stringProp> + <stringProp name="RegexExtractor.regex">"url_path":"([^"]+)"</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert category row id" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="89649215">^\d+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">admin_category_entity_id</stringProp> + </ResponseAssertion> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert category attribute set id" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="89649215">^\d+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">admin_category_attribute_set_id</stringProp> + </ResponseAssertion> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert category parent id" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="89649215">^\d+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">admin_category_parent_id</stringProp> + </ResponseAssertion> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert category created at" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="2845929">^.+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">admin_category_created_at</stringProp> + </ResponseAssertion> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert category updated at" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="2845929">^.+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">admin_category_updated_at</stringProp> + </ResponseAssertion> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert category path" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="59022110">^[\d\\\/]+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">admin_category_path</stringProp> + </ResponseAssertion> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert category level" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="89649215">^\d+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">admin_category_level</stringProp> + </ResponseAssertion> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert category name" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="2845929">^.+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">admin_category_name</stringProp> + </ResponseAssertion> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert category url key" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="2845929">^.+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">admin_category_url_key</stringProp> + </ResponseAssertion> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert category url path" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="2845929">^.+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">admin_category_url_path</stringProp> + </ResponseAssertion> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert products added" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="417284990">${simple_product_1_name}</stringProp> + <stringProp name="1304788671">${simple_product_2_name}</stringProp> + <stringProp name="-2102674944">${simple_product_3_name}</stringProp> + <stringProp name="-1215171263">${simple_product_4_name}</stringProp> + <stringProp name="-327667582">${simple_product_5_name}</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Move category" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="id" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_category_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">id</stringProp> + </elementProp> + <elementProp name="form_key" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_form_key}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">form_key</stringProp> + </elementProp> + <elementProp name="point" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">append</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">point</stringProp> + </elementProp> + <elementProp name="pid" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${new_parent_category_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">pid</stringProp> + </elementProp> + <elementProp name="paid" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${parent_category_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">paid</stringProp> + </elementProp> + <elementProp name="aid" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">aid</stringProp> + </elementProp> + <elementProp name="isAjax" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">true</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">isAjax</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/catalog/category/move/</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + </HTTPSamplerProxy> + <hashTree/> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Delete category" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="form_key" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_form_key}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">form_key</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/catalog/category/delete/id/${admin_category_id}/</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + </HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert category deleted" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="1277069529">You deleted the category.</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + <TestAction guiclass="TestActionGui" testclass="TestAction" testname="Pause" enabled="true"> + <intProp name="ActionProcessor.action">1</intProp> + <intProp name="ActionProcessor.target">0</intProp> + <stringProp name="ActionProcessor.duration">${__javaScript(Math.round(${adminCategoryManagementDelay}*1000))}</stringProp> + </TestAction> + <hashTree/> + </hashTree> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Logout" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/auth/logout/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/setup/admin_logout.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + + <BeanShellPostProcessor guiclass="TestBeanGUI" testclass="BeanShellPostProcessor" testname="Return Admin to Pool" enabled="true"> + <boolProp name="resetInterpreter">false</boolProp> + <stringProp name="parameters"/> + <stringProp name="filename"/> + <stringProp name="script"> + adminUsersDistribution = Integer.parseInt(vars.get("admin_users_distribution_per_admin_pool")); + if (adminUsersDistribution == 1) { + adminUserList = props.get("adminUserList"); + adminUserList.add(vars.get("admin_user")); + } + </stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/return_admin_email_to_pool.jmx</stringProp></BeanShellPostProcessor> + <hashTree/> + </hashTree> + </hashTree> + + + <ThroughputController guiclass="ThroughputControllerGui" testclass="ThroughputController" testname="[C] Admin Promotion Rules" enabled="true"> + <intProp name="ThroughputController.style">1</intProp> + <boolProp name="ThroughputController.perThread">false</boolProp> + <intProp name="ThroughputController.maxThroughput">1</intProp> + <stringProp name="ThroughputController.percentThroughput">${cAdminPromotionRulesPercentage}</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/_system/scenario_controller_tmpl.jmx</stringProp></ThroughputController> + <hashTree> + <JSR223PreProcessor guiclass="TestBeanGUI" testclass="JSR223PreProcessor" testname="Set Test Label" enabled="true"> + <stringProp name="script"> +var testLabel = "${testLabel}" ? " (${testLabel})" : ""; +if (testLabel + && sampler.getClass().getName() == 'org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy' +) { + if (sampler.getName().indexOf(testLabel) == -1) { + sampler.setName(sampler.getName() + testLabel); + } +} else if (sampler.getName().indexOf("SetUp - ") == -1) { + sampler.setName("SetUp - " + sampler.getName()); +} + </stringProp> + <stringProp name="scriptLanguage">javascript</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/_system/setup_label.jmx</stringProp></JSR223PreProcessor> + <hashTree/> + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Set Label" enabled="true"> + <stringProp name="BeanShellSampler.query"> + vars.put("testLabel", "[C] Admin Promotion Rules"); + </stringProp> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + </BeanShellSampler> + <hashTree/> + + <JSR223PostProcessor guiclass="TestBeanGUI" testclass="JSR223PostProcessor" testname="Get admin form key PostProcessor" enabled="true"> + <stringProp name="script"> + function getFormKeyFromResponse() + { + var url = prev.getUrlAsString(), + responseCode = prev.getResponseCode(), + formKey = null; + searchPattern = /var FORM_KEY = '(.+)'/; + if (responseCode == "200" && url) { + response = prev.getResponseDataAsString(); + formKey = response && response.match(searchPattern) ? response.match(searchPattern)[1] : null; + } + return formKey; + } + + formKey = vars.get("form_key_storage"); + + currentFormKey = getFormKeyFromResponse(); + + if (currentFormKey != null && currentFormKey != formKey) { + vars.put("form_key_storage", currentFormKey); + } + </stringProp> + <stringProp name="scriptLanguage">javascript</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin/handle_admin_form_key.jmx</stringProp></JSR223PostProcessor> + <hashTree/> + <JSR223PreProcessor guiclass="TestBeanGUI" testclass="JSR223PreProcessor" testname="Set admin form key PreProcessor" enabled="true"> + <stringProp name="script"> + formKey = vars.get("form_key_storage"); + if (formKey + && sampler.getClass().getName() == 'org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy' + && sampler.getMethod() == "POST") + { + arguments = sampler.getArguments(); + for (i=0; i<arguments.getArgumentCount(); i++) + { + argument = arguments.getArgument(i); + if (argument.getName() == 'form_key' && argument.getValue() != formKey) { + log.info("admin form key updated: " + argument.getValue() + " => " + formKey); + argument.setValue(formKey); + } + } + } + </stringProp> + <stringProp name="scriptLanguage">javascript</stringProp> + </JSR223PreProcessor> + <hashTree/> + + <CookieManager guiclass="CookiePanel" testclass="CookieManager" testname="HTTP Cookie Manager" enabled="true"> + <collectionProp name="CookieManager.cookies"/> + <boolProp name="CookieManager.clearEachIteration">false</boolProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/http_cookie_manager_without_clear_each_iteration.jmx</stringProp></CookieManager> + <hashTree/> + + <GenericController guiclass="LogicControllerGui" testclass="GenericController" testname="Admin Login" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/simple_controller.jmx</stringProp> +</GenericController> + <hashTree> + <CriticalSectionController guiclass="CriticalSectionControllerGui" testclass="CriticalSectionController" testname="Admin Login Lock" enabled="true"> + <stringProp name="CriticalSectionController.lockName">get-admin-email</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/lock_controller.jmx</stringProp></CriticalSectionController> + <hashTree> + + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Get Admin Email" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/get_admin_email.jmx</stringProp> + <stringProp name="BeanShellSampler.query"> +adminUserList = props.get("adminUserList"); +adminUserListIterator = props.get("adminUserListIterator"); +adminUsersDistribution = Integer.parseInt(vars.get("admin_users_distribution_per_admin_pool")); + +if (adminUsersDistribution == 1) { + adminUser = adminUserList.poll(); +} else { + if (!adminUserListIterator.hasNext()) { + adminUserListIterator = adminUserList.descendingIterator(); + } + + adminUser = adminUserListIterator.next(); +} + +if (adminUser == null) { + SampleResult.setResponseMessage("adminUser list is empty"); + SampleResult.setResponseData("adminUser list is empty","UTF-8"); + IsSuccess=false; + SampleResult.setSuccessful(false); + SampleResult.setStopThread(true); +} +vars.put("admin_user", adminUser); + </stringProp> + <stringProp name="BeanShellSampler.filename"/> + <stringProp name="BeanShellSampler.parameters"/> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + </BeanShellSampler> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="SetUp - Login" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_login/admin_login.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert login form shown" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-1397214398">Welcome</stringProp> + <stringProp name="-515240035"><title>Magento Admin</title></stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract form key" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">admin_form_key</stringProp> + <stringProp name="RegexExtractor.regex"><input name="form_key" type="hidden" value="([^'"]+)" /></stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert form_key extracted" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="2845929">^.+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">admin_form_key</stringProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="SetUp - Login Submit Form" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="dummy" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">dummy</stringProp> + </elementProp> + <elementProp name="form_key" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_form_key}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">form_key</stringProp> + </elementProp> + <elementProp name="login[password]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_password}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">login[password]</stringProp> + </elementProp> + <elementProp name="login[username]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_user}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">login[username]</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/dashboard/</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <stringProp name="HTTPSampler.implementation">Java</stringProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_login/admin_login_submit_form.jmx</stringProp> + </HTTPSamplerProxy> + <hashTree> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract form key" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">admin_form_key</stringProp> + <stringProp name="RegexExtractor.regex"><input name="form_key" type="hidden" value="([^'"]+)" /></stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_login/admin_retrieve_form_key.jmx</stringProp></RegexExtractor> + <hashTree/> + </hashTree> + </hashTree> + + <GenericController guiclass="LogicControllerGui" testclass="GenericController" testname="Simple Controller" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/simple_controller.jmx</stringProp> +</GenericController> + <hashTree> + <TestFragmentController guiclass="TestFragmentControllerGui" testclass="TestFragmentController" testname="Admin Promotions Management" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_promotions_management/admin_promotions_management.jmx</stringProp> +</TestFragmentController> + <hashTree> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Landing Page" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/sales_rule/promo_quote/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + </HTTPSamplerProxy> + <hashTree/> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Create New" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/sales_rule/promo_quote/new</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + </HTTPSamplerProxy> + <hashTree/> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Create New Conditional" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="isAjax" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">true</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">isAjax</stringProp> + </elementProp> + <elementProp name="form_key" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_form_key}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">form_key</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="id" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1--1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">id</stringProp> + </elementProp> + <elementProp name="type" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">Magento\SalesRule\Model\Rule\Condition\Address|base_subtotal</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">type</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/sales_rule/promo_quote/newConditionHtml/form/sales_rule_formrule_conditions_fieldset_/form_namespace/sales_rule_form</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + </HTTPSamplerProxy> + <hashTree/> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Save" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="name" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">Rule Name ${__time(YMDHMS)}-${__threadNum}-${__Random(1,1000000)}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">name</stringProp> + </elementProp> + <elementProp name="is_active" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">is_active</stringProp> + </elementProp> + <elementProp name="use_auto_generation" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">use_auto_generation</stringProp> + </elementProp> + <elementProp name="is_rss" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">is_rss</stringProp> + </elementProp> + <elementProp name="apply_to_shipping" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">apply_to_shipping</stringProp> + </elementProp> + <elementProp name="stop_rules_processing" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">stop_rules_processing</stringProp> + </elementProp> + <elementProp name="coupon_code" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">coupon_code</stringProp> + </elementProp> + <elementProp name="uses_per_coupon" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">uses_per_coupon</stringProp> + </elementProp> + <elementProp name="uses_per_customer" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">uses_per_customer</stringProp> + </elementProp> + <elementProp name="sort_order" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">sort_order</stringProp> + </elementProp> + <elementProp name="discount_amount" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">5</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">discount_amount</stringProp> + </elementProp> + <elementProp name="discount_qty" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">discount_qty</stringProp> + </elementProp> + <elementProp name="discount_step" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">discount_step</stringProp> + </elementProp> + <elementProp name="reward_points_delta" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">reward_points_delta</stringProp> + </elementProp> + <elementProp name="store_labels[0]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">store_labels[0]</stringProp> + </elementProp> + <elementProp name="description" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">Rule Description ${__time(YMDHMS)}-${__threadNum}-${__Random(1,1000000)}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">description</stringProp> + </elementProp> + <elementProp name="coupon_type" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">coupon_type</stringProp> + </elementProp> + <elementProp name="simple_action" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">cart_fixed</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">simple_action</stringProp> + </elementProp> + <elementProp name="website_ids[0]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">website_ids[0]</stringProp> + </elementProp> + <elementProp name="customer_group_ids[0]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">0</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">customer_group_ids[0]</stringProp> + </elementProp> + <elementProp name="from_date" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">from_date</stringProp> + </elementProp> + <elementProp name="to_date" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">to_date</stringProp> + </elementProp> + <elementProp name="rule[conditions][1][type]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">Magento\SalesRule\Model\Rule\Condition\Combine</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">rule[conditions][1][type]</stringProp> + </elementProp> + <elementProp name="rule[conditions][1][aggregator]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">all</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">rule[conditions][1][aggregator]</stringProp> + </elementProp> + <elementProp name="rule[conditions][1][value]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">rule[conditions][1][value]</stringProp> + </elementProp> + <elementProp name="rule[conditions][1--1][type]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">Magento\SalesRule\Model\Rule\Condition\Address</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">rule[conditions][1--1][type]</stringProp> + </elementProp> + <elementProp name="rule[conditions][1--1][attribute]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">base_subtotal</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">rule[conditions][1--1][attribute]</stringProp> + </elementProp> + <elementProp name="rule[conditions][1--1][operator]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">>=</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">rule[conditions][1--1][operator]</stringProp> + </elementProp> + <elementProp name="rule[conditions][1--1][value]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">100</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">rule[conditions][1--1][value]</stringProp> + </elementProp> + <elementProp name="rule[conditions][1][new_chlid]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">rule[conditions][1][new_chlid]</stringProp> + </elementProp> + <elementProp name="rule[actions][1][type]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">Magento\SalesRule\Model\Rule\Condition\Product\Combine</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">rule[actions][1][type]</stringProp> + </elementProp> + <elementProp name="rule[actions][1][aggregator]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">all</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">rule[actions][1][aggregator]</stringProp> + </elementProp> + <elementProp name="rule[actions][1][value]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">rule[actions][1][value]</stringProp> + </elementProp> + <elementProp name="rule[actions][1][new_child]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">rule[actions][1][new_child]</stringProp> + </elementProp> + <elementProp name="store_labels[1]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">store_labels[1]</stringProp> + </elementProp> + <elementProp name="store_labels[2]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">store_labels[2]</stringProp> + </elementProp> + <elementProp name="related_banners" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">related_banners</stringProp> + </elementProp> + <elementProp name="form_key" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_form_key}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">form_key</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/sales_rule/promo_quote/save/</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + </HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-396438583">You saved the rule.</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">16</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + <TestAction guiclass="TestActionGui" testclass="TestAction" testname="Pause" enabled="true"> + <intProp name="ActionProcessor.action">1</intProp> + <intProp name="ActionProcessor.target">0</intProp> + <stringProp name="ActionProcessor.duration">${__javaScript(Math.round(${adminPromotionsManagementDelay}*1000))}</stringProp> + </TestAction> + <hashTree/> + </hashTree> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Logout" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/auth/logout/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/setup/admin_logout.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + + <BeanShellPostProcessor guiclass="TestBeanGUI" testclass="BeanShellPostProcessor" testname="Return Admin to Pool" enabled="true"> + <boolProp name="resetInterpreter">false</boolProp> + <stringProp name="parameters"/> + <stringProp name="filename"/> + <stringProp name="script"> + adminUsersDistribution = Integer.parseInt(vars.get("admin_users_distribution_per_admin_pool")); + if (adminUsersDistribution == 1) { + adminUserList = props.get("adminUserList"); + adminUserList.add(vars.get("admin_user")); + } + </stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/return_admin_email_to_pool.jmx</stringProp></BeanShellPostProcessor> + <hashTree/> + </hashTree> + </hashTree> + + + <ThroughputController guiclass="ThroughputControllerGui" testclass="ThroughputController" testname="[C] Admin Customer Management" enabled="true"> + <intProp name="ThroughputController.style">1</intProp> + <boolProp name="ThroughputController.perThread">false</boolProp> + <intProp name="ThroughputController.maxThroughput">1</intProp> + <stringProp name="ThroughputController.percentThroughput">${cAdminCustomerManagementPercentage}</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/_system/scenario_controller_tmpl.jmx</stringProp></ThroughputController> + <hashTree> + <JSR223PreProcessor guiclass="TestBeanGUI" testclass="JSR223PreProcessor" testname="Set Test Label" enabled="true"> + <stringProp name="script"> +var testLabel = "${testLabel}" ? " (${testLabel})" : ""; +if (testLabel + && sampler.getClass().getName() == 'org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy' +) { + if (sampler.getName().indexOf(testLabel) == -1) { + sampler.setName(sampler.getName() + testLabel); + } +} else if (sampler.getName().indexOf("SetUp - ") == -1) { + sampler.setName("SetUp - " + sampler.getName()); +} + </stringProp> + <stringProp name="scriptLanguage">javascript</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/_system/setup_label.jmx</stringProp></JSR223PreProcessor> + <hashTree/> + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Set Label" enabled="true"> + <stringProp name="BeanShellSampler.query"> + vars.put("testLabel", "[C] Admin Customer Management"); + </stringProp> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + </BeanShellSampler> + <hashTree/> + + <JSR223PostProcessor guiclass="TestBeanGUI" testclass="JSR223PostProcessor" testname="Get admin form key PostProcessor" enabled="true"> + <stringProp name="script"> + function getFormKeyFromResponse() + { + var url = prev.getUrlAsString(), + responseCode = prev.getResponseCode(), + formKey = null; + searchPattern = /var FORM_KEY = '(.+)'/; + if (responseCode == "200" && url) { + response = prev.getResponseDataAsString(); + formKey = response && response.match(searchPattern) ? response.match(searchPattern)[1] : null; + } + return formKey; + } + + formKey = vars.get("form_key_storage"); + + currentFormKey = getFormKeyFromResponse(); + + if (currentFormKey != null && currentFormKey != formKey) { + vars.put("form_key_storage", currentFormKey); + } + </stringProp> + <stringProp name="scriptLanguage">javascript</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin/handle_admin_form_key.jmx</stringProp></JSR223PostProcessor> + <hashTree/> + <JSR223PreProcessor guiclass="TestBeanGUI" testclass="JSR223PreProcessor" testname="Set admin form key PreProcessor" enabled="true"> + <stringProp name="script"> + formKey = vars.get("form_key_storage"); + if (formKey + && sampler.getClass().getName() == 'org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy' + && sampler.getMethod() == "POST") + { + arguments = sampler.getArguments(); + for (i=0; i<arguments.getArgumentCount(); i++) + { + argument = arguments.getArgument(i); + if (argument.getName() == 'form_key' && argument.getValue() != formKey) { + log.info("admin form key updated: " + argument.getValue() + " => " + formKey); + argument.setValue(formKey); + } + } + } + </stringProp> + <stringProp name="scriptLanguage">javascript</stringProp> + </JSR223PreProcessor> + <hashTree/> + + <CookieManager guiclass="CookiePanel" testclass="CookieManager" testname="HTTP Cookie Manager" enabled="true"> + <collectionProp name="CookieManager.cookies"/> + <boolProp name="CookieManager.clearEachIteration">false</boolProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/http_cookie_manager_without_clear_each_iteration.jmx</stringProp></CookieManager> + <hashTree/> + + <GenericController guiclass="LogicControllerGui" testclass="GenericController" testname="Admin Login" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/simple_controller.jmx</stringProp> +</GenericController> + <hashTree> + <CriticalSectionController guiclass="CriticalSectionControllerGui" testclass="CriticalSectionController" testname="Admin Login Lock" enabled="true"> + <stringProp name="CriticalSectionController.lockName">get-admin-email</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/lock_controller.jmx</stringProp></CriticalSectionController> + <hashTree> + + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Get Admin Email" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/get_admin_email.jmx</stringProp> + <stringProp name="BeanShellSampler.query"> +adminUserList = props.get("adminUserList"); +adminUserListIterator = props.get("adminUserListIterator"); +adminUsersDistribution = Integer.parseInt(vars.get("admin_users_distribution_per_admin_pool")); + +if (adminUsersDistribution == 1) { + adminUser = adminUserList.poll(); +} else { + if (!adminUserListIterator.hasNext()) { + adminUserListIterator = adminUserList.descendingIterator(); + } + + adminUser = adminUserListIterator.next(); +} + +if (adminUser == null) { + SampleResult.setResponseMessage("adminUser list is empty"); + SampleResult.setResponseData("adminUser list is empty","UTF-8"); + IsSuccess=false; + SampleResult.setSuccessful(false); + SampleResult.setStopThread(true); +} +vars.put("admin_user", adminUser); + </stringProp> + <stringProp name="BeanShellSampler.filename"/> + <stringProp name="BeanShellSampler.parameters"/> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + </BeanShellSampler> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="SetUp - Login" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_login/admin_login.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert login form shown" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-1397214398">Welcome</stringProp> + <stringProp name="-515240035"><title>Magento Admin</title></stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract form key" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">admin_form_key</stringProp> + <stringProp name="RegexExtractor.regex"><input name="form_key" type="hidden" value="([^'"]+)" /></stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert form_key extracted" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="2845929">^.+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">admin_form_key</stringProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="SetUp - Login Submit Form" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="dummy" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">dummy</stringProp> + </elementProp> + <elementProp name="form_key" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_form_key}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">form_key</stringProp> + </elementProp> + <elementProp name="login[password]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_password}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">login[password]</stringProp> + </elementProp> + <elementProp name="login[username]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_user}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">login[username]</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/dashboard/</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <stringProp name="HTTPSampler.implementation">Java</stringProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_login/admin_login_submit_form.jmx</stringProp> + </HTTPSamplerProxy> + <hashTree> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract form key" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">admin_form_key</stringProp> + <stringProp name="RegexExtractor.regex"><input name="form_key" type="hidden" value="([^'"]+)" /></stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_login/admin_retrieve_form_key.jmx</stringProp></RegexExtractor> + <hashTree/> + </hashTree> + </hashTree> + + <GenericController guiclass="LogicControllerGui" testclass="GenericController" testname="Simple Controller" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/simple_controller.jmx</stringProp> +</GenericController> + <hashTree> + <TestFragmentController guiclass="TestFragmentControllerGui" testclass="TestFragmentController" testname="Admin Customer Management" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_customer_management/admin_customer_management.jmx</stringProp> +</TestFragmentController> + <hashTree> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Landing Page" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/customer/index</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + </HTTPSamplerProxy> + <hashTree> + <HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Header Manager" enabled="true"> + <collectionProp name="HeaderManager.headers"> + <elementProp name="Accept-Language" elementType="Header"> + <stringProp name="Header.name">Accept-Language</stringProp> + <stringProp name="Header.value">en-US,en;q=0.5</stringProp> + </elementProp> + <elementProp name="Accept" elementType="Header"> + <stringProp name="Header.name">Accept</stringProp> + <stringProp name="Header.value">text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8</stringProp> + </elementProp> + <elementProp name="User-Agent" elementType="Header"> + <stringProp name="Header.name">User-Agent</stringProp> + <stringProp name="Header.value">Mozilla/5.0 (Windows NT 6.1; WOW64; rv:27.0) Gecko/20100101 Firefox/27.0</stringProp> + </elementProp> + <elementProp name="Accept-Encoding" elementType="Header"> + <stringProp name="Header.name">Accept-Encoding</stringProp> + <stringProp name="Header.value">gzip, deflate</stringProp> + </elementProp> + </collectionProp> + </HeaderManager> + <hashTree/> + </hashTree> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Render" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="namespace" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">customer_listing</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">namespace</stringProp> + </elementProp> + <elementProp name="search" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">search</stringProp> + </elementProp> + <elementProp name="filters[placeholder]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">true</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">filters[placeholder]</stringProp> + </elementProp> + <elementProp name="paging[pageSize]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">20</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">paging[pageSize]</stringProp> + </elementProp> + <elementProp name="paging[current]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">paging[current]</stringProp> + </elementProp> + <elementProp name="sorting[field]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">entity_id</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">sorting[field]</stringProp> + </elementProp> + <elementProp name="sorting[direction]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">asc</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">sorting[direction]</stringProp> + </elementProp> + <elementProp name="isAjax" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">true</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">isAjax</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/mui/index/render/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + </HTTPSamplerProxy> + <hashTree> + <HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Header Manager" enabled="true"> + <collectionProp name="HeaderManager.headers"> + <elementProp name="" elementType="Header"> + <stringProp name="Header.name">X-Requested-With</stringProp> + <stringProp name="Header.value">XMLHttpRequest</stringProp> + </elementProp> + </collectionProp> + </HeaderManager> + <hashTree/> + </hashTree> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Search Render" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="namespace" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">customer_listing</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">namespace</stringProp> + </elementProp> + <elementProp name="search" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">Lastname</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">search</stringProp> + </elementProp> + <elementProp name="filters[placeholder]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">true</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">filters[placeholder]</stringProp> + </elementProp> + <elementProp name="paging[pageSize]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">20</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">paging[pageSize]</stringProp> + </elementProp> + <elementProp name="paging[current]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">paging[current]</stringProp> + </elementProp> + <elementProp name="sorting[field]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">entity_id</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">sorting[field]</stringProp> + </elementProp> + <elementProp name="sorting[direction]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">asc</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">sorting[direction]</stringProp> + </elementProp> + <elementProp name="isAjax" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">true</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">isAjax</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/mui/index/render/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + </HTTPSamplerProxy> + <hashTree> + <HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Header Manager" enabled="true"> + <collectionProp name="HeaderManager.headers"> + <elementProp name="" elementType="Header"> + <stringProp name="Header.name">X-Requested-With</stringProp> + <stringProp name="Header.value">XMLHttpRequest</stringProp> + </elementProp> + </collectionProp> + </HeaderManager> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract customer edit url" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">customer_edit_url_path</stringProp> + <stringProp name="RegexExtractor.regex">actions":\{"edit":\{"href":"(?:http|https):\\/\\/(.*?)\\/customer\\/index\\/edit\\/id\\/(\d+)\\/",</stringProp> + <stringProp name="RegexExtractor.template">/customer/index/edit/id/$2$/</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert customer edit url" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="2845929">^.+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">customer_edit_url_path</stringProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Edit Customer" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}${customer_edit_url_path}</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + </HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert edit customer page" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-1422614550">Customer Information</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract customer entity_id" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">admin_customer_entity_id</stringProp> + <stringProp name="RegexExtractor.regex">"entity_id":"(\d+)"</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract website_id" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">admin_customer_website_id</stringProp> + <stringProp name="RegexExtractor.regex">"website_id":"(\d+)"</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract customer firstname" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">admin_customer_firstname</stringProp> + <stringProp name="RegexExtractor.regex">"firstname":"([^"]+)"</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract customer lastname" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">admin_customer_lastname</stringProp> + <stringProp name="RegexExtractor.regex">"lastname":"([^"]+)"</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract customer email" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">admin_customer_email</stringProp> + <stringProp name="RegexExtractor.regex">"email":"([^\@]+@[^.]+.[^"]+)"</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract group_id" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">admin_customer_group_id</stringProp> + <stringProp name="RegexExtractor.regex">"group_id":"(\d+)"</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract store_id" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">admin_customer_store_id</stringProp> + <stringProp name="RegexExtractor.regex">"store_id":"(\d+)"</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extact created_at" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">admin_customer_created_at</stringProp> + <stringProp name="RegexExtractor.regex">"created_at":"([^"]+)"</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract updated_at" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">admin_customer_updated_at</stringProp> + <stringProp name="RegexExtractor.regex">"updated_at":"([^"]+)"</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract is_active" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">admin_customer_is_active</stringProp> + <stringProp name="RegexExtractor.regex">"is_active":"(\d+)"</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract disable_auto_group_change" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">admin_customer_disable_auto_group_change</stringProp> + <stringProp name="RegexExtractor.regex">"disable_auto_group_change":"(\d+)"</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract created_in" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">admin_customer_created_in</stringProp> + <stringProp name="RegexExtractor.regex">"created_in":"([^"]+)"</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract dob" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">admin_customer_dob</stringProp> + <stringProp name="RegexExtractor.regex">"dob":"(\d+)-(\d+)-(\d+)"</stringProp> + <stringProp name="RegexExtractor.template">$2$/$3$/$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract default_billing" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">admin_customer_default_billing</stringProp> + <stringProp name="RegexExtractor.regex">"default_billing":"(\d+)"</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract default_shipping" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">admin_customer_default_shipping</stringProp> + <stringProp name="RegexExtractor.regex">"default_shipping":"(\d+)"</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract gender" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">admin_customer_gender</stringProp> + <stringProp name="RegexExtractor.regex">"gender":"(\d+)"</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract failures_num" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">admin_customer_failures_num</stringProp> + <stringProp name="RegexExtractor.regex">"failures_num":"(\d+)"</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract address_entity_id" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">admin_customer_address_entity_id</stringProp> + <stringProp name="RegexExtractor.regex">_address":\{"entity_id":"(\d+)".+?"parent_id":"${admin_customer_entity_id}"</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract address_created_at" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">admin_customer_address_created_at</stringProp> + <stringProp name="RegexExtractor.regex">_address":\{.+?"parent_id":"${admin_customer_entity_id}".+?"created_at":"([^"]+)"</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract address_updated_at" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">admin_customer_address_updated_at</stringProp> + <stringProp name="RegexExtractor.regex">_address":\{.+?"parent_id":"${admin_customer_entity_id}".+?"updated_at":"([^"]+)"</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract address_is_active" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">admin_customer_address_is_active</stringProp> + <stringProp name="RegexExtractor.regex">_address":\{.+?"parent_id":"${admin_customer_entity_id}".+?"is_active":"(\d+)"</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract address_city" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">admin_customer_address_city</stringProp> + <stringProp name="RegexExtractor.regex">_address":\{.+?"parent_id":"${admin_customer_entity_id}".+?"city":"([^"]+)"</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract address_country_id" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">admin_customer_address_country_id</stringProp> + <stringProp name="RegexExtractor.regex">_address":\{.+?"parent_id":"${admin_customer_entity_id}".+?"country_id":"([^"]+)"</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract address_firstname" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">admin_customer_address_firstname</stringProp> + <stringProp name="RegexExtractor.regex">_address":\{.+?"parent_id":"${admin_customer_entity_id}".+?"firstname":"([^"]+)"</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract address_lastname" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">admin_customer_address_lastname</stringProp> + <stringProp name="RegexExtractor.regex">_address":\{.+?"parent_id":"${admin_customer_entity_id}".+?"lastname":"([^"]+)"</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract address_postcode" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">admin_customer_address_postcode</stringProp> + <stringProp name="RegexExtractor.regex">_address":\{.+?"parent_id":"${admin_customer_entity_id}".+?"postcode":"([^"]+)"</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract address_region" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">admin_customer_address_region</stringProp> + <stringProp name="RegexExtractor.regex">_address":\{.+?"parent_id":"${admin_customer_entity_id}".+?"region":"([^"]+)"</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract address_region_id" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">admin_customer_address_region_id</stringProp> + <stringProp name="RegexExtractor.regex">_address":\{.+?"parent_id":"${admin_customer_entity_id}".+?"region_id":"([^"]+)"</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract address street" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">admin_customer_address_street</stringProp> + <stringProp name="RegexExtractor.regex">_address":\{.+?"parent_id":"${admin_customer_entity_id}".+?"street":\["([^"]+)"\]</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract address_telephone" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">admin_customer_address_telephone</stringProp> + <stringProp name="RegexExtractor.regex">_address":\{.+?"parent_id":"${admin_customer_entity_id}".+?"telephone":"([^"]+)"</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract address_customer_id" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">admin_customer_address_customer_id</stringProp> + <stringProp name="RegexExtractor.regex">_address":\{.+?"parent_id":"${admin_customer_entity_id}".+?"customer_id":"([^"]+)"</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert customer entity_id" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="89649215">^\d+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">admin_customer_entity_id</stringProp> + </ResponseAssertion> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert website_id" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="89649215">^\d+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">admin_customer_website_id</stringProp> + </ResponseAssertion> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert customer firstname" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="2845929">^.+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">admin_customer_firstname</stringProp> + </ResponseAssertion> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert customer lastname" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="2845929">^.+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">admin_customer_lastname</stringProp> + </ResponseAssertion> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert customer email" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="2845929">^.+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">admin_customer_email</stringProp> + </ResponseAssertion> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert customer group_id" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="89649215">^\d+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">admin_customer_group_id</stringProp> + </ResponseAssertion> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert customer store_id" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="89649215">^\d+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">admin_customer_store_id</stringProp> + </ResponseAssertion> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert customer created_at" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="2845929">^.+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">admin_customer_created_at</stringProp> + </ResponseAssertion> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert customer updated_at" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="2845929">^.+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">admin_customer_updated_at</stringProp> + </ResponseAssertion> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert customer is_active" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="89649215">^\d+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">admin_customer_is_active</stringProp> + </ResponseAssertion> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert customer disable_auto_group_change" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="89649215">^\d+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">admin_customer_disable_auto_group_change</stringProp> + </ResponseAssertion> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert customer created_in" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="2845929">^.+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">admin_customer_created_in</stringProp> + </ResponseAssertion> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert customer dob" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="221072919">^\d+/\d+/\d+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">admin_customer_dob</stringProp> + </ResponseAssertion> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert customer default_billing" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="89649215">^\d+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">admin_customer_default_billing</stringProp> + </ResponseAssertion> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert customer default_shipping" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="89649215">^\d+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">admin_customer_default_shipping</stringProp> + </ResponseAssertion> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert customer gender" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="89649215">^\d+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">admin_customer_gender</stringProp> + </ResponseAssertion> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert customer failures_num" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="89649215">^\d+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">admin_customer_failures_num</stringProp> + </ResponseAssertion> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert address_entity_id" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="89649215">^\d+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">admin_customer_address_entity_id</stringProp> + </ResponseAssertion> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert address_created_at" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="2845929">^.+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">admin_customer_address_created_at</stringProp> + </ResponseAssertion> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert address_updated_at" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="2845929">^.+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">admin_customer_address_updated_at</stringProp> + </ResponseAssertion> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert address_is_active" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="89649215">^\d+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">admin_customer_address_is_active</stringProp> + </ResponseAssertion> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert address_city" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="2845929">^.+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">admin_customer_address_city</stringProp> + </ResponseAssertion> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert address_country_id" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="2845929">^.+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">admin_customer_address_country_id</stringProp> + </ResponseAssertion> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert address_firstname" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="2845929">^.+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">admin_customer_address_firstname</stringProp> + </ResponseAssertion> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert address_lastname" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="2845929">^.+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">admin_customer_address_lastname</stringProp> + </ResponseAssertion> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert address_postcode" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="2845929">^.+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">admin_customer_address_postcode</stringProp> + </ResponseAssertion> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert address_region" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="2845929">^.+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">admin_customer_address_region</stringProp> + </ResponseAssertion> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert address_region_id" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="89649215">^\d+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">admin_customer_address_region_id</stringProp> + </ResponseAssertion> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert address_street" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="2845929">^.+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">admin_customer_address_street</stringProp> + </ResponseAssertion> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert address_telephone" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="2845929">^.+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">admin_customer_address_telephone</stringProp> + </ResponseAssertion> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert address_customer_id" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="89649215">^\d+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">admin_customer_address_customer_id</stringProp> + </ResponseAssertion> + <hashTree/> + <HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Header Manager" enabled="true"> + <collectionProp name="HeaderManager.headers"> + <elementProp name="Accept-Language" elementType="Header"> + <stringProp name="Header.name">Accept-Language</stringProp> + <stringProp name="Header.value">en-US,en;q=0.5</stringProp> + </elementProp> + <elementProp name="Accept" elementType="Header"> + <stringProp name="Header.name">Accept</stringProp> + <stringProp name="Header.value">text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8</stringProp> + </elementProp> + <elementProp name="User-Agent" elementType="Header"> + <stringProp name="Header.name">User-Agent</stringProp> + <stringProp name="Header.value">Mozilla/5.0 (Windows NT 6.1; WOW64; rv:27.0) Gecko/20100101 Firefox/27.0</stringProp> + </elementProp> + <elementProp name="Accept-Encoding" elementType="Header"> + <stringProp name="Header.name">Accept-Encoding</stringProp> + <stringProp name="Header.value">gzip, deflate</stringProp> + </elementProp> + </collectionProp> + </HeaderManager> + <hashTree/> + </hashTree> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Customer Validate" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="isAjax " elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">true</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">isAjax </stringProp> + </elementProp> + <elementProp name="customer[entity_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_customer_entity_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">customer[entity_id]</stringProp> + </elementProp> + <elementProp name="customer[website_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_customer_website_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">customer[website_id]</stringProp> + </elementProp> + <elementProp name="customer[email]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_customer_email}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">customer[email]</stringProp> + </elementProp> + <elementProp name="customer[group_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_customer_group_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">customer[group_id]</stringProp> + </elementProp> + <elementProp name="customer[store_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_customer_store_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">customer[store_id]</stringProp> + </elementProp> + <elementProp name="customer[created_at]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_customer_created_at}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">customer[created_at]</stringProp> + </elementProp> + <elementProp name="customer[updated_at]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_customer_updated_at}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">customer[updated_at]</stringProp> + </elementProp> + <elementProp name="customer[is_active]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_customer_is_active}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">customer[is_active]</stringProp> + </elementProp> + <elementProp name="customer[disable_auto_group_change]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_customer_disable_auto_group_change}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">customer[disable_auto_group_change]</stringProp> + </elementProp> + <elementProp name="customer[created_in]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_customer_created_in}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">customer[created_in]</stringProp> + </elementProp> + <elementProp name="customer[prefix]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">customer[prefix]</stringProp> + </elementProp> + <elementProp name="customer[firstname]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_customer_firstname} 1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">customer[firstname]</stringProp> + </elementProp> + <elementProp name="customer[middlename]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">customer[middlename]</stringProp> + </elementProp> + <elementProp name="customer[lastname]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_customer_lastname} 1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">customer[lastname]</stringProp> + </elementProp> + <elementProp name="customer[suffix]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">customer[suffix]</stringProp> + </elementProp> + <elementProp name="customer[dob]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_customer_dob}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">customer[dob]</stringProp> + </elementProp> + <elementProp name="customer[default_billing]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_customer_default_billing}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">customer[default_billing]</stringProp> + </elementProp> + <elementProp name="customer[default_shipping]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_customer_default_shipping}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">customer[default_shipping]</stringProp> + </elementProp> + <elementProp name="customer[taxvat]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">customer[taxvat]</stringProp> + </elementProp> + <elementProp name="customer[gender]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_customer_gender}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">customer[gender]</stringProp> + </elementProp> + <elementProp name="customer[failures_num]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_customer_failures_num}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">customer[failures_num]</stringProp> + </elementProp> + <elementProp name="customer[sendemail_store_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_customer_store_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">customer[sendemail_store_id]</stringProp> + </elementProp> + <elementProp name="address[${admin_customer_address_entity_id}][entity_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_customer_address_entity_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[${admin_customer_address_entity_id}][entity_id]</stringProp> + </elementProp> + <elementProp name="address[${admin_customer_address_entity_id}][created_at]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_customer_address_created_at}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[${admin_customer_address_entity_id}][created_at]</stringProp> + </elementProp> + <elementProp name="address[${admin_customer_address_entity_id}][updated_at]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_customer_address_updated_at}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[${admin_customer_address_entity_id}][updated_at]</stringProp> + </elementProp> + <elementProp name="address[${admin_customer_address_entity_id}][is_active]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_customer_address_is_active}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[${admin_customer_address_entity_id}][is_active]</stringProp> + </elementProp> + <elementProp name="address[${admin_customer_address_entity_id}][city]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_customer_address_city}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[${admin_customer_address_entity_id}][city]</stringProp> + </elementProp> + <elementProp name="address[${admin_customer_address_entity_id}][company]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[${admin_customer_address_entity_id}][company]</stringProp> + </elementProp> + <elementProp name="address[${admin_customer_address_entity_id}][country_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_customer_address_country_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[${admin_customer_address_entity_id}][country_id]</stringProp> + </elementProp> + <elementProp name="address[${admin_customer_address_entity_id}][firstname]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_customer_address_firstname}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[${admin_customer_address_entity_id}][firstname]</stringProp> + </elementProp> + <elementProp name="address[${admin_customer_address_entity_id}][lastname]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_customer_address_lastname}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[${admin_customer_address_entity_id}][lastname]</stringProp> + </elementProp> + <elementProp name="address[${admin_customer_address_entity_id}][middlename]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[${admin_customer_address_entity_id}][middlename]</stringProp> + </elementProp> + <elementProp name="address[${admin_customer_address_entity_id}][postcode]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_customer_address_postcode}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[${admin_customer_address_entity_id}][postcode]</stringProp> + </elementProp> + <elementProp name="address[${admin_customer_address_entity_id}][prefix]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[${admin_customer_address_entity_id}][prefix]</stringProp> + </elementProp> + <elementProp name="address[${admin_customer_address_entity_id}][region]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_customer_address_region}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[${admin_customer_address_entity_id}][region]</stringProp> + </elementProp> + <elementProp name="address[${admin_customer_address_entity_id}][region_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_customer_address_region_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[${admin_customer_address_entity_id}][region_id]</stringProp> + </elementProp> + <elementProp name="address[${admin_customer_address_entity_id}][street][0]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_customer_address_street}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[${admin_customer_address_entity_id}][street][0]</stringProp> + </elementProp> + <elementProp name="address[${admin_customer_address_entity_id}][street][1]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[${admin_customer_address_entity_id}][street][1]</stringProp> + </elementProp> + <elementProp name="address[${admin_customer_address_entity_id}][suffix]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[${admin_customer_address_entity_id}][suffix]</stringProp> + </elementProp> + <elementProp name="address[${admin_customer_address_entity_id}][telephone]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_customer_address_telephone}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[${admin_customer_address_entity_id}][telephone]</stringProp> + </elementProp> + <elementProp name="address[${admin_customer_address_entity_id}][vat_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[${admin_customer_address_entity_id}][vat_id]</stringProp> + </elementProp> + <elementProp name="address[${admin_customer_address_entity_id}][customer_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_customer_address_customer_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[${admin_customer_address_entity_id}][customer_id]</stringProp> + </elementProp> + <elementProp name="address[${admin_customer_address_entity_id}][default_billing]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">true</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[${admin_customer_address_entity_id}][default_billing]</stringProp> + </elementProp> + <elementProp name="address[${admin_customer_address_entity_id}][default_shipping]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">true</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[${admin_customer_address_entity_id}][default_shipping]</stringProp> + </elementProp> + <elementProp name="address[new_0][prefix]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[new_0][prefix]</stringProp> + </elementProp> + <elementProp name="address[new_0][firstname]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">John</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[new_0][firstname]</stringProp> + </elementProp> + <elementProp name="address[new_0][middlename]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[new_0][middlename]</stringProp> + </elementProp> + <elementProp name="address[new_0][lastname]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">Doe</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[new_0][lastname]</stringProp> + </elementProp> + <elementProp name="address[new_0][suffix]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[new_0][suffix]</stringProp> + </elementProp> + <elementProp name="address[new_0][company]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">Test Company</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[new_0][company]</stringProp> + </elementProp> + <elementProp name="address[new_0][city]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">Folsom</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[new_0][city]</stringProp> + </elementProp> + <elementProp name="address[new_0][postcode]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">95630</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[new_0][postcode]</stringProp> + </elementProp> + <elementProp name="address[new_0][telephone]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1234567890</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[new_0][telephone]</stringProp> + </elementProp> + <elementProp name="address[new_0][vat_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[new_0][vat_id]</stringProp> + </elementProp> + <elementProp name="address[new_0][default_billing]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">false</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[new_0][default_billing]</stringProp> + </elementProp> + <elementProp name="address[new_0][default_shipping]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">false</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[new_0][default_shipping]</stringProp> + </elementProp> + <elementProp name="address[new_0][street][0]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">123 Main</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[new_0][street][0]</stringProp> + </elementProp> + <elementProp name="address[new_0][street][1]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[new_0][street][1]</stringProp> + </elementProp> + <elementProp name="address[new_0][region]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[new_0][region]</stringProp> + </elementProp> + <elementProp name="address[new_0][country_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">US</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[new_0][country_id]</stringProp> + </elementProp> + <elementProp name="address[new_0][region_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_customer_address_region_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[new_0][region_id]</stringProp> + </elementProp> + <elementProp name="form_key" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_form_key}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">form_key</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/customer/index/validate/</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + </HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="49586">200</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_code</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">16</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Customer Save" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="isAjax " elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">true</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">isAjax </stringProp> + </elementProp> + <elementProp name="customer[entity_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_customer_entity_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">customer[entity_id]</stringProp> + </elementProp> + <elementProp name="customer[website_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_customer_website_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">customer[website_id]</stringProp> + </elementProp> + <elementProp name="customer[email]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_customer_email}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">customer[email]</stringProp> + </elementProp> + <elementProp name="customer[group_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_customer_group_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">customer[group_id]</stringProp> + </elementProp> + <elementProp name="customer[store_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_customer_store_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">customer[store_id]</stringProp> + </elementProp> + <elementProp name="customer[created_at]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_customer_created_at}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">customer[created_at]</stringProp> + </elementProp> + <elementProp name="customer[updated_at]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_customer_updated_at}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">customer[updated_at]</stringProp> + </elementProp> + <elementProp name="customer[is_active]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_customer_is_active}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">customer[is_active]</stringProp> + </elementProp> + <elementProp name="customer[disable_auto_group_change]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_customer_disable_auto_group_change}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">customer[disable_auto_group_change]</stringProp> + </elementProp> + <elementProp name="customer[created_in]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_customer_created_in}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">customer[created_in]</stringProp> + </elementProp> + <elementProp name="customer[prefix]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">customer[prefix]</stringProp> + </elementProp> + <elementProp name="customer[firstname]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_customer_firstname} 1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">customer[firstname]</stringProp> + </elementProp> + <elementProp name="customer[middlename]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">customer[middlename]</stringProp> + </elementProp> + <elementProp name="customer[lastname]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_customer_lastname} 1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">customer[lastname]</stringProp> + </elementProp> + <elementProp name="customer[suffix]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">customer[suffix]</stringProp> + </elementProp> + <elementProp name="customer[dob]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_customer_dob}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">customer[dob]</stringProp> + </elementProp> + <elementProp name="customer[default_billing]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_customer_default_billing}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">customer[default_billing]</stringProp> + </elementProp> + <elementProp name="customer[default_shipping]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_customer_default_shipping}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">customer[default_shipping]</stringProp> + </elementProp> + <elementProp name="customer[taxvat]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">customer[taxvat]</stringProp> + </elementProp> + <elementProp name="customer[gender]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_customer_gender}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">customer[gender]</stringProp> + </elementProp> + <elementProp name="customer[failures_num]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_customer_failures_num}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">customer[failures_num]</stringProp> + </elementProp> + <elementProp name="customer[sendemail_store_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_customer_store_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">customer[sendemail_store_id]</stringProp> + </elementProp> + <elementProp name="address[${admin_customer_address_entity_id}][entity_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_customer_address_entity_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[${admin_customer_address_entity_id}][entity_id]</stringProp> + </elementProp> + + <elementProp name="address[${admin_customer_address_entity_id}][created_at]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_customer_address_created_at}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[${admin_customer_address_entity_id}][created_at]</stringProp> + </elementProp> + <elementProp name="address[${admin_customer_address_entity_id}][updated_at]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_customer_address_updated_at}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[${admin_customer_address_entity_id}][updated_at]</stringProp> + </elementProp> + <elementProp name="address[${admin_customer_address_entity_id}][is_active]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_customer_address_is_active}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[${admin_customer_address_entity_id}][is_active]</stringProp> + </elementProp> + <elementProp name="address[${admin_customer_address_entity_id}][city]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_customer_address_city}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[${admin_customer_address_entity_id}][city]</stringProp> + </elementProp> + <elementProp name="address[${admin_customer_address_entity_id}][company]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[${admin_customer_address_entity_id}][company]</stringProp> + </elementProp> + <elementProp name="address[${admin_customer_address_entity_id}][country_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_customer_address_country_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[${admin_customer_address_entity_id}][country_id]</stringProp> + </elementProp> + <elementProp name="address[${admin_customer_address_entity_id}][firstname]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_customer_address_firstname}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[${admin_customer_address_entity_id}][firstname]</stringProp> + </elementProp> + <elementProp name="address[${admin_customer_address_entity_id}][lastname]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_customer_address_lastname}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[${admin_customer_address_entity_id}][lastname]</stringProp> + </elementProp> + <elementProp name="address[${admin_customer_address_entity_id}][middlename]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[${admin_customer_address_entity_id}][middlename]</stringProp> + </elementProp> + <elementProp name="address[${admin_customer_address_entity_id}][postcode]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_customer_address_postcode}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[${admin_customer_address_entity_id}][postcode]</stringProp> + </elementProp> + <elementProp name="address[${admin_customer_address_entity_id}][prefix]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[${admin_customer_address_entity_id}][prefix]</stringProp> + </elementProp> + <elementProp name="address[${admin_customer_address_entity_id}][region]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_customer_address_region}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[${admin_customer_address_entity_id}][region]</stringProp> + </elementProp> + <elementProp name="address[${admin_customer_address_entity_id}][region_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_customer_address_region_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[${admin_customer_address_entity_id}][region_id]</stringProp> + </elementProp> + <elementProp name="address[${admin_customer_address_entity_id}][street][0]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_customer_address_street}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[${admin_customer_address_entity_id}][street][0]</stringProp> + </elementProp> + <elementProp name="address[${admin_customer_address_entity_id}][street][1]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[${admin_customer_address_entity_id}][street][1]</stringProp> + </elementProp> + <elementProp name="address[${admin_customer_address_entity_id}][suffix]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[${admin_customer_address_entity_id}][suffix]</stringProp> + </elementProp> + <elementProp name="address[${admin_customer_address_entity_id}][telephone]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_customer_address_telephone}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[${admin_customer_address_entity_id}][telephone]</stringProp> + </elementProp> + <elementProp name="address[${admin_customer_address_entity_id}][vat_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[${admin_customer_address_entity_id}][vat_id]</stringProp> + </elementProp> + <elementProp name="address[${admin_customer_address_entity_id}][customer_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_customer_address_customer_id}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[${admin_customer_address_entity_id}][customer_id]</stringProp> + </elementProp> + <elementProp name="address[${admin_customer_address_entity_id}][default_billing]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">true</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[${admin_customer_address_entity_id}][default_billing]</stringProp> + </elementProp> + <elementProp name="address[${admin_customer_address_entity_id}][default_shipping]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">true</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[${admin_customer_address_entity_id}][default_shipping]</stringProp> + </elementProp> + <elementProp name="address[new_0][prefix]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[new_0][prefix]</stringProp> + </elementProp> + <elementProp name="address[new_0][firstname]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">John</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[new_0][firstname]</stringProp> + </elementProp> + <elementProp name="address[new_0][middlename]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[new_0][middlename]</stringProp> + </elementProp> + <elementProp name="address[new_0][lastname]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">Doe</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[new_0][lastname]</stringProp> + </elementProp> + <elementProp name="address[new_0][suffix]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[new_0][suffix]</stringProp> + </elementProp> + <elementProp name="address[new_0][company]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">Test Company</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[new_0][company]</stringProp> + </elementProp> + <elementProp name="address[new_0][city]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">Folsom</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[new_0][city]</stringProp> + </elementProp> + <elementProp name="address[new_0][postcode]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">95630</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[new_0][postcode]</stringProp> + </elementProp> + <elementProp name="address[new_0][telephone]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1234567890</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[new_0][telephone]</stringProp> + </elementProp> + <elementProp name="address[new_0][vat_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[new_0][vat_id]</stringProp> + </elementProp> + <elementProp name="address[new_0][default_billing]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">false</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[new_0][default_billing]</stringProp> + </elementProp> + <elementProp name="address[new_0][default_shipping]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">false</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[new_0][default_shipping]</stringProp> + </elementProp> + <elementProp name="address[new_0][street][0]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">123 Main</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[new_0][street][0]</stringProp> + </elementProp> + <elementProp name="address[new_0][street][1]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[new_0][street][1]</stringProp> + </elementProp> + <elementProp name="address[new_0][region]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[new_0][region]</stringProp> + </elementProp> + <elementProp name="address[new_0][country_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">US</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[new_0][country_id]</stringProp> + </elementProp> + <elementProp name="address[new_0][region_id]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">12</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">address[new_0][region_id]</stringProp> + </elementProp> + <elementProp name="form_key" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_form_key}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">form_key</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/customer/index/save/</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">true</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + </HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert customer saved" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="292987815">You saved the customer.</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + <TestAction guiclass="TestActionGui" testclass="TestAction" testname="Pause" enabled="true"> + <intProp name="ActionProcessor.action">1</intProp> + <intProp name="ActionProcessor.target">0</intProp> + <stringProp name="ActionProcessor.duration">${__javaScript(Math.round(${adminCustomerManagementDelay}*1000))}</stringProp> + </TestAction> + <hashTree/> + </hashTree> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Logout" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/auth/logout/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/setup/admin_logout.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + + <BeanShellPostProcessor guiclass="TestBeanGUI" testclass="BeanShellPostProcessor" testname="Return Admin to Pool" enabled="true"> + <boolProp name="resetInterpreter">false</boolProp> + <stringProp name="parameters"/> + <stringProp name="filename"/> + <stringProp name="script"> + adminUsersDistribution = Integer.parseInt(vars.get("admin_users_distribution_per_admin_pool")); + if (adminUsersDistribution == 1) { + adminUserList = props.get("adminUserList"); + adminUserList.add(vars.get("admin_user")); + } + </stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/return_admin_email_to_pool.jmx</stringProp></BeanShellPostProcessor> + <hashTree/> + </hashTree> + </hashTree> + + + <ThroughputController guiclass="ThroughputControllerGui" testclass="ThroughputController" testname="[C] Admin Edit Order" enabled="true"> + <intProp name="ThroughputController.style">1</intProp> + <boolProp name="ThroughputController.perThread">false</boolProp> + <intProp name="ThroughputController.maxThroughput">1</intProp> + <stringProp name="ThroughputController.percentThroughput">${cAdminEditOrderPercentage}</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/_system/scenario_controller_tmpl.jmx</stringProp></ThroughputController> + <hashTree> + <JSR223PreProcessor guiclass="TestBeanGUI" testclass="JSR223PreProcessor" testname="Set Test Label" enabled="true"> + <stringProp name="script"> +var testLabel = "${testLabel}" ? " (${testLabel})" : ""; +if (testLabel + && sampler.getClass().getName() == 'org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy' +) { + if (sampler.getName().indexOf(testLabel) == -1) { + sampler.setName(sampler.getName() + testLabel); + } +} else if (sampler.getName().indexOf("SetUp - ") == -1) { + sampler.setName("SetUp - " + sampler.getName()); +} + </stringProp> + <stringProp name="scriptLanguage">javascript</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/_system/setup_label.jmx</stringProp></JSR223PreProcessor> + <hashTree/> + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Set Label" enabled="true"> + <stringProp name="BeanShellSampler.query"> + vars.put("testLabel", "[C] Admin Edit Order"); + </stringProp> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + </BeanShellSampler> + <hashTree/> + + <JSR223PostProcessor guiclass="TestBeanGUI" testclass="JSR223PostProcessor" testname="Get admin form key PostProcessor" enabled="true"> + <stringProp name="script"> + function getFormKeyFromResponse() + { + var url = prev.getUrlAsString(), + responseCode = prev.getResponseCode(), + formKey = null; + searchPattern = /var FORM_KEY = '(.+)'/; + if (responseCode == "200" && url) { + response = prev.getResponseDataAsString(); + formKey = response && response.match(searchPattern) ? response.match(searchPattern)[1] : null; + } + return formKey; + } + + formKey = vars.get("form_key_storage"); + + currentFormKey = getFormKeyFromResponse(); + + if (currentFormKey != null && currentFormKey != formKey) { + vars.put("form_key_storage", currentFormKey); + } + </stringProp> + <stringProp name="scriptLanguage">javascript</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin/handle_admin_form_key.jmx</stringProp></JSR223PostProcessor> + <hashTree/> + <JSR223PreProcessor guiclass="TestBeanGUI" testclass="JSR223PreProcessor" testname="Set admin form key PreProcessor" enabled="true"> + <stringProp name="script"> + formKey = vars.get("form_key_storage"); + if (formKey + && sampler.getClass().getName() == 'org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy' + && sampler.getMethod() == "POST") + { + arguments = sampler.getArguments(); + for (i=0; i<arguments.getArgumentCount(); i++) + { + argument = arguments.getArgument(i); + if (argument.getName() == 'form_key' && argument.getValue() != formKey) { + log.info("admin form key updated: " + argument.getValue() + " => " + formKey); + argument.setValue(formKey); + } + } + } + </stringProp> + <stringProp name="scriptLanguage">javascript</stringProp> + </JSR223PreProcessor> + <hashTree/> + + <CookieManager guiclass="CookiePanel" testclass="CookieManager" testname="HTTP Cookie Manager" enabled="true"> + <collectionProp name="CookieManager.cookies"/> + <boolProp name="CookieManager.clearEachIteration">false</boolProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/http_cookie_manager_without_clear_each_iteration.jmx</stringProp></CookieManager> + <hashTree/> + + <GenericController guiclass="LogicControllerGui" testclass="GenericController" testname="Admin Login" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/simple_controller.jmx</stringProp> +</GenericController> + <hashTree> + <CriticalSectionController guiclass="CriticalSectionControllerGui" testclass="CriticalSectionController" testname="Admin Login Lock" enabled="true"> + <stringProp name="CriticalSectionController.lockName">get-admin-email</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/lock_controller.jmx</stringProp></CriticalSectionController> + <hashTree> + + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Get Admin Email" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/get_admin_email.jmx</stringProp> + <stringProp name="BeanShellSampler.query"> +adminUserList = props.get("adminUserList"); +adminUserListIterator = props.get("adminUserListIterator"); +adminUsersDistribution = Integer.parseInt(vars.get("admin_users_distribution_per_admin_pool")); + +if (adminUsersDistribution == 1) { + adminUser = adminUserList.poll(); +} else { + if (!adminUserListIterator.hasNext()) { + adminUserListIterator = adminUserList.descendingIterator(); + } + + adminUser = adminUserListIterator.next(); +} + +if (adminUser == null) { + SampleResult.setResponseMessage("adminUser list is empty"); + SampleResult.setResponseData("adminUser list is empty","UTF-8"); + IsSuccess=false; + SampleResult.setSuccessful(false); + SampleResult.setStopThread(true); +} +vars.put("admin_user", adminUser); + </stringProp> + <stringProp name="BeanShellSampler.filename"/> + <stringProp name="BeanShellSampler.parameters"/> + <boolProp name="BeanShellSampler.resetInterpreter">true</boolProp> + </BeanShellSampler> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="SetUp - Login" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_login/admin_login.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert login form shown" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-1397214398">Welcome</stringProp> + <stringProp name="-515240035"><title>Magento Admin</title></stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract form key" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">admin_form_key</stringProp> + <stringProp name="RegexExtractor.regex"><input name="form_key" type="hidden" value="([^'"]+)" /></stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + </RegexExtractor> + <hashTree/> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert form_key extracted" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="2845929">^.+$</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">1</intProp> + <stringProp name="Assertion.scope">variable</stringProp> + <stringProp name="Scope.variable">admin_form_key</stringProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="SetUp - Login Submit Form" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="dummy" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">dummy</stringProp> + </elementProp> + <elementProp name="form_key" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_form_key}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">form_key</stringProp> + </elementProp> + <elementProp name="login[password]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_password}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">login[password]</stringProp> + </elementProp> + <elementProp name="login[username]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_user}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">login[username]</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/dashboard/</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <stringProp name="HTTPSampler.implementation">Java</stringProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_login/admin_login_submit_form.jmx</stringProp> + </HTTPSamplerProxy> + <hashTree> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract form key" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">admin_form_key</stringProp> + <stringProp name="RegexExtractor.regex"><input name="form_key" type="hidden" value="([^'"]+)" /></stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_login/admin_retrieve_form_key.jmx</stringProp></RegexExtractor> + <hashTree/> + </hashTree> + </hashTree> + + <GenericController guiclass="LogicControllerGui" testclass="GenericController" testname="Simple Controller" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/simple_controller.jmx</stringProp> +</GenericController> + <hashTree> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Orders page" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/sales/order/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_create_process_returns/orders_page.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="1204796042">Create New Order</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Open Orders" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="namespace" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">sales_order_grid</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">namespace</stringProp> + </elementProp> + <elementProp name="search" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">search</stringProp> + </elementProp> + <elementProp name="filters[placeholder]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">true</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">filters[placeholder]</stringProp> + </elementProp> + <elementProp name="paging[pageSize]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">200</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">paging[pageSize]</stringProp> + </elementProp> + <elementProp name="paging[current]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">paging[current]</stringProp> + </elementProp> + <elementProp name="sorting[field]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">increment_id</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">sorting[field]</stringProp> + </elementProp> + <elementProp name="sorting[direction]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">desc</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">sorting[direction]</stringProp> + </elementProp> + <elementProp name="isAjax" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">true</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">isAjax</stringProp> + </elementProp> + <elementProp name="form_key" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_form_key}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">form_key</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="filters[status]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">pending</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">filters[status]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="_" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${__time()}${__Random(1,1000000)}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">_</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/mui/index/render/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_create_process_returns/open_orders.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="1637639774">totalRecords</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Search Pending Orders" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="form_key" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_form_key}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">form_key</stringProp> + </elementProp> + <elementProp name="namespace" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">sales_order_grid</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">namespace</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="search" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value"/> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">search</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="filters[placeholder]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">true</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">filters[placeholder]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="paging[pageSize]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">200</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">paging[pageSize]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="paging[current]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">paging[current]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="sorting[field]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">increment_id</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">sorting[field]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="sorting[direction]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">asc</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">sorting[direction]</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="isAjax" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">true</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">isAjax</stringProp> + <stringProp name="Argument.desc">true</stringProp> + </elementProp> + <elementProp name="filters[status]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">pending</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">filters[status]</stringProp> + </elementProp> + <elementProp name="_" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${__time()}${__Random(1,1000000)}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">_</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/mui/index/render/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_create_process_returns/search_orders.jmx</stringProp></HTTPSamplerProxy> +<hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="1637639774">totalRecords</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract order numbers" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">order_numbers</stringProp> + <stringProp name="RegexExtractor.regex">\"increment_id\":\"(\d+)\"\,</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">-1</stringProp> + <stringProp name="Scope.variable">simple_products</stringProp> + </RegexExtractor> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract order ids" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">order_ids</stringProp> + <stringProp name="RegexExtractor.regex">\"entity_id\":\"(\d+)\"\,</stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">-1</stringProp> + <stringProp name="Scope.variable">simple_products</stringProp> + </RegexExtractor> + <hashTree/> + </hashTree> + + <BeanShellSampler guiclass="BeanShellSamplerGui" testclass="BeanShellSampler" testname="SetUp - Generate Unique Ids for each Thread" enabled="true"> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_create_process_returns/setup.jmx</stringProp> + <stringProp name="BeanShellSampler.query"> + import java.util.ArrayList; + import java.util.HashMap; + import org.apache.jmeter.protocol.http.util.Base64Encoder; + import java.util.Random; + + // get count of "order_numbers" variable defined in "Search Pending Orders Limit" + int ordersCount = Integer.parseInt(vars.get("order_numbers_matchNr")); + + + int clusterLength; + int threadsNumber = ctx.getThreadGroup().getNumThreads(); + if (threadsNumber == 0) { + //Number of orders for one thread + clusterLength = ordersCount; + } else { + clusterLength = Math.round(ordersCount / threadsNumber); + if (clusterLength == 0) { + clusterLength = 1; + } + } + + //Current thread number starts from 0 + int currentThreadNum = ctx.getThreadNum(); + + //Index of the current product from the cluster + Random random = new Random(); + if (${seedForRandom} > 0) { + random.setSeed(${seedForRandom} + ${__threadNum}); + } + int iterator = random.nextInt(clusterLength); + if (iterator == 0) { + iterator = 1; + } + + int i = clusterLength * currentThreadNum + iterator; + + orderNumber = vars.get("order_numbers_" + i.toString()); + orderId = vars.get("order_ids_" + i.toString()); + vars.put("order_number", orderNumber); + vars.put("order_id", orderId); + + </stringProp> + <stringProp name="BeanShellSampler.filename"/> + <stringProp name="BeanShellSampler.parameters"/> + <boolProp name="BeanShellSampler.resetInterpreter">false</boolProp> + </BeanShellSampler> + <hashTree/> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Open Order" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/sales/order/view/order_id/${order_id}/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_create_process_returns/open_order.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="2103620713">#${order_number}</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract order status" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">order_status</stringProp> + <stringProp name="RegexExtractor.regex"><span id="order_status">([^<]+)</span></stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">1</stringProp> + <stringProp name="Scope.variable">simple_products</stringProp> + </RegexExtractor> + <hashTree/> + </hashTree> + + <IfController guiclass="IfControllerPanel" testclass="IfController" testname="If Controller" enabled="true"> + <stringProp name="IfController.condition">"${order_status}" == "Pending"</stringProp> + <boolProp name="IfController.evaluateAll">false</boolProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_edit_order/if_controller.jmx</stringProp></IfController> + <hashTree> + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Add Comment" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="history[status]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">pending</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">history[status]</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="history[comment]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">Some text</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">history[comment]</stringProp> + </elementProp> + <elementProp name="form_key" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_form_key}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">form_key</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/sales/order/addComment/order_id/${order_id}/?isAjax=true</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_edit_order/add_comment.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-2089278331">Not Notified</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Invoice Start" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/sales/order_invoice/start/order_id/${order_id}/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_create_process_returns/invoice_start.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-1233850814">Invoice Totals</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Extract ordered items ids" enabled="true"> + <stringProp name="RegexExtractor.useHeaders">false</stringProp> + <stringProp name="RegexExtractor.refname">item_ids</stringProp> + <stringProp name="RegexExtractor.regex"><div id="order_item_(\d+)_title"\s*class="product-title"></stringProp> + <stringProp name="RegexExtractor.template">$1$</stringProp> + <stringProp name="RegexExtractor.default"/> + <stringProp name="RegexExtractor.match_number">-1</stringProp> + <stringProp name="Scope.variable">simple_products</stringProp> + </RegexExtractor> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Invoice Submit" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="form_key" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_form_key}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">form_key</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="invoice[items][${item_ids_1}]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">invoice[items][${item_ids_1}]</stringProp> + </elementProp> + <elementProp name="invoice[items][${item_ids_2}]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">invoice[items][${item_ids_2}]</stringProp> + </elementProp> + <elementProp name="invoice[comment_text]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">Invoiced</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">invoice[comment_text]</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/sales/order_invoice/save/order_id/${order_id}/</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_create_process_returns/invoice_submit.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="1740524604">The invoice has been created</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Shipment Start" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/order_shipment/start/order_id/${order_id}/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_edit_order/shipment_start.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="304100442">New Shipment</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Shipment Submit" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"> + <elementProp name="form_key" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">${admin_form_key}</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">form_key</stringProp> + <stringProp name="Argument.desc">false</stringProp> + </elementProp> + <elementProp name="shipment[items][${item_ids_1}]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">shipment[items][${item_ids_1}]</stringProp> + </elementProp> + <elementProp name="shipment[items][${item_ids_2}]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">1</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">shipment[items][${item_ids_2}]</stringProp> + </elementProp> + <elementProp name="shipment[comment_text]" elementType="HTTPArgument"> + <boolProp name="HTTPArgument.always_encode">true</boolProp> + <stringProp name="Argument.value">Shipped</stringProp> + <stringProp name="Argument.metadata">=</stringProp> + <boolProp name="HTTPArgument.use_equals">true</boolProp> + <stringProp name="Argument.name">shipment[comment_text]</stringProp> + </elementProp> + </collectionProp> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/order_shipment/save/order_id/${order_id}/</stringProp> + <stringProp name="HTTPSampler.method">POST</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/admin_edit_order/shipment_submit.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true"> + <collectionProp name="Asserion.test_strings"> + <stringProp name="-2089453199">The shipment has been created</stringProp> + </collectionProp> + <stringProp name="Assertion.test_field">Assertion.response_data</stringProp> + <boolProp name="Assertion.assume_success">false</boolProp> + <intProp name="Assertion.test_type">2</intProp> + </ResponseAssertion> + <hashTree/> + </hashTree> + </hashTree> + </hashTree> + + <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Logout" enabled="true"> + <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" enabled="true"> + <collectionProp name="Arguments.arguments"/> + </elementProp> + <stringProp name="HTTPSampler.domain"/> + <stringProp name="HTTPSampler.port"/> + <stringProp name="HTTPSampler.connect_timeout">60000</stringProp> + <stringProp name="HTTPSampler.response_timeout">200000</stringProp> + <stringProp name="HTTPSampler.protocol">${request_protocol}</stringProp> + <stringProp name="HTTPSampler.contentEncoding"/> + <stringProp name="HTTPSampler.path">${base_path}${admin_path}/admin/auth/logout/</stringProp> + <stringProp name="HTTPSampler.method">GET</stringProp> + <boolProp name="HTTPSampler.follow_redirects">true</boolProp> + <boolProp name="HTTPSampler.auto_redirects">false</boolProp> + <boolProp name="HTTPSampler.use_keepalive">true</boolProp> + <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp> + <boolProp name="HTTPSampler.monitor">false</boolProp> + <stringProp name="HTTPSampler.embedded_url_re"/> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/setup/admin_logout.jmx</stringProp></HTTPSamplerProxy> + <hashTree> + + <BeanShellPostProcessor guiclass="TestBeanGUI" testclass="BeanShellPostProcessor" testname="Return Admin to Pool" enabled="true"> + <boolProp name="resetInterpreter">false</boolProp> + <stringProp name="parameters"/> + <stringProp name="filename"/> + <stringProp name="script"> + adminUsersDistribution = Integer.parseInt(vars.get("admin_users_distribution_per_admin_pool")); + if (adminUsersDistribution == 1) { + adminUserList = props.get("adminUserList"); + adminUserList.add(vars.get("admin_user")); + } + </stringProp> + <stringProp name="TestPlan.comments">mpaf/tool/fragments/ce/common/return_admin_email_to_pool.jmx</stringProp></BeanShellPostProcessor> + <hashTree/> + </hashTree> + </hashTree> + </hashTree> diff --git a/setup/src/Magento/Setup/Model/FixtureGenerator/ProductGenerator.php b/setup/src/Magento/Setup/Model/FixtureGenerator/ProductGenerator.php index 661d6bfc94048..613572b8af0ad 100644 --- a/setup/src/Magento/Setup/Model/FixtureGenerator/ProductGenerator.php +++ b/setup/src/Magento/Setup/Model/FixtureGenerator/ProductGenerator.php @@ -8,8 +8,15 @@ use Magento\Catalog\Api\Data\ProductInterface; use Magento\Catalog\Model\Product\Attribute\Source\Status; +use Magento\Catalog\Model\ProductFactory; +use Magento\Catalog\Model\ResourceModel\Category\CollectionFactory as CategoryCollectionFactory; use Magento\CatalogUrlRewrite\Model\ProductUrlPathGenerator; +use Magento\Framework\Exception\LocalizedException; +use Magento\Store\Model\ResourceModel\Store\CollectionFactory as StoreCollectionFactory; use Magento\Store\Model\ScopeInterface; +use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Store\Model\StoreManagerInterface; +use Magento\UrlRewrite\Service\V1\Data\UrlRewriteFactory; /** * Generate specified amount of products based on passed fixture @@ -39,22 +46,22 @@ class ProductGenerator { /** - * @var \Magento\Catalog\Model\ProductFactory + * @var ProductFactory */ private $productFactory; /** - * @var \Magento\Catalog\Model\ResourceModel\Category\CollectionFactory + * @var CategoryCollectionFactory */ private $categoryCollectionFactory; /** - * @var \Magento\UrlRewrite\Service\V1\Data\UrlRewriteFactory + * @var UrlRewriteFactory */ private $urlRewriteFactory; /** - * @var \Magento\Store\Model\ResourceModel\Store\CollectionFactory + * @var StoreCollectionFactory */ private $storeCollectionFactory; @@ -74,7 +81,7 @@ class ProductGenerator private $entityGeneratorFactory; /** - * @var \Magento\Store\Model\StoreManagerInterface + * @var StoreManagerInterface */ private $storeManager; @@ -89,7 +96,7 @@ class ProductGenerator private $productUrlSuffix = []; /** - * @var \Magento\Framework\App\Config\ScopeConfigInterface + * @var ScopeConfigInterface */ private $scopeConfig; @@ -99,25 +106,25 @@ class ProductGenerator private $customTableMap; /** - * @param \Magento\Catalog\Model\ProductFactory $productFactory - * @param \Magento\Catalog\Model\ResourceModel\Category\CollectionFactory $categoryCollectionFactory - * @param \Magento\UrlRewrite\Service\V1\Data\UrlRewriteFactory $urlRewriteFactory - * @param \Magento\Store\Model\ResourceModel\Store\CollectionFactory $storeCollectionFactory + * @param ProductFactory $productFactory + * @param CategoryCollectionFactory $categoryCollectionFactory + * @param UrlRewriteFactory $urlRewriteFactory + * @param StoreCollectionFactory $storeCollectionFactory * @param EntityGeneratorFactory $entityGeneratorFactory - * @param \Magento\Store\Model\StoreManagerInterface $storeManager + * @param StoreManagerInterface $storeManager * @param ProductTemplateGeneratorFactory $productTemplateGeneratorFactory - * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig + * @param ScopeConfigInterface $scopeConfig * @param array $customTableMap */ public function __construct( - \Magento\Catalog\Model\ProductFactory $productFactory, - \Magento\Catalog\Model\ResourceModel\Category\CollectionFactory $categoryCollectionFactory, - \Magento\UrlRewrite\Service\V1\Data\UrlRewriteFactory $urlRewriteFactory, - \Magento\Store\Model\ResourceModel\Store\CollectionFactory $storeCollectionFactory, + ProductFactory $productFactory, + CategoryCollectionFactory $categoryCollectionFactory, + UrlRewriteFactory $urlRewriteFactory, + StoreCollectionFactory $storeCollectionFactory, EntityGeneratorFactory $entityGeneratorFactory, - \Magento\Store\Model\StoreManagerInterface $storeManager, + StoreManagerInterface $storeManager, ProductTemplateGeneratorFactory $productTemplateGeneratorFactory, - \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, + ScopeConfigInterface $scopeConfig, $customTableMap = [] ) { $this->productFactory = $productFactory; @@ -202,6 +209,8 @@ function ($productNumber, $entityNumber) use ($attributeSetId, $fixtureMap) { } /** + * Initialize fixture default values + * * @param array $fixture * @return void */ @@ -230,6 +239,8 @@ private function initializeFixtureDefaultValues(array &$fixture) } /** + * Get fixture value + * * @param string $fixtureKey * @param int $productId * @param int $entityNumber @@ -243,6 +254,8 @@ private function getFixtureValue($fixtureKey, $productId, $entityNumber, $fixtur } /** + * Get bind value + * * @param callable|mixed $fixtureValue * @param int $productId * @param int $entityNumber @@ -251,7 +264,7 @@ private function getFixtureValue($fixtureKey, $productId, $entityNumber, $fixtur private function getBindValue($fixtureValue, $productId, $entityNumber) { return is_callable($fixtureValue) - ? call_user_func($fixtureValue, $productId, $entityNumber) + ? $fixtureValue($productId, $entityNumber) : $fixtureValue; } @@ -262,6 +275,7 @@ private function getBindValue($fixtureValue, $productId, $entityNumber) * @param int $entityNumber * @param array $fixtureMap * @return array + * @throws LocalizedException */ private function urlRewriteHandler($productId, $entityNumber, $fixtureMap) { @@ -280,7 +294,7 @@ private function urlRewriteHandler($productId, $entityNumber, $fixtureMap) ->setEntityType('product'); $binds[] = $urlRewrite->toArray(); - if (isset($fixtureMap['category_ids'])) { + if (isset($fixtureMap['category_ids']) && $this->isCategoryProductUrlRewriteGenerationEnabled()) { $categoryId = $fixtureMap['category_ids']($productId, $entityNumber); if (!isset($this->categories[$categoryId])) { $this->categories[$categoryId] = $this->categoryCollectionFactory @@ -333,4 +347,14 @@ private function getUrlSuffix($storeId) } return $this->productUrlSuffix[$storeId]; } + + /** + * Check config value of generate_category_product_rewrites + * + * @return bool + */ + private function isCategoryProductUrlRewriteGenerationEnabled() + { + return (bool)$this->scopeConfig->getValue('catalog/seo/generate_category_product_rewrites'); + } }