Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:
- uses: actions/checkout@v4

- name: Validate composer.json and composer.lock
run: composer validate --strict
run: composer validate

- name: Setup PHP
uses: shivammathur/setup-php@v2
Expand Down
21 changes: 21 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Build and test artifacts
build/

# Composer
vendor/
composer.lock

# IDE
.vscode/
.idea/

# OS
.DS_Store
Thumbs.db

# Logs
*.log

# Cache
.phpunit.result.cache
.php_cs.cache
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Antom Payment Adminhtml Module for Magento 2. This module provides the admin fun

This module can be installed via composer:
```
composer require antom-magento/magento2-adminhtml
composer require antom/magento2-adminhtml
```
Next, enable the module:
```
Expand Down
23 changes: 18 additions & 5 deletions Test/Unit/Block/Adminhtml/Config/ValidateApiKeyButtonTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,10 @@ public function testGetStoreIdReturnsStoreFromRequestWhenNoWebsite(): void
['store', null, 5]
]);

$websiteMock = $this->createMock(Website::class);
$websiteMock = $this->getMockBuilder(Website::class)
->disableOriginalConstructor()
->addMethods(['getStoreIds'])
->getMock();
$websiteMock->method('getStoreIds')->willReturn([1, 2, 3]);

$this->storeManagerMock->method('getWebsite')
Expand All @@ -218,7 +221,10 @@ public function testGetStoreIdReturnsStoreFromRequestWhenNoWebsite(): void

public function testGetStoreIdReturnsStoreFromWebsiteWhenWebsiteProvided(): void
{
$websiteMock = $this->createMock(Website::class);
$websiteMock = $this->getMockBuilder(Website::class)
->disableOriginalConstructor()
->addMethods(['getStoreIds'])
->getMock();
$websiteMock->method('getStoreIds')->willReturn([1, 2, 3]);

$this->requestMock->method('getParam')
Expand Down Expand Up @@ -251,7 +257,10 @@ public function testGetStoreIdReturnsDefaultStoreWhenNoParams(): void

public function testGetStoreIdWithWebsiteHavingNoStores(): void
{
$websiteMock = $this->createMock(Website::class);
$websiteMock = $this->getMockBuilder(Website::class)
->disableOriginalConstructor()
->addMethods(['getStoreIds'])
->getMock();
$websiteMock->method('getStoreIds')->willReturn([]);

$this->requestMock->method('getParam')
Expand All @@ -271,6 +280,10 @@ public function testGetStoreIdWithWebsiteHavingNoStores(): void

public function testGetElementHtmlThrowsLocalizedException(): void
{
$reflection = new \ReflectionClass(ValidateApiKeyButton::class);
$method = $reflection->getMethod('_getElementHtml');
$method->setAccessible(true);

$this->expectException(LocalizedException::class);

$elementMock = $this->createMock(AbstractElement::class);
Expand All @@ -279,6 +292,6 @@ public function testGetElementHtmlThrowsLocalizedException(): void
$this->layoutMock->method('createBlock')
->willThrowException(new LocalizedException(__('Test exception')));

$this->validateApiKeyButton->_getElementHtml($elementMock);
$method->invoke($this->validateApiKeyButton, $elementMock);
}
}
}
117 changes: 46 additions & 71 deletions Test/bootstrap.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,31 +35,25 @@
// 创建必要的临时目录
$mockDir = TESTS_TEMP_DIR . '/generated_mocks';
if (!is_dir($mockDir)) {
// 使用 @ 抑制在并发环境中可能出现的目录已存在警告
@mkdir($mockDir, 0777, true);
}

// 2. 设置 Magento generated 目录
$generatedPaths = [
__DIR__ . '/../generated/code',
__DIR__ . '/../../generated/code'
];

foreach ($generatedPaths as $generatedPath) {
if (is_dir($generatedPath)) {
set_include_path(get_include_path() . PATH_SEPARATOR . $generatedPath);
break;
}
}

/**
* 将类/接口代码写入临时文件并加载,作为 eval() 的安全替代方案。
*
* @param string $classCode The PHP code to generate the class/interface.
* @throws RuntimeException If a temporary file cannot be created.
*/
function generateAndLoadClassSafe(string $classCode): void
{
global $mockDir; // 使用全局定义的 mock 目录
// 使用 tempnam 创建一个唯一的文件名以避免冲突

// 如果 $mockDir 不是字符串或目录无法使用,则退回系统临时目录
if (empty($mockDir) || !is_dir($mockDir)) {
$mockDir = sys_get_temp_dir();
}

$filePath = tempnam($mockDir, 'mock_class_');
if ($filePath === false) {
throw new RuntimeException("Could not create temporary file in {$mockDir}");
Expand All @@ -73,6 +67,19 @@ function generateAndLoadClassSafe(string $classCode): void
}


// 2. 设置 Magento generated 目录
$generatedPaths = [
__DIR__ . '/../generated/code',
__DIR__ . '/../../generated/code'
];

foreach ($generatedPaths as $generatedPath) {
if (is_dir($generatedPath)) {
set_include_path(get_include_path() . PATH_SEPARATOR . $generatedPath);
break;
}
}

// 3. 定义 Mock 类和接口创建函数
function createMockClass(string $fullClassName, array $methods = [], bool $isInterface = false): void
{
Expand All @@ -86,6 +93,7 @@ function createMockClass(string $fullClassName, array $methods = [], bool $isInt

$methodsCode = '';
foreach ($methods as $method => $returnValue) {
// var_export 可以正确处理所有类型,包括字符串、null和布尔值
$returnCode = var_export($returnValue, true);

if ($isInterface) {
Expand Down Expand Up @@ -119,10 +127,10 @@ public static function __callStatic(\$name, \$arguments) {

// 4. 创建必要的接口(简化版本,避免参数冲突)
$mockInterfaces = [
'Magento\Framework\App\Config\ScopeConfigInterface' => [
'getValue' => null,
'isSetFlag' => false
],
// 'Magento\Framework\App\Config\ScopeConfigInterface' => [
// 'getValue' => null,
// 'isSetFlag' => false
// ],
'Magento\Payment\Gateway\Validator\ResultInterfaceFactory' => [
'create' => null
]
Expand All @@ -134,59 +142,26 @@ public static function __callStatic(\$name, \$arguments) {

// 5. 创建必要的 Mock 类
$mockClasses = [
'Magento\Sales\Model\Order' => [
'loadByIncrementId' => null,
'getId' => null,
'getPayment' => null,
'setId' => null,
'setPayment' => null,
'getIncrementId' => null,
'getState' => null,
'setState' => null,
'getStatus' => null,
'setStatus' => null,
'save' => null
],
'Magento\Sales\Model\OrderFactory' => [
'create' => null
],
// 'Magento\Sales\Model\Order' => [
// 'loadByIncrementId' => null, 'getId' => null, 'getPayment' => null, 'setId' => null,
// 'setPayment' => null, 'getIncrementId' => null, 'getState' => null, 'setState' => null,
// 'getStatus' => null, 'setStatus' => null, 'save' => null
// ],
'Magento\Sales\Model\OrderFactory' => ['create' => null],
'Magento\Sales\Model\Order\Payment' => [
'getAdditionalInformation' => null,
'setAdditionalInformation' => null,
'getMethod' => 'mock_method',
'getMethodInstance' => null,
'setMethod' => null,
'getOrder' => null,
'setOrder' => null
],
'Magento\Quote\Model\QuoteFactory' => [
'create' => null
'getAdditionalInformation' => null, 'setAdditionalInformation' => null, 'getMethod' => 'mock_method',
'getMethodInstance' => null, 'setMethod' => null, 'getOrder' => null, 'setOrder' => null
],
'Magento\Quote\Model\QuoteFactory' => ['create' => null],
'Magento\Quote\Model\Quote' => [
'getId' => null,
'getReservedOrderId' => null,
'setReservedOrderId' => null,
'collectTotals' => null,
'save' => null
],
'Magento\Framework\DB\TransactionFactory' => [
'create' => null
],
'Magento\Framework\DB\Transaction' => [
'addObject' => null,
'save' => null
'getId' => null, 'getReservedOrderId' => null, 'setReservedOrderId' => null,
'collectTotals' => null, 'save' => null
],
'Magento\Framework\Controller\ResultFactory' => [
'create' => null
],
'Magento\Framework\Controller\Result\Json' => [
'setData' => null,
'setHttpResponseCode' => null
],
'Magento\Framework\Controller\Result\Redirect' => [
'setUrl' => null,
'setPath' => null
]
'Magento\Framework\DB\TransactionFactory' => ['create' => null],
'Magento\Framework\DB\Transaction' => ['addObject' => null, 'save' => null],
// 'Magento\Framework\Controller\ResultFactory' => ['create' => null],
'Magento\Framework\Controller\Result\Json' => ['setData' => null, 'setHttpResponseCode' => null],
'Magento\Framework\Controller\Result\Redirect' => ['setUrl' => null, 'setPath' => null]
];

foreach ($mockClasses as $className => $methods) {
Expand All @@ -197,11 +172,11 @@ public static function __callStatic(\$name, \$arguments) {
if (interface_exists('Magento\Framework\App\Config\ScopeConfigInterface')) {
if (!class_exists('MockScopeConfig')) {
generateAndLoadClassSafe('
class MockScopeConfig implements Magento\Framework\App\Config\ScopeConfigInterface {
public function getValue($path, $scopeType = null, $scopeCode = null) { return null; }
public function isSetFlag($path, $scopeType = null, $scopeCode = null) { return false; }
class MockScopeConfig {
public function getValue($path = null, $scopeType = null, $scopeCode = null) { return null; }
public function isSetFlag($path = null, $scopeType = null, $scopeCode = null) { return false; }
}
');
');
}
}

Expand Down
9 changes: 7 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"version": "1.0.0",
"version": "1.1.0",
"name": "antom/magento2-adminhtml",
"description": "Antom payments admin module for magento 2",
"require": {
Expand All @@ -9,7 +9,7 @@
"magento/module-customer": ">=103.0.0 <105.0.0"
},
"require-dev": {
"antom/magento2-core": "1.0.0",
"antom/magento2-core": "1.1.0",
"phpunit/phpunit": "^9.6",
"php-http/mock-client": "^1.3",
"magento/magento-coding-standard": "^31.0",
Expand All @@ -34,5 +34,10 @@
"mkdir -p build/coverage",
"vendor/bin/phpunit --configuration phpunit.xml.dist --coverage-html build/coverage"
]
},
"config": {
"audit": {
"ignore": ["PKSA-rkkf-636k-qjb3", "PKSA-wws7-mr54-jsny"]
}
}
}
2 changes: 1 addition & 1 deletion etc/module.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
<module name="Antom_Adminhtml" setup_version="1.0.0" >
<module name="Antom_Adminhtml" setup_version="1.1.0" >
<sequence>
<module name="Antom_Core"/>
<module name="Magento_Backend"/>
Expand Down