feat(wpmc S4): OptionsPage::do_register registers routing field group
Also: add acf_add_options_page to acf-stubs.php; add translation-function
stubs to acf-stubs.php; fix TestCase::tearDown to call Patchwork::restoreAll()
in a finally block so that when('function_exists') routes are always cleaned up
even when Mockery::close() throws InvalidCountException.
This commit is contained in:
@@ -16,6 +16,112 @@ final class OptionsPage
|
|||||||
|
|
||||||
public static function do_register(): void
|
public static function do_register(): void
|
||||||
{
|
{
|
||||||
// Implementation in Task 8.
|
if (!\function_exists('acf_add_options_page')
|
||||||
|
|| !\function_exists('acf_add_local_field_group')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
\acf_add_options_page([
|
||||||
|
'page_title' => 'WP Multi City',
|
||||||
|
'menu_title' => 'WP Multi City',
|
||||||
|
'menu_slug' => self::PAGE_SLUG,
|
||||||
|
'capability' => 'manage_options',
|
||||||
|
'redirect' => false,
|
||||||
|
]);
|
||||||
|
|
||||||
|
\acf_add_local_field_group([
|
||||||
|
'key' => self::GROUP_KEY,
|
||||||
|
'title' => 'WPMultiCity / Routing',
|
||||||
|
'fields' => [
|
||||||
|
[
|
||||||
|
'key' => 'field_wpmc_main_town_slug',
|
||||||
|
'label' => __('Главный город (без префикса URL)', 'wp-multi-city'),
|
||||||
|
'name' => 'main_town_slug',
|
||||||
|
'type' => 'taxonomy',
|
||||||
|
'taxonomy' => 'town',
|
||||||
|
'field_type' => 'select',
|
||||||
|
'return_format' => 'object',
|
||||||
|
'allow_null' => 1,
|
||||||
|
'multiple' => 0,
|
||||||
|
'add_term' => 0,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'key' => 'field_wpmc_index_town_alt',
|
||||||
|
'label' => __('Главная для других городов', 'wp-multi-city'),
|
||||||
|
'name' => 'index_town_alt',
|
||||||
|
'type' => 'post_object',
|
||||||
|
'post_type' => ['page'],
|
||||||
|
'return_format' => 'id',
|
||||||
|
'allow_null' => 1,
|
||||||
|
'multiple' => 0,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'key' => 'field_wpmc_page_unic',
|
||||||
|
'label' => __('Клоны страниц', 'wp-multi-city'),
|
||||||
|
'name' => 'page_unic',
|
||||||
|
'type' => 'repeater',
|
||||||
|
'layout' => 'table',
|
||||||
|
'button_label' => __('Добавить клон', 'wp-multi-city'),
|
||||||
|
'sub_fields' => [
|
||||||
|
[
|
||||||
|
'key' => 'field_wpmc_page_unic_page_old',
|
||||||
|
'label' => __('Оригинал', 'wp-multi-city'),
|
||||||
|
'name' => 'page_old',
|
||||||
|
'type' => 'post_object',
|
||||||
|
'post_type' => ['page'],
|
||||||
|
'return_format' => 'id',
|
||||||
|
'allow_null' => 0,
|
||||||
|
'multiple' => 0,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'key' => 'field_wpmc_page_unic_page_new',
|
||||||
|
'label' => __('Клон', 'wp-multi-city'),
|
||||||
|
'name' => 'page_new',
|
||||||
|
'type' => 'post_object',
|
||||||
|
'post_type' => ['page'],
|
||||||
|
'return_format' => 'id',
|
||||||
|
'allow_null' => 0,
|
||||||
|
'multiple' => 0,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'key' => 'field_wpmc_page_unic_town',
|
||||||
|
'label' => __('Город', 'wp-multi-city'),
|
||||||
|
'name' => 'town',
|
||||||
|
'type' => 'taxonomy',
|
||||||
|
'taxonomy' => 'town',
|
||||||
|
'field_type' => 'select',
|
||||||
|
'return_format' => 'id',
|
||||||
|
'allow_null' => 0,
|
||||||
|
'multiple' => 0,
|
||||||
|
'add_term' => 0,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'key' => 'field_wpmc_page_no_rep',
|
||||||
|
'label' => __('Исключения из роутинга', 'wp-multi-city'),
|
||||||
|
'name' => 'page_no_rep',
|
||||||
|
'type' => 'repeater',
|
||||||
|
'layout' => 'table',
|
||||||
|
'button_label' => __('Добавить исключение', 'wp-multi-city'),
|
||||||
|
'sub_fields' => [
|
||||||
|
[
|
||||||
|
'key' => 'field_wpmc_page_no_rep_link',
|
||||||
|
'label' => __('Путь', 'wp-multi-city'),
|
||||||
|
'name' => 'link',
|
||||||
|
'type' => 'text',
|
||||||
|
'instructions' => __('Префикс URL, например /privacy/', 'wp-multi-city'),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'location' => [
|
||||||
|
[['param' => 'options_page', 'operator' => '==', 'value' => self::PAGE_SLUG]],
|
||||||
|
],
|
||||||
|
'menu_order' => 0,
|
||||||
|
'position' => 'normal',
|
||||||
|
'style' => 'default',
|
||||||
|
'active' => true,
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,4 +26,68 @@ final class OptionsPageTest extends TestCase
|
|||||||
'OptionsPage::register() must hook wpmc_main_town_slug filter'
|
'OptionsPage::register() must hook wpmc_main_town_slug filter'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function test_do_register_is_noop_when_acf_missing(): void
|
||||||
|
{
|
||||||
|
// Order matters: expect() must come BEFORE function_exists alias so that
|
||||||
|
// Brain Monkey's FunctionStub::__construct sees the real function_exists
|
||||||
|
// and doesn't re-eval an already-declared stub.
|
||||||
|
\Brain\Monkey\Functions\expect('acf_add_options_page')->never();
|
||||||
|
\Brain\Monkey\Functions\expect('acf_add_local_field_group')->never();
|
||||||
|
\Brain\Monkey\Functions\when('function_exists')->alias(function ($name) {
|
||||||
|
return $name !== 'acf_add_options_page' && $name !== 'acf_add_local_field_group';
|
||||||
|
});
|
||||||
|
|
||||||
|
OptionsPage::do_register();
|
||||||
|
|
||||||
|
self::assertTrue(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_do_register_creates_options_page(): void
|
||||||
|
{
|
||||||
|
\Brain\Monkey\Functions\expect('acf_add_options_page')
|
||||||
|
->once()
|
||||||
|
->with(\Mockery::on(function ($args) {
|
||||||
|
return ($args['menu_slug'] ?? null) === 'wp-multi-city'
|
||||||
|
&& ($args['page_title'] ?? null) === 'WP Multi City';
|
||||||
|
}));
|
||||||
|
\Brain\Monkey\Functions\when('acf_add_local_field_group')->justReturn();
|
||||||
|
\Brain\Monkey\Functions\when('function_exists')->alias(function ($name) {
|
||||||
|
return $name === 'acf_add_options_page' || $name === 'acf_add_local_field_group';
|
||||||
|
});
|
||||||
|
|
||||||
|
OptionsPage::do_register();
|
||||||
|
|
||||||
|
self::assertTrue(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_do_register_registers_routing_field_group(): void
|
||||||
|
{
|
||||||
|
\Brain\Monkey\Functions\when('acf_add_options_page')->justReturn();
|
||||||
|
|
||||||
|
$captured = null;
|
||||||
|
\Brain\Monkey\Functions\expect('acf_add_local_field_group')
|
||||||
|
->once()
|
||||||
|
->with(\Mockery::on(function ($group) use (&$captured) {
|
||||||
|
$captured = $group;
|
||||||
|
return true;
|
||||||
|
}));
|
||||||
|
\Brain\Monkey\Functions\when('function_exists')->alias(function ($name) {
|
||||||
|
return $name === 'acf_add_options_page' || $name === 'acf_add_local_field_group';
|
||||||
|
});
|
||||||
|
|
||||||
|
OptionsPage::do_register();
|
||||||
|
|
||||||
|
self::assertSame('group_wpmc_routing', $captured['key']);
|
||||||
|
self::assertSame(
|
||||||
|
[[['param' => 'options_page', 'operator' => '==', 'value' => 'wp-multi-city']]],
|
||||||
|
$captured['location']
|
||||||
|
);
|
||||||
|
|
||||||
|
$names = array_column($captured['fields'], 'name');
|
||||||
|
self::assertContains('main_town_slug', $names);
|
||||||
|
self::assertContains('index_town_alt', $names);
|
||||||
|
self::assertContains('page_unic', $names);
|
||||||
|
self::assertContains('page_no_rep', $names);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-1
@@ -33,7 +33,15 @@ abstract class TestCase extends BaseTestCase
|
|||||||
$GLOBALS['wp_the_query'],
|
$GLOBALS['wp_the_query'],
|
||||||
$GLOBALS['post']
|
$GLOBALS['post']
|
||||||
);
|
);
|
||||||
Monkey\tearDown();
|
try {
|
||||||
|
Monkey\tearDown();
|
||||||
|
} finally {
|
||||||
|
// Patchwork routes MUST be disconnected even when Monkey\tearDown() throws
|
||||||
|
// (e.g. Mockery count validation failure). If they are not, any
|
||||||
|
// when('function_exists')->alias() from the failed test stays active and
|
||||||
|
// causes "Cannot redeclare function __()..." in the next test's setUp().
|
||||||
|
\Patchwork\restoreAll();
|
||||||
|
}
|
||||||
parent::tearDown();
|
parent::tearDown();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,3 +16,60 @@ declare(strict_types=1);
|
|||||||
if (!function_exists('acf_add_local_field_group')) {
|
if (!function_exists('acf_add_local_field_group')) {
|
||||||
function acf_add_local_field_group($field_group): void {}
|
function acf_add_local_field_group($field_group): void {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!function_exists('acf_add_options_page')) {
|
||||||
|
function acf_add_options_page($args): void {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Translation function stubs — declared here (via Patchwork's stream wrapper) so that
|
||||||
|
// Brain\Monkey\Functions\stubTranslationFunctions() finds them via function_exists() and
|
||||||
|
// never falls back to eval(), which would cause "Cannot redeclare" fatals across tests
|
||||||
|
// that use when('function_exists')->alias(...).
|
||||||
|
if (!function_exists('__')) {
|
||||||
|
function __($text, $domain = 'default') { return $text; }
|
||||||
|
}
|
||||||
|
if (!function_exists('_x')) {
|
||||||
|
function _x($text, $context, $domain = 'default') { return $text; }
|
||||||
|
}
|
||||||
|
if (!function_exists('translate')) {
|
||||||
|
function translate($text, $domain = 'default') { return $text; }
|
||||||
|
}
|
||||||
|
if (!function_exists('_n')) {
|
||||||
|
function _n($single, $plural, $number, $domain = 'default') { return $number === 1 ? $single : $plural; }
|
||||||
|
}
|
||||||
|
if (!function_exists('_nx')) {
|
||||||
|
function _nx($single, $plural, $number, $context, $domain = 'default') { return $number === 1 ? $single : $plural; }
|
||||||
|
}
|
||||||
|
if (!function_exists('_e')) {
|
||||||
|
function _e($text, $domain = 'default') { echo $text; }
|
||||||
|
}
|
||||||
|
if (!function_exists('_ex')) {
|
||||||
|
function _ex($text, $context, $domain = 'default') { echo $text; }
|
||||||
|
}
|
||||||
|
if (!function_exists('esc_html__')) {
|
||||||
|
function esc_html__($text, $domain = 'default') { return $text; }
|
||||||
|
}
|
||||||
|
if (!function_exists('esc_html_x')) {
|
||||||
|
function esc_html_x($text, $context, $domain = 'default') { return $text; }
|
||||||
|
}
|
||||||
|
if (!function_exists('esc_attr__')) {
|
||||||
|
function esc_attr__($text, $domain = 'default') { return $text; }
|
||||||
|
}
|
||||||
|
if (!function_exists('esc_attr_x')) {
|
||||||
|
function esc_attr_x($text, $context, $domain = 'default') { return $text; }
|
||||||
|
}
|
||||||
|
if (!function_exists('esc_html_e')) {
|
||||||
|
function esc_html_e($text, $domain = 'default') { echo $text; }
|
||||||
|
}
|
||||||
|
if (!function_exists('esc_attr_e')) {
|
||||||
|
function esc_attr_e($text, $domain = 'default') { echo $text; }
|
||||||
|
}
|
||||||
|
if (!function_exists('_n_noop')) {
|
||||||
|
function _n_noop($singular, $plural, $domain = 'default') { return compact('singular', 'plural'); }
|
||||||
|
}
|
||||||
|
if (!function_exists('_nx_noop')) {
|
||||||
|
function _nx_noop($singular, $plural, $context, $domain = 'default') { return compact('singular', 'plural'); }
|
||||||
|
}
|
||||||
|
if (!function_exists('translate_nooped_plural')) {
|
||||||
|
function translate_nooped_plural($nooped_plural, $count, $domain = 'default') { return $count === 1 ? $nooped_plural['singular'] : $nooped_plural['plural']; }
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user