Adds a 'This project is a website' toggle to the Repository meta box. When checked, site fields (URL, platform, optional screenshot) replace the git-repo fields; when unchecked, repo fields are shown and site fields are cleared. Existing posts default to repo mode. - admin/metabox.php: checkbox, site URL/platform/screenshot inputs with media-library picker, JS toggle, save handler that switches modes - includes/helper-functions.php: projects_portfolio_is_website / get _site_url / get_site_platform / get_site_screenshot_url helpers - templates/single-projects.php: website-mode rendering (Visit Site button, platform row, screenshot) - tests/test-metabox.php: website-mode persistence + mode-switch tests - tests/wp-stubs.php: sanitize_text_field, absint stubs
257 lines
11 KiB
PHP
257 lines
11 KiB
PHP
<?php
|
|
require_once __DIR__ . '/../admin/metabox.php';
|
|
|
|
/**
|
|
* @covers \projects_portfolio_save_meta_box
|
|
*/
|
|
class Metabox_Test extends \PHPUnit\Framework\TestCase {
|
|
|
|
protected function setUp(): void {
|
|
\Brain\Monkey\setUp();
|
|
}
|
|
|
|
protected function tearDown(): void {
|
|
$_POST = [];
|
|
\Brain\Monkey\tearDown();
|
|
}
|
|
|
|
/** @var array<string,string> */
|
|
private array $saved = [];
|
|
|
|
protected function setUpSaveStubs(): void {
|
|
$this->saved = [];
|
|
\Brain\Monkey\Functions\stubs( [
|
|
'update_post_meta' => function ( $post_id, $key, $value ) {
|
|
$this->saved[ $key ] = $value;
|
|
return true;
|
|
},
|
|
'wp_verify_nonce' => function () { return true; },
|
|
'wp_nonce_field' => function () { /* noop */ },
|
|
] );
|
|
}
|
|
|
|
public function test_save_persists_provider_and_repo_url(): void {
|
|
$this->setUpSaveStubs();
|
|
$_POST['projects_portfolio_meta_box_nonce'] = 'nonce';
|
|
$_POST['projects_portfolio_provider'] = 'gitea';
|
|
$_POST['projects_portfolio_repo_url'] = 'https://codeberg.org/owner/repo';
|
|
$_POST['projects_portfolio_gitea_base_url'] = 'https://codeberg.org';
|
|
|
|
projects_portfolio_save_meta_box( 42 );
|
|
|
|
$this->assertSame( 'gitea', $this->saved['_projects_portfolio_provider'] );
|
|
$this->assertSame( 'https://codeberg.org/owner/repo', $this->saved['_projects_portfolio_repo_url'] );
|
|
$this->assertSame( 'https://codeberg.org', $this->saved['_projects_portfolio_gitea_base_url'] );
|
|
}
|
|
|
|
public function test_lazy_migration_from_legacy_url(): void {
|
|
$existing = [
|
|
'_projects_portfolio_provider' => '',
|
|
'_projects_portfolio_repo_url' => '',
|
|
'_projects_portfolio_github_url' => 'https://github.com/owner/repo',
|
|
];
|
|
\Brain\Monkey\Functions\stubs( [
|
|
'get_post_meta' => function ( $post_id, $key, $single = false ) use ( &$existing ) {
|
|
return $existing[ $key ] ?? '';
|
|
},
|
|
'update_post_meta' => function ( $post_id, $key, $value ) use ( &$existing ) {
|
|
$existing[ $key ] = $value;
|
|
return true;
|
|
},
|
|
'wp_verify_nonce' => function () { return true; },
|
|
'wp_nonce_field' => function () { /* noop */ },
|
|
] );
|
|
|
|
// No $_POST provider/repo_url. Lazy migration should fire.
|
|
$_POST['projects_portfolio_meta_box_nonce'] = 'nonce';
|
|
|
|
projects_portfolio_save_meta_box( 1 );
|
|
|
|
$this->assertSame( 'github', $existing['_projects_portfolio_provider'] );
|
|
$this->assertSame( 'https://github.com/owner/repo', $existing['_projects_portfolio_repo_url'] );
|
|
}
|
|
|
|
public function test_switching_to_gitea_deletes_legacy_github_url(): void {
|
|
$existing = [
|
|
'_projects_portfolio_provider' => 'github',
|
|
'_projects_portfolio_repo_url' => 'https://github.com/owner/repo',
|
|
'_projects_portfolio_github_url' => 'https://github.com/owner/repo',
|
|
];
|
|
$deleted = [];
|
|
\Brain\Monkey\Functions\stubs( [
|
|
'get_post_meta' => function ( $post_id, $key, $single = false ) use ( &$existing ) {
|
|
return $existing[ $key ] ?? '';
|
|
},
|
|
'update_post_meta' => function ( $post_id, $key, $value ) use ( &$existing ) {
|
|
$existing[ $key ] = $value;
|
|
return true;
|
|
},
|
|
'delete_post_meta' => function ( $post_id, $key ) use ( &$existing, &$deleted ) {
|
|
unset( $existing[ $key ] );
|
|
$deleted[] = $key;
|
|
return true;
|
|
},
|
|
'wp_verify_nonce' => function () { return true; },
|
|
'wp_nonce_field' => function () { /* noop */ },
|
|
] );
|
|
|
|
$_POST['projects_portfolio_meta_box_nonce'] = 'nonce';
|
|
$_POST['projects_portfolio_provider'] = 'gitea';
|
|
$_POST['projects_portfolio_repo_url'] = 'https://codeberg.org/owner/repo';
|
|
|
|
projects_portfolio_save_meta_box( 7 );
|
|
|
|
$this->assertSame( 'gitea', $existing['_projects_portfolio_provider'] );
|
|
$this->assertSame( 'https://codeberg.org/owner/repo', $existing['_projects_portfolio_repo_url'] );
|
|
$this->assertArrayNotHasKey( '_projects_portfolio_github_url', $existing );
|
|
$this->assertContains( '_projects_portfolio_github_url', $deleted );
|
|
}
|
|
|
|
public function test_github_with_empty_repo_url_deletes_legacy_github_url(): void {
|
|
$existing = [
|
|
'_projects_portfolio_provider' => 'github',
|
|
'_projects_portfolio_repo_url' => 'https://github.com/owner/repo',
|
|
'_projects_portfolio_github_url' => 'https://github.com/owner/repo',
|
|
];
|
|
$deleted = [];
|
|
\Brain\Monkey\Functions\stubs( [
|
|
'get_post_meta' => function ( $post_id, $key, $single = false ) use ( &$existing ) {
|
|
return $existing[ $key ] ?? '';
|
|
},
|
|
'update_post_meta' => function ( $post_id, $key, $value ) use ( &$existing ) {
|
|
$existing[ $key ] = $value;
|
|
return true;
|
|
},
|
|
'delete_post_meta' => function ( $post_id, $key ) use ( &$existing, &$deleted ) {
|
|
unset( $existing[ $key ] );
|
|
$deleted[] = $key;
|
|
return true;
|
|
},
|
|
'wp_verify_nonce' => function () { return true; },
|
|
'wp_nonce_field' => function () { /* noop */ },
|
|
] );
|
|
|
|
$_POST['projects_portfolio_meta_box_nonce'] = 'nonce';
|
|
$_POST['projects_portfolio_provider'] = 'github';
|
|
$_POST['projects_portfolio_repo_url'] = '';
|
|
|
|
projects_portfolio_save_meta_box( 9 );
|
|
|
|
$this->assertSame( 'github', $existing['_projects_portfolio_provider'] );
|
|
$this->assertSame( '', $existing['_projects_portfolio_repo_url'] );
|
|
$this->assertArrayNotHasKey( '_projects_portfolio_github_url', $existing );
|
|
$this->assertContains( '_projects_portfolio_github_url', $deleted );
|
|
}
|
|
|
|
public function test_website_mode_persists_website_fields_and_clears_repo_fields(): void {
|
|
$existing = [
|
|
'_projects_portfolio_provider' => 'github',
|
|
'_projects_portfolio_repo_url' => 'https://github.com/owner/repo',
|
|
'_projects_portfolio_github_url' => 'https://github.com/owner/repo',
|
|
];
|
|
$deleted = [];
|
|
\Brain\Monkey\Functions\stubs( [
|
|
'get_post_meta' => function ( $post_id, $key, $single = false ) use ( &$existing ) {
|
|
return $existing[ $key ] ?? '';
|
|
},
|
|
'update_post_meta' => function ( $post_id, $key, $value ) use ( &$existing ) {
|
|
$existing[ $key ] = $value;
|
|
return true;
|
|
},
|
|
'delete_post_meta' => function ( $post_id, $key ) use ( &$existing, &$deleted ) {
|
|
unset( $existing[ $key ] );
|
|
$deleted[] = $key;
|
|
return true;
|
|
},
|
|
'wp_verify_nonce' => function () { return true; },
|
|
'wp_nonce_field' => function () { /* noop */ },
|
|
] );
|
|
|
|
$_POST['projects_portfolio_meta_box_nonce'] = 'nonce';
|
|
$_POST['projects_portfolio_is_website'] = '1';
|
|
$_POST['projects_portfolio_site_url'] = 'https://example.com';
|
|
$_POST['projects_portfolio_site_platform'] = 'WordPress';
|
|
$_POST['projects_portfolio_site_screenshot_id'] = '42';
|
|
|
|
projects_portfolio_save_meta_box( 11 );
|
|
|
|
$this->assertSame( '1', $existing['_projects_portfolio_is_website'] );
|
|
$this->assertSame( 'https://example.com', $existing['_projects_portfolio_site_url'] );
|
|
$this->assertSame( 'WordPress', $existing['_projects_portfolio_site_platform'] );
|
|
$this->assertSame( 42, $existing['_projects_portfolio_site_screenshot_id'] );
|
|
|
|
// Repo-mode fields should be cleared when switching to website mode.
|
|
$this->assertArrayNotHasKey( '_projects_portfolio_provider', $existing );
|
|
$this->assertArrayNotHasKey( '_projects_portfolio_repo_url', $existing );
|
|
$this->assertArrayNotHasKey( '_projects_portfolio_github_url', $existing );
|
|
$this->assertContains( '_projects_portfolio_provider', $deleted );
|
|
$this->assertContains( '_projects_portfolio_repo_url', $deleted );
|
|
$this->assertContains( '_projects_portfolio_github_url', $deleted );
|
|
}
|
|
|
|
public function test_unchecked_website_checkbox_clears_website_fields(): void {
|
|
$existing = [
|
|
'_projects_portfolio_is_website' => '1',
|
|
'_projects_portfolio_site_url' => 'https://example.com',
|
|
'_projects_portfolio_site_platform' => 'WordPress',
|
|
'_projects_portfolio_site_screenshot_id' => 42,
|
|
];
|
|
$deleted = [];
|
|
\Brain\Monkey\Functions\stubs( [
|
|
'get_post_meta' => function ( $post_id, $key, $single = false ) use ( &$existing ) {
|
|
return $existing[ $key ] ?? '';
|
|
},
|
|
'update_post_meta' => function ( $post_id, $key, $value ) use ( &$existing ) {
|
|
$existing[ $key ] = $value;
|
|
return true;
|
|
},
|
|
'delete_post_meta' => function ( $post_id, $key ) use ( &$existing, &$deleted ) {
|
|
unset( $existing[ $key ] );
|
|
$deleted[] = $key;
|
|
return true;
|
|
},
|
|
'wp_verify_nonce' => function () { return true; },
|
|
'wp_nonce_field' => function () { /* noop */ },
|
|
] );
|
|
|
|
$_POST['projects_portfolio_meta_box_nonce'] = 'nonce';
|
|
// is_website is absent — repo mode is implied.
|
|
|
|
projects_portfolio_save_meta_box( 13 );
|
|
|
|
$this->assertArrayNotHasKey( '_projects_portfolio_is_website', $existing );
|
|
$this->assertArrayNotHasKey( '_projects_portfolio_site_url', $existing );
|
|
$this->assertArrayNotHasKey( '_projects_portfolio_site_platform', $existing );
|
|
$this->assertArrayNotHasKey( '_projects_portfolio_site_screenshot_id', $existing );
|
|
$this->assertContains( '_projects_portfolio_is_website', $deleted );
|
|
}
|
|
|
|
public function test_existing_repo_post_without_checkbox_keeps_repo_fields(): void {
|
|
$existing = [
|
|
'_projects_portfolio_provider' => 'github',
|
|
'_projects_portfolio_repo_url' => 'https://github.com/owner/repo',
|
|
];
|
|
\Brain\Monkey\Functions\stubs( [
|
|
'get_post_meta' => function ( $post_id, $key, $single = false ) use ( &$existing ) {
|
|
return $existing[ $key ] ?? '';
|
|
},
|
|
'update_post_meta' => function ( $post_id, $key, $value ) use ( &$existing ) {
|
|
$existing[ $key ] = $value;
|
|
return true;
|
|
},
|
|
'delete_post_meta' => function () { return true; },
|
|
'wp_verify_nonce' => function () { return true; },
|
|
'wp_nonce_field' => function () { /* noop */ },
|
|
] );
|
|
|
|
$_POST['projects_portfolio_meta_box_nonce'] = 'nonce';
|
|
// is_website not present in $_POST — should be treated as unchecked.
|
|
|
|
projects_portfolio_save_meta_box( 17 );
|
|
|
|
$this->assertSame( 'github', $existing['_projects_portfolio_provider'] );
|
|
$this->assertSame( 'https://github.com/owner/repo', $existing['_projects_portfolio_repo_url'] );
|
|
$this->assertArrayNotHasKey( '_projects_portfolio_is_website', $existing );
|
|
}
|
|
}
|