getResponse()->getBody());
+ }
+
+ /**
+ * @return void
+ */
+ public function testProductReviews(): void
+ {
+ $customerId = 1;
+ $this->dispatchWithIdParam($customerId);
+ $block = $this->layout->getBlock('admin.customer.reviews');
+ $this->assertNotFalse($block);
+ $this->assertEquals(
+ $customerId,
+ $block->getCustomerId(),
+ 'Block customer id value does not match expected value'
+ );
+ }
+
+ /**
+ * Dispatch request with id parameter
+ *
+ * @param int $id
+ * @return void
+ */
+ private function dispatchWithIdParam(int $id): void
+ {
+ $this->getRequest()->setMethod(HttpRequest::METHOD_POST);
+ $this->getRequest()->setParams(['id' => $id]);
$this->dispatch('backend/review/customer/productReviews');
- $body = $this->getResponse()->getBody();
- $this->assertContains('
objectManager = Bootstrap::getObjectManager();
+ $this->layout = $this->objectManager->get(LayoutInterface::class);
+ $this->block = $this->layout->createBlock(Items::class, 'block');
+ $this->creditmemo = $this->objectManager->get(CreditmemoInterface::class);
+ $this->registry = $this->objectManager->get(Registry::class);
+ $this->orderFactory = $this->objectManager->get(OrderInterfaceFactory::class);
+ $this->pageFactory = $this->objectManager->get(PageFactory::class);
+ }
/**
- * @var \Magento\Sales\Model\Order\Creditmemo
+ * @inheritdoc
*/
- protected $_creditmemo;
-
- protected function setUp()
+ protected function tearDown()
{
- $this->_layout = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(
- \Magento\Framework\View\LayoutInterface::class
- );
- $this->_block = $this->_layout->createBlock(\Magento\Sales\Block\Order\Creditmemo\Items::class, 'block');
- $this->_creditmemo = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(
- \Magento\Sales\Model\Order\Creditmemo::class
- );
+ $this->registry->unregister('current_order');
+
+ parent::tearDown();
}
/**
* @magentoAppIsolation enabled
+ *
+ * @return void
*/
- public function testGetTotalsHtml()
+ public function testGetTotalsHtml(): void
{
- $childBlock = $this->_layout->addBlock(
- \Magento\Framework\View\Element\Text::class,
+ $childBlock = $this->layout->addBlock(
+ Text::class,
'creditmemo_totals',
'block'
);
-
$expectedHtml = '
Any html';
$this->assertEmpty($childBlock->getCreditmemo());
- $this->assertNotEquals($expectedHtml, $this->_block->getTotalsHtml($this->_creditmemo));
-
+ $this->assertNotEquals($expectedHtml, $this->block->getTotalsHtml($this->creditmemo));
$childBlock->setText($expectedHtml);
- $actualHtml = $this->_block->getTotalsHtml($this->_creditmemo);
- $this->assertSame($this->_creditmemo, $childBlock->getCreditmemo());
+ $actualHtml = $this->block->getTotalsHtml($this->creditmemo);
+ $this->assertSame($this->creditmemo, $childBlock->getCreditmemo());
$this->assertEquals($expectedHtml, $actualHtml);
}
- public function testGetCommentsHtml()
+ /**
+ * @magentoAppIsolation enabled
+ *
+ * @return void
+ */
+ public function testGetCommentsHtml(): void
{
- $childBlock = $this->_layout->addBlock(
- \Magento\Framework\View\Element\Text::class,
+ $childBlock = $this->layout->addBlock(
+ Text::class,
'creditmemo_comments',
'block'
);
-
$expectedHtml = '
Any html';
$this->assertEmpty($childBlock->getEntity());
$this->assertEmpty($childBlock->getTitle());
- $this->assertNotEquals($expectedHtml, $this->_block->getCommentsHtml($this->_creditmemo));
-
+ $this->assertNotEquals($expectedHtml, $this->block->getCommentsHtml($this->creditmemo));
$childBlock->setText($expectedHtml);
- $actualHtml = $this->_block->getCommentsHtml($this->_creditmemo);
- $this->assertSame($this->_creditmemo, $childBlock->getEntity());
+ $actualHtml = $this->block->getCommentsHtml($this->creditmemo);
+ $this->assertSame($this->creditmemo, $childBlock->getEntity());
$this->assertNotEmpty($childBlock->getTitle());
$this->assertEquals($expectedHtml, $actualHtml);
}
+
+ /**
+ * @magentoDataFixture Magento/Sales/_files/refunds_for_items.php
+ *
+ * @return void
+ */
+ public function testDisplayingCreditmemos(): void
+ {
+ $order = $this->orderFactory->create()->loadByIncrementId('100000555');
+ $this->registerOrder($order);
+ $blockHtml = $this->renderCreditmemoItemsBlock();
+ $this->assertCreditmemosBlock($order, $blockHtml);
+ }
+
+ /**
+ * Assert creditmemos block.
+ *
+ * @param OrderInterface $order
+ * @param string $blockHtml
+ * @return void
+ */
+ private function assertCreditmemosBlock(OrderInterface $order, string $blockHtml): void
+ {
+ $this->assertEquals(
+ 1,
+ Xpath::getElementsCountForXpath(
+ sprintf(
+ "//a[contains(@href, 'sales/order/printCreditmemo/order_id/%s')]/span[contains(text(), '%s')]",
+ $order->getId(),
+ __('Print All Refunds')
+ ),
+ $blockHtml
+ ),
+ sprintf('%s button was not found.', __('Print All Refunds'))
+ );
+ $this->assertNotCount(0, $order->getCreditmemosCollection(), 'Creditmemos collection is empty');
+ foreach ($order->getCreditmemosCollection() as $creditmemo) {
+ $this->assertEquals(
+ 1,
+ Xpath::getElementsCountForXpath(
+ sprintf(
+ "//div[contains(@class, 'order-title')]/strong[contains(text(), '%s')]",
+ __('Refund #') . $creditmemo->getIncrementId()
+ ),
+ $blockHtml
+ ),
+ sprintf('Title for %s was not found.', __('Refund #') . $creditmemo->getIncrementId())
+ );
+ $this->assertEquals(
+ 1,
+ Xpath::getElementsCountForXpath(
+ sprintf(
+ "//a[contains(@href, 'sales/order/printCreditmemo/creditmemo_id/%s')]"
+ . "/span[contains(text(), '%s')]",
+ $creditmemo->getId(),
+ __('Print Refund')
+ ),
+ $blockHtml
+ ),
+ sprintf('%s button for #%s was not found.', __('Print Refund'), $creditmemo->getIncrementId())
+ );
+ $this->assertCreditmemoItems($creditmemo, $blockHtml);
+ }
+ }
+
+ /**
+ * Assert creditmemo items list.
+ *
+ * @param CreditmemoInterface $creditmemo
+ * @param string $html
+ * @return void
+ */
+ private function assertCreditmemoItems(CreditmemoInterface $creditmemo, string $html): void
+ {
+ $this->assertNotCount(0, $creditmemo->getItemsCollection(), 'Creditmemo items collection is empty');
+ $fieldsToCheck = [
+ 'name' => "/td[contains(@class, 'name')]/strong[contains(text(), '%s')]",
+ 'sku' => "/td[contains(@class, 'sku') and contains(text(), '%s')]",
+ 'price' => "/td[contains(@class, 'price')]//span[contains(text(), '%01.2f')]",
+ 'qty' => "/td[contains(@class, 'qty') and contains(text(), '%d')]",
+ 'row_total' => "/td[contains(@class, 'subtotal')]//span[contains(text(), '%01.2f')]",
+ 'discount_amount' => "/td[contains(@class, 'discount')]/span[contains(text(), '%01.2f')]",
+ ];
+ foreach ($creditmemo->getItemsCollection() as $item) {
+ $rowXpath = sprintf(
+ "//table[@id='my-refund-table-%s']//tr[@id='order-item-row-%s']",
+ $creditmemo->getId(),
+ $item->getId()
+ );
+ foreach ($fieldsToCheck as $key => $xpath) {
+ $this->assertEquals(
+ 1,
+ Xpath::getElementsCountForXpath(sprintf($rowXpath . $xpath, $item->getData($key)), $html),
+ sprintf('Item %s wasn\'t found or not equals to %s.', $key, $item->getData($key))
+ );
+ }
+ }
+ }
+
+ /**
+ * Render creditmemo items block.
+ *
+ * @return string
+ */
+ private function renderCreditmemoItemsBlock(): string
+ {
+ $page = $this->pageFactory->create();
+ $page->addHandle([
+ 'default',
+ 'sales_order_creditmemo',
+ ]);
+ $page->getLayout()->generateXml();
+ $creditmemoItemsBlock = $page->getLayout()->getBlock('creditmemo_items')->unsetChild('creditmemo_totals');
+ $creditmemoItemsBlock->getRequest()->setRouteName('sales')->setControllerName('order')
+ ->setActionName('creditmemo');
+
+ return $creditmemoItemsBlock->toHtml();
+ }
+
+ /**
+ * Register order in registry.
+ *
+ * @param OrderInterface $order
+ * @return void
+ */
+ private function registerOrder(OrderInterface $order): void
+ {
+ $this->registry->unregister('current_order');
+ $this->registry->register('current_order', $order);
+ }
}
diff --git a/dev/tests/integration/testsuite/Magento/Sales/Block/Order/Creditmemo/TotalsTest.php b/dev/tests/integration/testsuite/Magento/Sales/Block/Order/Creditmemo/TotalsTest.php
new file mode 100644
index 0000000000000..a88b2d3a5f0df
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Sales/Block/Order/Creditmemo/TotalsTest.php
@@ -0,0 +1,97 @@
+objectManager = Bootstrap::getObjectManager();
+ $this->block = $this->objectManager->get(LayoutInterface::class)->createBlock(Totals::class)
+ ->setTemplate('Magento_Sales::order/totals.phtml');
+ $this->orderFactory = $this->objectManager->get(OrderInterfaceFactory::class);
+ }
+
+ /**
+ * @magentoDataFixture Magento/Sales/_files/refunds_for_items.php
+ *
+ * @return void
+ */
+ public function testCreditmemoTotalsBlock(): void
+ {
+ $order = $this->orderFactory->create()->loadByIncrementId('100000555');
+ $creditmemo = $order->getCreditmemosCollection()->getFirstItem();
+ $this->assertNotNull($creditmemo->getId());
+ $blockHtml = $this->block->setOrder($order)->setCreditmemo($creditmemo)->toHtml();
+ $message = '"%s" for creditmemo wasn\'t found or not equals to %01.2f';
+ $this->assertEquals(
+ 1,
+ Xpath::getElementsCountForXpath(
+ sprintf(
+ "//th[contains(text(), '%s')]/following-sibling::td/span[contains(text(), '%01.2f')]",
+ __('Subtotal'),
+ $creditmemo->getSubtotal()
+ ),
+ $blockHtml
+ ),
+ sprintf($message, __('Subtotal'), $creditmemo->getSubtotal())
+ );
+ $this->assertEquals(
+ 1,
+ Xpath::getElementsCountForXpath(
+ sprintf(
+ "//th[contains(text(), '%s')]/following-sibling::td/span[contains(text(), '%01.2f')]",
+ __('Shipping & Handling'),
+ $creditmemo->getShippingAmount()
+ ),
+ $blockHtml
+ ),
+ sprintf($message, __('Shipping & Handling'), $creditmemo->getShippingAmount())
+ );
+ $this->assertEquals(
+ 1,
+ Xpath::getElementsCountForXpath(
+ sprintf(
+ "//tr[contains(@class, 'grand_total') and contains(.//strong, '%s')]"
+ . "//span[contains(text(), '%01.2f')]",
+ __('Grand Total'),
+ $creditmemo->getGrandTotal()
+ ),
+ $blockHtml
+ ),
+ sprintf($message, __('Grand Total'), $creditmemo->getGrandTotal())
+ );
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Sales/Block/Order/HistoryTest.php b/dev/tests/integration/testsuite/Magento/Sales/Block/Order/HistoryTest.php
new file mode 100644
index 0000000000000..d7e342c096dc9
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Sales/Block/Order/HistoryTest.php
@@ -0,0 +1,177 @@
+objectManager = Bootstrap::getObjectManager();
+ $this->customerSession = $this->objectManager->get(Session::class);
+ $this->block = $this->objectManager->get(LayoutInterface::class)->createBlock(History::class);
+ $this->orderFactory = $this->objectManager->get(OrderInterfaceFactory::class);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function tearDown()
+ {
+ $this->customerSession->logout();
+
+ parent::tearDown();
+ }
+
+ /**
+ * @magentoDataFixture Magento/Customer/_files/customer.php
+ *
+ * @return void
+ */
+ public function testCustomerOrderGridWithoutOrders(): void
+ {
+ $this->customerSession->loginById(1);
+ $this->assertContains((string)$this->block->getEmptyOrdersMessage(), strip_tags($this->block->toHtml()));
+ }
+
+ /**
+ * @magentoDataFixture Magento/Sales/_files/order_with_customer.php
+ *
+ * @return void
+ */
+ public function testCustomerOrderGridWithOrder(): void
+ {
+ $this->customerSession->loginById(1);
+ $this->assertCustomerOrderGrid(['100000001'], $this->block->toHtml());
+ }
+
+ /**
+ * @magentoDataFixture Magento/Customer/_files/customer.php
+ * @magentoDataFixture Magento/Sales/_files/orders_with_customer.php
+ *
+ * @return void
+ */
+ public function testCustomerOrderGridWithSomeOrders(): void
+ {
+ $this->customerSession->loginById(1);
+ $ordersToCheck = ['100000002', '100000003', '100000004', '100000005', '100000006'];
+ $this->assertCustomerOrderGrid($ordersToCheck, $this->block->toHtml());
+ }
+
+ /**
+ * Assert customer order grid.
+ *
+ * @param array $ordersToCheck
+ * @param string $blockHtml
+ * @return void
+ */
+ private function assertCustomerOrderGrid(array $ordersToCheck, string $blockHtml): void
+ {
+ foreach ($ordersToCheck as $orderIncrementId) {
+ $order = $this->orderFactory->create()->loadByIncrementId($orderIncrementId);
+ $rowXpath = sprintf("//td[contains(@class,'id') and contains(text(), '%s')]", $order->getRealOrderId());
+ $this->assertEquals(
+ 1,
+ Xpath::getElementsCountForXpath(
+ sprintf(
+ $rowXpath . "/following-sibling::td[contains(@class, 'date') and contains(text(), '%s')]",
+ $this->block->formatDate($order->getCreatedAt())
+ ),
+ $blockHtml
+ ),
+ sprintf('Created date for order #%s wasn\'t found in row.', $orderIncrementId)
+ );
+ $this->assertEquals(
+ 1,
+ Xpath::getElementsCountForXpath(
+ sprintf(
+ $rowXpath . "/following-sibling::td[contains(@class, 'total')]/span[contains(text(), '%s')]",
+ $order->getTotal()
+ ),
+ $blockHtml
+ ),
+ sprintf(
+ 'Order Totals for order #%s wasn\'t found or not equal to "%s" in row.',
+ $orderIncrementId,
+ $order->getTotal()
+ )
+ );
+ $this->assertEquals(
+ 1,
+ Xpath::getElementsCountForXpath(
+ sprintf(
+ $rowXpath . "/following-sibling::td[contains(@class, 'status') and contains(text(), '%s')]",
+ $order->getStatusLabel()
+ ),
+ $blockHtml
+ ),
+ sprintf(
+ 'Order status for order #%s wasn\'t found or not equal to "%s" in row.',
+ $orderIncrementId,
+ $order->getStatusLabel()
+ )
+ );
+ $this->assertEquals(
+ 1,
+ Xpath::getElementsCountForXpath(
+ sprintf(
+ $rowXpath . "/following-sibling::td/a[contains(@href, 'sales/order/view/order_id/%s')]"
+ . "/span[contains(text(), '%s')]",
+ $order->getId(),
+ __('View Order')
+ ),
+ $blockHtml
+ ),
+ sprintf('View order button for order #%s wasn\'t found.', $orderIncrementId)
+ );
+ $this->assertEquals(
+ 1,
+ Xpath::getElementsCountForXpath(
+ sprintf(
+ $rowXpath . "/following-sibling::td/a[contains(@data-post,"
+ . "'sales\/order\/reorder\/order_id\/%s')]/span[contains(text(), '%s')]",
+ $order->getId(),
+ __('Reorder')
+ ),
+ $blockHtml
+ ),
+ sprintf('Reorder button for order #%s wasn\'t found.', $orderIncrementId)
+ );
+ }
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Sales/Block/Order/Info/ButtonsTest.php b/dev/tests/integration/testsuite/Magento/Sales/Block/Order/Info/ButtonsTest.php
new file mode 100644
index 0000000000000..676415629fdc9
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Sales/Block/Order/Info/ButtonsTest.php
@@ -0,0 +1,115 @@
+objectManager = Bootstrap::getObjectManager();
+ $this->registry = $this->objectManager->get(Registry::class);
+ $this->customerSession = $this->objectManager->get(Session::class);
+ $this->order = $this->objectManager->get(OrderInterface::class);
+ $this->block = $this->objectManager->get(LayoutInterface::class)->createBlock(Buttons::class);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function tearDown()
+ {
+ $this->registry->unregister('current_order');
+ $this->customerSession->logout();
+
+ parent::tearDown();
+ }
+
+ /**
+ * @magentoDataFixture Magento/Sales/_files/order_with_customer.php
+ *
+ * @return void
+ */
+ public function testDisplayingOrderActionButtons(): void
+ {
+ $this->customerSession->loginById(1);
+ $order = $this->order->loadByIncrementId('100000001');
+ $this->registerOrder($order);
+ $blockHtml = $this->block->toHtml();
+ $this->assertEquals(
+ 1,
+ Xpath::getElementsCountForXpath(
+ sprintf(
+ "//a[contains(@data-post, 'sales\/order\/reorder\/order_id\/%s')]/span[contains(text(), '%s')]",
+ $order->getId(),
+ __('Reorder')
+ ),
+ $blockHtml
+ ),
+ sprintf('%s button wasn\'t found.', __('Reorder'))
+ );
+ $this->assertEquals(
+ 1,
+ Xpath::getElementsCountForXpath(
+ sprintf(
+ "//a[contains(@href, 'sales/order/print/order_id/%s')]/span[contains(text(), '%s')]",
+ $order->getId(),
+ __('Print Order')
+ ),
+ $blockHtml
+ ),
+ sprintf('%s button wasn\'t found.', __('Print Order'))
+ );
+ }
+
+ /**
+ * Register order in registry.
+ *
+ * @param OrderInterface $order
+ * @return void
+ */
+ private function registerOrder(OrderInterface $order): void
+ {
+ $this->registry->unregister('current_order');
+ $this->registry->register('current_order', $order);
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Sales/Block/Order/InfoTest.php b/dev/tests/integration/testsuite/Magento/Sales/Block/Order/InfoTest.php
new file mode 100644
index 0000000000000..1b84db76869e3
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Sales/Block/Order/InfoTest.php
@@ -0,0 +1,172 @@
+objectManager = Bootstrap::getObjectManager();
+ $this->registry = $this->objectManager->get(Registry::class);
+ $this->layout = $this->objectManager->get(LayoutInterface::class);
+ $this->orderFactory = $this->objectManager->get(OrderInterfaceFactory::class);
+ $this->countryFactory = $this->objectManager->get(CountryFactory::class);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function tearDown()
+ {
+ $this->registry->unregister('current_order');
+
+ parent::tearDown();
+ }
+
+ /**
+ * @magentoDataFixture Magento/Sales/_files/order_with_customer.php
+ *
+ * @return void
+ */
+ public function testOrderStatus(): void
+ {
+ $order = $this->orderFactory->create()->loadByIncrementId('100000001');
+ $this->registerOrder($order);
+ $block = $this->layout->createBlock(Info::class)->setTemplate('Magento_Sales::order/order_status.phtml');
+ $this->assertContains((string)__($order->getStatusLabel()), strip_tags($block->toHtml()));
+ }
+
+ /**
+ * @magentoDataFixture Magento/Sales/_files/order_with_customer.php
+ *
+ * @return void
+ */
+ public function testOrderDate(): void
+ {
+ $order = $this->orderFactory->create()->loadByIncrementId('100000001');
+ $this->registerOrder($order);
+ $block = $this->layout->createBlock(Info::class)->setTemplate('Magento_Sales::order/order_date.phtml');
+ $this->assertContains(
+ (string)__('Order Date: %1', $block->formatDate($order->getCreatedAt(), \IntlDateFormatter::LONG)),
+ strip_tags($block->toHtml())
+ );
+ }
+
+ /**
+ * @magentoDataFixture Magento/Sales/_files/customer_order_with_two_items.php
+ *
+ * @return void
+ */
+ public function testOrderInformation(): void
+ {
+ $order = $this->orderFactory->create()->loadByIncrementId('100000555');
+ $this->registerOrder($order);
+ $blockHtml = $this->layout->createBlock(Info::class)->toHtml();
+ $this->assertOrderAddress($order->getShippingAddress(), $blockHtml);
+ $this->assertOrderAddress($order->getBillingAddress(), $blockHtml);
+ $this->assertEquals(
+ 1,
+ Xpath::getElementsCountForXpath(
+ sprintf(
+ "//div[contains(@class, 'box-order-shipping-method') and contains(.//span, '%s')]"
+ . "//div[contains(text(), '%s')]",
+ __('Shipping Method'),
+ $order->getShippingDescription()
+ ),
+ $blockHtml
+ ),
+ 'Shipping method for order wasn\'t found.'
+ );
+ }
+
+ /**
+ * Assert order address.
+ *
+ * @param OrderAddressInterface $address
+ * @param string $html
+ * @return void
+ */
+ private function assertOrderAddress(OrderAddressInterface $address, string $html): void
+ {
+ $addressBoxXpath = sprintf("//div[contains(@class, 'box-order-%s-address')]", $address->getAddressType())
+ . "//address[contains(., '%s')]";
+ $this->assertEquals(
+ 1,
+ Xpath::getElementsCountForXpath(sprintf($addressBoxXpath, $address->getName()), $html),
+ sprintf('Customer name for %s address wasn\'t found.', $address->getAddressType())
+ );
+ $this->assertEquals(
+ 1,
+ Xpath::getElementsCountForXpath(
+ sprintf(
+ $addressBoxXpath,
+ $this->countryFactory->create()->loadByCode($address->getData('country_id'))->getName()
+ ),
+ $html
+ ),
+ sprintf('Country for %s address wasn\'t found.', $address->getAddressType())
+ );
+ $attributes = ['company', 'street', 'city', 'region', 'postcode', 'telephone'];
+ foreach ($attributes as $key) {
+ $this->assertEquals(
+ 1,
+ Xpath::getElementsCountForXpath(sprintf($addressBoxXpath, $address->getData($key)), $html),
+ sprintf('%s for %s address wasn\'t found.', $key, $address->getAddressType())
+ );
+ }
+ }
+
+ /**
+ * Register order in registry.
+ *
+ * @param OrderInterface $order
+ * @return void
+ */
+ private function registerOrder(OrderInterface $order): void
+ {
+ $this->registry->unregister('current_order');
+ $this->registry->register('current_order', $order);
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Sales/Block/Order/Invoice/ItemsTest.php b/dev/tests/integration/testsuite/Magento/Sales/Block/Order/Invoice/ItemsTest.php
index cfb97ee4298ec..bf13d5a296eee 100644
--- a/dev/tests/integration/testsuite/Magento/Sales/Block/Order/Invoice/ItemsTest.php
+++ b/dev/tests/integration/testsuite/Magento/Sales/Block/Order/Invoice/ItemsTest.php
@@ -3,70 +3,243 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+declare(strict_types=1);
+
namespace Magento\Sales\Block\Order\Invoice;
-class ItemsTest extends \PHPUnit\Framework\TestCase
+use Magento\Framework\ObjectManagerInterface;
+use Magento\Framework\Registry;
+use Magento\Framework\View\Element\Text;
+use Magento\Framework\View\LayoutInterface;
+use Magento\Framework\View\Result\PageFactory;
+use Magento\Sales\Api\Data\InvoiceInterface;
+use Magento\Sales\Api\Data\OrderInterface;
+use Magento\Sales\Api\Data\OrderInterfaceFactory;
+use Magento\Sales\Api\Data\InvoiceInterfaceFactory;
+use Magento\TestFramework\Helper\Bootstrap;
+use Magento\TestFramework\Helper\Xpath;
+use PHPUnit\Framework\TestCase;
+
+/**
+ * Tests for view invoice items block.
+ *
+ * @magentoAppArea frontend
+ * @magentoDbIsolation enabled
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
+ */
+class ItemsTest extends TestCase
{
- /**
- * @var \Magento\Framework\View\LayoutInterface
- */
- protected $_layout;
+ /** @var ObjectManagerInterface */
+ private $objectManager;
+
+ /** @var Registry */
+ private $registry;
+
+ /** @var LayoutInterface */
+ private $layout;
+
+ /** @var Items */
+ private $block;
+
+ /** @var InvoiceInterfaceFactory */
+ private $invoiceFactory;
+
+ /** @var OrderInterfaceFactory */
+ private $orderFactory;
+
+ /** @var PageFactory */
+ private $pageFactory;
/**
- * @var \Magento\Sales\Block\Order\Invoice\Items
+ * @inheritdoc
*/
- protected $_block;
+ protected function setUp()
+ {
+ $this->objectManager = Bootstrap::getObjectManager();
+ $this->registry = $this->objectManager->get(Registry::class);
+ $this->layout = $this->objectManager->get(LayoutInterface::class);
+ $this->block = $this->layout->createBlock(Items::class, 'block');
+ $this->invoiceFactory = $this->objectManager->get(InvoiceInterfaceFactory::class);
+ $this->orderFactory = $this->objectManager->get(OrderInterfaceFactory::class);
+ $this->pageFactory = $this->objectManager->get(PageFactory::class);
+ }
/**
- * @var \Magento\Sales\Model\Order\Invoice
+ * @inheritdoc
*/
- protected $_invoice;
-
- protected function setUp()
+ protected function tearDown()
{
- $this->_layout = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(
- \Magento\Framework\View\LayoutInterface::class
- );
- $this->_block = $this->_layout->createBlock(\Magento\Sales\Block\Order\Invoice\Items::class, 'block');
- $this->_invoice = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(
- \Magento\Sales\Model\Order\Invoice::class
- );
+ $this->registry->unregister('current_order');
+
+ parent::tearDown();
}
/**
* @magentoAppIsolation enabled
+ *
+ * @return void
*/
- public function testGetInvoiceTotalsHtml()
+ public function testGetInvoiceTotalsHtml(): void
{
- $childBlock = $this->_layout->addBlock(\Magento\Framework\View\Element\Text::class, 'invoice_totals', 'block');
-
+ $childBlock = $this->layout->addBlock(Text::class, 'invoice_totals', 'block');
$expectedHtml = '
Any html';
$this->assertEmpty($childBlock->getInvoice());
- $this->assertNotEquals($expectedHtml, $this->_block->getInvoiceTotalsHtml($this->_invoice));
-
+ $invoice = $this->invoiceFactory->create();
+ $this->assertNotEquals($expectedHtml, $this->block->getInvoiceTotalsHtml($invoice));
$childBlock->setText($expectedHtml);
- $actualHtml = $this->_block->getInvoiceTotalsHtml($this->_invoice);
- $this->assertSame($this->_invoice, $childBlock->getInvoice());
+ $actualHtml = $this->block->getInvoiceTotalsHtml($invoice);
+ $this->assertSame($invoice, $childBlock->getInvoice());
$this->assertEquals($expectedHtml, $actualHtml);
}
- public function testGetInvoiceCommentsHtml()
+ /**
+ * @magentoAppIsolation enabled
+ *
+ * @return void
+ */
+ public function testGetInvoiceCommentsHtml(): void
{
- $childBlock = $this->_layout->addBlock(
- \Magento\Framework\View\Element\Text::class,
+ $childBlock = $this->layout->addBlock(
+ Text::class,
'invoice_comments',
'block'
);
-
$expectedHtml = '
Any html';
$this->assertEmpty($childBlock->getEntity());
$this->assertEmpty($childBlock->getTitle());
- $this->assertNotEquals($expectedHtml, $this->_block->getInvoiceCommentsHtml($this->_invoice));
-
+ $invoice = $this->invoiceFactory->create();
+ $this->assertNotEquals($expectedHtml, $this->block->getInvoiceCommentsHtml($invoice));
$childBlock->setText($expectedHtml);
- $actualHtml = $this->_block->getInvoiceCommentsHtml($this->_invoice);
- $this->assertSame($this->_invoice, $childBlock->getEntity());
+ $actualHtml = $this->block->getInvoiceCommentsHtml($invoice);
+ $this->assertSame($invoice, $childBlock->getEntity());
$this->assertNotEmpty($childBlock->getTitle());
$this->assertEquals($expectedHtml, $actualHtml);
}
+
+ /**
+ * @magentoDataFixture Magento/Sales/_files/invoices_for_items.php
+ *
+ * @return void
+ */
+ public function testDisplayingInvoices(): void
+ {
+ $order = $this->orderFactory->create()->loadByIncrementId('100000555');
+ $this->registerOrder($order);
+ $blockHtml = $this->renderInvoiceItemsBlock();
+ $this->assertInvoicesBlock($order, $blockHtml);
+ }
+
+ /**
+ * Assert invoices block.
+ *
+ * @param OrderInterface $order
+ * @param string $blockHtml
+ * @return void
+ */
+ private function assertInvoicesBlock(OrderInterface $order, string $blockHtml): void
+ {
+ $this->assertEquals(
+ 1,
+ Xpath::getElementsCountForXpath(
+ sprintf(
+ "//a[contains(@href, 'sales/order/printInvoice/order_id/%s')]/span[contains(text(), '%s')]",
+ $order->getId(),
+ __('Print All Invoices')
+ ),
+ $blockHtml
+ ),
+ sprintf('%s button was not found.', __('Print All Invoices'))
+ );
+ $this->assertNotCount(0, $order->getInvoiceCollection(), 'Invoice collection is empty');
+ foreach ($order->getInvoiceCollection() as $invoice) {
+ $this->assertEquals(
+ 1,
+ Xpath::getElementsCountForXpath(
+ sprintf(
+ "//div[contains(@class, 'order-title')]/strong[contains(text(), '%s')]",
+ __('Invoice #') . $invoice->getIncrementId()
+ ),
+ $blockHtml
+ ),
+ sprintf('Title for %s was not found.', __('Invoice #') . $invoice->getIncrementId())
+ );
+ $this->assertEquals(
+ 1,
+ Xpath::getElementsCountForXpath(
+ sprintf(
+ "//a[contains(@href, 'sales/order/printInvoice/invoice_id/%s')]/span[contains(text(), '%s')]",
+ $invoice->getId(),
+ __('Print Invoice')
+ ),
+ $blockHtml
+ ),
+ sprintf('%s button for #%s was not found.', __('Print Invoice'), $invoice->getIncrementId())
+ );
+ $this->assertInvoiceItems($invoice, $blockHtml);
+ }
+ }
+
+ /**
+ * Assert invoice items list.
+ *
+ * @param InvoiceInterface $invoice
+ * @param string $blockHtml
+ * @return void
+ */
+ private function assertInvoiceItems(InvoiceInterface $invoice, string $blockHtml): void
+ {
+ $this->assertNotCount(0, $invoice->getItemsCollection(), 'Invoice items collection is empty');
+ $fieldsToCheck = [
+ 'name' => "/td[contains(@class, 'name')]/strong[contains(text(), '%s')]",
+ 'sku' => "/td[contains(@class, 'sku') and contains(text(), '%s')]",
+ 'price' => "/td[contains(@class, 'price')]//span[contains(text(), '%01.2f')]",
+ 'qty' => "/td[contains(@class, 'qty')]/span[contains(text(), '%d')]",
+ 'row_total' => "/td[contains(@class, 'subtotal')]//span[contains(text(), '%01.2f')]",
+ ];
+ foreach ($invoice->getItemsCollection() as $item) {
+ $itemRowXpath = sprintf(
+ "//table[@id='my-invoice-table-%s']//tr[@id='order-item-row-%s']",
+ $invoice->getId(),
+ $item->getId()
+ );
+ foreach ($fieldsToCheck as $key => $xpath) {
+ $this->assertEquals(
+ 1,
+ Xpath::getElementsCountForXpath(sprintf($itemRowXpath . $xpath, $item->getData($key)), $blockHtml),
+ sprintf('Item %s wasn\'t found or not equals to %s.', $key, $item->getData($key))
+ );
+ }
+ }
+ }
+
+ /**
+ * Render invoice items block.
+ *
+ * @return string
+ */
+ private function renderInvoiceItemsBlock(): string
+ {
+ $page = $this->pageFactory->create();
+ $page->addHandle([
+ 'default',
+ 'sales_order_invoice',
+ ]);
+ $page->getLayout()->generateXml();
+ $invoiceItemsBlock = $page->getLayout()->getBlock('invoice_items')->unsetChild('invoice_totals');
+ $invoiceItemsBlock->getRequest()->setRouteName('sales')->setControllerName('order')->setActionName('invoice');
+
+ return $invoiceItemsBlock->toHtml();
+ }
+
+ /**
+ * Register order in registry.
+ *
+ * @param OrderInterface $order
+ * @return void
+ */
+ private function registerOrder(OrderInterface $order): void
+ {
+ $this->registry->unregister('current_order');
+ $this->registry->register('current_order', $order);
+ }
}
diff --git a/dev/tests/integration/testsuite/Magento/Sales/Block/Order/Invoice/TotalsTest.php b/dev/tests/integration/testsuite/Magento/Sales/Block/Order/Invoice/TotalsTest.php
new file mode 100644
index 0000000000000..1fe7e02833a2f
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Sales/Block/Order/Invoice/TotalsTest.php
@@ -0,0 +1,97 @@
+objectManager = Bootstrap::getObjectManager();
+ $this->block = $this->objectManager->get(LayoutInterface::class)->createBlock(Totals::class)
+ ->setTemplate('Magento_Sales::order/totals.phtml');
+ $this->orderFactory = $this->objectManager->get(OrderInterfaceFactory::class);
+ }
+
+ /**
+ * @magentoDataFixture Magento/Sales/_files/invoices_for_items.php
+ *
+ * @return void
+ */
+ public function testInvoiceTotalsBlock(): void
+ {
+ $order = $this->orderFactory->create()->loadByIncrementId('100000555');
+ $invoice = $order->getInvoiceCollection()->getFirstItem();
+ $this->assertNotNull($invoice->getId());
+ $blockHtml = $this->block->setOrder($order)->setInvoice($invoice)->toHtml();
+ $message = '"%s" for invoice wasn\'t found or not equals to %01.2f';
+ $this->assertEquals(
+ 1,
+ Xpath::getElementsCountForXpath(
+ sprintf(
+ "//th[contains(text(), '%s')]/following-sibling::td/span[contains(text(), '%01.2f')]",
+ __('Subtotal'),
+ $invoice->getSubtotal()
+ ),
+ $blockHtml
+ ),
+ sprintf($message, __('Subtotal'), $invoice->getSubtotal())
+ );
+ $this->assertEquals(
+ 1,
+ Xpath::getElementsCountForXpath(
+ sprintf(
+ "//th[contains(text(), '%s')]/following-sibling::td/span[contains(text(), '%01.2f')]",
+ __('Shipping & Handling'),
+ $invoice->getShippingAmount()
+ ),
+ $blockHtml
+ ),
+ sprintf($message, __('Shipping & Handling'), $invoice->getShippingAmount())
+ );
+ $this->assertEquals(
+ 1,
+ Xpath::getElementsCountForXpath(
+ sprintf(
+ "//tr[contains(@class, 'grand_total') and contains(.//strong, '%s')]"
+ . "//span[contains(text(), '%01.2f')]",
+ __('Grand Total'),
+ $invoice->getGrandTotal()
+ ),
+ $blockHtml
+ ),
+ sprintf($message, __('Grand Total'), $invoice->getGrandTotal())
+ );
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Sales/Block/Order/Item/Renderer/DefaultRendererTest.php b/dev/tests/integration/testsuite/Magento/Sales/Block/Order/Item/Renderer/DefaultRendererTest.php
new file mode 100644
index 0000000000000..147efc2cd6c78
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Sales/Block/Order/Item/Renderer/DefaultRendererTest.php
@@ -0,0 +1,111 @@
+objectManager = Bootstrap::getObjectManager();
+ $this->block = $this->objectManager->get(LayoutInterface::class)->createBlock(DefaultRenderer::class);
+ $this->orderFactory = $this->objectManager->get(OrderInterfaceFactory::class);
+ }
+
+ /**
+ * @magentoDataFixture Magento/Sales/_files/shipment_for_order_with_customer.php
+ *
+ * @return void
+ */
+ public function testDisplayingShipmentItem(): void
+ {
+ $order = $this->orderFactory->create()->loadByIncrementId('100000001');
+ $shipment = $order->getShipmentsCollection()->getFirstItem();
+ $this->assertNotNull($shipment->getId());
+ $item = $shipment->getAllItems()[0] ?? null;
+ $this->assertNotNull($item);
+ $blockHtml = $this->block->setTemplate('Magento_Sales::order/shipment/items/renderer/default.phtml')
+ ->setItem($item)->toHtml();
+ $this->assertEquals(
+ 1,
+ Xpath::getElementsCountForXpath(
+ sprintf(
+ "//td[contains(@class, 'name')]/strong[contains(text(), '%s')]",
+ $item->getName()
+ ),
+ $blockHtml
+ ),
+ sprintf('Item with name %s wasn\'t found.', $item->getName())
+ );
+ $this->assertEquals(
+ 1,
+ Xpath::getElementsCountForXpath(
+ sprintf(
+ "//td[contains(@class, 'sku') and contains(text(), '%s')]",
+ $item->getSku()
+ ),
+ $blockHtml
+ ),
+ sprintf('Item with sku %s wasn\'t found.', $item->getSku())
+ );
+ $this->assertEquals(
+ 1,
+ Xpath::getElementsCountForXpath(
+ sprintf(
+ "//td[contains(@class, 'qty') and contains(text(), '%d')]",
+ $item->getQty()
+ ),
+ $blockHtml
+ ),
+ sprintf(
+ 'Qty for item %s wasn\'t found or not equals to %s.',
+ $item->getName(),
+ $item->getQty()
+ )
+ );
+ }
+
+ /**
+ * @magentoDataFixture Magento/Sales/_files/refunds_for_items.php
+ *
+ * @return void
+ */
+ public function testCreditmemoItemTotalAmount(): void
+ {
+ $order = $this->orderFactory->create()->loadByIncrementId('100000555');
+ $creditmemo = $order->getCreditmemosCollection()->getFirstItem();
+ $this->assertNotNull($creditmemo->getId());
+ $item = $creditmemo->getItemsCollection()->getFirstItem();
+ $this->assertNotNull($item->getId());
+ $this->assertEquals(10.00, $this->block->getTotalAmount($item));
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Sales/Block/Order/ItemsTest.php b/dev/tests/integration/testsuite/Magento/Sales/Block/Order/ItemsTest.php
index 4c7b202fc1351..cb211b343cf0d 100644
--- a/dev/tests/integration/testsuite/Magento/Sales/Block/Order/ItemsTest.php
+++ b/dev/tests/integration/testsuite/Magento/Sales/Block/Order/ItemsTest.php
@@ -3,133 +3,228 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+declare(strict_types=1);
namespace Magento\Sales\Block\Order;
-class ItemsTest extends \PHPUnit\Framework\TestCase
+use Magento\Framework\ObjectManagerInterface;
+use Magento\Framework\Registry;
+use Magento\Framework\View\LayoutInterface;
+use Magento\Framework\View\Result\PageFactory;
+use Magento\Sales\Api\Data\OrderInterface;
+use Magento\Sales\Api\Data\OrderInterfaceFactory;
+use Magento\Sales\Model\ResourceModel\Order\Item\Collection;
+use Magento\TestFramework\Helper\Bootstrap;
+use Magento\TestFramework\Helper\Xpath;
+use Magento\Theme\Block\Html\Pager;
+use PHPUnit\Framework\TestCase;
+
+/**
+ * Tests order items block.
+ *
+ * @magentoAppArea frontend
+ * @magentoDbIsolation enabled
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
+ */
+class ItemsTest extends TestCase
{
- /**
- * @var \Magento\Sales\Block\Order\Items
- */
- private $model;
+ /** @var Items */
+ private $block;
- /**
- * @var \Magento\Framework\View\LayoutInterface
- */
+ /** @var LayoutInterface */
private $layout;
- /**
- * @var \Magento\Framework\ObjectManagerInterface
- */
+ /** @var ObjectManagerInterface */
private $objectManager;
- /**
- * @var \Magento\Framework\Registry
- */
+ /** @var Registry */
private $registry;
+ /** @var OrderInterfaceFactory */
+ private $orderFactory;
+
+ /** @var PageFactory */
+ private $pageFactory;
+
+ /**
+ * @inheritdoc
+ */
protected function setUp()
{
- $this->objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
- $this->layout = $this->objectManager->get(\Magento\Framework\View\LayoutInterface::class);
- $this->registry = $this->objectManager->get(\Magento\Framework\Registry::class);
+ $this->objectManager = Bootstrap::getObjectManager();
+ $this->layout = $this->objectManager->get(LayoutInterface::class);
+ $this->registry = $this->objectManager->get(Registry::class);
+ $this->orderFactory = $this->objectManager->get(OrderInterfaceFactory::class);
+ $this->pageFactory = $this->objectManager->get(PageFactory::class);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ protected function tearDown()
+ {
+ $this->registry->unregister('current_order');
+
+ parent::tearDown();
}
/**
* @magentoDataFixture Magento/Sales/_files/order.php
+ *
+ * @return void
*/
- public function testGetOrderItems()
+ public function testGetOrderItems(): void
{
- $this->registerOrder();
- $this->model = $this->layout->createBlock(\Magento\Sales\Block\Order\Items::class);
- $this->assertTrue(count($this->model->getItems()) > 0);
+ $order = $this->orderFactory->create()->loadByIncrementId('100000001');
+ $this->registerOrder($order);
+ $this->block = $this->layout->createBlock(Items::class);
+ $this->assertCount(1, $this->block->getItems());
}
/**
- * @magentoAppIsolation enabled
* @magentoConfigFixture default/sales/orders/items_per_page 3
* @magentoDataFixture Magento/Sales/_files/order_item_list.php
+ *
+ * @return void
*/
- public function testPagerIsDisplayed()
+ public function testPagerIsDisplayed(): void
{
- $this->registerOrder();
-
- /** @var \Magento\Sales\Block\Order\Items model */
- $this->model = $this->layout->createBlock(\Magento\Sales\Block\Order\Items::class, 'items_block');
+ $order = $this->orderFactory->create()->loadByIncrementId('100000001');
+ $this->registerOrder($order);
+ $this->block = $this->layout->createBlock(Items::class, 'items_block');
$this->layout->addBlock(
- $this->objectManager->get(\Magento\Theme\Block\Html\Pager::class),
+ $this->objectManager->get(Pager::class),
'sales_order_item_pager',
'items_block'
);
- $this->model->setLayout($this->layout);
-
- $this->assertTrue($this->model->isPagerDisplayed());
+ $this->block->setLayout($this->layout);
+ $this->assertTrue($this->block->isPagerDisplayed());
}
/**
* @magentoDataFixture Magento/Sales/_files/order_item_list.php
+ *
+ * @return void
*/
- public function testPagerIsNotDisplayed()
+ public function testPagerIsNotDisplayed(): void
{
- $this->registerOrder();
-
- /** @var \Magento\Sales\Block\Order\Items model */
- $this->model = $this->layout->createBlock(\Magento\Sales\Block\Order\Items::class, 'items_block');
+ $order = $this->orderFactory->create()->loadByIncrementId('100000001');
+ $this->registerOrder($order);
+ $this->block = $this->layout->createBlock(Items::class, 'items_block');
$this->layout->addBlock(
- $this->objectManager->get(\Magento\Theme\Block\Html\Pager::class),
+ $this->objectManager->get(Pager::class),
'sales_order_item_pager',
'items_block'
);
- $this->model->setLayout($this->layout);
-
- $this->assertFalse($this->model->isPagerDisplayed());
+ $this->block->setLayout($this->layout);
+ $this->assertFalse($this->block->isPagerDisplayed());
+ $this->assertEmpty(preg_replace('/\s+/', '', strip_tags($this->block->getPagerHtml())));
}
/**
- * @magentoAppIsolation enabled
- * @magentoAppArea frontend
* @magentoConfigFixture default/sales/orders/items_per_page 3
* @magentoDataFixture Magento/Sales/_files/order_item_list.php
+ *
+ * @return void
*/
- public function testGetPagerHtml()
+ public function testGetPagerHtml(): void
{
- $this->registerOrder();
-
- /** @var \Magento\Sales\Block\Order\Items model */
- $this->model = $this->layout->createBlock(\Magento\Sales\Block\Order\Items::class, 'items_block');
+ $order = $this->orderFactory->create()->loadByIncrementId('100000001');
+ $this->registerOrder($order);
+ $this->block = $this->layout->createBlock(Items::class, 'items_block');
$this->layout->addBlock(
- $this->objectManager->get(\Magento\Theme\Block\Html\Pager::class),
+ $this->objectManager->get(Pager::class),
'sales_order_item_pager',
'items_block'
);
- $this->model->setLayout($this->layout);
-
- $this->assertNotEmpty($this->model->getPagerHtml());
+ $this->block->setLayout($this->layout);
+ $this->assertNotEmpty(preg_replace('/\s+/', '', strip_tags($this->block->getPagerHtml())));
+ $this->assertTrue($this->block->isPagerDisplayed());
}
/**
* @magentoDataFixture Magento/Sales/_files/order.php
+ *
+ * @return void
+ */
+ public function testGetOrder(): void
+ {
+ $order = $this->orderFactory->create()->loadByIncrementId('100000001');
+ $this->registerOrder($order);
+ $this->block = $this->layout->createBlock(Items::class, 'items_block');
+ $this->assertEquals($order, $this->block->getOrder());
+ }
+
+ /**
+ * @magentoDataFixture Magento/Sales/_files/customer_order_with_two_items.php
+ *
+ * @return void
+ */
+ public function testDisplayingOrderItems(): void
+ {
+ $order = $this->orderFactory->create()->loadByIncrementId('100000555');
+ $this->registerOrder($order);
+ $blockHtml = $this->renderOrderItemsBlock();
+ $this->assertOrderItems($order->getItemsCollection(), $blockHtml);
+ }
+
+ /**
+ * Render order items block.
+ *
+ * @return string
*/
- public function testGetOrder()
+ private function renderOrderItemsBlock(): string
{
- $order = $this->registerOrder();
+ $page = $this->pageFactory->create();
+ $page->addHandle([
+ 'default',
+ 'sales_order_view',
+ ]);
+ $page->getLayout()->generateXml();
+ $orderItemsBlock = $page->getLayout()->getBlock('order_items')->unsetChild('order_totals');
+
+ return $orderItemsBlock->toHtml();
+ }
- /** @var \Magento\Sales\Block\Order\Items model */
- $this->model = $this->layout->createBlock(\Magento\Sales\Block\Order\Items::class, 'items_block');
- $this->assertEquals($order, $this->model->getOrder());
+ /**
+ * Assert order items list.
+ *
+ * @param Collection $orderItems
+ * @param string $blockHtml
+ * @return void
+ */
+ private function assertOrderItems(Collection $orderItems, string $blockHtml): void
+ {
+ $this->assertNotCount(0, $orderItems, 'Order items collection is empty');
+ $fieldsToCheck = [
+ 'name' => "/td[contains(@class, 'name')]/strong[contains(text(), '%s')]",
+ 'sku' => "/td[contains(@class, 'sku') and contains(text(), '%s')]",
+ 'price' => "/td[contains(@class, 'price')]//span[contains(text(), '%01.2f')]",
+ 'qty_ordered' => "/td[contains(@class, 'qty')]//span[contains(text(), '" . __('Ordered')
+ . "')]/following-sibling::span[contains(text(), '%d')]",
+ 'row_total' => "/td[contains(@class, 'subtotal')]//span[contains(text(), '%01.2f')]",
+ ];
+ foreach ($orderItems as $item) {
+ $itemRowXpath = sprintf("//tr[@id='order-item-row-%s']", $item->getItemId());
+ foreach ($fieldsToCheck as $key => $xpath) {
+ $this->assertEquals(
+ 1,
+ Xpath::getElementsCountForXpath(sprintf($itemRowXpath . $xpath, $item->getData($key)), $blockHtml),
+ sprintf('Item %s wasn\'t found or not equals to %s.', $key, $item->getData($key))
+ );
+ }
+ }
}
/**
- * Register order in registry
+ * Register order in registry.
*
- * @return \Magento\Sales\Model\Order
+ * @param OrderInterface $order
+ * @return void
*/
- private function registerOrder()
+ private function registerOrder(OrderInterface $order): void
{
- /** @var \Magento\Sales\Model\Order $order */
- $order = $this->objectManager->get(\Magento\Sales\Model\Order::class);
- $order->loadByIncrementId('100000001');
+ $this->registry->unregister('current_order');
$this->registry->register('current_order', $order);
- return $order;
}
}
diff --git a/dev/tests/integration/testsuite/Magento/Sales/Block/Order/TotalsTest.php b/dev/tests/integration/testsuite/Magento/Sales/Block/Order/TotalsTest.php
index b23e7bcc140d4..7361ddc985b21 100644
--- a/dev/tests/integration/testsuite/Magento/Sales/Block/Order/TotalsTest.php
+++ b/dev/tests/integration/testsuite/Magento/Sales/Block/Order/TotalsTest.php
@@ -3,54 +3,129 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+declare(strict_types=1);
+
namespace Magento\Sales\Block\Order;
+use Magento\Framework\ObjectManagerInterface;
+use Magento\Framework\View\Element\Context;
+use Magento\Framework\View\Element\Text;
+use Magento\Framework\View\Layout;
+use Magento\Framework\View\LayoutInterface;
+use Magento\Sales\Api\Data\OrderInterfaceFactory;
+use Magento\TestFramework\Helper\Bootstrap;
+use Magento\TestFramework\Helper\Xpath;
+use PHPUnit\Framework\TestCase;
+
/**
+ * Tests for order totals block.
+ *
* @magentoAppArea frontend
+ * @magentoDbIsolation enabled
*/
-class TotalsTest extends \PHPUnit\Framework\TestCase
+class TotalsTest extends TestCase
{
- public function testToHtmlChildrenInitialized()
+ /** @var ObjectManagerInterface */
+ private $objectManager;
+
+ /** @var Layout */
+ private $layout;
+
+ /** @var Totals */
+ private $block;
+
+ /** @var OrderInterfaceFactory */
+ private $orderFactory;
+
+ /**
+ * @inheritdoc
+ */
+ protected function setUp()
{
- \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(\Magento\Framework\App\State::class)
- ->setAreaCode('frontend');
+ parent::setUp();
- /** @var $layout \Magento\Framework\View\Layout */
- $layout = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(
- \Magento\Framework\View\LayoutInterface::class
- );
- /** @var \Magento\Sales\Block\Order\Totals $block */
- $block = $layout->createBlock(\Magento\Sales\Block\Order\Totals::class, 'block');
- $block->setOrder(
- \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->create(\Magento\Sales\Model\Order::class)
- )->setTemplate(
- 'order/totals.phtml'
- );
+ $this->objectManager = Bootstrap::getObjectManager();
+ $this->layout = $this->objectManager->get(LayoutInterface::class);
+ $this->block = $this->layout->createBlock(Totals::class, 'block');
+ $this->orderFactory = $this->objectManager->get(OrderInterfaceFactory::class);
+ }
- $context = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get(
- \Magento\Framework\View\Element\Context::class
- );
- $childOne = $this->getMockBuilder(\Magento\Framework\View\Element\Text::class)
+ /**
+ * @magentoAppIsolation enabled
+ *
+ * @return void
+ */
+ public function testToHtmlChildrenInitialized(): void
+ {
+ $this->block->setOrder($this->orderFactory->create())->setTemplate('order/totals.phtml');
+ $context = $this->objectManager->get(Context::class);
+ $childOne = $this->getMockBuilder(Text::class)
->setMethods(['initTotals'])
->setConstructorArgs([$context])
->getMock();
$childOne->expects($this->once())->method('initTotals');
- $layout->addBlock($childOne, 'child1', 'block');
-
- $childTwo = $this->getMockBuilder(\Magento\Framework\View\Element\Text::class)
+ $this->layout->addBlock($childOne, 'child1', 'block');
+ $childTwo = $this->getMockBuilder(Text::class)
->setMethods(['initTotals'])
->setConstructorArgs([$context])
->getMock();
$childTwo->expects($this->once())->method('initTotals');
- $layout->addBlock($childTwo, 'child2', 'block');
-
- $childThree = $this->getMockBuilder(\Magento\Framework\View\Element\Text::class)
+ $this->layout->addBlock($childTwo, 'child2', 'block');
+ $childThree = $this->getMockBuilder(Text::class)
->setMethods(['initTotals'])
->setConstructorArgs([$context])
->getMock();
$childThree->expects($this->once())->method('initTotals');
- $layout->addBlock($childThree, 'child3', 'block');
+ $this->layout->addBlock($childThree, 'child3', 'block');
+ $this->block->toHtml();
+ }
- $block->toHtml();
+ /**
+ * @magentoDataFixture Magento/Sales/_files/customer_order_with_two_items.php
+ *
+ * @return void
+ */
+ public function testOrderTotalsBlock(): void
+ {
+ $order = $this->orderFactory->create()->loadByIncrementId('100000555');
+ $blockHtml = $this->block->setTemplate('Magento_Sales::order/totals.phtml')->setOrder($order)->toHtml();
+ $message = '"%s" for order wasn\'t found or not equals to %01.2f';
+ $this->assertEquals(
+ 1,
+ Xpath::getElementsCountForXpath(
+ sprintf(
+ "//th[contains(text(), '%s')]/following-sibling::td/span[contains(text(), '%01.2f')]",
+ __('Subtotal'),
+ $order->getSubtotal()
+ ),
+ $blockHtml
+ ),
+ sprintf($message, __('Subtotal'), $order->getSubtotal())
+ );
+ $this->assertEquals(
+ 1,
+ Xpath::getElementsCountForXpath(
+ sprintf(
+ "//th[contains(text(), '%s')]/following-sibling::td/span[contains(text(), '%01.2f')]",
+ __('Shipping & Handling'),
+ $order->getShippingAmount()
+ ),
+ $blockHtml
+ ),
+ sprintf($message, __('Shipping & Handling'), $order->getShippingAmount())
+ );
+ $this->assertEquals(
+ 1,
+ Xpath::getElementsCountForXpath(
+ sprintf(
+ "//tr[contains(@class, 'grand_total') and contains(.//strong, '%s')]"
+ . "//span[contains(text(), '%01.2f')]",
+ __('Grand Total'),
+ $order->getGrandTotal()
+ ),
+ $blockHtml
+ ),
+ sprintf($message, __('Grand Total'), $order->getGrandTotal())
+ );
}
}
diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/customer_order_with_two_items.php b/dev/tests/integration/testsuite/Magento/Sales/_files/customer_order_with_two_items.php
new file mode 100644
index 0000000000000..5270b291a56d4
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Sales/_files/customer_order_with_two_items.php
@@ -0,0 +1,96 @@
+get(OrderInterfaceFactory::class);
+/** @var OrderItemInterfaceFactory $orderItemFactory */
+$orderItemFactory = $objectManager->get(OrderItemInterfaceFactory::class);
+/** @var OrderRepositoryInterface $orderRepository */
+$orderRepository = $objectManager->get(OrderRepositoryInterface::class);
+/** @var OrderAddressInterfaceFactory $orderAddressFactory */
+$orderAddressFactory = $objectManager->get(OrderAddressInterfaceFactory::class);
+/** @var OrderPaymentInterfaceFactory $orderPaymentFactory */
+$orderPaymentFactory = $objectManager->get(OrderPaymentInterfaceFactory::class);
+/** @var ProductRepositoryInterface $productRepository */
+$productRepository = $objectManager->get(ProductRepositoryInterface::class);
+$productRepository->cleanCache();
+
+$billingAddress = $orderAddressFactory->create(['data' => $customerAddress->getData()]);
+$billingAddress->setAddressType(Address::TYPE_BILLING);
+
+$shippingAddress = $orderAddressFactory->create(['data' => $customerAddress->getData()]);
+$shippingAddress->setAddressType(Address::TYPE_SHIPPING)
+ ->setStreet('street for shipping')
+ ->setRegion('North West')
+ ->setPostcode('GU16 7HF')
+ ->setShippingMethod('flatrate_flatrate');
+
+$orderPayment = $orderPaymentFactory->create();
+$orderPayment->setMethod('checkmo')
+ ->setAdditionalInformation('last_trans_id', '11122')
+ ->setAdditionalInformation(
+ 'metadata',
+ ['type' => 'free', 'fraudulent' => false]
+ );
+
+$firstProduct = $productRepository->get('simple-1');
+$firstOrderItem = $orderItemFactory->create();
+$firstOrderItem->setProductId($firstProduct->getId())
+ ->setQtyOrdered(1)
+ ->setBasePrice($firstProduct->getPrice())
+ ->setPrice($firstProduct->getPrice())
+ ->setRowTotal($firstProduct->getPrice())
+ ->setProductType(Type::TYPE_SIMPLE)
+ ->setName($firstProduct->getName())
+ ->setSku($firstProduct->getSku());
+
+$secondProduct = $productRepository->get('simple');
+$secondOrderItem = $orderItemFactory->create();
+$secondOrderItem->setProductId($secondProduct->getId())
+ ->setQtyOrdered(1)
+ ->setBasePrice($secondProduct->getPrice())
+ ->setPrice($secondProduct->getPrice())
+ ->setRowTotal($secondProduct->getPrice())
+ ->setProductType(Type::TYPE_SIMPLE)
+ ->setName($secondProduct->getName())
+ ->setSku($secondProduct->getSku());
+
+$order = $orderFactory->create();
+$order->setIncrementId('100000555')
+ ->setState(Order::STATE_PROCESSING)
+ ->setStatus(Order::STATE_PROCESSING)
+ ->setSubtotal(20)
+ ->setShippingAmount(10)
+ ->setGrandTotal(30)
+ ->setBaseSubtotal(20)
+ ->setBaseShippingAmount(10)
+ ->setBaseGrandTotal(30)
+ ->setCustomerIsGuest(false)
+ ->setCustomerEmail($customerDataModel->getEmail())
+ ->setCustomerId($customerDataModel->getId())
+ ->setBillingAddress($billingAddress)
+ ->setShippingAddress($shippingAddress)
+ ->setShippingDescription('Flat Rate - Fixed')
+ ->setStoreId($mainWebsite->getDefaultStore()->getId())
+ ->addItem($firstOrderItem)
+ ->addItem($secondOrderItem)
+ ->setPayment($orderPayment);
+$orderRepository->save($order);
diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/customer_order_with_two_items_rollback.php b/dev/tests/integration/testsuite/Magento/Sales/_files/customer_order_with_two_items_rollback.php
new file mode 100644
index 0000000000000..a6225b247466e
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Sales/_files/customer_order_with_two_items_rollback.php
@@ -0,0 +1,29 @@
+get(OrderRepositoryInterface::class);
+/** @var OrderInterfaceFactory $orderFactory */
+$orderFactory = $objectManager->create(OrderInterfaceFactory::class);
+
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', true);
+
+$order = $orderFactory->create()->loadByIncrementId('100000555');
+if ($order->getId()) {
+ $orderRepository->delete($order);
+}
+
+$registry->unregister('isSecureArea');
+$registry->register('isSecureArea', false);
diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/invoices_for_items.php b/dev/tests/integration/testsuite/Magento/Sales/_files/invoices_for_items.php
new file mode 100644
index 0000000000000..dbec254b69482
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Sales/_files/invoices_for_items.php
@@ -0,0 +1,23 @@
+get(InvoiceItemCreationInterfaceFactory::class);
+/** @var InvoiceOrderInterface $invoiceOrder */
+$invoiceOrder = $objectManager->get(InvoiceOrderInterface::class);
+
+foreach ($order->getItems() as $orderItem) {
+ $invoiceItem = $invoiceItemFactory->create();
+ $invoiceItem->setOrderItemId($orderItem->getItemId());
+ $invoiceItem->setQty($orderItem->getQtyOrdered());
+ $invoiceOrder->execute($order->getId(), false, [$invoiceItem]);
+}
diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/invoices_for_items_rollback.php b/dev/tests/integration/testsuite/Magento/Sales/_files/invoices_for_items_rollback.php
new file mode 100644
index 0000000000000..91200f3c1b46c
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Sales/_files/invoices_for_items_rollback.php
@@ -0,0 +1,8 @@
+get(CreditmemoItemCreationInterfaceFactory::class);
+/** @var RefundOrderInterface $refundOrder */
+$refundOrder = $objectManager->get(RefundOrderInterface::class);
+
+foreach ($order->getItems() as $item) {
+ $creditmemoItem = $creditmemoItemFactory->create();
+ $creditmemoItem->setOrderItemId($item->getId());
+ $creditmemoItem->setQty($item->getQtyOrdered());
+ $refundOrder->execute($order->getId(), [$creditmemoItem]);
+}
diff --git a/dev/tests/integration/testsuite/Magento/Sales/_files/refunds_for_items_rollback.php b/dev/tests/integration/testsuite/Magento/Sales/_files/refunds_for_items_rollback.php
new file mode 100644
index 0000000000000..67a4321c182ec
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Sales/_files/refunds_for_items_rollback.php
@@ -0,0 +1,8 @@
+disableOriginalConstructor()
->getMock();
$this->dataMock = $this->createMock(\Magento\Framework\Config\DataInterface::class);
+ $sanitizerMock = $this->createMock(Sanitizer::class);
+ $sanitizerMock->method('sanitize')->willReturnArgument(0);
+ $sanitizerMock->method('sanitizeComponentMetadata')->willReturnArgument(0);
$this->objectManagerHelper = new ObjectManagerHelper($this);
$this->model = $this->objectManagerHelper->getObject(
\Magento\Framework\View\Element\UiComponentFactory::class,
@@ -68,7 +73,8 @@ protected function setUp()
'configFactory' => $this->dataInterfaceFactoryMock,
'data' => [],
'componentChildFactories' => [],
- 'definitionData' => $this->dataMock
+ 'definitionData' => $this->dataMock,
+ 'sanitizer' => $sanitizerMock
]
);
}
diff --git a/dev/tests/integration/testsuite/Magento/Setup/Module/DataSetupTest.php b/dev/tests/integration/testsuite/Magento/Setup/Module/DataSetupTest.php
index 425840b44ba0e..4c35d9882a0ac 100644
--- a/dev/tests/integration/testsuite/Magento/Setup/Module/DataSetupTest.php
+++ b/dev/tests/integration/testsuite/Magento/Setup/Module/DataSetupTest.php
@@ -39,7 +39,7 @@ public function testUpdateTableRow()
}
/**
- * @expectedException \Zend_Db_Statement_Exception
+ * @expectedException \Magento\Framework\DB\Adapter\TableNotFoundException
*/
public function testDeleteTableRow()
{
@@ -47,8 +47,7 @@ public function testDeleteTableRow()
}
/**
- * @covers \Magento\Setup\Module\DataSetup::updateTableRow
- * @expectedException \Zend_Db_Statement_Exception
+ * @expectedException \Magento\Framework\DB\Adapter\TableNotFoundException
*/
public function testUpdateTableRowNameConversion()
{
diff --git a/dev/tests/integration/testsuite/Magento/Translation/Model/Js/DataProviderTest.php b/dev/tests/integration/testsuite/Magento/Translation/Model/Js/DataProviderTest.php
new file mode 100644
index 0000000000000..305beae5b9562
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Translation/Model/Js/DataProviderTest.php
@@ -0,0 +1,74 @@
+stringUtils = $objectManager->get(StringUtils::class);
+ $this->translate = $objectManager->get(TranslateInterface::class);
+ $this->translationDataProvider = $objectManager->get(DataProviderInterface::class);
+ }
+
+ /**
+ * Test translation data.
+ *
+ * @magentoAppArea frontend
+ * @magentoConfigFixture default_store dev/translate_inline/active 1
+ */
+ public function testGetData()
+ {
+ $expectedDictionary = ['Proceed to Checkout' => 'Proceed to Checkout - Translated'];
+
+ $this->stringUtils->saveTranslate('Proceed to Checkout', 'Proceed to Checkout - Translated');
+ $this->translate->setLocale('en_US')->loadData('frontend', true);
+ $dictionary = $this->translationDataProvider->getData('Magento/luma');
+ $this->assertEquals($expectedDictionary, $dictionary);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function tearDown()
+ {
+ try {
+ $this->stringUtils->deleteTranslate('Proceed to Checkout');
+ } catch (NoSuchEntityException $exception) {
+ // translate already deleted
+ }
+ }
+}
diff --git a/dev/tests/integration/testsuite/Magento/Ui/Component/Form/Element/MultiSelectTest.php b/dev/tests/integration/testsuite/Magento/Ui/Component/Form/Element/MultiSelectTest.php
new file mode 100644
index 0000000000000..6c7e369419a55
--- /dev/null
+++ b/dev/tests/integration/testsuite/Magento/Ui/Component/Form/Element/MultiSelectTest.php
@@ -0,0 +1,89 @@
+factory = Bootstrap::getObjectManager()->get(MultiSelectFactory::class);
+ }
+
+ /**
+ * Options data to verify
+ *
+ * @return array
+ */
+ public function getTestOptions(): array
+ {
+ return [
+ 'List' => [
+ [
+ ['value' => '${\'my-value\'}', 'label' => 'My label'],
+ ['value' => '1', 'label' => 'Label'],
+ ['value' => '${\'my-value-2\'}', 'label' => 'This is ${\'My label\'}']
+ ],
+ [
+ ['value' => '${\'my-value\'}', 'label' => 'My label', '__disableTmpl' => ['value' => true]],
+ ['value' => '1', 'label' => 'Label'],
+ [
+ 'value' => '${\'my-value-2\'}',
+ 'label' => 'This is ${\'My label\'}',
+ '__disableTmpl' => ['value' => true, 'label' => true]
+ ]
+ ]
+ ],
+ 'provider' => [
+ new class implements OptionSourceInterface
+ {
+ /**
+ * @inheritDoc
+ */
+ public function toOptionArray()
+ {
+ return [['value' => '${\'value\'}', 'label' => 'Test']];
+ }
+ },
+ [['value' => '${\'value\'}', 'label' => 'Test', '__disableTmpl' => ['value' => true]]]
+ ]
+ ];
+ }
+
+ /**
+ * Check that options received from an options provider properly initiated.
+ *
+ * @param array|OptionSourceInterface $options Options provided
+ * @param array $expected Expected initialized options
+ * @return void
+ * @dataProvider getTestOptions
+ */
+ public function testOptions($options, array $expected): void
+ {
+ /** @var MultiSelect $component */
+ $component = $this->factory->create(['options' => $options]);
+ $component->prepare();
+
+ $this->assertEquals($expected, $component->getData('config')['options']);
+ }
+}
diff --git a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/ui-select.test.js b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/ui-select.test.js
index ac6e230e7ed1c..f46ff6b30abbe 100644
--- a/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/ui-select.test.js
+++ b/dev/tests/js/jasmine/tests/app/code/Magento/Ui/base/js/form/ui-select.test.js
@@ -624,6 +624,24 @@ define([
expect(obj.options).toHaveBeenCalledWith([]);
expect(obj.processRequest).toHaveBeenCalledWith(searchKey, 1);
});
+ it('Should update cacheOptions if response was cached', function () {
+ var searchKey = 'cake',
+ searchResult = 'piece a cake';
+
+ obj.deviation = 30;
+ obj.cachedSearchResults = {
+ cake: {
+ options: [searchResult],
+ lastPage: 1,
+ total: 1
+ }
+ };
+
+ spyOn(obj, 'options');
+ obj.loadOptions(searchKey);
+ expect(obj.options).toHaveBeenCalledWith([searchResult]);
+ expect(obj.cacheOptions.plain).toContain(searchResult);
+ });
});
describe('"isSearchKeyCached" method', function () {
it('Should return false if searchKey has already been cached and total covers > 1 page', function () {
@@ -672,7 +690,7 @@ define([
});
});
describe('"processRequest" method', function () {
- it('Should store options successfully fetched from ajax request', function () {
+ it('Should store options and update cache successfully after fetched from ajax request', function () {
var ajaxRequest,
successfulAjaxResponse = {
options: {
@@ -686,6 +704,24 @@ define([
}
};
+ // place a number of options to cache prior fetch
+ obj.cacheOptions.plain = [{
+ '2053': {
+ value: '2057',
+ label: 'testProductName5a8ddfd933b5c',
+ 'is_active': 1,
+ path: 'testSku5a8ddfd933b5c',
+ optgroup: false
+ },
+ '2054': {
+ value: '2058',
+ label: 'testProductName5a8ddfd933b5c',
+ 'is_active': 1,
+ path: 'testSku5a8ddfd933b5c',
+ optgroup: false
+ }
+ }];
+
$.ajax = jasmine.createSpy().and.callFake(function (request) {
ajaxRequest = request.success.bind(obj);
});
@@ -693,7 +729,18 @@ define([
expect(obj.processRequest()).toBeUndefined();
ajaxRequest(successfulAjaxResponse);
- expect(JSON.stringify(obj.options())).toEqual(JSON.stringify([successfulAjaxResponse.options['2053']]));
+
+ expect(
+ JSON.stringify(obj.options())
+ ).toEqual(
+ JSON.stringify([successfulAjaxResponse.options['2053']])
+ );
+
+ expect(
+ JSON.stringify(obj.cacheOptions.plain)
+ ).toEqual(
+ JSON.stringify([successfulAjaxResponse.options['2053']])
+ );
});
});
});
diff --git a/dev/tests/static/framework/Magento/TestFramework/Dependency/AnalyticsConfigRule.php b/dev/tests/static/framework/Magento/TestFramework/Dependency/AnalyticsConfigRule.php
index b1a6da5e43822..d6f9ba6268cf9 100644
--- a/dev/tests/static/framework/Magento/TestFramework/Dependency/AnalyticsConfigRule.php
+++ b/dev/tests/static/framework/Magento/TestFramework/Dependency/AnalyticsConfigRule.php
@@ -30,7 +30,7 @@ public function getDependencyInfo($currentModule, $fileType, $file, &$contents)
$module = implode('\\', array_slice($classParts, 0, 2));
if (strtolower($currentModule) !== strtolower($module)) {
$dependenciesInfo[] = [
- 'module' => $module,
+ 'modules' => [$module],
'type' => RuleInterface::TYPE_HARD,
'source' => $file,
];
diff --git a/dev/tests/static/framework/Magento/TestFramework/Dependency/DbRule.php b/dev/tests/static/framework/Magento/TestFramework/Dependency/DbRule.php
index 3e3fa7686a296..cb3a9e2cc9c35 100644
--- a/dev/tests/static/framework/Magento/TestFramework/Dependency/DbRule.php
+++ b/dev/tests/static/framework/Magento/TestFramework/Dependency/DbRule.php
@@ -52,7 +52,7 @@ public function getDependencyInfo($currentModule, $fileType, $file, &$contents)
}
if (strtolower($currentModule) !== strtolower($this->_moduleTableMap[$table])) {
$dependenciesInfo[] = [
- 'module' => $this->_moduleTableMap[$table],
+ 'modules' => [$this->_moduleTableMap[$table]],
'type' => \Magento\TestFramework\Dependency\RuleInterface::TYPE_HARD,
'source' => $table,
];
@@ -61,7 +61,7 @@ public function getDependencyInfo($currentModule, $fileType, $file, &$contents)
}
foreach ($unKnowTables as $tables) {
foreach ($tables as $table) {
- $dependenciesInfo[] = ['module' => 'Unknown', 'source' => $table];
+ $dependenciesInfo[] = ['modules' => ['Unknown'], 'source' => $table];
}
}
return $dependenciesInfo;
diff --git a/dev/tests/static/framework/Magento/TestFramework/Dependency/DiRule.php b/dev/tests/static/framework/Magento/TestFramework/Dependency/DiRule.php
index b3ee0a2880308..122c3da3afcb1 100644
--- a/dev/tests/static/framework/Magento/TestFramework/Dependency/DiRule.php
+++ b/dev/tests/static/framework/Magento/TestFramework/Dependency/DiRule.php
@@ -93,7 +93,7 @@ public function getDependencyInfo($currentModule, $fileType, $file, &$contents)
continue;
}
$dependenciesInfo[] = [
- 'module' => $referenceModule,
+ 'modules' => [$referenceModule],
'type' => $type,
'source' => $matches['class'],
];
diff --git a/dev/tests/static/framework/Magento/TestFramework/Dependency/LayoutRule.php b/dev/tests/static/framework/Magento/TestFramework/Dependency/LayoutRule.php
index 27017c6069538..75825077e03ec 100644
--- a/dev/tests/static/framework/Magento/TestFramework/Dependency/LayoutRule.php
+++ b/dev/tests/static/framework/Magento/TestFramework/Dependency/LayoutRule.php
@@ -133,8 +133,8 @@ public function getDependencyInfo($currentModule, $fileType, $file, &$contents)
*
* Ex.:
*
- * @param $currentModule
- * @param $contents
+ * @param string $currentModule
+ * @param string $contents
* @return array
*/
protected function _caseAttributeModule($currentModule, &$contents)
@@ -154,8 +154,8 @@ protected function _caseAttributeModule($currentModule, &$contents)
* Ex.:
*
*
- * @param $currentModule
- * @param $contents
+ * @param string $currentModule
+ * @param string $contents
* @return array
*/
protected function _caseElementBlock($currentModule, &$contents)
@@ -182,8 +182,8 @@ protected function _caseElementBlock($currentModule, &$contents)
* {path}
*
*
- * @param $currentModule
- * @param $contents
+ * @param string $currentModule
+ * @param string $contents
* @return array
*/
protected function _caseElementAction($currentModule, &$contents)
@@ -218,9 +218,9 @@ protected function _caseElementAction($currentModule, &$contents)
*
* Ex.: <{name}>...
*
- * @param $currentModule
- * @param $file
- * @param $contents
+ * @param string $currentModule
+ * @param string $file
+ * @param string $contents
* @return array
*/
protected function _caseLayoutHandle($currentModule, $file, &$contents)
@@ -235,11 +235,8 @@ protected function _caseLayoutHandle($currentModule, $file, &$contents)
$result = [];
foreach ((array)$xml->xpath('/layout/child::*') as $element) {
$check = $this->_checkDependencyLayoutHandle($currentModule, $area, $element->getName());
- $modules = isset($check['module']) ? $check['module'] : null;
+ $modules = isset($check['modules']) ? $check['modules'] : null;
if ($modules) {
- if (!is_array($modules)) {
- $modules = [$modules];
- }
foreach ($modules as $module) {
$result[$module] = [
'type' => \Magento\Test\Integrity\DependencyTest::TYPE_SOFT,
@@ -256,9 +253,9 @@ protected function _caseLayoutHandle($currentModule, $file, &$contents)
*
* Ex.:
*
- * @param $currentModule
- * @param $file
- * @param $contents
+ * @param string $currentModule
+ * @param string $file
+ * @param string $contents
* @return array
*/
protected function _caseLayoutHandleParent($currentModule, $file, &$contents)
@@ -273,11 +270,8 @@ protected function _caseLayoutHandleParent($currentModule, $file, &$contents)
$result = [];
foreach ((array)$xml->xpath('/layout/child::*/@parent') as $element) {
$check = $this->_checkDependencyLayoutHandle($currentModule, $area, (string)$element);
- $modules = isset($check['module']) ? $check['module'] : null;
+ $modules = isset($check['modules']) ? $check['modules'] : null;
if ($modules) {
- if (!is_array($modules)) {
- $modules = [$modules];
- }
foreach ($modules as $module) {
$result[$module] = [
'type' => \Magento\Test\Integrity\DependencyTest::TYPE_HARD,
@@ -294,9 +288,9 @@ protected function _caseLayoutHandleParent($currentModule, $file, &$contents)
*
* Ex.:
*
- * @param $currentModule
- * @param $file
- * @param $contents
+ * @param string $currentModule
+ * @param string $file
+ * @param string $contents
* @return array
*/
protected function _caseLayoutHandleUpdate($currentModule, $file, &$contents)
@@ -311,11 +305,8 @@ protected function _caseLayoutHandleUpdate($currentModule, $file, &$contents)
$result = [];
foreach ((array)$xml->xpath('//update/@handle') as $element) {
$check = $this->_checkDependencyLayoutHandle($currentModule, $area, (string)$element);
- $modules = isset($check['module']) ? $check['module'] : null;
+ $modules = isset($check['modules']) ? $check['modules'] : null;
if ($modules) {
- if (!is_array($modules)) {
- $modules = [$modules];
- }
foreach ($modules as $module) {
$result[$module] = [
'type' => \Magento\Test\Integrity\DependencyTest::TYPE_SOFT,
@@ -332,9 +323,9 @@ protected function _caseLayoutHandleUpdate($currentModule, $file, &$contents)
*
* Ex.:
*
- * @param $currentModule
- * @param $file
- * @param $contents
+ * @param string $currentModule
+ * @param string $file
+ * @param string $contents
* @return array
*/
protected function _caseLayoutReference($currentModule, $file, &$contents)
@@ -349,12 +340,14 @@ protected function _caseLayoutReference($currentModule, $file, &$contents)
$result = [];
foreach ((array)$xml->xpath('//reference/@name') as $element) {
$check = $this->_checkDependencyLayoutBlock($currentModule, $area, (string)$element);
- $module = isset($check['module']) ? $check['module'] : null;
- if ($module) {
- $result[$module] = [
- 'type' => \Magento\TestFramework\Dependency\RuleInterface::TYPE_SOFT,
- 'source' => (string)$element,
- ];
+ $modules = isset($check['modules']) ? $check['modules'] : null;
+ if ($modules) {
+ foreach ($modules as $module) {
+ $result[$module] = [
+ 'type' => \Magento\Test\Integrity\DependencyTest::TYPE_SOFT,
+ 'source' => (string)$element,
+ ];
+ }
}
}
return $this->_getUniqueDependencies($result);
@@ -363,8 +356,8 @@ protected function _caseLayoutReference($currentModule, $file, &$contents)
/**
* Search dependencies by defined regexp patterns
*
- * @param $currentModule
- * @param $contents
+ * @param string $currentModule
+ * @param string $contents
* @param array $patterns
* @return array
*/
@@ -390,14 +383,14 @@ protected function _checkDependenciesByRegexp($currentModule, &$contents, $patte
* Check layout handle dependency
*
* Return: array(
- * 'module' // dependent module
+ * 'modules' // dependent modules
* 'source' // source text
* )
*
- * @param $currentModule
- * @param $area
- * @param $handle
- * @return array
+ * @param admin $currentModule
+ * @param string $area
+ * @param string $handle
+ * @return string[]
*/
protected function _checkDependencyLayoutHandle($currentModule, $area, $handle)
{
@@ -411,7 +404,7 @@ protected function _checkDependencyLayoutHandle($currentModule, $area, $handle)
// CASE 1: Single dependency
$modules = $this->_mapRouters[$router];
if (!in_array($currentModule, $modules)) {
- return ['module' => $modules];
+ return ['modules' => $modules];
}
}
@@ -419,18 +412,18 @@ protected function _checkDependencyLayoutHandle($currentModule, $area, $handle)
// CASE 2: No dependencies
$modules = $this->_mapLayoutHandles[$area][$handle];
if (isset($modules[$currentModule])) {
- return ['module' => null];
+ return ['modules' => []];
}
// CASE 3: Single dependency
if (1 == count($modules)) {
- return ['module' => current($modules)];
+ return ['modules' => $modules];
}
// CASE 4: Default module dependency
$defaultModule = $this->_getDefaultModuleName($area);
if (isset($modules[$defaultModule])) {
- return ['module' => $defaultModule];
+ return ['modules' => [$defaultModule]];
}
}
@@ -441,14 +434,14 @@ protected function _checkDependencyLayoutHandle($currentModule, $area, $handle)
* Check layout block dependency
*
* Return: array(
- * 'module' // dependent module
+ * 'modules' // dependent modules
* 'source' // source text
* )
*
- * @param $currentModule
- * @param $area
- * @param $block
- * @return array
+ * @param string $currentModule
+ * @param string $area
+ * @param string $block
+ * @return string[]
*/
protected function _checkDependencyLayoutBlock($currentModule, $area, $block)
{
@@ -456,18 +449,18 @@ protected function _checkDependencyLayoutBlock($currentModule, $area, $block)
// CASE 1: No dependencies
$modules = $this->_mapLayoutBlocks[$area][$block];
if (isset($modules[$currentModule])) {
- return ['module' => null];
+ return ['modules' => []];
}
// CASE 2: Single dependency
if (1 == count($modules)) {
- return ['module' => current($modules)];
+ return ['modules' => $modules];
}
// CASE 3: Default module dependency
$defaultModule = $this->_getDefaultModuleName($area);
if (isset($modules[$defaultModule])) {
- return ['module' => $defaultModule];
+ return ['modules' => [$defaultModule]];
}
}
return [];
@@ -476,7 +469,7 @@ protected function _checkDependencyLayoutBlock($currentModule, $area, $block)
/**
* Get area from file path
*
- * @param $file
+ * @param string $file
* @return string
*/
protected function _getAreaByFile($file)
@@ -498,7 +491,7 @@ protected function _getUniqueDependencies($dependencies = [])
{
$result = [];
foreach ($dependencies as $module => $value) {
- $result[] = ['module' => $module, 'type' => $value['type'], 'source' => $value['source']];
+ $result[] = ['modules' => [$module], 'type' => $value['type'], 'source' => $value['source']];
}
return $result;
}
@@ -507,7 +500,7 @@ protected function _getUniqueDependencies($dependencies = [])
* Retrieve default module name (by area)
*
* @param string $area
- * @return null
+ * @return string|null
*/
protected function _getDefaultModuleName($area = 'default')
{
diff --git a/dev/tests/static/framework/Magento/TestFramework/Dependency/PhpRule.php b/dev/tests/static/framework/Magento/TestFramework/Dependency/PhpRule.php
index 913cc9448b978..7ae854885affa 100644
--- a/dev/tests/static/framework/Magento/TestFramework/Dependency/PhpRule.php
+++ b/dev/tests/static/framework/Magento/TestFramework/Dependency/PhpRule.php
@@ -94,7 +94,6 @@ public function __construct(
) {
$this->_mapRouters = $mapRouters;
$this->_mapLayoutBlocks = $mapLayoutBlocks;
- $this->_namespaces = implode('|', \Magento\Framework\App\Utility\Files::init()->getNamespaces());
$this->pluginMap = $pluginMap ?: null;
$this->routeMapper = new RouteMapper();
$this->whitelists = $whitelists;
@@ -184,7 +183,7 @@ private function caseClassesAndIdentifiers($currentModule, $file, &$contents)
}
$dependenciesInfo[] = [
- 'module' => $referenceModule,
+ 'modules' => [$referenceModule],
'type' => $dependencyType,
'source' => $dependencyClass,
];
@@ -317,11 +316,8 @@ protected function _caseGetUrl(string $currentModule, string &$contents): array
$actionName
);
if (!in_array($currentModule, $modules)) {
- if (count($modules) === 1) {
- $modules = reset($modules);
- }
$dependencies[] = [
- 'module' => $modules,
+ 'modules' => $modules,
'type' => RuleInterface::TYPE_HARD,
'source' => $item['source'],
];
@@ -361,12 +357,14 @@ protected function _caseLayoutBlock(string $currentModule, string $fileType, str
continue;
}
$check = $this->_checkDependencyLayoutBlock($currentModule, $area, $match['block']);
- $module = isset($check['module']) ? $check['module'] : null;
- if ($module) {
- $result[$module] = [
- 'type' => RuleInterface::TYPE_HARD,
- 'source' => $match['source'],
- ];
+ $modules = isset($check['modules']) ? $check['modules'] : null;
+ if ($modules) {
+ foreach ($modules as $module) {
+ $result[$module] = [
+ 'type' => RuleInterface::TYPE_HARD,
+ 'source' => $match['source'],
+ ];
+ }
}
}
return $this->_getUniqueDependencies($result);
@@ -395,7 +393,7 @@ protected function _getAreaByFile(string $file, string $fileType): ?string
* Check layout block dependency
*
* Return: array(
- * 'module' // dependent module
+ * 'modules' // dependent modules
* 'source' // source text
* )
*
@@ -419,16 +417,16 @@ protected function _checkDependencyLayoutBlock(string $currentModule, ?string $a
$modules = $this->_mapLayoutBlocks[$area][$block];
}
if (isset($modules[$currentModule])) {
- return ['module' => null];
+ return ['modules' => []];
}
// CASE 2: Single dependency
if (1 == count($modules)) {
- return ['module' => current($modules)];
+ return ['modules' => $modules];
}
// CASE 3: Default module dependency
$defaultModule = $this->_getDefaultModuleName($area);
if (isset($modules[$defaultModule])) {
- return ['module' => $defaultModule];
+ return ['modules' => [$defaultModule]];
}
}
// CASE 4: \Exception - Undefined block
@@ -459,7 +457,7 @@ protected function _getUniqueDependencies($dependencies = [])
{
$result = [];
foreach ($dependencies as $module => $value) {
- $result[] = ['module' => $module, 'type' => $value['type'], 'source' => $value['source']];
+ $result[] = ['modules' => [$module], 'type' => $value['type'], 'source' => $value['source']];
}
return $result;
}
diff --git a/dev/tests/static/framework/Magento/TestFramework/Dependency/ReportsConfigRule.php b/dev/tests/static/framework/Magento/TestFramework/Dependency/ReportsConfigRule.php
index ab450fba5781d..4c8daaebdd698 100644
--- a/dev/tests/static/framework/Magento/TestFramework/Dependency/ReportsConfigRule.php
+++ b/dev/tests/static/framework/Magento/TestFramework/Dependency/ReportsConfigRule.php
@@ -43,7 +43,7 @@ public function getDependencyInfo($currentModule, $fileType, $file, &$contents)
}
if (strtolower($currentModule) !== strtolower($this->moduleTableMap[$table])) {
$dependenciesInfo[] = [
- 'module' => $this->moduleTableMap[$table],
+ 'modules' => [$this->moduleTableMap[$table]],
'type' => RuleInterface::TYPE_HARD,
'source' => $table,
];
diff --git a/dev/tests/static/framework/Magento/TestFramework/Dependency/Route/RouteMapper.php b/dev/tests/static/framework/Magento/TestFramework/Dependency/Route/RouteMapper.php
index 315bb2ae26b02..6cc55b49edcca 100644
--- a/dev/tests/static/framework/Magento/TestFramework/Dependency/Route/RouteMapper.php
+++ b/dev/tests/static/framework/Magento/TestFramework/Dependency/Route/RouteMapper.php
@@ -154,7 +154,7 @@ function (&$modules) {
* @param string $routeId
* @param string $controllerName
* @param string $actionName
- * @return array
+ * @return string[]
* @throws NoSuchActionException
* @throws \Exception
*/
diff --git a/dev/tests/static/framework/tests/unit/testsuite/Magento/TestFramework/Dependency/DbRuleTest.php b/dev/tests/static/framework/tests/unit/testsuite/Magento/TestFramework/Dependency/DbRuleTest.php
index 986ddf72ec3d3..9a617ba9b8da2 100644
--- a/dev/tests/static/framework/tests/unit/testsuite/Magento/TestFramework/Dependency/DbRuleTest.php
+++ b/dev/tests/static/framework/tests/unit/testsuite/Magento/TestFramework/Dependency/DbRuleTest.php
@@ -37,7 +37,7 @@ public function getDependencyInfoDataProvider()
'any',
'/app/some/path/Setup/some-file.php',
'$install->getTableName("unknown_table")',
- [['module' => 'Unknown', 'source' => 'unknown_table']]
+ [['modules' => ['Unknown'], 'source' => 'unknown_table']]
],
[
'SomeModule',
@@ -51,7 +51,7 @@ public function getDependencyInfoDataProvider()
'$install->getTableName("some_table")',
[
[
- 'module' => 'SomeModule',
+ 'modules' => ['SomeModule'],
'type' => \Magento\TestFramework\Dependency\RuleInterface::TYPE_HARD,
'source' => 'some_table',
]
diff --git a/dev/tests/static/framework/tests/unit/testsuite/Magento/TestFramework/Dependency/DiRuleTest.php b/dev/tests/static/framework/tests/unit/testsuite/Magento/TestFramework/Dependency/DiRuleTest.php
index 1f4c11f17e9e5..aa69b72ef6419 100644
--- a/dev/tests/static/framework/tests/unit/testsuite/Magento/TestFramework/Dependency/DiRuleTest.php
+++ b/dev/tests/static/framework/tests/unit/testsuite/Magento/TestFramework/Dependency/DiRuleTest.php
@@ -48,32 +48,32 @@ public function getDependencyInfoDataProvider()
$this->getFileContent('di_external_dependency.xml'),
[
[
- 'module' => 'Magento\ExternalModule3',
+ 'modules' => ['Magento\ExternalModule3'],
'type' => RuleInterface::TYPE_SOFT,
'source' => 'Magento\ExternalModule3\Some\Another\Class'
],
[
- 'module' => 'Magento\ExternalModule5',
+ 'modules' => ['Magento\ExternalModule5'],
'type' => RuleInterface::TYPE_SOFT,
'source' => 'Magento\ExternalModule5\Some\Another\Class'
],
[
- 'module' => 'Magento\ExternalModule6',
+ 'modules' => ['Magento\ExternalModule6'],
'type' => RuleInterface::TYPE_SOFT,
'source' => 'Magento\ExternalModule6\Some\Plugin\Class'
],
[
- 'module' => 'Magento\ExternalModule1',
+ 'modules' => ['Magento\ExternalModule1'],
'type' => RuleInterface::TYPE_HARD,
'source' => 'Magento\ExternalModule1\Some\Argument1'
],
[
- 'module' => 'Magento\ExternalModule2',
+ 'modules' => ['Magento\ExternalModule2'],
'type' => RuleInterface::TYPE_HARD,
'source' => 'Magento\ExternalModule2\Some\Argument2'
],
[
- 'module' => 'Magento\ExternalModule4',
+ 'modules' => ['Magento\ExternalModule4'],
'type' => RuleInterface::TYPE_HARD,
'source' => 'Magento\ExternalModule4\Some\Argument3'
]
@@ -84,12 +84,12 @@ public function getDependencyInfoDataProvider()
$this->getFileContent('di_virtual_dependency.xml'),
[
[
- 'module' => 'Magento\AnotherModule',
+ 'modules' => ['Magento\AnotherModule'],
'type' => RuleInterface::TYPE_HARD,
'source' => 'Magento\AnotherModule\Some\Class1',
],
[
- 'module' => 'Magento\AnotherModule',
+ 'modules' => ['Magento\AnotherModule'],
'type' => RuleInterface::TYPE_HARD,
'source' => 'Magento\AnotherModule\Some\Class2',
]
diff --git a/dev/tests/static/framework/tests/unit/testsuite/Magento/TestFramework/Dependency/LayoutRuleTest.php b/dev/tests/static/framework/tests/unit/testsuite/Magento/TestFramework/Dependency/LayoutRuleTest.php
index dd0e90eefe4f5..1ee2e8ca24791 100644
--- a/dev/tests/static/framework/tests/unit/testsuite/Magento/TestFramework/Dependency/LayoutRuleTest.php
+++ b/dev/tests/static/framework/tests/unit/testsuite/Magento/TestFramework/Dependency/LayoutRuleTest.php
@@ -32,7 +32,7 @@ public function getDependencyInfoDataProvider()
'',
[
[
- 'module' => 'Magento\AnotherModule',
+ 'modules' => ['Magento\AnotherModule'],
'type' => \Magento\Test\Integrity\DependencyTest::TYPE_SOFT,
'source' => '',
]
@@ -43,7 +43,7 @@ public function getDependencyInfoDataProvider()
'',
[
[
- 'module' => 'Magento\AnotherModule',
+ 'modules' => ['Magento\AnotherModule'],
'type' => \Magento\Test\Integrity\DependencyTest::TYPE_HARD,
'source' => '',
]
@@ -56,7 +56,7 @@ public function getDependencyInfoDataProvider()
',
[
[
- 'module' => 'Magento\AnotherModule',
+ 'modules' => ['Magento\AnotherModule'],
'type' => \Magento\Test\Integrity\DependencyTest::TYPE_SOFT,
'source' => '',
]
@@ -67,7 +67,7 @@ public function getDependencyInfoDataProvider()
'Magento\AnotherModule\Several\Chunks',
[
[
- 'module' => 'Magento\AnotherModule',
+ 'modules' => ['Magento\AnotherModule'],
'type' => \Magento\Test\Integrity\DependencyTest::TYPE_SOFT,
'source' => 'Magento\AnotherModule\Several\Chunks',
]
@@ -78,7 +78,7 @@ public function getDependencyInfoDataProvider()
'Magento_AnotherModule::template/path.phtml',
[
[
- 'module' => 'Magento\AnotherModule',
+ 'modules' => ['Magento\AnotherModule'],
'type' => \Magento\Test\Integrity\DependencyTest::TYPE_SOFT,
'source' => 'Magento_AnotherModule::template/path.phtml',
]
@@ -89,7 +89,7 @@ public function getDependencyInfoDataProvider()
'Magento_AnotherModule::file/path.txt',
[
[
- 'module' => 'Magento\AnotherModule',
+ 'modules' => ['Magento\AnotherModule'],
'type' => \Magento\Test\Integrity\DependencyTest::TYPE_SOFT,
'source' => 'Magento_AnotherModule::file/path.txt',
]
@@ -100,7 +100,7 @@ public function getDependencyInfoDataProvider()
'',
[
[
- 'module' => 'Magento\AnotherModule',
+ 'modules' => ['Magento\AnotherModule'],
'type' => \Magento\Test\Integrity\DependencyTest::TYPE_SOFT,
'source' => '',
]
@@ -120,7 +120,7 @@ public function testUpdatesRouterGetDependencyInfo($contents, $type)
$model = new LayoutRule(['router_name' => ['Magento\RouterModule']], [], []);
$this->assertEquals([], $model->getDependencyInfo('Magento\RouterModule', 'layout', 'any', $contents));
$this->assertEquals(
- [['module' => 'Magento\RouterModule', 'type' => $type, 'source' => 'router_name_action']],
+ [['modules' => ['Magento\RouterModule'], 'type' => $type, 'source' => 'router_name_action']],
$model->getDependencyInfo('Magento\AnotherModule', 'layout', 'any', $contents)
);
}
@@ -148,11 +148,11 @@ public function testLayoutGetDependencyInfo($contents, $type, $isHandle)
$model->getDependencyInfo('Magento\DefaultHandleModule', 'layout', 'any', $contents)
);
$this->assertEquals(
- [['module' => 'Magento\DefaultHandleModule', 'type' => $type, 'source' => 'singlechunk']],
+ [['modules' => ['Magento\DefaultHandleModule'], 'type' => $type, 'source' => 'singlechunk']],
$model->getDependencyInfo('any', 'layout', 'any', $contents)
);
$this->assertEquals(
- [['module' => 'Magento\AnyHandleModule', 'type' => $type, 'source' => 'any_handle_name']],
+ [['modules' => ['Magento\AnyHandleModule'], 'type' => $type, 'source' => 'any_handle_name']],
$model->getDependencyInfo('any', 'layout', 'path/frontend/file.txt', $contents)
);
// test several modules
@@ -166,7 +166,7 @@ public function testLayoutGetDependencyInfo($contents, $type, $isHandle)
];
$model = $isHandle ? new LayoutRule([], [], $data) : new LayoutRule([], $data, []);
$this->assertEquals(
- [['module' => 'Magento\Theme', 'type' => $type, 'source' => 'any_handle_name']],
+ [['modules' => ['Magento\Theme'], 'type' => $type, 'source' => 'any_handle_name']],
$model->getDependencyInfo('any', 'layout', 'path/frontend/file.txt', $contents)
);
$this->assertEquals(
diff --git a/dev/tests/static/framework/tests/unit/testsuite/Magento/TestFramework/Dependency/PhpRuleTest.php b/dev/tests/static/framework/tests/unit/testsuite/Magento/TestFramework/Dependency/PhpRuleTest.php
index a636aa81800d1..d1350d1b54db9 100644
--- a/dev/tests/static/framework/tests/unit/testsuite/Magento/TestFramework/Dependency/PhpRuleTest.php
+++ b/dev/tests/static/framework/tests/unit/testsuite/Magento/TestFramework/Dependency/PhpRuleTest.php
@@ -89,7 +89,7 @@ public function getDependencyInfoDataProvider()
'something extends \Magento\SomeModule\Any\ClassName {',
[
[
- 'module' => 'Magento\SomeModule',
+ 'modules' => ['Magento\SomeModule'],
'type' => \Magento\TestFramework\Dependency\RuleInterface::TYPE_HARD,
'source' => 'Magento\SomeModule\Any\ClassName',
]
@@ -105,7 +105,7 @@ public function getDependencyInfoDataProvider()
'$this->getViewFileUrl("Magento_SomeModule::js/order-by-sku-failure.js")',
[
[
- 'module' => 'Magento\SomeModule',
+ 'modules' => ['Magento\SomeModule'],
'type' => \Magento\TestFramework\Dependency\RuleInterface::TYPE_HARD,
'source' => 'Magento_SomeModule',
]
@@ -121,7 +121,7 @@ public function getDependencyInfoDataProvider()
'$this->helper("Magento\SomeModule\Any\ClassName")',
[
[
- 'module' => 'Magento\SomeModule',
+ 'modules' => ['Magento\SomeModule'],
'type' => \Magento\TestFramework\Dependency\RuleInterface::TYPE_HARD,
'source' => 'Magento\SomeModule\Any\ClassName',
]
@@ -136,7 +136,7 @@ public function getDependencyInfoDataProvider()
'$this->getLayout()->getBlock(\'block.name\');',
[
[
- 'module' => 'Magento\SomeModule',
+ 'modules' => ['Magento\SomeModule'],
'type' => \Magento\TestFramework\Dependency\RuleInterface::TYPE_HARD,
'source' => 'getBlock(\'block.name\')',
]
@@ -157,7 +157,7 @@ public function getDependencyInfoDataProvider()
'Magento\Module2\Subject',
[
[
- 'module' => 'Magento\Module2',
+ 'modules' => ['Magento\Module2'],
'type' => \Magento\TestFramework\Dependency\RuleInterface::TYPE_SOFT,
'source' => 'Magento\Module2\Subject',
]
@@ -168,7 +168,7 @@ public function getDependencyInfoDataProvider()
'Magento\Module2\NotSubject',
[
[
- 'module' => 'Magento\Module2',
+ 'modules' => ['Magento\Module2'],
'type' => \Magento\TestFramework\Dependency\RuleInterface::TYPE_SOFT,
'source' => 'Magento\Module2\NotSubject',
]
@@ -179,7 +179,7 @@ public function getDependencyInfoDataProvider()
'Magento\OtherModule\NotSubject',
[
[
- 'module' => 'Magento\OtherModule',
+ 'modules' => ['Magento\OtherModule'],
'type' => \Magento\TestFramework\Dependency\RuleInterface::TYPE_HARD,
'source' => 'Magento\OtherModule\NotSubject',
]
@@ -222,7 +222,7 @@ public function getDependencyInfoDataCaseGetUrlDataProvider()
'$this->getUrl("cms/index/index")',
[
[
- 'module' => 'Magento\Cms',
+ 'modules' => ['Magento\Cms'],
'type' => \Magento\TestFramework\Dependency\RuleInterface::TYPE_HARD,
'source' => 'getUrl("cms/index/index"',
]
@@ -295,7 +295,7 @@ public function getDefaultModelDependencyDataProvider()
'$this->getLayout()->getBlock(\'block.name\');',
[
[
- 'module' => 'Magento\SomeModule',
+ 'modules' => ['Magento\SomeModule'],
'type' => \Magento\TestFramework\Dependency\RuleInterface::TYPE_HARD,
'source' => 'getBlock(\'block.name\')',
]
diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/ClassesTest.php b/dev/tests/static/testsuite/Magento/Test/Integrity/ClassesTest.php
index c5447ef716eb2..c53586599ef00 100644
--- a/dev/tests/static/testsuite/Magento/Test/Integrity/ClassesTest.php
+++ b/dev/tests/static/testsuite/Magento/Test/Integrity/ClassesTest.php
@@ -306,6 +306,8 @@ private function assertClassNamespace(string $file, string $relativePath, string
public function testClassReferences()
{
+ $this->markTestSkipped("To be fixed in MC-33329. The test is not working properly "
+ . "after blacklisting logic was fixed. Previously it was ignoring all files.");
$invoker = new \Magento\Framework\App\Utility\AggregateInvoker($this);
$invoker(
/**
@@ -427,9 +429,9 @@ private function handleAliasClasses(array $aliasClasses, array $badClasses): arr
private function referenceBlacklistFilter(array $classes): array
{
// exceptions made for the files from the blacklist
- $classes = $this->getReferenceBlacklist();
+ $blacklistClasses = $this->getReferenceBlacklist();
foreach ($classes as $class) {
- if (in_array($class, $this->referenceBlackList)) {
+ if (in_array($class, $blacklistClasses)) {
unset($classes[array_search($class, $classes)]);
}
}
diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/DependencyTest.php b/dev/tests/static/testsuite/Magento/Test/Integrity/DependencyTest.php
index 60855043a8c4e..302894c3cface 100644
--- a/dev/tests/static/testsuite/Magento/Test/Integrity/DependencyTest.php
+++ b/dev/tests/static/testsuite/Magento/Test/Integrity/DependencyTest.php
@@ -166,6 +166,21 @@ class DependencyTest extends \PHPUnit\Framework\TestCase
*/
private static $routeMapper = null;
+ /**
+ * @var ComponentRegistrar
+ */
+ private static $componentRegistrar = null;
+
+ /**
+ * @var array
+ */
+ private $externalDependencyBlacklist;
+
+ /**
+ * @var array
+ */
+ private $undeclaredDependencyBlacklist;
+
/**
* Sets up data
*
@@ -258,13 +273,11 @@ protected static function _initThemes()
*/
protected static function _initRules()
{
- $replaceFilePattern = str_replace('\\', '/', realpath(__DIR__))
- . '/_files/dependency_test/tables_*.php';
- $dbRuleTables = [];
- foreach (glob($replaceFilePattern) as $fileName) {
- //phpcs:ignore Magento2.Performance.ForeachArrayMerge
- $dbRuleTables = array_merge($dbRuleTables, include $fileName);
- }
+ $tableToPrimaryModuleMap= self::getTableToPrimaryModuleMap();
+ $tableToAnyModuleMap = self::getTableToAnyModuleMap();
+ // In case primary module declaring the table cannot be identified, use any module referencing this table
+ $tableToModuleMap = array_merge($tableToAnyModuleMap, $tableToPrimaryModuleMap);
+
self::$_rulesInstances = [
new PhpRule(
self::$routeMapper->getRoutes(),
@@ -272,14 +285,14 @@ protected static function _initRules()
[],
['routes' => self::getRoutesWhitelist()]
),
- new DbRule($dbRuleTables),
+ new DbRule($tableToModuleMap),
new LayoutRule(
self::$routeMapper->getRoutes(),
self::$_mapLayoutBlocks,
self::$_mapLayoutHandles
),
new DiRule(new VirtualTypeMapper()),
- new ReportsConfigRule($dbRuleTables),
+ new ReportsConfigRule($tableToModuleMap),
new AnalyticsConfigRule(),
];
}
@@ -303,6 +316,78 @@ private static function getRoutesWhitelist(): array
return self::$routesWhitelist;
}
+ /**
+ * Get full path to app/code directory, assuming these tests are run from the dev/tests directory.
+ *
+ * @return string
+ * @throws \LogicException
+ */
+ private static function getAppCodeDir()
+ {
+ $appCode = BP . '/app/code';
+ if (!$appCode) {
+ throw new \LogicException('app/code directory cannot be located');
+ }
+ return $appCode;
+ }
+
+ /**
+ * Get a map of tables to primary modules.
+ *
+ * Primary module is the one which initially defines the table (versus the module extending its declaration).
+ *
+ * @see getTableToAnyModuleMap
+ *
+ * @return array
+ */
+ private static function getTableToPrimaryModuleMap(): array
+ {
+ $appCode = self::getAppCodeDir();
+ $tableToPrimaryModuleMap = [];
+ foreach (glob($appCode . '/*/*/etc/db_schema_whitelist.json') as $file) {
+ $dbSchemaWhitelist = (array)json_decode(file_get_contents($file));
+ preg_match('|.*/(.*)/(.*)/etc/db_schema_whitelist.json|', $file, $matches);
+ $moduleName = $matches[1] . '\\' . $matches[2];
+ $isStagingModule = (substr_compare($moduleName, 'Staging', -strlen('Staging')) === 0);
+ if ($isStagingModule) {
+ // even though staging modules modify the constraints, they almost never declare new tables
+ continue;
+ }
+ foreach ($dbSchemaWhitelist as $tableName => $tableMetadata) {
+ if (isset($tableMetadata->constraint)) {
+ $tableToPrimaryModuleMap[$tableName] = $moduleName;
+ }
+ }
+ }
+ return $tableToPrimaryModuleMap;
+ }
+
+ /**
+ * Get a map of tables matching to module names.
+ *
+ * Every table will have a module associated with it,
+ * even if the primary module cannot be defined based on declared constraints.
+ *
+ * @see getTableToPrimaryModuleMap
+ *
+ * @return array
+ */
+ private static function getTableToAnyModuleMap(): array
+ {
+ $appCode = self::getAppCodeDir();
+ $tableToAnyModuleMap = [];
+ foreach (glob($appCode . '/*/*/etc/db_schema_whitelist.json') as $file) {
+ $dbSchemaWhitelist = (array)json_decode(file_get_contents($file));
+ $tables = array_keys($dbSchemaWhitelist);
+ preg_match('|.*/(.*)/(.*)/etc/db_schema_whitelist.json|', $file, $matches);
+ $moduleName = $matches[1] . '\\' . $matches[2];
+ foreach ($tables as $table) {
+ $tableToAnyModuleMap[$table] = $moduleName;
+ }
+ }
+ return $tableToAnyModuleMap;
+ }
+
/**
* Return cleaned file contents
*
@@ -353,6 +438,7 @@ function ($matches) use ($contents, &$contentsWithoutHtml) {
public function testUndeclared()
{
$invoker = new \Magento\Framework\App\Utility\AggregateInvoker($this);
+ $blackList = $this->getUndeclaredDependencyBlacklist();
$invoker(
/**
* Check undeclared modules dependencies for specified file
@@ -360,27 +446,12 @@ public function testUndeclared()
* @param string $fileType
* @param string $file
*/
- function ($fileType, $file) {
- // Validates file when it is belonged to default themes
- $componentRegistrar = new ComponentRegistrar();
- foreach ($componentRegistrar->getPaths(ComponentRegistrar::THEME) as $themeDir) {
- if (strpos($file, $themeDir . '/') !== false) {
- return;
- }
- }
-
- $foundModuleName = '';
- foreach ($componentRegistrar->getPaths(ComponentRegistrar::MODULE) as $moduleName => $moduleDir) {
- if (strpos($file, $moduleDir . '/') !== false) {
- $foundModuleName = str_replace('_', '\\', $moduleName);
- break;
- }
- }
- if (empty($foundModuleName)) {
+ function ($fileType, $file) use ($blackList) {
+ $module = $this->getModuleNameForRelevantFile($file);
+ if (!$module) {
return;
}
- $module = $foundModuleName;
$contents = $this->_getCleanedFileContents($fileType, $file);
$dependencies = $this->getDependenciesFromFiles($module, $fileType, $file, $contents);
@@ -390,7 +461,9 @@ function ($fileType, $file) {
// Prepare output message
$result = [];
+
foreach ($undeclaredDependency as $type => $modules) {
+ $modules = $this->filterOutBlacklistedDependencies($file, $modules, $blackList);
$modules = array_unique($modules);
if (empty($modules)) {
continue;
@@ -405,6 +478,180 @@ function ($fileType, $file) {
);
}
+ /**
+ * Filter out list of module dependencies based on the provided blacklist.
+ *
+ * Always exclude dependency on Setup because it is part of base Magento package.
+ *
+ * @param string $filePath
+ * @param string[] $modules
+ * @param array $blackList
+ * @return string[]
+ */
+ private function filterOutBlacklistedDependencies($filePath, $modules, array $blackList): array
+ {
+ $relativeFilePath = substr_replace($filePath, '', 0, strlen(BP . '/'));
+ foreach ($modules as $moduleKey => $module) {
+ if ($module == 'Magento\Setup') {
+ unset($modules[$moduleKey]);
+ }
+ if (isset($blackList[$relativeFilePath])
+ && in_array($module, $blackList[$relativeFilePath])
+ ) {
+ unset($modules[$moduleKey]);
+ }
+ }
+ return $modules;
+ }
+
+ /**
+ * Identify dependencies on the components which are not part of the current project.
+ *
+ * For example, such test allows to prevent invalid dependencies from the storefront application to the monolith.
+ *
+ * @throws \Exception
+ */
+ public function testExternalDependencies()
+ {
+ $invoker = new \Magento\Framework\App\Utility\AggregateInvoker($this);
+ $blackList = $this->getExternalDependencyBlacklist();
+ $invoker(
+ /**
+ * Check external modules dependencies for specified file
+ *
+ * @param string $fileType
+ * @param string $file
+ */
+ function ($fileType, $file) use ($blackList) {
+ $module = $this->getModuleNameForRelevantFile($file);
+ if (!$module) {
+ return;
+ }
+ $externalDependencies = $this->collectExternalDependencies($file, $fileType, $module);
+ // Prepare output message
+ $result = [];
+ foreach ($externalDependencies as $type => $modules) {
+ $modules = $this->filterOutBlacklistedDependencies($file, $modules, $blackList);
+ $modules = array_unique($modules);
+ if (empty($modules)) {
+ continue;
+ }
+ $result[] = sprintf("%s [%s]", $type, implode(', ', $modules));
+ }
+ if (!empty($result)) {
+ $this->fail('Module ' . $module . ' has external dependencies: ' . implode(', ', $result));
+ }
+ },
+ $this->getAllFiles()
+ );
+ }
+
+ /**
+ * Return module name for the file being tested if it should be tested. Return empty string otherwise.
+ *
+ * @param string $file
+ * @return string
+ */
+ private function getModuleNameForRelevantFile($file)
+ {
+ if (!isset(self::$componentRegistrar)) {
+ self::$componentRegistrar = new ComponentRegistrar();
+ }
+ // Validates file when it belongs to default themes
+ foreach (self::$componentRegistrar->getPaths(ComponentRegistrar::THEME) as $themeDir) {
+ if (strpos($file, $themeDir . '/') !== false) {
+ return '';
+ }
+ }
+
+ $foundModuleName = '';
+ foreach (self::$componentRegistrar->getPaths(ComponentRegistrar::MODULE) as $moduleName => $moduleDir) {
+ if (strpos($file, $moduleDir . '/') !== false) {
+ $foundModuleName = str_replace('_', '\\', $moduleName);
+ break;
+ }
+ }
+ if (empty($foundModuleName)) {
+ return '';
+ }
+
+ return $foundModuleName;
+ }
+
+ /**
+ * Collect a list of external dependencies of the specified file.
+ *
+ * Dependency is considered external if it cannot be traced withing current codebase.
+ *
+ * @param string $file
+ * @param string $fileType
+ * @param string $module
+ * @return array
+ * @throws LocalizedException
+ */
+ private function collectExternalDependencies($file, $fileType, $module)
+ {
+ $contents = $this->_getCleanedFileContents($fileType, $file);
+
+ $dependencies = $this->getDependenciesFromFiles($module, $fileType, $file, $contents);
+ $externalDependencies = [];
+ foreach ($dependencies as $dependency) {
+ $dependencyModules = $dependency['modules'];
+ foreach ($dependencyModules as $dependencyModule) {
+ if ($dependency['type'] !== 'soft'
+ && !isset(self::$mapDependencies[$dependencyModule])
+ && (strpos($dependencyModule, 'Magento\Framework') !== 0)
+ ) {
+ $dependencySummary = ($dependencyModule !== 'Unknown')
+ ? $dependencyModule
+ : $dependency['source'];
+ $externalDependencies[$dependency['type']][] = $dependencySummary;
+ }
+ }
+ }
+ return $externalDependencies;
+ }
+
+ /**
+ * Return a list of blacklisted external dependencies.
+ *
+ * @return array
+ */
+ private function getExternalDependencyBlacklist(): array
+ {
+ if (!isset($this->externalDependencyBlacklist)) {
+ $this->externalDependencyBlacklist = [];
+ foreach (glob(__DIR__ . '/_files/blacklist/external_dependency/*.php') as $filename) {
+ $this->externalDependencyBlacklist = array_merge_recursive(
+ $this->externalDependencyBlacklist,
+ include $filename
+ );
+ }
+ }
+
+ return $this->externalDependencyBlacklist;
+ }
+
+ /**
+ * Return a list of blacklisted undeclared dependencies.
+ *
+ * @return array
+ */
+ private function getUndeclaredDependencyBlacklist(): array
+ {
+ if (!isset($this->undeclaredDependencyBlacklist)) {
+ $this->undeclaredDependencyBlacklist = [];
+ foreach (glob(__DIR__ . '/_files/blacklist/undeclared_dependency/*.php') as $filename) {
+ $this->undeclaredDependencyBlacklist = array_merge_recursive(
+ $this->undeclaredDependencyBlacklist,
+ include $filename
+ );
+ }
+ }
+
+ return $this->undeclaredDependencyBlacklist;
+ }
+
/**
* Retrieve dependencies from files
*
@@ -412,7 +659,14 @@ function ($fileType, $file) {
* @param string $fileType
* @param string $file
* @param string $contents
- * @return string[]
+ * @return array [
+ * [
+ * 'modules' => string[],
+ * 'type' => string
+ * 'source' => string
+ * ],
+ * ...
+ * ]
* @throws LocalizedException
*/
protected function getDependenciesFromFiles($module, $fileType, $file, $contents)
@@ -425,14 +679,20 @@ protected function getDependenciesFromFiles($module, $fileType, $file, $contents
//phpcs:ignore Magento2.Performance.ForeachArrayMerge
$dependencies = array_merge($dependencies, $newDependencies);
}
- foreach ($dependencies as $key => $dependency) {
+
+ foreach ($dependencies as $dependencyKey => $dependency) {
foreach (self::$whiteList as $namespace) {
if (strpos($dependency['source'], $namespace) !== false) {
- $dependency['module'] = $namespace;
- $dependencies[$key] = $dependency;
+ $dependency['modules'] = [$namespace];
+ $dependencies[$dependencyKey] = $dependency;
}
}
+ $dependency['type'] = $dependency['type'] ?? 'type is unknown';
+ if (empty($dependency['modules'])) {
+ unset($dependencies[$dependencyKey]);
+ }
}
+
return $dependencies;
}
@@ -471,20 +731,8 @@ private function collectDependency($dependency, $currentModule, &$undeclared)
$declared = $type == self::TYPE_SOFT ? array_merge($soft, $hard) : $hard;
- $module = $dependency['module'];
- if (is_array($module)) {
- $this->collectConditionalDependencies($module, $type, $currentModule, $declared, $undeclared);
- } else {
- $nsModule = str_replace('_', '\\', $module);
-
- if (!in_array($nsModule, $declared) && !$this->_isFake($nsModule)) {
- $undeclared[$type][] = $module;
- } elseif (in_array($nsModule, $declared) && $this->_isFake($nsModule)) {
- $this->_setDependencies($currentModule, $type, self::MAP_TYPE_REDUNDANT, $module);
- }
-
- $this->addDependency($currentModule, $type, self::MAP_TYPE_FOUND, $nsModule);
- }
+ $modules = $dependency['modules'];
+ $this->collectConditionalDependencies($modules, $type, $currentModule, $declared, $undeclared);
}
/**
@@ -746,6 +994,14 @@ protected static function convertModuleName(string $jsonName, array $packageModu
return $moduleName;
}
+ // convert names of the modules not registered in any composer.json
+ preg_match('|magento/module-(.*)|', $jsonName, $matches);
+ if (isset($matches[1])) {
+ $moduleNameHyphenated = $matches[1];
+ $moduleNameUpperCamelCase = 'Magento\\' . str_replace('-', '', ucwords($moduleNameHyphenated, '-'));
+ return $moduleNameUpperCamelCase;
+ }
+
return $jsonName;
}
diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/_files/blacklist/external_dependency/ce.php b/dev/tests/static/testsuite/Magento/Test/Integrity/_files/blacklist/external_dependency/ce.php
new file mode 100644
index 0000000000000..3330b5a941207
--- /dev/null
+++ b/dev/tests/static/testsuite/Magento/Test/Integrity/_files/blacklist/external_dependency/ce.php
@@ -0,0 +1,10 @@
+ ['Magento\Cart'],
+];
diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/_files/blacklist/undeclared_dependency/ce.php b/dev/tests/static/testsuite/Magento/Test/Integrity/_files/blacklist/undeclared_dependency/ce.php
new file mode 100644
index 0000000000000..3a8c59c07848b
--- /dev/null
+++ b/dev/tests/static/testsuite/Magento/Test/Integrity/_files/blacklist/undeclared_dependency/ce.php
@@ -0,0 +1,10 @@
+ ['Magento\Cart'],
+];
diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/_files/dependency_test/tables_ce.php b/dev/tests/static/testsuite/Magento/Test/Integrity/_files/dependency_test/tables_ce.php
deleted file mode 100644
index b120c65d3a759..0000000000000
--- a/dev/tests/static/testsuite/Magento/Test/Integrity/_files/dependency_test/tables_ce.php
+++ /dev/null
@@ -1,304 +0,0 @@
- 'Magento\Backend',
- 'authorization_role' => 'Magento\Authorization',
- 'authorization_rule' => 'Magento\Authorization',
- 'admin_user' => 'Magento\User',
- 'adminnotification_inbox' => 'Magento\AdminNotification',
- 'catalog_category_entity_datetime' => 'Magento\Catalog',
- 'catalog_category_entity_decimal' => 'Magento\Catalog',
- 'catalog_category_entity_int' => 'Magento\Catalog',
- 'catalog_category_entity_text' => 'Magento\Catalog',
- 'catalog_category_entity_varchar' => 'Magento\Catalog',
- 'catalog_product_entity_datetime' => 'Magento\Catalog',
- 'catalog_product_entity_decimal' => 'Magento\Catalog',
- 'catalog_product_entity_gallery' => 'Magento\Catalog',
- 'catalog_product_entity_int' => 'Magento\Catalog',
- 'catalog_product_entity_text' => 'Magento\Catalog',
- 'catalog_product_entity_varchar' => 'Magento\Catalog',
- 'catalog_product_bundle_option' => 'Magento\Bundle',
- 'catalog_product_index_price_bundle_opt_idx' => 'Magento\Bundle',
- 'catalog_product_index_price_bundle_opt_tmp' => 'Magento\Bundle',
- 'catalog_product_bundle_option_value' => 'Magento\Bundle',
- 'catalog_product_bundle_price_index' => 'Magento\Bundle',
- 'catalog_product_index_price_bundle_idx' => 'Magento\Bundle',
- 'catalog_product_index_price_bundle_tmp' => 'Magento\Bundle',
- 'catalog_product_bundle_selection' => 'Magento\Bundle',
- 'catalog_product_index_price_bundle_sel_idx' => 'Magento\Bundle',
- 'catalog_product_index_price_bundle_sel_tmp' => 'Magento\Bundle',
- 'catalog_product_bundle_selection_price' => 'Magento\Bundle',
- 'catalog_product_bundle_stock_index' => 'Magento\Bundle',
- 'captcha_log' => 'Magento\Captcha',
- 'catalog_category_entity' => 'Magento\Catalog',
- 'catalog_category_flat' => 'Magento\Catalog',
- 'catalog_category_product' => 'Magento\Catalog',
- 'catalog_category_product_index' => 'Magento\Catalog',
- 'catalog_compare_item' => 'Magento\Catalog',
- 'catalog_eav_attribute' => 'Magento\Catalog',
- 'catalog_product_entity' => 'Magento\Catalog',
- 'catalog_product_entity_media_gallery' => 'Magento\Catalog',
- 'catalog_product_entity_media_gallery_value' => 'Magento\Catalog',
- 'catalog_product_entity_tier_price' => 'Magento\Catalog',
- 'catalog_product_index_eav_decimal_idx' => 'Magento\Catalog',
- 'catalog_product_index_eav_decimal_tmp' => 'Magento\Catalog',
- 'catalog_product_index_eav_idx' => 'Magento\Catalog',
- 'catalog_product_index_eav_tmp' => 'Magento\Catalog',
- 'catalog_product_flat' => 'Magento\Catalog',
- 'catalog_product_index_eav' => 'Magento\Catalog',
- 'catalog_product_index_eav_decimal' => 'Magento\Catalog',
- 'catalog_product_index_price' => 'Magento\Catalog',
- 'catalog_product_index_tier_price' => 'Magento\Catalog',
- 'catalog_product_index_website' => 'Magento\Catalog',
- 'catalog_product_link' => 'Magento\Catalog',
- 'catalog_product_link_attribute' => 'Magento\Catalog',
- 'catalog_product_link_attribute_decimal' => 'Magento\Catalog',
- 'catalog_product_link_attribute_int' => 'Magento\Catalog',
- 'catalog_product_link_attribute_varchar' => 'Magento\Catalog',
- 'catalog_product_link_type' => 'Magento\Catalog',
- 'catalog_product_option' => 'Magento\Catalog',
- 'catalog_product_option_price' => 'Magento\Catalog',
- 'catalog_product_option_title' => 'Magento\Catalog',
- 'catalog_product_option_type_price' => 'Magento\Catalog',
- 'catalog_product_option_type_title' => 'Magento\Catalog',
- 'catalog_product_option_type_value' => 'Magento\Catalog',
- 'catalog_product_index_price_cfg_opt_agr_idx' => 'Magento\Catalog',
- 'catalog_product_index_price_cfg_opt_agr_tmp' => 'Magento\Catalog',
- 'catalog_product_index_price_cfg_opt_idx' => 'Magento\Catalog',
- 'catalog_product_index_price_cfg_opt_tmp' => 'Magento\Catalog',
- 'catalog_product_index_price_final_idx' => 'Magento\Catalog',
- 'catalog_product_index_price_final_tmp' => 'Magento\Catalog',
- 'catalog_product_index_price_idx' => 'Magento\Catalog',
- 'catalog_product_index_price_opt_agr_idx' => 'Magento\Catalog',
- 'catalog_product_index_price_opt_agr_tmp' => 'Magento\Catalog',
- 'catalog_product_index_price_opt_idx' => 'Magento\Catalog',
- 'catalog_product_index_price_opt_tmp' => 'Magento\Catalog',
- 'catalog_product_index_price_tmp' => 'Magento\Catalog',
- 'catalog_product_relation' => 'Magento\Catalog',
- 'catalog_product_super_attribute' => 'Magento\Catalog',
- 'catalog_product_super_attribute_label' => 'Magento\Catalog',
- 'catalog_product_super_attribute_pricing' => 'Magento\Catalog',
- 'catalog_product_super_link' => 'Magento\Catalog',
- 'catalog_product_website' => 'Magento\Catalog',
- 'cataloginventory_stock' => 'Magento\CatalogInventory',
- 'cataloginventory_stock_item' => 'Magento\CatalogInventory',
- 'cataloginventory_stock_status' => 'Magento\CatalogInventory',
- 'cataloginventory_stock_status_idx' => 'Magento\CatalogInventory',
- 'cataloginventory_stock_status_tmp' => 'Magento\CatalogInventory',
- 'catalogrule_customer_group' => 'Magento\CatalogRule',
- 'catalogrule' => 'Magento\CatalogRule',
- 'catalogrule_group_website' => 'Magento\CatalogRule',
- 'catalogrule_product' => 'Magento\CatalogRule',
- 'catalogrule_product_price' => 'Magento\CatalogRule',
- 'catalogrule_website' => 'Magento\CatalogRule',
- 'catalogsearch_fulltext' => 'Magento\CatalogSearch',
- 'catalogsearch_result' => 'Magento\CatalogSearch',
- 'search_query' => 'Magento\Search',
- 'checkout_agreement' => 'Magento\Checkout',
- 'checkout_agreement_store' => 'Magento\Checkout',
- 'cms_block' => 'Magento\Cms',
- 'cms_block_store' => 'Magento\Cms',
- 'cms_page' => 'Magento\Cms',
- 'cms_page_store' => 'Magento\Cms',
- 'core_config_data' => 'Magento\Config',
- 'design_change' => 'Magento\Theme',
- 'media_storage_directory_storage' => 'Magento\MediaStorage',
- 'email_template' => 'Magento\Email',
- 'media_storage_file_storage' => 'Magento\MediaStorage',
- 'store' => 'Magento\Store',
- 'store_group' => 'Magento\Store',
- 'store_website' => 'Magento\Store',
- 'cron_schedule' => 'Magento\Cron',
- 'customer_address_entity' => 'Magento\Customer',
- 'customer_group' => 'Magento\Customer',
- 'customer_eav_attribute' => 'Magento\Customer',
- 'customer_eav_attribute_website' => 'Magento\Customer',
- 'customer_entity' => 'Magento\Customer',
- 'customer_form_attribute' => 'Magento\Customer',
- 'customer_address_entity_datetime' => 'Magento\Customer',
- 'customer_address_entity_decimal' => 'Magento\Customer',
- 'customer_address_entity_int' => 'Magento\Customer',
- 'customer_address_entity_text' => 'Magento\Customer',
- 'customer_address_entity_varchar' => 'Magento\Customer',
- 'customer_entity_datetime' => 'Magento\Customer',
- 'customer_entity_decimal' => 'Magento\Customer',
- 'customer_entity_int' => 'Magento\Customer',
- 'customer_entity_text' => 'Magento\Customer',
- 'customer_entity_varchar' => 'Magento\Customer',
- 'customer_visitor' => 'Magento\Customer',
- 'directory_country' => 'Magento\Directory',
- 'directory_country_format' => 'Magento\Directory',
- 'directory_country_name' => 'Magento\Directory',
- 'directory_country_region' => 'Magento\Directory',
- 'directory_country_region_name' => 'Magento\Directory',
- 'directory_currency_rate' => 'Magento\Directory',
- 'downloadable_link' => 'Magento\Downloadable',
- 'downloadable_link_price' => 'Magento\Downloadable',
- 'downloadable_link_purchased' => 'Magento\Downloadable',
- 'downloadable_link_purchased_item' => 'Magento\Downloadable',
- 'downloadable_link_title' => 'Magento\Downloadable',
- 'catalog_product_index_price_downlod_idx' => 'Magento\Downloadable',
- 'catalog_product_index_price_downlod_tmp' => 'Magento\Downloadable',
- 'downloadable_sample' => 'Magento\Downloadable',
- 'downloadable_sample_title' => 'Magento\Downloadable',
- 'eav_attribute' => 'Magento\Eav',
- 'eav_attribute_group' => 'Magento\Eav',
- 'eav_attribute_label' => 'Magento\Eav',
- 'eav_attribute_option' => 'Magento\Eav',
- 'eav_attribute_option_value' => 'Magento\Eav',
- 'eav_attribute_set' => 'Magento\Eav',
- 'eav_entity' => 'Magento\Eav',
- 'eav_entity_attribute' => 'Magento\Eav',
- 'eav_entity_attribute_source_table' => 'Magento\Eav',
- 'eav_entity_store' => 'Magento\Eav',
- 'eav_entity_type' => 'Magento\Eav',
- 'eav_form_element' => 'Magento\Eav',
- 'eav_form_fieldset' => 'Magento\Eav',
- 'eav_form_fieldset_label' => 'Magento\Eav',
- 'eav_form_type' => 'Magento\Eav',
- 'eav_form_type_entity' => 'Magento\Eav',
- 'eav_entity_datetime' => 'Magento\Eav',
- 'eav_entity_decimal' => 'Magento\Eav',
- 'eav_entity_int' => 'Magento\Eav',
- 'eav_entity_text' => 'Magento\Eav',
- 'eav_entity_varchar' => 'Magento\Eav',
- 'find_feed_import_codes' => 'Find\Feed',
- 'gift_message' => 'Magento\GiftMessage',
- 'googleoptimizer_code' => 'Magento\GoogleOptimizer',
- 'importexport_importdata' => 'Magento\ImportExport',
- 'integration' => 'Magento\Integration',
- 'layout_link' => 'Magento\Widget',
- 'layout_update' => 'Magento\Widget',
- 'log_customer' => 'Magento\Log',
- 'log_quote' => 'Magento\Log',
- 'log_summary' => 'Magento\Log',
- 'log_summary_type' => 'Magento\Log',
- 'log_url_info' => 'Magento\Log',
- 'log_url' => 'Magento\Log',
- 'log_visitor' => 'Magento\Log',
- 'log_visitor_info' => 'Magento\Log',
- 'log_visitor_online' => 'Magento\Log',
- 'newsletter_problem' => 'Magento\Newsletter',
- 'newsletter_queue' => 'Magento\Newsletter',
- 'newsletter_queue_link' => 'Magento\Newsletter',
- 'newsletter_queue_store_link' => 'Magento\Newsletter',
- 'newsletter_subscriber' => 'Magento\Newsletter',
- 'newsletter_template' => 'Magento\Newsletter',
- 'oauth_consumer' => 'Magento\Integration',
- 'oauth_nonce' => 'Magento\Integration',
- 'oauth_token' => 'Magento\Integration',
- 'admin_passwords' => 'Magento\User',
- 'paypal_cert' => 'Magento\Paypal',
- 'paypal_payment_transaction' => 'Magento\Paypal',
- 'paypal_settlement_report' => 'Magento\Paypal',
- 'paypal_settlement_report_row' => 'Magento\Paypal',
- 'persistent_session' => 'Magento\Persistent',
- 'product_alert_price' => 'Magento\ProductAlert',
- 'product_alert_stock' => 'Magento\ProductAlert',
- 'rating' => 'Magento\Review',
- 'rating_entity' => 'Magento\Review',
- 'rating_option' => 'Magento\Review',
- 'rating_option_vote' => 'Magento\Review',
- 'rating_store' => 'Magento\Review',
- 'rating_title' => 'Magento\Review',
- 'rating_option_vote_aggregated' => 'Magento\Review',
- 'report_compared_product_index' => 'Magento\Reports',
- 'report_event' => 'Magento\Reports',
- 'report_event_types' => 'Magento\Reports',
- 'report_viewed_product_index' => 'Magento\Reports',
- 'review' => 'Magento\Review',
- 'review_entity_summary' => 'Magento\Review',
- 'review_detail' => 'Magento\Review',
- 'review_entity' => 'Magento\Review',
- 'review_status' => 'Magento\Review',
- 'review_store' => 'Magento\Review',
- 'sales_bestsellers_aggregated_daily' => 'Magento\Sales',
- 'sales_bestsellers_aggregated_monthly' => 'Magento\Sales',
- 'sales_bestsellers_aggregated_yearly' => 'Magento\Sales',
- 'paypal_billing_agreement' => 'Magento\Paypal',
- 'paypal_billing_agreement_order' => 'Magento\Paypal',
- 'sales_creditmemo' => 'Magento\Sales',
- 'sales_creditmemo_comment' => 'Magento\Sales',
- 'sales_creditmemo_grid' => 'Magento\Sales',
- 'sales_creditmemo_item' => 'Magento\Sales',
- 'sales_invoice' => 'Magento\Sales',
- 'sales_invoice_comment' => 'Magento\Sales',
- 'sales_invoice_grid' => 'Magento\Sales',
- 'sales_invoice_item' => 'Magento\Sales',
- 'sales_invoiced_aggregated' => 'Magento\Sales',
- 'sales_invoiced_aggregated_order' => 'Magento\Sales',
- 'sales_order' => 'Magento\Sales',
- 'sales_order_address' => 'Magento\Sales',
- 'sales_order_aggregated_created' => 'Magento\Sales',
- 'sales_order_aggregated_updated' => 'Magento\Sales',
- 'sales_order_grid' => 'Magento\Sales',
- 'sales_order_item' => 'Magento\Sales',
- 'sales_order_item_option' => 'Magento\Sales',
- 'sales_order_payment' => 'Magento\Sales',
- 'sales_order_status' => 'Magento\Sales',
- 'sales_order_status_history' => 'Magento\Sales',
- 'sales_order_status_label' => 'Magento\Sales',
- 'sales_order_status_state' => 'Magento\Sales',
- 'sales_order_tax' => 'Magento\Tax',
- 'sales_payment_transaction' => 'Magento\Sales',
- 'quote' => 'Magento\Quote',
- 'quote_address' => 'Magento\Quote',
- 'quote_address_item' => 'Magento\Quote',
- 'quote_shipping_rate' => 'Magento\Quote',
- 'quote_item' => 'Magento\Quote',
- 'quote_item_option' => 'Magento\Quote',
- 'quote_payment' => 'Magento\Quote',
- 'sales_refunded_aggregated' => 'Magento\Sales',
- 'sales_refunded_aggregated_order' => 'Magento\Sales',
- 'sales_shipment' => 'Magento\Sales',
- 'sales_shipment_comment' => 'Magento\Sales',
- 'sales_shipment_grid' => 'Magento\Sales',
- 'sales_shipment_item' => 'Magento\Sales',
- 'sales_shipment_track' => 'Magento\Sales',
- 'sales_shipping_aggregated' => 'Magento\Sales',
- 'sales_shipping_aggregated_order' => 'Magento\Sales',
- 'quote_entity' => 'Magento\Quote',
- 'quote_temp' => 'Magento\Quote',
- 'salesrule_coupon' => 'Magento\SalesRule',
- 'coupon_aggregated' => 'Magento\SalesRule',
- 'coupon_aggregated_order' => 'Magento\SalesRule',
- 'coupon_aggregated_updated' => 'Magento\SalesRule',
- 'salesrule_coupon_usage' => 'Magento\SalesRule',
- 'salesrule_website' => 'Magento\SalesRule',
- 'salesrule_label' => 'Magento\SalesRule',
- 'salesrule_product_attribute' => 'Magento\SalesRule',
- 'salesrule' => 'Magento\SalesRule',
- 'salesrule_customer' => 'Magento\SalesRule',
- 'salesrule_customer_group' => 'Magento\SalesRule',
- 'sendfriend_log' => 'Magento\sendfriend',
- 'shipping_tablerate' => 'Magento\shipping',
- 'sitemap' => 'Magento\Sitemap',
- 'social_facebook_actions' => 'Social\Facebook',
- 'sales_order_tax_item' => 'Magento\Tax',
- 'tax_calculation' => 'Magento\Tax',
- 'tax_calculation_rate' => 'Magento\Tax',
- 'tax_calculation_rate_title' => 'Magento\Tax',
- 'tax_calculation_rule' => 'Magento\Tax',
- 'tax_class' => 'Magento\Tax',
- 'tax_order_aggregated_created' => 'Magento\Tax',
- 'tax_order_aggregated_updated' => 'Magento\Tax',
- 'translation' => 'Magento\Translation',
- 'weee_tax' => 'Magento\Weee',
- 'widget' => 'Magento\Widget',
- 'widget_instance' => 'Magento\Widget',
- 'widget_instance_page' => 'Magento\Widget',
- 'widget_instance_page_layout' => 'Magento\Widget',
- 'wishlist_item' => 'Magento\Wishlist',
- 'wishlist_item_option' => 'Magento\Wishlist',
- 'wishlist' => 'Magento\Wishlist',
- 'admin_system_messages' => 'Magento\AdminNotification',
- 'theme' => 'Magento\Theme',
- 'theme_files' => 'Magento\Theme',
- 'variable' => 'Magento\Variable',
- 'variable_value' => 'Magento\Variable',
- 'job_queue' => 'Magento\Queue',
- 'catalogsearch_recommendations' => 'Magento\AdvancedSearch',
-];
diff --git a/lib/internal/Magento/Framework/Api/AbstractSimpleObjectBuilder.php b/lib/internal/Magento/Framework/Api/AbstractSimpleObjectBuilder.php
index d9a972ac237f5..029a78bf0ddf7 100644
--- a/lib/internal/Magento/Framework/Api/AbstractSimpleObjectBuilder.php
+++ b/lib/internal/Magento/Framework/Api/AbstractSimpleObjectBuilder.php
@@ -3,10 +3,13 @@
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
+declare(strict_types=1);
+
namespace Magento\Framework\Api;
/**
* Base Builder Class for simple data Objects
+ * @deprecated Every builder should have their own implementation of \Magento\Framework\Api\SimpleBuilderInterface
* @SuppressWarnings(PHPMD.NumberOfChildren)
*/
abstract class AbstractSimpleObjectBuilder implements SimpleBuilderInterface
@@ -44,6 +47,8 @@ public function create()
}
/**
+ * Overwrite data in Object.
+ *
* @param string $key
* @param mixed $value
*
@@ -62,9 +67,12 @@ protected function _set($key, $value)
*/
protected function _getDataObjectType()
{
- $currentClass = get_class($this);
- $builderSuffix = 'Builder';
- $dataObjectType = substr($currentClass, 0, -strlen($builderSuffix));
+ $dataObjectType = '';
+ $pattern = '/(?.*?)Builder(\\Interceptor)?/';
+ if (preg_match($pattern, get_class($this), $match)) {
+ $dataObjectType = $match['data_object'];
+ }
+
return $dataObjectType;
}
diff --git a/lib/internal/Magento/Framework/App/DeploymentConfig.php b/lib/internal/Magento/Framework/App/DeploymentConfig.php
index ddd7faa80b906..6aeec2c2d0192 100644
--- a/lib/internal/Magento/Framework/App/DeploymentConfig.php
+++ b/lib/internal/Magento/Framework/App/DeploymentConfig.php
@@ -15,6 +15,7 @@
* Application deployment configuration
*
* @api
+ * @since 100.0.2
*/
class DeploymentConfig
{
@@ -148,7 +149,7 @@ public function isDbAvailable()
*/
private function load()
{
- if (null === $this->data) {
+ if (empty($this->data)) {
$this->data = $this->reader->load();
if ($this->overrideData) {
$this->data = array_replace($this->data, $this->overrideData);
@@ -183,6 +184,7 @@ private function flattenParams(array $params, $path = null, array &$flattenResul
$newPath = $key;
}
if (isset($flattenResult[$newPath])) {
+ //phpcs:ignore Magento2.Exceptions.DirectThrow
throw new RuntimeException(new Phrase("Key collision '%1' is already defined.", [$newPath]));
}
$flattenResult[$newPath] = $param;
diff --git a/lib/internal/Magento/Framework/App/Test/Unit/DeploymentConfigTest.php b/lib/internal/Magento/Framework/App/Test/Unit/DeploymentConfigTest.php
index 81a4f842bdf1d..f93153f3cd75c 100644
--- a/lib/internal/Magento/Framework/App/Test/Unit/DeploymentConfigTest.php
+++ b/lib/internal/Magento/Framework/App/Test/Unit/DeploymentConfigTest.php
@@ -6,10 +6,12 @@
namespace Magento\Framework\App\Test\Unit;
-use \Magento\Framework\App\DeploymentConfig;
-use \Magento\Framework\Config\ConfigOptionsListConstants;
+use Magento\Framework\App\DeploymentConfig;
+use Magento\Framework\Config\ConfigOptionsListConstants;
+use PHPUnit\Framework\MockObject\MockObject;
+use PHPUnit\Framework\TestCase;
-class DeploymentConfigTest extends \PHPUnit\Framework\TestCase
+class DeploymentConfigTest extends TestCase
{
/**
* @var array
@@ -63,7 +65,7 @@ class DeploymentConfigTest extends \PHPUnit\Framework\TestCase
protected $_deploymentConfigMerged;
/**
- * @var \PHPUnit_Framework_MockObject_MockObject
+ * @var MockObject
*/
private $reader;
@@ -124,7 +126,7 @@ public function testNotAvailable(): void
*/
public function testNotAvailableThenAvailable(): void
{
- $this->reader->expects($this->once())->method('load')->willReturn([]);
+ $this->reader->expects($this->once())->method('load')->willReturn(['Test']);
$object = new DeploymentConfig($this->reader);
$this->assertFalse($object->isAvailable());
$this->assertFalse($object->isAvailable());
diff --git a/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php b/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php
index ec2731c667ee6..80d8808ab1768 100644
--- a/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php
+++ b/lib/internal/Magento/Framework/DB/Adapter/Pdo/Mysql.php
@@ -13,6 +13,7 @@
use Magento\Framework\DB\Adapter\DeadlockException;
use Magento\Framework\DB\Adapter\DuplicateException;
use Magento\Framework\DB\Adapter\LockWaitException;
+use Magento\Framework\DB\Adapter\TableNotFoundException;
use Magento\Framework\DB\Ddl\Table;
use Magento\Framework\DB\ExpressionConverter;
use Magento\Framework\DB\LoggerInterface;
@@ -38,6 +39,7 @@
* @SuppressWarnings(PHPMD.TooManyFields)
* @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
+ * @since 100.0.2
*/
class Mysql extends \Zend_Db_Adapter_Pdo_Mysql implements AdapterInterface
{
@@ -257,6 +259,8 @@ public function __construct(
1213 => DeadlockException::class,
// SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry
1062 => DuplicateException::class,
+ // SQLSTATE[42S02]: Base table or view not found: 1146
+ 1146 => TableNotFoundException::class,
];
try {
parent::__construct($config);
@@ -687,7 +691,8 @@ public function proccessBindCallback($matches)
if (isset($matches[6]) && (
strpos($matches[6], "'") !== false ||
strpos($matches[6], ':') !== false ||
- strpos($matches[6], '?') !== false)
+ strpos($matches[6], '?') !== false
+ )
) {
$bindName = ':_mage_bind_var_' . (++$this->_bindIncrement);
$this->_bindParams[$bindName] = $this->_unQuote($matches[6]);
@@ -1822,7 +1827,7 @@ public function modifyColumnByDdl($tableName, $columnName, $definition, $flushDa
* Retrieve column data type by data from describe table
*
* @param array $column
- * @return string
+ * @return string|null
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*/
protected function _getColumnTypeByDdl($column)
@@ -1862,6 +1867,7 @@ protected function _getColumnTypeByDdl($column)
case 'numeric':
return Table::TYPE_DECIMAL;
}
+ return null;
}
/**
@@ -4020,6 +4026,7 @@ public function getAutoIncrementField($tableName, $schemaName = null)
* Required to listen all DDL changes done by 3-rd party modules with old Install/UpgradeSchema scripts.
*
* @return SchemaListener
+ * @since 102.0.0
*/
public function getSchemaListener()
{
@@ -4031,6 +4038,8 @@ public function getSchemaListener()
/**
* Closes the connection.
+ *
+ * @since 102.0.4
*/
public function closeConnection()
{
diff --git a/lib/internal/Magento/Framework/DB/Adapter/TableNotFoundException.php b/lib/internal/Magento/Framework/DB/Adapter/TableNotFoundException.php
new file mode 100644
index 0000000000000..7fded1fa01e91
--- /dev/null
+++ b/lib/internal/Magento/Framework/DB/Adapter/TableNotFoundException.php
@@ -0,0 +1,15 @@
+where('id = :id');
*
*
+ * You may also construct IN statements:
+ *
+ *
+ * $select->where('entity_id IN (?)', ['1', '2', '3']);
+ *
+ *
* Note that it is more correct to use named bindings in your
* queries for values other than strings. When you use named
* bindings, don't forget to pass the values when actually
@@ -101,7 +107,7 @@ public function __construct(
*
*
* @param string $cond The WHERE condition.
- * @param string $value OPTIONAL A single value to quote into the condition.
+ * @param string|array|null $value OPTIONAL An optional single or array value to quote into the condition.
* @param string|int|null $type OPTIONAL The type of the given value
* @return \Magento\Framework\DB\Select
*/
diff --git a/lib/internal/Magento/Framework/Locale/Resolver.php b/lib/internal/Magento/Framework/Locale/Resolver.php
index d058bfd41ab1a..55ef2a4e9a30c 100644
--- a/lib/internal/Magento/Framework/Locale/Resolver.php
+++ b/lib/internal/Magento/Framework/Locale/Resolver.php
@@ -15,7 +15,7 @@
class Resolver implements ResolverInterface
{
/**
- * Default locale
+ * Resolver default locale
*/
const DEFAULT_LOCALE = 'en_US';
@@ -79,7 +79,7 @@ public function __construct(
$this->scopeConfig = $scopeConfig;
$this->defaultLocalePath = $defaultLocalePath;
$this->scopeType = $scopeType;
- $this->deploymentConfig = $deploymentConfig ?: ObjectManager::getInstance()->create(DeploymentConfig::class);
+ $this->deploymentConfig = $deploymentConfig ?: ObjectManager::getInstance()->get(DeploymentConfig::class);
$this->setLocale($locale);
}
diff --git a/lib/internal/Magento/Framework/Module/ModuleList.php b/lib/internal/Magento/Framework/Module/ModuleList.php
index 5a60a1c102b05..b3cf433bbaf45 100644
--- a/lib/internal/Magento/Framework/Module/ModuleList.php
+++ b/lib/internal/Magento/Framework/Module/ModuleList.php
@@ -140,11 +140,8 @@ public function isModuleInfoAvailable()
*/
private function loadConfigData()
{
- if (null === $this->configData) {
- $this->config->resetData();
- if (null !== $this->config->get(ConfigOptionsListConstants::KEY_MODULES)) {
- $this->configData = $this->config->get(ConfigOptionsListConstants::KEY_MODULES);
- }
+ if (null === $this->configData && null !== $this->config->get(ConfigOptionsListConstants::KEY_MODULES)) {
+ $this->configData = $this->config->get(ConfigOptionsListConstants::KEY_MODULES);
}
}
}
diff --git a/lib/internal/Magento/Framework/Module/Test/Unit/ModuleListTest.php b/lib/internal/Magento/Framework/Module/Test/Unit/ModuleListTest.php
index 3142bbbc6771a..f8b02d177e097 100644
--- a/lib/internal/Magento/Framework/Module/Test/Unit/ModuleListTest.php
+++ b/lib/internal/Magento/Framework/Module/Test/Unit/ModuleListTest.php
@@ -5,9 +5,14 @@
*/
namespace Magento\Framework\Module\Test\Unit;
-use \Magento\Framework\Module\ModuleList;
+use Magento\Framework\Module\ModuleList;
+use PHPUnit\Framework\MockObject\MockObject;
+use PHPUnit\Framework\TestCase;
-class ModuleListTest extends \PHPUnit\Framework\TestCase
+/**
+ * Test for module list
+ */
+class ModuleListTest extends TestCase
{
/**
* Fixture for all modules' meta-information
@@ -24,12 +29,12 @@ class ModuleListTest extends \PHPUnit\Framework\TestCase
private static $enabledFixture = ['foo' => 1, 'bar' => 0];
/**
- * @var \PHPUnit_Framework_MockObject_MockObject
+ * @var MockObject
*/
private $config;
/**
- * @var \PHPUnit_Framework_MockObject_MockObject
+ * @var MockObject
*/
private $loader;
@@ -47,7 +52,6 @@ protected function setUp()
public function testGetAll()
{
- $this->config->expects($this->once())->method('resetData');
$this->setLoadAllExpectation();
$this->setLoadConfigExpectation();
$expected = ['foo' => self::$allFixture['foo']];
@@ -65,7 +69,6 @@ public function testGetAllNoData()
public function testGetOne()
{
- $this->config->expects($this->once())->method('resetData');
$this->setLoadAllExpectation();
$this->setLoadConfigExpectation();
$this->assertSame(['key' => 'value'], $this->model->getOne('foo'));
@@ -74,7 +77,6 @@ public function testGetOne()
public function testGetNames()
{
- $this->config->expects($this->once())->method('resetData');
$this->setLoadAllExpectation(false);
$this->setLoadConfigExpectation();
$this->assertSame(['foo'], $this->model->getNames());
@@ -83,7 +85,6 @@ public function testGetNames()
public function testHas()
{
- $this->config->expects($this->once())->method('resetData');
$this->setLoadAllExpectation(false);
$this->setLoadConfigExpectation();
$this->assertTrue($this->model->has('foo'));
@@ -92,7 +93,6 @@ public function testHas()
public function testIsModuleInfoAvailable()
{
- $this->config->expects($this->once())->method('resetData');
$this->setLoadConfigExpectation(true);
$this->assertTrue($this->model->isModuleInfoAvailable());
}
diff --git a/lib/internal/Magento/Framework/View/Element/UiComponent/Context.php b/lib/internal/Magento/Framework/View/Element/UiComponent/Context.php
index fbb84712b2afd..8f980d82d4be0 100644
--- a/lib/internal/Magento/Framework/View/Element/UiComponent/Context.php
+++ b/lib/internal/Magento/Framework/View/Element/UiComponent/Context.php
@@ -15,12 +15,13 @@
use Magento\Framework\View\Element\UiComponent\Control\ButtonProviderFactory;
use Magento\Framework\View\Element\UiComponent\Control\ButtonProviderInterface;
use Magento\Framework\View\Element\UiComponent\DataProvider\DataProviderInterface;
+use Magento\Framework\View\Element\UiComponent\DataProvider\Sanitizer;
use Magento\Framework\View\Element\UiComponentFactory;
use Magento\Framework\View\Element\UiComponentInterface;
use Magento\Framework\View\LayoutInterface as PageLayoutInterface;
/**
- * Class Context
+ * Request context for UI components to utilize.
*
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
@@ -101,6 +102,11 @@ class Context implements ContextInterface
*/
private $authorization;
+ /**
+ * @var Sanitizer
+ */
+ private $sanitizer;
+
/**
* @param PageLayoutInterface $pageLayout
* @param RequestInterface $request
@@ -113,6 +119,7 @@ class Context implements ContextInterface
* @param DataProviderInterface|null $dataProvider
* @param string $namespace
* @param AuthorizationInterface|null $authorization
+ * @param Sanitizer|null $sanitizer
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
*/
public function __construct(
@@ -126,7 +133,8 @@ public function __construct(
UiComponentFactory $uiComponentFactory,
DataProviderInterface $dataProvider = null,
$namespace = null,
- AuthorizationInterface $authorization = null
+ AuthorizationInterface $authorization = null,
+ ?Sanitizer $sanitizer = null
) {
$this->namespace = $namespace;
$this->request = $request;
@@ -141,6 +149,7 @@ public function __construct(
$this->authorization = $authorization ?: ObjectManager::getInstance()->get(
AuthorizationInterface::class
);
+ $this->sanitizer = $sanitizer ?? ObjectManager::getInstance()->get(Sanitizer::class);
$this->setAcceptType();
}
@@ -241,16 +250,20 @@ public function getDataProvider()
*/
public function getDataSourceData(UiComponentInterface $component)
{
+ //Getting dynamic data for the component
$dataSource = $component->getDataSourceData();
$this->prepareDataSource($dataSource, $component);
$dataProviderConfig = $this->getDataProvider()->getConfigData();
+ //Dynamic UI component data should not contain templates.
+ $config = $this->sanitizer->sanitize(array_merge($dataSource, $dataProviderConfig));
+
return [
$this->getDataProvider()->getName() => [
'type' => 'dataSource',
'name' => $this->getDataProvider()->getName(),
'dataScope' => $this->getNamespace(),
'config' => array_replace_recursive(
- array_merge($dataSource, $dataProviderConfig),
+ $config,
[
'params' => [
'namespace' => $this->getNamespace()
diff --git a/lib/internal/Magento/Framework/View/Element/UiComponent/DataProvider/Sanitizer.php b/lib/internal/Magento/Framework/View/Element/UiComponent/DataProvider/Sanitizer.php
new file mode 100644
index 0000000000000..27a42fb337ce8
--- /dev/null
+++ b/lib/internal/Magento/Framework/View/Element/UiComponent/DataProvider/Sanitizer.php
@@ -0,0 +1,102 @@
+extractConfig($data);
+ $toProcess = [];
+ array_walk(
+ $data,
+ function ($datum, string $key) use (&$config, &$toProcess) : void {
+ if (is_array($datum)) {
+ //Each array must have it's own __disableTmpl property
+ $toProcess[$key] = $datum;
+ } elseif ((
+ !is_bool($config) && !array_key_exists($key, $config)
+ )
+ && (is_string($datum) || $datum instanceof Phrase)
+ && preg_match('/\$\{.+\}/', (string)$datum)
+ ) {
+ //Templating is not disabled for all properties or for this property specifically
+ //Property is a string that contains template syntax so we are disabling it's rendering
+ $config[$key] = true;
+ }
+ }
+ );
+ if ($toProcess) {
+ //Processing sub-arrays
+ $data = array_replace($data, array_map([$this, 'sanitize'], $toProcess));
+ }
+ if ($config !== []) {
+ //Some properties require rendering configuration.
+ $data['__disableTmpl'] = $config;
+ }
+
+ return $data;
+ }
+
+ /**
+ * Sanitize a component's metadata.
+ *
+ * Will sanitize full component's metadata as well as metadata of it's child components.
+ *
+ * @param array $meta
+ * @return array
+ */
+ public function sanitizeComponentMetadata(array $meta): array
+ {
+ if (array_key_exists('arguments', $meta)
+ && is_array($meta['arguments'])
+ && array_key_exists('data', $meta['arguments'])
+ && is_array($meta['arguments']['data'])
+ && array_key_exists('config', $meta['arguments']['data'])
+ && is_array($meta['arguments']['data']['config'])
+ ) {
+ $meta['arguments']['data']['config'] = $this->sanitize($meta['arguments']['data']['config']);
+ }
+ if (array_key_exists('children', $meta) && is_array($meta['children'])) {
+ $meta['children'] = array_map([$this, 'sanitizeComponentMetadata'], $meta['children']);
+ }
+
+ return $meta;
+ }
+}
diff --git a/lib/internal/Magento/Framework/View/Element/UiComponentFactory.php b/lib/internal/Magento/Framework/View/Element/UiComponentFactory.php
index b395e95f0baaf..16c06fe3abf3a 100644
--- a/lib/internal/Magento/Framework/View/Element/UiComponentFactory.php
+++ b/lib/internal/Magento/Framework/View/Element/UiComponentFactory.php
@@ -17,10 +17,11 @@
use Magento\Framework\View\Element\UiComponent\ContextFactory;
use Magento\Framework\Phrase;
use Magento\Framework\View\Element\UiComponent\DataProvider\DataProviderInterface;
+use Magento\Framework\View\Element\UiComponent\DataProvider\Sanitizer;
use Magento\Framework\View\Element\UiComponent\Factory\ComponentFactoryInterface;
/**
- * Class UiComponentFactory
+ * Factory that creates UI component descriptor based on configuration provided.
*
* @api
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
@@ -69,6 +70,11 @@ class UiComponentFactory extends DataObject
*/
private $definitionData;
+ /**
+ * @var Sanitizer
+ */
+ private $sanitizer;
+
/**
* @param ObjectManagerInterface $objectManager
* @param ManagerInterface $componentManager
@@ -78,6 +84,7 @@ class UiComponentFactory extends DataObject
* @param array $componentChildFactories
* @param DataInterface $definitionData
* @param DataInterfaceFactory $configFactory
+ * @param Sanitizer|null $sanitizer
*/
public function __construct(
ObjectManagerInterface $objectManager,
@@ -87,7 +94,8 @@ public function __construct(
array $data = [],
array $componentChildFactories = [],
DataInterface $definitionData = null,
- DataInterfaceFactory $configFactory = null
+ DataInterfaceFactory $configFactory = null,
+ ?Sanitizer $sanitizer = null
) {
$this->objectManager = $objectManager;
$this->componentManager = $componentManager;
@@ -98,6 +106,7 @@ public function __construct(
parent::__construct($data);
$this->definitionData = $definitionData ?:
$this->objectManager->get(DataInterface::class);
+ $this->sanitizer = $sanitizer ?? $this->objectManager->get(Sanitizer::class);
}
/**
@@ -198,8 +207,12 @@ protected function argumentsResolver($identifier, array $componentData)
*/
public function create($identifier, $name = null, array $arguments = [])
{
+ //$argument contain dynamic data generated by UI component classes which should not contain templates.
+ $arguments = $this->sanitizer->sanitize($arguments);
if ($name === null) {
- $componentData = $this->configFactory->create(['componentName' => $identifier])->get($identifier);
+ /** @var DataInterface $config */
+ $config = $this->configFactory->create(['componentName' => $identifier]);
+ $componentData = $config->get($identifier);
$bundleComponents = [$identifier => $componentData];
list($className, $componentArguments) = $this->argumentsResolver(
@@ -312,10 +325,10 @@ protected function mergeMetadata($identifier, array $bundleComponents, $reverseM
{
$dataProvider = $this->getDataProvider($identifier, $bundleComponents);
if ($dataProvider instanceof DataProviderInterface) {
+ //Dynamic meta from data providers should not contain templates.
+ $metadata = $dataProvider->getMeta();
$metadata = [
- $identifier => [
- 'children' => $dataProvider->getMeta(),
- ],
+ $identifier => $this->sanitizer->sanitizeComponentMetadata(['children' => $metadata])
];
$bundleComponents = $this->mergeMetadataItem($bundleComponents, $metadata, $reverseMerge);
}
diff --git a/lib/internal/Magento/Framework/View/Test/Unit/Element/UiComponent/DataProvider/SanitizerTest.php b/lib/internal/Magento/Framework/View/Test/Unit/Element/UiComponent/DataProvider/SanitizerTest.php
new file mode 100644
index 0000000000000..75fbfd868cfc1
--- /dev/null
+++ b/lib/internal/Magento/Framework/View/Test/Unit/Element/UiComponent/DataProvider/SanitizerTest.php
@@ -0,0 +1,185 @@
+sanitizer = new Sanitizer();
+ }
+
+ /**
+ * Data sets to sanitize.
+ *
+ * @return array
+ */
+ public function getSanitizeDataSets(): array
+ {
+ return [
+ 'simpleSet' => [
+ ['foo' => '${\'bar\'}', 'xyz' => 42],
+ ['foo' => '${\'bar\'}', 'xyz' => 42, '__disableTmpl' => ['foo' => true]]
+ ],
+ 'configuredSet' => [
+ ['foo' => 'bar', 'xyz' => '${\'zyx\'}', '__disableTmpl' => true],
+ ['foo' => 'bar', 'xyz' => '${\'zyx\'}', '__disableTmpl' => true]
+ ],
+ 'partiallyConfiguredSet' => [
+ ['foo' => '${\'bar\'}', 'xyz' => '${\'zyx\'}', '__disableTmpl' => ['foo' => false]],
+ ['foo' => '${\'bar\'}', 'xyz' => '${\'zyx\'}', '__disableTmpl' => ['foo' => false, 'xyz' => true]]
+ ],
+ 'enabledSet' => [
+ ['foo' => 'bar', 'xyz' => '${\'zyx\'}', '__disableTmpl' => false],
+ ['foo' => 'bar', 'xyz' => '${\'zyx\'}', '__disableTmpl' => false]
+ ],
+ 'complexSet' => [
+ [
+ 'foo' => 'bar',
+ 'sub1' => ['foo' => '${\'bar\'}'],
+ 'sub2' => [
+ 'field' => '${\'value\'}',
+ 'subSub1' => ['foo' => 'bar'],
+ 'subSub2' => ['foo' => '${\'bar\'}', '__disableTmpl' => false],
+ 'subSub3' => [
+ 'fooSub' => [
+ 'foo' => '${\'bar\'}',
+ '__disableTmpl' => false,
+ 'subSubSub1' => ['field' => '${\'value\'}']
+ ]
+ ],
+ 'subSub4' => [['foo' => '${\'bar\'}'], ['foo' => '${\'bar\'}', 'xyz' => '${\'zyx\'}']]
+ ]
+ ],
+ [
+ 'foo' => 'bar',
+ 'sub1' => ['foo' => '${\'bar\'}', '__disableTmpl' => ['foo' => true]],
+ 'sub2' => [
+ 'field' => '${\'value\'}',
+ 'subSub1' => ['foo' => 'bar'],
+ 'subSub2' => ['foo' => '${\'bar\'}', '__disableTmpl' => false],
+ 'subSub3' => [
+ 'fooSub' => [
+ 'foo' => '${\'bar\'}',
+ '__disableTmpl' => false,
+ 'subSubSub1' => ['field' => '${\'value\'}', '__disableTmpl' => ['field' => true]]
+ ]
+ ],
+ 'subSub4' => [
+ ['foo' => '${\'bar\'}', '__disableTmpl' => ['foo' => true]],
+ [
+ 'foo' => '${\'bar\'}',
+ 'xyz' => '${\'zyx\'}',
+ '__disableTmpl' => ['foo' => true, 'xyz' => true]
+ ]
+ ],
+ '__disableTmpl' => ['field' => true]
+ ]
+ ]
+ ]
+ ];
+ }
+
+ /**
+ * Test sanitize method for different data sets.
+ *
+ * @param array $input
+ * @param array $expectedOutput
+ * @return void
+ * @dataProvider getSanitizeDataSets
+ */
+ public function testSanitize(array $input, array $expectedOutput): void
+ {
+ $this->assertEquals($expectedOutput, $this->sanitizer->sanitize($input));
+ }
+
+ /**
+ * Full UI component data sets to sanitize.
+ *
+ * @return array
+ */
+ public function getSanitizeComponentDataSets(): array
+ {
+ return [
+ 'simpleComponent' => [
+ [
+ 'arguments' => ['data' => ['config' => ['foo' => '${\'bar\'}', 'xyz' => 42]]],
+ 'children' => [
+ 'child_component' => [
+ 'arguments' => ['data' => ['config' => ['foo' => '${\'bar\'}', 'xyz' => '${\'xyz\'}']]]
+ ]
+ ]
+ ],
+ [
+ 'arguments' => [
+ 'data' => [
+ 'config' => ['foo' => '${\'bar\'}', 'xyz' => 42, '__disableTmpl' => ['foo' => true]]
+ ]
+ ],
+ 'children' => [
+ 'child_component' => [
+ 'arguments' => [
+ 'data' => [
+ 'config' => [
+ 'foo' => '${\'bar\'}',
+ 'xyz' => '${\'xyz\'}',
+ '__disableTmpl' => ['foo' => true, 'xyz' => true]
+ ]
+ ]
+ ]
+ ]
+ ]
+ ]
+ ],
+ 'argumentsOnly' => [
+ ['arguments' => ['data' => ['config' => ['foo' => '${\'bar\'}']]]],
+ ['arguments' => ['data' => ['config' => ['foo' => '${\'bar\'}', '__disableTmpl' => ['foo' => true]]]]]
+ ],
+ 'childrenOnly' => [
+ ['children' => ['child1' => ['arguments' => ['data' => ['config' => ['foo' => '${\'bar\'}']]]]]],
+ [
+ 'children' => [
+ 'child1' => [
+ 'arguments' => [
+ 'data' => ['config' => ['foo' => '${\'bar\'}', '__disableTmpl' => ['foo' => true]]]
+ ]
+ ]
+ ]
+ ]
+ ]
+ ];
+ }
+
+ /**
+ * Test sanitizeComponentMetadata method for different data sets.
+ *
+ * @param array $input
+ * @param array $expectedOutput
+ * @return void
+ * @dataProvider getSanitizeComponentDataSets
+ */
+ public function testSanitizeComponentMetadata(array $input, array $expectedOutput): void
+ {
+ $this->assertEquals($expectedOutput, $this->sanitizer->sanitizeComponentMetadata($input));
+ }
+}