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
48 changes: 0 additions & 48 deletions src/Integration/WordPressSeo/Breadcrumbs.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
use Yoast\WP\SEO\Context\Meta_Tags_Context;
use Yoast\WP\SEO\Main;
use Yoast\WP\SEO\Models\Indexable;
use Yoast\WP\SEO\Repositories\Indexable_Repository;
use Yoast\WP\SEO\Surfaces\Values\Meta;

/**
Expand All @@ -26,7 +25,6 @@ public function __construct(

public function registerHooks(): void
{
add_filter('wpseo_breadcrumb_indexables', [$this, 'fixHomeBreadcrumbs'], 10, 2);
add_filter('wpseo_breadcrumb_indexables', [$this, 'fixTaxonomyBreadcrumbs'], 10, 2);
add_filter('wpseo_breadcrumb_indexables', [$this, 'fixPostBreadcrumbs'], 10, 2);
}
Expand Down Expand Up @@ -125,52 +123,6 @@ public function fixTaxonomyBreadcrumbs(array $indexables, Meta_Tags_Context $con
return $indexables;
}

/**
* Fix Yoast breadcrumbs on home.
*
* @param Indexable[] $indexables
* @return Indexable[]
*/
public function fixHomeBreadcrumbs(array $indexables, Meta_Tags_Context $context): array
{
if (!$this->api->isQueryPageForCustomPostType()) {
return $indexables;
}

$yoast = $this->getYoast();

if ($yoast->helpers->current_page->get_page_type() !== 'Home_Page') {
return $indexables;
}

/** @var Indexable_Repository $indexableRepository */
$indexableRepository = $yoast->classes->get(Indexable_Repository::class);
$staticAncestors = [];

$breadcrumbsHome = $yoast->helpers->options->get('breadcrumbs-home');

if ($breadcrumbsHome !== '') {
$frontPageId = $yoast->helpers->current_page->get_front_page_id();

$staticAncestor = $frontPageId === 0
? $indexableRepository->find_for_home_page()
: $indexableRepository->find_by_id_and_type($frontPageId, 'post');

if (
$staticAncestor instanceof Indexable
&& ($frontPageId === 0 || $staticAncestor->post_status !== 'unindexed')
) {
$staticAncestors[] = $staticAncestor;
}
}

if (!empty($staticAncestors)) {
array_unshift($indexables, ...$staticAncestors);
}

return $indexables;
}

private function getYoast(): Main
{
/** @var Main */
Expand Down
5 changes: 5 additions & 0 deletions src/functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ function get_page_url_for_custom_post_type(?string $postType = null): ?string
}

namespace {
// Deprecated back-compat shims. The function_exists() guards run during
// Composer's files autoload, before coverage starts, so they cannot be
// covered; the function bodies are exercised by PublicApiTest.
// @codeCoverageIgnoreStart
if (!\function_exists('is_page_for_custom_post_type')) {
/**
* @deprecated 1.0.0 Use \n5s\PageForCustomPostType\is_page_for_custom_post_type() instead.
Expand Down Expand Up @@ -124,4 +128,5 @@ function get_page_url_for_custom_post_type(?string $postType = null): ?string
return \n5s\PageForCustomPostType\get_page_url_for_custom_post_type($postType);
}
}
// @codeCoverageIgnoreEnd
}
2 changes: 1 addition & 1 deletion tests/Integration/AdminScreenTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ protected function setUp(): void
if (!\function_exists('add_settings_section')) {
require_once \ABSPATH . 'wp-admin/includes/template.php';
}
if (!\function_exists('register_setting')) {
if (!\function_exists('add_submenu_page')) {
require_once \ABSPATH . 'wp-admin/includes/plugin.php';
}

Expand Down
58 changes: 58 additions & 0 deletions tests/Integration/Integration/AutodescriptionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@

namespace n5s\PageForCustomPostType\Tests\Integration\Integration;

use n5s\PageForCustomPostType\Core\Api;
use n5s\PageForCustomPostType\Integration\Autodescription\Autodescription;
use n5s\PageForCustomPostType\Integration\Autodescription\Breadcrumbs;
use n5s\PageForCustomPostType\Integration\Autodescription\QueryType;
use n5s\PageForCustomPostType\Tests\Fixtures\TestCase;
use PHPUnit\Framework\Attributes\RequiresFunction;
use PHPUnit\Framework\Attributes\WithoutErrorHandler;
Expand Down Expand Up @@ -195,6 +199,60 @@ public function testPostTypeWithoutPfcptPageHasNoBreadcrumbChange(): void
}
}

public function testIsSupported(): void
{
$autodescription = new Autodescription(new QueryType(new Api()), new Breadcrumbs(new Api()));

$this->assertTrue($autodescription->isSupported());
}

public function testRegisterHooksRegistersSubIntegrations(): void
{
$queryType = new QueryType(new Api());
$breadcrumbs = new Breadcrumbs(new Api());

(new Autodescription($queryType, $breadcrumbs))->registerHooks();

$this->assertNotFalse(has_filter('the_seo_framework_is_singular_archive', [$queryType, 'markPfcptAsSingularArchive']));
$this->assertNotFalse(has_filter('the_seo_framework_breadcrumb_list', [$breadcrumbs, 'addPfcptPageCrumb']));
}

public function testSingularArchiveShortCircuitsWhenAlreadyTrue(): void
{
$queryType = new QueryType(new Api());

$this->assertTrue($queryType->markPfcptAsSingularArchive(true, null));
}

public function testSingularArchiveResolvedFromExplicitPageId(): void
{
$queryType = new QueryType(new Api());

$this->assertTrue($queryType->markPfcptAsSingularArchive(false, $this->homeForBookId));

$regularId = self::factory()->post->create(['post_type' => 'post']);
$this->assertFalse($queryType->markPfcptAsSingularArchive(false, $regularId));
}

public function testBreadcrumbCrumbSkippedForUnresolvableArgs(): void
{
$breadcrumbs = new Breadcrumbs(new Api());
$list = [['url' => home_url('/'), 'name' => 'Home']];

// id is neither int nor WP_Post
$this->assertSame($list, $breadcrumbs->addPfcptPageCrumb($list, ['id' => 'not-an-id']));
// id points to a non-existent post
$this->assertSame($list, $breadcrumbs->addPfcptPageCrumb($list, ['id' => 999999]));
// taxonomy is not a string
$this->assertSame($list, $breadcrumbs->addPfcptPageCrumb($list, ['id' => 1, 'tax' => 123]));
// neither a single post nor a taxonomy archive
$this->assertSame($list, $breadcrumbs->addPfcptPageCrumb($list, ['uid' => 7]));
// unknown taxonomy
$this->assertSame($list, $breadcrumbs->addPfcptPageCrumb($list, ['id' => 1, 'tax' => 'does_not_exist']));
// real taxonomy with no PFCPT-bound post type
$this->assertSame($list, $breadcrumbs->addPfcptPageCrumb($list, ['id' => 1, 'tax' => 'category']));
}

/**
* Clear TSF's internal memoization cache between tests.
*
Expand Down
79 changes: 79 additions & 0 deletions tests/Integration/Integration/WordPressSeoTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@

namespace n5s\PageForCustomPostType\Tests\Integration\Integration;

use n5s\PageForCustomPostType\Core\Api;
use n5s\PageForCustomPostType\Integration\WordPressSeo\Breadcrumbs;
use n5s\PageForCustomPostType\Integration\WordPressSeo\Indexables;
use n5s\PageForCustomPostType\Integration\WordPressSeo\Schema;
use n5s\PageForCustomPostType\Integration\WordPressSeo\WordPressSeo;
use n5s\PageForCustomPostType\Tests\Fixtures\TestCase;
use PHPUnit\Framework\Attributes\RequiresFunction;
use Yoast\WP\SEO\Memoizers\Meta_Tags_Context_Memoizer;
Expand Down Expand Up @@ -169,4 +174,78 @@ public function testSchemaWebpageTypeUnchangedOnRegularPage(): void

$this->assertEquals('WebPage', $type);
}

public function testSchemaWebpageTypeDoesNotDuplicateCollectionPage(): void
{
$this->get($this->getBookHomeUrl());

// CollectionPage already present: the filter must return it unchanged.
$type = apply_filters('wpseo_schema_webpage_type', ['WebPage', 'CollectionPage']);

$this->assertSame(['WebPage', 'CollectionPage'], $type);
}

public function testIsSupported(): void
{
$seo = new WordPressSeo(new Schema(new Api()), new Breadcrumbs(new Api()), new Indexables(new Api()));

$this->assertTrue($seo->isSupported());
}

public function testRegisterHooksRegistersAllSubIntegrations(): void
{
$schema = new Schema(new Api());
$breadcrumbs = new Breadcrumbs(new Api());
$indexables = new Indexables(new Api());

(new WordPressSeo($schema, $breadcrumbs, $indexables))->registerHooks();

$this->assertNotFalse(has_filter('wpseo_schema_webpage_type', [$schema, 'addCollectionPageType']));
$this->assertNotFalse(has_filter('wpseo_breadcrumb_indexables', [$breadcrumbs, 'fixPostBreadcrumbs']));
$this->assertNotFalse(has_filter('wpseo_breadcrumb_indexables', [$breadcrumbs, 'fixTaxonomyBreadcrumbs']));
$this->assertNotFalse(has_action('wp', [$indexables, 'configurePageDetection']));
}

public function testPageDetectionResolvesPfcptPageWithStaticFrontPage(): void
{
// With a static front page, Yoast's Current_Page_Helper would resolve the
// posts page instead of the PFCPT page. Indexables::configurePageDetection
// intercepts show_on_front (only for that helper) so detection falls through
// to the PFCPT page id.
$this->configureStaticFrontPage();

$memoizer = \YoastSEO()->classes->get(Meta_Tags_Context_Memoizer::class);
$memoizer->clear();

$this->get($this->getBookHomeUrl());

$meta = \YoastSEO()->meta->for_current_page();

$this->assertSame($this->getBookHomeUrl(), $meta->canonical);
}

public function testTaxonomyBreadcrumbsSkippedForNonMainTaxonomy(): void
{
// The breadcrumb fix only applies when the current taxonomy is the post
// type's configured main taxonomy. Point books' main taxonomy elsewhere
// and confirm the genre archive is left untouched (no PFCPT crumb).
\YoastSEO()->helpers->options->set('post_types-' . self::BOOK_POST_TYPE . '-maintax', 'category');

$genreId = $this->getOrCreateTerm(self::GENRE_TAXONOMY, 'Fantasy');
foreach ($this->bookIds as $bookId) {
wp_set_object_terms($bookId, $genreId, self::GENRE_TAXONOMY);
}

$memoizer = \YoastSEO()->classes->get(Meta_Tags_Context_Memoizer::class);
$memoizer->clear();

$genre = get_term($genreId, self::GENRE_TAXONOMY);
$this->get(get_term_link($genre));

$meta = \YoastSEO()->meta->for_current_page();

foreach ($meta->breadcrumbs as $crumb) {
$this->assertNotSame($this->homeForBookId, \is_array($crumb) ? ($crumb['id'] ?? null) : null);
}
}
}
8 changes: 8 additions & 0 deletions tests/Integration/PublicApiTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,14 @@ public function testGetPageIdForCustomPostTypeUsesCurrentQueryWhenNull(): void
);
}

public function testGetPageIdForCustomPostTypeReturnsNullOnRegularPage(): void
{
$this->setExpectedDeprecated('get_page_id_for_custom_post_type');
$this->get(get_permalink($this->staticFrontPageId));

$this->assertNull(get_page_id_for_custom_post_type());
}

public function testGetPageUrlForCustomPostTypeReturnsUrl(): void
{
$this->setExpectedDeprecated('get_page_url_for_custom_post_type');
Expand Down
10 changes: 10 additions & 0 deletions tests/bootstrap.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,16 @@
return;
}

// Polylang only loads its API (the pll_* functions and PLL()) when
// init() enters a context via init_context(). In PHPUnit there is no
// request context and no languages exist yet, so init() detects an
// empty context and returns before requiring src/api.php — leaving
// PLL() undefined and every PolylangTest skipped. Force a frontend
// context so the API loads; languages are (re)created per test by
// TestCase::setPolylangDefaultLanguage() after Refresh_Database rolls
// them back.
add_filter('pll_context', static fn (string $class): string => $class ?: 'PLL_Frontend');

$polylangBootstrap = new \Polylang();
$polylangBootstrap->init();
});
Expand Down
Loading