Skip to content

Commit c1b71d9

Browse files
Merge branch '2.4-develop' into kishore/fix-28198
2 parents 1abb423 + 2d09485 commit c1b71d9

File tree

8 files changed

+293
-90
lines changed

8 files changed

+293
-90
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
/**
4+
* Copyright © Magento, Inc. All rights reserved.
5+
* See COPYING.txt for license details.
6+
*/
7+
-->
8+
9+
<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
10+
xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd">
11+
<actionGroup name="AdminCreateSimpleProductActionGroup">
12+
<annotations>
13+
<description>Goes to the Admin Product grid page. Clicks on Add. Fills the provided Product details (Name, SKU, Price, Quantity, Category and URL). Clicks on Save. Validates that the Product details are present and correct.</description>
14+
</annotations>
15+
<arguments>
16+
<argument name="category"/>
17+
<argument name="simpleProduct"/>
18+
</arguments>
19+
20+
<amOnPage url="{{AdminProductIndexPage.url}}" stepKey="navigateToProductIndex"/>
21+
<click selector="{{AdminProductGridActionSection.addProductToggle}}" stepKey="clickAddProductDropdown"/>
22+
<click selector="{{AdminProductGridActionSection.addSimpleProduct}}" stepKey="clickAddSimpleProduct"/>
23+
<fillField userInput="{{simpleProduct.name}}" selector="{{AdminProductFormSection.productName}}" stepKey="fillName"/>
24+
<fillField userInput="{{simpleProduct.sku}}" selector="{{AdminProductFormSection.productSku}}" stepKey="fillSKU"/>
25+
<fillField userInput="{{simpleProduct.price}}" selector="{{AdminProductFormSection.productPrice}}" stepKey="fillPrice"/>
26+
<fillField userInput="{{simpleProduct.quantity}}" selector="{{AdminProductFormSection.productQuantity}}" stepKey="fillQuantity"/>
27+
<searchAndMultiSelectOption selector="{{AdminProductFormSection.categoriesDropdown}}" parameterArray="[{{category.name}}]" stepKey="searchAndSelectCategory"/>
28+
<click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="openSeoSection"/>
29+
<fillField userInput="{{simpleProduct.urlKey}}" selector="{{AdminProductSEOSection.urlKeyInput}}" stepKey="fillUrlKey"/>
30+
31+
<click selector="{{AdminProductFormActionSection.saveButton}}" stepKey="saveProduct"/>
32+
<seeElement selector="{{AdminProductMessagesSection.successMessage}}" stepKey="assertSaveMessageSuccess"/>
33+
<seeInField userInput="{{simpleProduct.name}}" selector="{{AdminProductFormSection.productName}}" stepKey="assertFieldName"/>
34+
<seeInField userInput="{{simpleProduct.sku}}" selector="{{AdminProductFormSection.productSku}}" stepKey="assertFieldSku"/>
35+
<seeInField userInput="{{simpleProduct.price}}" selector="{{AdminProductFormSection.productPrice}}" stepKey="assertFieldPrice"/>
36+
<click selector="{{AdminProductSEOSection.sectionHeader}}" stepKey="openSeoSectionAssert"/>
37+
<seeInField userInput="{{simpleProduct.urlKey}}" selector="{{AdminProductSEOSection.urlKeyInput}}" stepKey="assertFieldUrlKey"/>
38+
</actionGroup>
39+
</actionGroups>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
/**
4+
* Copyright © Magento, Inc. All rights reserved.
5+
* See COPYING.txt for license details.
6+
*/
7+
-->
8+
9+
<entities xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
10+
xsi:noNamespaceSchemaLocation="urn:magento:mftf:DataGenerator/etc/dataProfileSchema.xsd">
11+
<entity name="StorefrontDisableMoveJsCodeBottom">
12+
<!-- Magento default value -->
13+
<data key="path">dev/js/move_script_to_bottom</data>
14+
<data key="label">No</data>
15+
<data key="value">0</data>
16+
</entity>
17+
<entity name="StorefrontEnableMoveJsCodeBottom">
18+
<data key="path">dev/js/move_script_to_bottom</data>
19+
<data key="label">Yes</data>
20+
<data key="value">1</data>
21+
</entity>
22+
</entities>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
/**
4+
* Copyright © Magento, Inc. All rights reserved.
5+
* See COPYING.txt for license details.
6+
*/
7+
-->
8+
9+
<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
10+
xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd">
11+
<test name="StorefrontCreateOrdersWithMoveJSCodeBottomTest">
12+
<annotations>
13+
<title value="Create a product and orders with set 'Move Js code to the bottom' to 'Yes'."/>
14+
<description value="Create a product and orders with a set 'Move JS code to the bottom of the page' to 'Yes' for registered customers and guests."/>
15+
</annotations>
16+
<before>
17+
<magentoCLI command="config:set {{StorefrontEnableMoveJsCodeBottom.path}} {{StorefrontEnableMoveJsCodeBottom.value}}" stepKey="moveJsCodeBottomEnable"/>
18+
<magentoCLI command="cache:clean config full_page" stepKey="cleanInvalidatedCaches"/>
19+
<actionGroup ref="AdminLoginActionGroup" stepKey="logInAsAdmin"/>
20+
<actionGroup ref="AdminOpenCategoryPageActionGroup" stepKey="goToCategoryPage"/>
21+
<actionGroup ref="CreateCategoryActionGroup" stepKey="createCategory">
22+
<argument name="categoryEntity" value="_defaultCategory"/>
23+
</actionGroup>
24+
<actionGroup ref="AdminCreateSimpleProductActionGroup" stepKey="createSimpleProduct">
25+
<argument name="category" value="_defaultCategory"/>
26+
<argument name="simpleProduct" value="_defaultProduct"/>
27+
</actionGroup>
28+
</before>
29+
<after>
30+
<actionGroup ref="AdminOpenCategoryPageActionGroup" stepKey="goToCategoryPage"/>
31+
<actionGroup ref="DeleteCategoryActionGroup" stepKey="deleteCategory">
32+
<argument name="categoryEntity" value="_defaultCategory"/>
33+
</actionGroup>
34+
<actionGroup ref="DeleteProductActionGroup" stepKey="deleteSimpleProduct">
35+
<argument name="productName" value="_defaultProduct.name"/>
36+
</actionGroup>
37+
<actionGroup ref="AdminDeleteCustomerActionGroup" stepKey="deleteCustomer">
38+
<argument name="customerEmail" value="Simple_US_Customer.email"/>
39+
</actionGroup>
40+
<actionGroup ref="AdminLogoutActionGroup" stepKey="logoutFromAdmin"/>
41+
42+
<magentoCLI command="config:set {{StorefrontDisableMoveJsCodeBottom.path}} {{StorefrontDisableMoveJsCodeBottom.value}}" stepKey="moveJsCodeBottomDisable"/>
43+
<magentoCLI command="cache:clean config full_page" stepKey="cleanInvalidatedCaches"/>
44+
</after>
45+
46+
<!-- Go to Storefront and place order for guest -->
47+
<actionGroup ref="StorefrontOpenProductPageActionGroup" stepKey="openStorefrontProductPage">
48+
<argument name="productUrl" value="{{_defaultProduct.urlKey}}"/>
49+
</actionGroup>
50+
51+
<actionGroup ref="StorefrontAddProductToCartActionGroup" stepKey="addProductToCart">
52+
<argument name="product" value="_defaultProduct"/>
53+
<argument name="productCount" value="1"/>
54+
</actionGroup>
55+
<actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="guestGoToCheckoutFromCart"/>
56+
<actionGroup ref="GuestCheckoutFillNewShippingAddressActionGroup" stepKey="fillNewShippingAddress">
57+
<argument name="customer" value="Simple_Customer_Without_Address" />
58+
<argument name="address" value="US_Address_TX"/>
59+
</actionGroup>
60+
<actionGroup ref="StorefrontSetShippingMethodActionGroup" stepKey="setShippingMethodFreeShipping">
61+
<argument name="shippingMethodName" value="Flat Rate"/>
62+
</actionGroup>
63+
<actionGroup ref="StorefrontCheckoutClickNextOnShippingStepActionGroup" stepKey="goToCheckoutReview"/>
64+
<actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyPayment"/>
65+
<actionGroup ref="ClickPlaceOrderActionGroup" stepKey="guestPlaceOrder" />
66+
67+
<!-- Go to frontend and make a user account and login with it -->
68+
<actionGroup ref="StorefrontOpenCustomerAccountCreatePageActionGroup" stepKey="openCreateAccountPage"/>
69+
<actionGroup ref="StorefrontFillCustomerAccountCreationFormActionGroup" stepKey="fillCreateAccountForm">
70+
<argument name="customer" value="Simple_US_Customer"/>
71+
</actionGroup>
72+
<actionGroup ref="StorefrontClickCreateAnAccountCustomerAccountCreationFormActionGroup" stepKey="submitCreateAccountForm"/>
73+
<actionGroup ref="AssertMessageCustomerCreateAccountActionGroup" stepKey="seeSuccessMessage">
74+
<argument name="messageType" value="success"/>
75+
<argument name="message" value="Thank you for registering with Main Website Store."/>
76+
</actionGroup>
77+
<actionGroup ref="StorefrontAddNewCustomerAddressActionGroup" stepKey="AddNewAddress">
78+
<argument name="Address" value="US_Address_TX"/>
79+
</actionGroup>
80+
81+
<!-- Go to Storefront and place order for customer -->
82+
<actionGroup ref="StorefrontOpenProductPageActionGroup" stepKey="openStorefrontProductPage2">
83+
<argument name="productUrl" value="{{_defaultProduct.urlKey}}"/>
84+
</actionGroup>
85+
86+
<actionGroup ref="StorefrontAddProductToCartActionGroup" stepKey="addProductToCart2">
87+
<argument name="product" value="_defaultProduct"/>
88+
<argument name="productCount" value="1"/>
89+
</actionGroup>
90+
<actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="customerGoToCheckoutFromCart"/>
91+
92+
<actionGroup ref="StorefrontSetShippingMethodActionGroup" stepKey="setShippingMethodFreeShipping2">
93+
<argument name="shippingMethodName" value="Flat Rate"/>
94+
</actionGroup>
95+
<actionGroup ref="StorefrontCheckoutClickNextOnShippingStepActionGroup" stepKey="goToCheckoutReview2"/>
96+
<actionGroup ref="CheckoutSelectCheckMoneyOrderPaymentActionGroup" stepKey="selectCheckMoneyPayment2"/>
97+
<actionGroup ref="ClickPlaceOrderActionGroup" stepKey="customerPlaceOrder" />
98+
<actionGroup ref="StorefrontSignOutActionGroup" stepKey="singOutCustomer" />
99+
</test>
100+
</tests>

app/code/Magento/Theme/Controller/Result/JsFooterPlugin.php

+71-26
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,13 @@
88
namespace Magento\Theme\Controller\Result;
99

1010
use Magento\Framework\App\Config\ScopeConfigInterface;
11+
use Magento\Framework\App\Response\HttpInterface as HttpResponseInterface;
12+
use Magento\Framework\App\ResponseInterface;
13+
use Magento\Framework\View\Result\Layout;
1114
use Magento\Store\Model\ScopeInterface;
12-
use Magento\Framework\App\Response\Http;
1315

1416
/**
15-
* Plugin for putting all js to footer.
17+
* Plugin for putting all JavaScript tags to the end of body.
1618
*/
1719
class JsFooterPlugin
1820
{
@@ -32,34 +34,77 @@ public function __construct(ScopeConfigInterface $scopeConfig)
3234
}
3335

3436
/**
35-
* Put all javascript to footer before sending the response.
37+
* Moves all JavaScript tags to the end of body if this feature is enabled.
3638
*
37-
* @param Http $subject
38-
* @return void
39+
* @param Layout $subject
40+
* @param Layout $result
41+
* @param HttpResponseInterface|ResponseInterface $httpResponse
42+
* @return Layout (That should be void, actually)
43+
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
3944
*/
40-
public function beforeSendResponse(Http $subject)
45+
public function afterRenderResult(Layout $subject, Layout $result, ResponseInterface $httpResponse)
4146
{
42-
$content = $subject->getContent();
43-
$script = [];
44-
if (is_string($content) && strpos($content, '</body') !== false) {
45-
if ($this->scopeConfig->isSetFlag(
46-
self::XML_PATH_DEV_MOVE_JS_TO_BOTTOM,
47-
ScopeInterface::SCOPE_STORE
48-
)
49-
) {
50-
$pattern = '#<script[^>]*+(?<!text/x-magento-template.)>.*?</script>#is';
51-
$content = preg_replace_callback(
52-
$pattern,
53-
function ($matchPart) use (&$script) {
54-
$script[] = $matchPart[0];
55-
return '';
56-
},
57-
$content
58-
);
59-
$subject->setContent(
60-
str_replace('</body', implode("\n", $script) . "\n</body", $content)
61-
);
47+
if (!$this->isDeferEnabled()) {
48+
return $result;
49+
}
50+
51+
$content = (string)$httpResponse->getContent();
52+
$bodyEndTag = '</body';
53+
$bodyEndTagFound = strrpos($content, $bodyEndTag) !== false;
54+
55+
if ($bodyEndTagFound) {
56+
$scripts = $this->extractScriptTags($content);
57+
if ($scripts) {
58+
$newBodyEndTagPosition = strrpos($content, $bodyEndTag);
59+
$content = substr_replace($content, $scripts . "\n", $newBodyEndTagPosition, 0);
60+
$httpResponse->setContent($content);
61+
}
62+
}
63+
64+
return $result;
65+
}
66+
67+
/**
68+
* Extracts and returns script tags found in given content.
69+
*
70+
* @param string $content
71+
*/
72+
private function extractScriptTags(&$content): string
73+
{
74+
$scripts = '';
75+
$scriptOpen = '<script';
76+
$scriptClose = '</script>';
77+
$scriptOpenPos = strpos($content, $scriptOpen);
78+
79+
while ($scriptOpenPos !== false) {
80+
$scriptClosePos = strpos($content, $scriptClose, $scriptOpenPos);
81+
$script = substr($content, $scriptOpenPos, $scriptClosePos - $scriptOpenPos + strlen($scriptClose));
82+
$isXMagentoTemplate = strpos($script, 'text/x-magento-template') !== false;
83+
84+
if ($isXMagentoTemplate) {
85+
$scriptOpenPos = strpos($content, $scriptOpen, $scriptClosePos);
86+
continue;
6287
}
88+
89+
$scripts .= "\n" . $script;
90+
$content = str_replace($script, '', $content);
91+
// Script cut out, continue search from its position.
92+
$scriptOpenPos = strpos($content, $scriptOpen, $scriptOpenPos);
6393
}
94+
95+
return $scripts;
96+
}
97+
98+
/**
99+
* Returns information whether moving JS to footer is enabled
100+
*
101+
* @return bool
102+
*/
103+
private function isDeferEnabled(): bool
104+
{
105+
return $this->scopeConfig->isSetFlag(
106+
self::XML_PATH_DEV_MOVE_JS_TO_BOTTOM,
107+
ScopeInterface::SCOPE_STORE
108+
);
64109
}
65110
}

0 commit comments

Comments
 (0)