Skip to content

Commit c721375

Browse files
committed
feat: add PHPUnit workflow and unit tests for VendorFileMapper
1 parent 54d6a80 commit c721375

2 files changed

Lines changed: 240 additions & 0 deletions

File tree

.github/workflows/phpunit.yml

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
name: Unit Tests
2+
3+
on:
4+
pull_request:
5+
branches: [main]
6+
push:
7+
branches: [main]
8+
workflow_dispatch:
9+
10+
permissions:
11+
contents: read
12+
13+
jobs:
14+
unit-tests:
15+
name: Unit Tests (PHP 8.4)
16+
runs-on: ubuntu-latest
17+
18+
services:
19+
# No DB/Elasticsearch needed for Unit Tests usually, but keeping minimal env helps if tests expand
20+
# Using just PHP is often enough for pure unit tests mocking everything.
21+
# But since we install full Magento to ensure dependencies, we might need DB for setup:install
22+
mariadb:
23+
image: mariadb:11.4
24+
env:
25+
MYSQL_ROOT_PASSWORD: magento
26+
MYSQL_DATABASE: magento
27+
ports:
28+
- 3306:3306
29+
options: --health-cmd="healthcheck.sh --connect --innodb_initialized" --health-interval=10s --health-timeout=5s --health-retries=3
30+
31+
opensearch:
32+
image: opensearchproject/opensearch:3
33+
ports:
34+
- 9200:9200
35+
env:
36+
discovery.type: single-node
37+
DISABLE_SECURITY_PLUGIN: true
38+
OPENSEARCH_JAVA_OPTS: -Xms512m -Xmx512m
39+
options: --health-cmd="curl http://localhost:9200/_cluster/health" --health-interval=10s --health-timeout=5s --health-retries=10
40+
41+
steps:
42+
- name: Checkout code
43+
uses: actions/checkout@v4
44+
with:
45+
path: mageforge
46+
47+
- name: Setup PHP
48+
uses: shivammathur/setup-php@v2
49+
with:
50+
php-version: "8.4"
51+
extensions: mbstring, intl, gd, xml, soap, zip, bcmath, pdo_mysql, curl, sockets
52+
tools: composer:v2
53+
54+
- name: Cache Composer packages
55+
id: composer-cache
56+
uses: actions/cache@v4
57+
with:
58+
path: ~/.composer/cache/files
59+
key: ${{ runner.os }}-composer-2.4.8-${{ hashFiles('**/composer.json') }}
60+
restore-keys: ${{ runner.os }}-composer-2.4.8
61+
62+
- name: Download Magento
63+
run: |
64+
composer create-project \
65+
--repository-url=https://mirror.mage-os.org/ \
66+
magento/project-community-edition \
67+
magento2
68+
69+
- name: Install Magento
70+
working-directory: magento2
71+
env:
72+
COMPOSER_AUTH: ${{ secrets.COMPOSER_AUTH }}
73+
run: |
74+
composer config minimum-stability stable
75+
composer config prefer-stable true
76+
composer install --no-interaction --no-progress
77+
bin/magento setup:install \
78+
--base-url=http://localhost \
79+
--db-host=127.0.0.1 \
80+
--db-name=magento \
81+
--db-user=root \
82+
--db-password=magento \
83+
--admin-firstname=Admin \
84+
--admin-lastname=User \
85+
--admin-email=admin@example.com \
86+
--admin-user=admin \
87+
--admin-password=admin12345 \
88+
--language=en_US \
89+
--currency=USD \
90+
--timezone=Europe/Berlin \
91+
--use-rewrites=1 \
92+
--backend-frontname=admin \
93+
--search-engine=opensearch \
94+
--opensearch-host=localhost \
95+
--opensearch-port=9200 \
96+
--opensearch-index-prefix=magento \
97+
--cleanup-database
98+
99+
- name: Install MageForge Module
100+
working-directory: magento2
101+
run: |
102+
# Add local repository
103+
composer config repositories.mageforge-local path ../mageforge
104+
105+
# Install module
106+
composer require --no-update openforgeproject/mageforge:@dev
107+
108+
# Update
109+
composer update --with-dependencies
110+
111+
- name: Run PHPUnit
112+
working-directory: magento2
113+
run: |
114+
# Run tests located in the installed module vendor folder
115+
# Mapping: ../mageforge/src/src/Test -> vendor/openforgeproject/mageforge/src/Test
116+
vendor/bin/phpunit vendor/openforgeproject/mageforge/src/Test/Unit
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace OpenForgeProject\MageForge\Test\Unit\Service;
6+
7+
use Magento\Framework\Component\ComponentRegistrar;
8+
use Magento\Framework\Component\ComponentRegistrarInterface;
9+
use Magento\Framework\Filesystem\DirectoryList;
10+
use OpenForgeProject\MageForge\Service\VendorFileMapper;
11+
use PHPUnit\Framework\MockObject\MockObject;
12+
use PHPUnit\Framework\TestCase;
13+
14+
class VendorFileMapperTest extends TestCase
15+
{
16+
private VendorFileMapper $mapper;
17+
private ComponentRegistrarInterface|MockObject $componentRegistrarMock;
18+
private DirectoryList|MockObject $directoryListMock;
19+
20+
protected function setUp(): void
21+
{
22+
$this->componentRegistrarMock = $this->getMockBuilder(ComponentRegistrarInterface::class)
23+
->getMock();
24+
25+
$this->directoryListMock = $this->getMockBuilder(DirectoryList::class)
26+
->disableOriginalConstructor()
27+
->getMock();
28+
29+
// Default root path for tests
30+
$this->directoryListMock->method('getRoot')
31+
->willReturn('/var/www/html');
32+
33+
$this->mapper = new VendorFileMapper(
34+
$this->componentRegistrarMock,
35+
$this->directoryListMock
36+
);
37+
}
38+
39+
public function testMapToThemePathWithStandardModule(): void
40+
{
41+
$sourceFile = 'vendor/magento/module-catalog/view/frontend/templates/product/list.phtml';
42+
$themePath = 'app/design/frontend/My/Theme';
43+
44+
// Mock ComponentRegistrar to find the module
45+
$this->componentRegistrarMock->expects($this->once())
46+
->method('getPaths')
47+
->with(ComponentRegistrar::MODULE)
48+
->willReturn([
49+
'Magento_Catalog' => '/var/www/html/vendor/magento/module-catalog'
50+
]);
51+
52+
$result = $this->mapper->mapToThemePath($sourceFile, $themePath);
53+
54+
$this->assertEquals(
55+
'app/design/frontend/My/Theme/Magento_Catalog/templates/product/list.phtml',
56+
$result
57+
);
58+
}
59+
60+
public function testMapToThemePathWithNestedCompatModule(): void
61+
{
62+
// Path simulates a Hyvä Compat module with nested target module folder
63+
$sourceFile = 'vendor/mollie/magento2-hyva-compatibility/src/Mollie_HyvaCompatibility/view/frontend/templates/Mollie_Payment/product/view/applepay.phtml';
64+
$themePath = 'app/design/frontend/My/Theme';
65+
66+
// Regex detection should prioritize this, so ComponentRegistrar might not even be called
67+
// or if called, it shouldn't matter for the logic flow if regex matches first.
68+
69+
$result = $this->mapper->mapToThemePath($sourceFile, $themePath);
70+
71+
$this->assertEquals(
72+
'app/design/frontend/My/Theme/Mollie_Payment/templates/product/view/applepay.phtml',
73+
$result
74+
);
75+
}
76+
77+
public function testMapToThemePathWithVendorTheme(): void
78+
{
79+
// Path simulates a Vendor Theme (e.g. Hyva Default)
80+
$sourceFile = 'vendor/hyva-themes/magento2-default-theme/Magento_GiftMessage/templates/php-cart/gift-options-body.phtml';
81+
$themePath = 'app/design/frontend/My/Theme';
82+
83+
$result = $this->mapper->mapToThemePath($sourceFile, $themePath);
84+
85+
$this->assertEquals(
86+
'app/design/frontend/My/Theme/Magento_GiftMessage/templates/php-cart/gift-options-body.phtml',
87+
$result
88+
);
89+
}
90+
91+
public function testMapToThemePathWithAbsolutePaths(): void
92+
{
93+
$sourceFile = '/var/www/html/vendor/magento/module-customer/view/frontend/templates/account/dashboard.phtml';
94+
// Note: passing absolute theme path here
95+
$themePath = '/var/www/html/app/design/frontend/Vendor/Theme';
96+
97+
$this->componentRegistrarMock->method('getPaths')
98+
->with(ComponentRegistrar::MODULE)
99+
->willReturn([
100+
'Magento_Customer' => '/var/www/html/vendor/magento/module-customer'
101+
]);
102+
103+
$result = $this->mapper->mapToThemePath($sourceFile, $themePath);
104+
105+
// Expect absolute path return because input theme path was absolute (logic prepends theme path)
106+
$this->assertEquals(
107+
'/var/www/html/app/design/frontend/Vendor/Theme/Magento_Customer/templates/account/dashboard.phtml',
108+
$result
109+
);
110+
}
111+
112+
public function testThrowsExceptionIfModuleNotFound(): void
113+
{
114+
$this->expectException(\RuntimeException::class);
115+
116+
$sourceFile = 'vendor/unknown/package/somefile.txt';
117+
$themePath = 'app/design/frontend/My/Theme';
118+
119+
$this->componentRegistrarMock->method('getPaths')
120+
->willReturn([]); // No modules registered
121+
122+
$this->mapper->mapToThemePath($sourceFile, $themePath);
123+
}
124+
}

0 commit comments

Comments
 (0)