From ca321d477d14e51b5fd9967009d400fb865e1049 Mon Sep 17 00:00:00 2001 From: Peter Wilson Date: Wed, 26 Mar 2025 11:37:58 +1100 Subject: [PATCH 01/10] First pass. --- src/wp-includes/general-template.php | 6 + tests/phpunit/tests/general/paginateLinks.php | 127 ++++++++++++++++++ 2 files changed, 133 insertions(+) diff --git a/src/wp-includes/general-template.php b/src/wp-includes/general-template.php index f16d787132452..57453107f6a57 100644 --- a/src/wp-includes/general-template.php +++ b/src/wp-includes/general-template.php @@ -4676,6 +4676,12 @@ function paginate_links( $args = '' ) { $format = $wp_rewrite->using_index_permalinks() && ! strpos( $pagenum_link, 'index.php' ) ? 'index.php/' : ''; $format .= $wp_rewrite->using_permalinks() ? user_trailingslashit( $wp_rewrite->pagination_base . '/%#%', 'paged' ) : '?paged=%#%'; + // Modify base and format default values if rewrite rules do not include a trailing slash. + if ( $wp_rewrite->using_permalinks() && ! $wp_rewrite->use_trailing_slashes ) { + $pagenum_link = str_replace( '/%_%', '%_%', $pagenum_link ); + $format = '/' . ltrim( $format, '/' ); + } + $defaults = array( 'base' => $pagenum_link, // http://example.com/all_posts.php%_% : %_% is replaced by format (below). 'format' => $format, // ?page=%#% : %#% is replaced by the page number. diff --git a/tests/phpunit/tests/general/paginateLinks.php b/tests/phpunit/tests/general/paginateLinks.php index d9833c6245488..bbbb1734f91b9 100644 --- a/tests/phpunit/tests/general/paginateLinks.php +++ b/tests/phpunit/tests/general/paginateLinks.php @@ -9,6 +9,25 @@ class Tests_General_PaginateLinks extends WP_UnitTestCase { private $i18n_count = 0; + public static $post_ids = array(); + + public static $category_id = 0; + + public static function wpSetUpBeforeClass( $factory ) { + self::$category_id = $factory->term->create( + array( + 'taxonomy' => 'category', + 'name' => 'Categorized', + array( 'slug' => 'categorized' ), + ) + ); + + self::$post_ids = $factory->post->create_many( 10 ); + foreach ( self::$post_ids as $post_id ) { + wp_set_post_categories( $post_id, array( self::$category_id ) ); + } + } + public function set_up() { parent::set_up(); @@ -383,4 +402,112 @@ public function test_custom_base_query_arg_should_be_stripped_from_current_url_b $page_2_url = home_url() . '?foo=2'; $this->assertContains( "2", $links ); } + + /** + * Ensures pagination links include trailing slashes when the permalink structure includes them. + * + * @ticket 61393 + */ + public function test_permalinks_with_trailing_slash_produce_links_with_trailing_slashes() { + update_option( 'posts_per_page', 2 ); + $this->set_permalink_structure( '/%postname%/' ); + + $this->go_to( '/category/categorized/page/2/' ); + + // For some reason current isn't picked up. + $links = paginate_links( array( 'current' => 2 ) ); + + $this->assertMatchesRegularExpression( '/\/categorized\/page\/[0-9]\/"/', $links, 'Pagination links with trailing slashes should be included.' ); + $this->assertDoesNotMatchRegularExpression( '/\/categorized\/page\/[0-9]"/', $links, 'Pagination links without trailing slashes should not be included.' ); + + $this->assertStringContainsString( '/category/categorized/"', $links, 'The links should link to page one with a trailing slash.' ); + $this->assertStringNotContainsString( '/category/categorized"', $links, 'No links to page on should lack a trailing slash.' ); + } + + /** + * Ensures pagination links do not include trailing slashes when the permalink structure doesn't includes them. + * + * @ticket 61393 + */ + public function test_permalinks_without_trailing_slash_produce_links_without_trailing_slashes() { + update_option( 'posts_per_page', 2 ); + $this->set_permalink_structure( '/%postname%' ); + + $this->go_to( '/category/categorized/page/2' ); + + // For some reason current isn't picked up. + $links = paginate_links( array( 'current' => 2 ) ); + + $this->assertDoesNotMatchRegularExpression( '/\/categorized\/page\/[0-9]\/"/', $links, 'Pagination links with trailing slashes should not be included.' ); + $this->assertMatchesRegularExpression( '/\/categorized\/page\/[0-9]"/', $links, 'Pagination links without trailing slashes should be included.' ); + + $this->assertStringNotContainsString( '/category/categorized/"', $links, 'The links should link to page one should not contain a trailing slash.' ); + $this->assertStringContainsString( '/category/categorized"', $links, 'The links to page one should not include a trailing slash.' ); + } + + /** + * Ensures the pagination links do not modify query strings (permalinks with trailing slash). + * + * @ticket 61393 + * @ticket 63123 + * + * @dataProvider data_query_strings + * + * @param string $query_string Query string. + * @param string $unexpected Unexpected query string. + */ + public function test_permalinks_with_trailing_slash_do_not_modify_query_strings( $query_string, $unexpected ) { + update_option( 'posts_per_page', 2 ); + $this->set_permalink_structure( '/%postname%/' ); + + $this->go_to( "/page/2/?{$query_string}" ); + + // For some reason current isn't picked up. + $links = paginate_links( array( 'current' => 2 ) ); + + $this->assertStringContainsString( "/page/3/?{$query_string}\"", $links, 'The query string should appear in the links.' ); + $this->assertStringNotContainsString( "/page/3/?{$unexpected}\"", $links, 'The query string should not be modified.' ); + } + + /** + * Ensures the pagination links do not modify query strings (permalinks without trailing slash). + * + * @ticket 61393 + * @ticket 63123 + * + * @dataProvider data_query_strings + * + * @param string $query_string Query string. + * @param string $unexpected Unexpected query string. + */ + public function test_permalinks_without_trailing_slash_do_not_modify_query_strings( $query_string, $unexpected ) { + update_option( 'posts_per_page', 2 ); + $this->set_permalink_structure( '/%postname%' ); + + $this->go_to( "/page/2?{$query_string}" ); + + // For some reason current isn't picked up. + $links = paginate_links( array( 'current' => 2 ) ); + + $this->assertStringContainsString( "/page/3?{$query_string}\"", $links, 'The query string should appear in the links.' ); + $this->assertStringNotContainsString( "/page/3?{$unexpected}\"", $links, 'The query string should not be modified.' ); + } + + /** + * Data provider for + * - test_permalinks_without_trailing_slash_do_not_modify_query_strings + * - test_permalinks_with_trailing_slash_do_not_modify_query_strings + * + * @return array[] Data provider. + */ + public function data_query_strings() { + return array( + array( 'foo=bar', 'foo=bar/' ), + array( 'foo=bar', 'foo=bar%2F' ), + array( 'foo=bar%2F', 'foo=bar' ), + + array( 's=post', 's=post/' ), + array( 's=post', 's=post%2F' ), + ); + } } From a7f8f3d0e43cf5d652c4cfbcf57d338105871e15 Mon Sep 17 00:00:00 2001 From: Peter Wilson Date: Thu, 27 Mar 2025 09:38:35 +1100 Subject: [PATCH 02/10] Tidy up tests a bit to include href portion of links. --- tests/phpunit/tests/general/paginateLinks.php | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/tests/phpunit/tests/general/paginateLinks.php b/tests/phpunit/tests/general/paginateLinks.php index bbbb1734f91b9..fd0d49050204c 100644 --- a/tests/phpunit/tests/general/paginateLinks.php +++ b/tests/phpunit/tests/general/paginateLinks.php @@ -417,11 +417,13 @@ public function test_permalinks_with_trailing_slash_produce_links_with_trailing_ // For some reason current isn't picked up. $links = paginate_links( array( 'current' => 2 ) ); - $this->assertMatchesRegularExpression( '/\/categorized\/page\/[0-9]\/"/', $links, 'Pagination links with trailing slashes should be included.' ); - $this->assertDoesNotMatchRegularExpression( '/\/categorized\/page\/[0-9]"/', $links, 'Pagination links without trailing slashes should not be included.' ); + $base_url = untrailingslashit( home_url( '/category/categorized/' ) ); + $this->assertStringContainsString( "href=\"{$base_url}/\"", $links, 'The links should link to page one with a trailing slash.' ); + $this->assertStringNotContainsString( "href=\"{$base_url}\"", $links, 'The links should not link to page one without a trailing slash.' ); - $this->assertStringContainsString( '/category/categorized/"', $links, 'The links should link to page one with a trailing slash.' ); - $this->assertStringNotContainsString( '/category/categorized"', $links, 'No links to page on should lack a trailing slash.' ); + $base_url_regex = preg_quote( $base_url, '/' ); + $this->assertMatchesRegularExpression( "/href=\"{$base_url_regex}\/page\/[0-9]\/\"/", $links, 'Pagination links with trailing slashes should be included.' ); + $this->assertDoesNotMatchRegularExpression( "/href=\"{$base_url_regex}\/page\/[0-9]\"/", $links, 'Pagination links without trailing slashes should not be included.' ); } /** @@ -438,11 +440,13 @@ public function test_permalinks_without_trailing_slash_produce_links_without_tra // For some reason current isn't picked up. $links = paginate_links( array( 'current' => 2 ) ); - $this->assertDoesNotMatchRegularExpression( '/\/categorized\/page\/[0-9]\/"/', $links, 'Pagination links with trailing slashes should not be included.' ); - $this->assertMatchesRegularExpression( '/\/categorized\/page\/[0-9]"/', $links, 'Pagination links without trailing slashes should be included.' ); + $base_url = untrailingslashit( home_url( '/category/categorized/' ) ); + $this->assertStringNotContainsString( "href=\"{$base_url}/\"", $links, 'The links should link to page one without a trailing slash.' ); + $this->assertStringContainsString( "href=\"{$base_url}\"", $links, 'The links should not link to page one with a trailing slash.' ); - $this->assertStringNotContainsString( '/category/categorized/"', $links, 'The links should link to page one should not contain a trailing slash.' ); - $this->assertStringContainsString( '/category/categorized"', $links, 'The links to page one should not include a trailing slash.' ); + $base_url_regex = preg_quote( $base_url, '/' ); + $this->assertDoesNotMatchRegularExpression( "/href=\"$base_url_regex}\/page\/[0-9]\/\"/", $links, 'Pagination links with trailing slashes should not be included.' ); + $this->assertMatchesRegularExpression( "/href=\"{$base_url_regex}\/page\/[0-9]\"/", $links, 'Pagination links without trailing slashes should be included.' ); } /** From 72f32ca84c29014111c10856e0384afdb6a4000e Mon Sep 17 00:00:00 2001 From: Peter Wilson Date: Thu, 27 Mar 2025 09:55:42 +1100 Subject: [PATCH 03/10] Expand the comment. --- src/wp-includes/general-template.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/wp-includes/general-template.php b/src/wp-includes/general-template.php index 57453107f6a57..8e862cbe03f5f 100644 --- a/src/wp-includes/general-template.php +++ b/src/wp-includes/general-template.php @@ -4676,7 +4676,15 @@ function paginate_links( $args = '' ) { $format = $wp_rewrite->using_index_permalinks() && ! strpos( $pagenum_link, 'index.php' ) ? 'index.php/' : ''; $format .= $wp_rewrite->using_permalinks() ? user_trailingslashit( $wp_rewrite->pagination_base . '/%#%', 'paged' ) : '?paged=%#%'; - // Modify base and format default values if rewrite rules do not include a trailing slash. + /* + * Modify defaults for sites without trailing slashed permalinks. + * + * Ensures sites not using trailing slashes get links in the form + * `/page/2` rather than `/page/2/`. On these sites, linking to the + * URL with a trailing slash will results in a 301 redirect from the + * incorrect URL to the correctly formattted one. This presents an + * unnecessary performance hit. + */ if ( $wp_rewrite->using_permalinks() && ! $wp_rewrite->use_trailing_slashes ) { $pagenum_link = str_replace( '/%_%', '%_%', $pagenum_link ); $format = '/' . ltrim( $format, '/' ); From c8fe26757838a6d234e4ad5e7ba601e27ba79ed1 Mon Sep 17 00:00:00 2001 From: Peter Wilson Date: Thu, 27 Mar 2025 10:37:54 +1100 Subject: [PATCH 04/10] Document shared fixtures. --- tests/phpunit/tests/general/paginateLinks.php | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/phpunit/tests/general/paginateLinks.php b/tests/phpunit/tests/general/paginateLinks.php index fd0d49050204c..131ac0bd3e973 100644 --- a/tests/phpunit/tests/general/paginateLinks.php +++ b/tests/phpunit/tests/general/paginateLinks.php @@ -9,10 +9,25 @@ class Tests_General_PaginateLinks extends WP_UnitTestCase { private $i18n_count = 0; + /** + * Post IDs created for shared fixtures. + * + * @var int[] + */ public static $post_ids = array(); + /** + * Category ID created for shared fixtures. + * + * @var int + */ public static $category_id = 0; + /** + * Set up shared fixtures. + * + * @param WP_UnitTest_Factory $factory Factory instance. + */ public static function wpSetUpBeforeClass( $factory ) { self::$category_id = $factory->term->create( array( From c0c384f6bec59b905119c602cebf3f27866447f2 Mon Sep 17 00:00:00 2001 From: Peter Wilson <519727+peterwilsoncc@users.noreply.github.com> Date: Fri, 13 Mar 2026 13:06:21 +1100 Subject: [PATCH 05/10] Apply suggestions from code review Co-authored-by: Weston Ruter Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/wp-includes/general-template.php | 2 +- tests/phpunit/tests/general/paginateLinks.php | 14 ++++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/wp-includes/general-template.php b/src/wp-includes/general-template.php index 8e862cbe03f5f..e813220673922 100644 --- a/src/wp-includes/general-template.php +++ b/src/wp-includes/general-template.php @@ -4677,7 +4677,7 @@ function paginate_links( $args = '' ) { $format .= $wp_rewrite->using_permalinks() ? user_trailingslashit( $wp_rewrite->pagination_base . '/%#%', 'paged' ) : '?paged=%#%'; /* - * Modify defaults for sites without trailing slashed permalinks. + * Modify defaults for sites without trailing slash permalinks. * * Ensures sites not using trailing slashes get links in the form * `/page/2` rather than `/page/2/`. On these sites, linking to the diff --git a/tests/phpunit/tests/general/paginateLinks.php b/tests/phpunit/tests/general/paginateLinks.php index 131ac0bd3e973..2a0f48be89853 100644 --- a/tests/phpunit/tests/general/paginateLinks.php +++ b/tests/phpunit/tests/general/paginateLinks.php @@ -14,14 +14,14 @@ class Tests_General_PaginateLinks extends WP_UnitTestCase { * * @var int[] */ - public static $post_ids = array(); + public protected $post_ids = array(); /** * Category ID created for shared fixtures. * * @var int */ - public static $category_id = 0; + protected static $category_id = 0; /** * Set up shared fixtures. @@ -442,7 +442,7 @@ public function test_permalinks_with_trailing_slash_produce_links_with_trailing_ } /** - * Ensures pagination links do not include trailing slashes when the permalink structure doesn't includes them. + * Ensures pagination links do not include trailing slashes when the permalink structure doesn't include them. * * @ticket 61393 */ @@ -475,7 +475,7 @@ public function test_permalinks_without_trailing_slash_produce_links_without_tra * @param string $query_string Query string. * @param string $unexpected Unexpected query string. */ - public function test_permalinks_with_trailing_slash_do_not_modify_query_strings( $query_string, $unexpected ) { + public function test_permalinks_with_trailing_slash_do_not_modify_query_strings( string $query_string, string $unexpected ) { update_option( 'posts_per_page', 2 ); $this->set_permalink_structure( '/%postname%/' ); @@ -499,7 +499,7 @@ public function test_permalinks_with_trailing_slash_do_not_modify_query_strings( * @param string $query_string Query string. * @param string $unexpected Unexpected query string. */ - public function test_permalinks_without_trailing_slash_do_not_modify_query_strings( $query_string, $unexpected ) { + public function test_permalinks_without_trailing_slash_do_not_modify_query_strings( string $query_string, string $unexpected ) { update_option( 'posts_per_page', 2 ); $this->set_permalink_structure( '/%postname%' ); @@ -519,7 +519,9 @@ public function test_permalinks_without_trailing_slash_do_not_modify_query_strin * * @return array[] Data provider. */ - public function data_query_strings() { + * @return array Data provider. + */ + public function data_query_strings(): array { return array( array( 'foo=bar', 'foo=bar/' ), array( 'foo=bar', 'foo=bar%2F' ), From ac9b0523468a258a9738c4822c1f8b28abcd734d Mon Sep 17 00:00:00 2001 From: Peter Wilson Date: Fri, 13 Mar 2026 13:20:27 +1100 Subject: [PATCH 06/10] Group pagenum_link defintion in one location. Co-Authored-By: westonruter --- src/wp-includes/general-template.php | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/wp-includes/general-template.php b/src/wp-includes/general-template.php index e813220673922..3dbf954f16d1c 100644 --- a/src/wp-includes/general-template.php +++ b/src/wp-includes/general-template.php @@ -4672,13 +4672,7 @@ function paginate_links( $args = '' ) { // Append the format placeholder to the base URL. $pagenum_link = trailingslashit( $url_parts[0] ) . '%_%'; - // URL base depends on permalink settings. - $format = $wp_rewrite->using_index_permalinks() && ! strpos( $pagenum_link, 'index.php' ) ? 'index.php/' : ''; - $format .= $wp_rewrite->using_permalinks() ? user_trailingslashit( $wp_rewrite->pagination_base . '/%#%', 'paged' ) : '?paged=%#%'; - /* - * Modify defaults for sites without trailing slash permalinks. - * * Ensures sites not using trailing slashes get links in the form * `/page/2` rather than `/page/2/`. On these sites, linking to the * URL with a trailing slash will results in a 301 redirect from the @@ -4686,8 +4680,17 @@ function paginate_links( $args = '' ) { * unnecessary performance hit. */ if ( $wp_rewrite->using_permalinks() && ! $wp_rewrite->use_trailing_slashes ) { - $pagenum_link = str_replace( '/%_%', '%_%', $pagenum_link ); - $format = '/' . ltrim( $format, '/' ); + $pagenum_link = untrailingslashit( $url_parts[0] ); + } else { + $pagenum_link = trailingslashit( $url_parts[0] ); + } + $pagenum_link .= '%_%'; + + // URL base depends on permalink settings. + $format = $wp_rewrite->using_index_permalinks() && ! strpos( $pagenum_link, 'index.php' ) ? 'index.php/' : ''; + $format .= $wp_rewrite->using_permalinks() ? user_trailingslashit( $wp_rewrite->pagination_base . '/%#%', 'paged' ) : '?paged=%#%'; + if ( $wp_rewrite->using_permalinks() && ! $wp_rewrite->use_trailing_slashes ) { + $format = '/' . ltrim( $format, '/' ); } $defaults = array( From 8eb206baf9c0885f6b7fcb029b17a575e201265a Mon Sep 17 00:00:00 2001 From: Peter Wilson Date: Fri, 13 Mar 2026 13:25:40 +1100 Subject: [PATCH 07/10] Fix suggestions that github did not apply correctly earlier. --- tests/phpunit/tests/general/paginateLinks.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/phpunit/tests/general/paginateLinks.php b/tests/phpunit/tests/general/paginateLinks.php index 2a0f48be89853..0505cc38c0707 100644 --- a/tests/phpunit/tests/general/paginateLinks.php +++ b/tests/phpunit/tests/general/paginateLinks.php @@ -14,7 +14,7 @@ class Tests_General_PaginateLinks extends WP_UnitTestCase { * * @var int[] */ - public protected $post_ids = array(); + protected static $post_ids = array(); /** * Category ID created for shared fixtures. @@ -517,8 +517,6 @@ public function test_permalinks_without_trailing_slash_do_not_modify_query_strin * - test_permalinks_without_trailing_slash_do_not_modify_query_strings * - test_permalinks_with_trailing_slash_do_not_modify_query_strings * - * @return array[] Data provider. - */ * @return array Data provider. */ public function data_query_strings(): array { From 03a8893a164d3e084aaa27e14a44806960eafd2e Mon Sep 17 00:00:00 2001 From: Peter Wilson Date: Fri, 13 Mar 2026 13:28:07 +1100 Subject: [PATCH 08/10] Clarify comments. --- tests/phpunit/tests/general/paginateLinks.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/phpunit/tests/general/paginateLinks.php b/tests/phpunit/tests/general/paginateLinks.php index 0505cc38c0707..db867754f4c1a 100644 --- a/tests/phpunit/tests/general/paginateLinks.php +++ b/tests/phpunit/tests/general/paginateLinks.php @@ -429,7 +429,7 @@ public function test_permalinks_with_trailing_slash_produce_links_with_trailing_ $this->go_to( '/category/categorized/page/2/' ); - // For some reason current isn't picked up. + // `current` needs to be passed as it's not picked up from the query vars set by `go_to()` above. $links = paginate_links( array( 'current' => 2 ) ); $base_url = untrailingslashit( home_url( '/category/categorized/' ) ); @@ -452,7 +452,7 @@ public function test_permalinks_without_trailing_slash_produce_links_without_tra $this->go_to( '/category/categorized/page/2' ); - // For some reason current isn't picked up. + // `current` needs to be passed as it's not picked up from the query vars set by `go_to()` above. $links = paginate_links( array( 'current' => 2 ) ); $base_url = untrailingslashit( home_url( '/category/categorized/' ) ); @@ -481,7 +481,7 @@ public function test_permalinks_with_trailing_slash_do_not_modify_query_strings( $this->go_to( "/page/2/?{$query_string}" ); - // For some reason current isn't picked up. + // `current` needs to be passed as it's not picked up from the query vars set by `go_to()` above. $links = paginate_links( array( 'current' => 2 ) ); $this->assertStringContainsString( "/page/3/?{$query_string}\"", $links, 'The query string should appear in the links.' ); @@ -505,7 +505,7 @@ public function test_permalinks_without_trailing_slash_do_not_modify_query_strin $this->go_to( "/page/2?{$query_string}" ); - // For some reason current isn't picked up. + // `current` needs to be passed as it's not picked up from the query vars set by `go_to()` above. $links = paginate_links( array( 'current' => 2 ) ); $this->assertStringContainsString( "/page/3?{$query_string}\"", $links, 'The query string should appear in the links.' ); From 736a4b344a596033477cfb336ac737dcb38cc8bf Mon Sep 17 00:00:00 2001 From: Peter Wilson Date: Fri, 13 Mar 2026 13:29:49 +1100 Subject: [PATCH 09/10] Remove additional args. --- tests/phpunit/tests/general/paginateLinks.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/phpunit/tests/general/paginateLinks.php b/tests/phpunit/tests/general/paginateLinks.php index db867754f4c1a..d151b607fd77f 100644 --- a/tests/phpunit/tests/general/paginateLinks.php +++ b/tests/phpunit/tests/general/paginateLinks.php @@ -33,7 +33,6 @@ public static function wpSetUpBeforeClass( $factory ) { array( 'taxonomy' => 'category', 'name' => 'Categorized', - array( 'slug' => 'categorized' ), ) ); From 76fc55e4b3789ef9562381eb43fd070cd831a015 Mon Sep 17 00:00:00 2001 From: Peter Wilson Date: Fri, 13 Mar 2026 13:48:11 +1100 Subject: [PATCH 10/10] Use `WP_HTML_Tag_Processor` for tests. --- tests/phpunit/tests/general/paginateLinks.php | 63 +++++++++++-------- 1 file changed, 37 insertions(+), 26 deletions(-) diff --git a/tests/phpunit/tests/general/paginateLinks.php b/tests/phpunit/tests/general/paginateLinks.php index d151b607fd77f..587e4639136ae 100644 --- a/tests/phpunit/tests/general/paginateLinks.php +++ b/tests/phpunit/tests/general/paginateLinks.php @@ -431,13 +431,14 @@ public function test_permalinks_with_trailing_slash_produce_links_with_trailing_ // `current` needs to be passed as it's not picked up from the query vars set by `go_to()` above. $links = paginate_links( array( 'current' => 2 ) ); - $base_url = untrailingslashit( home_url( '/category/categorized/' ) ); - $this->assertStringContainsString( "href=\"{$base_url}/\"", $links, 'The links should link to page one with a trailing slash.' ); - $this->assertStringNotContainsString( "href=\"{$base_url}\"", $links, 'The links should not link to page one without a trailing slash.' ); - - $base_url_regex = preg_quote( $base_url, '/' ); - $this->assertMatchesRegularExpression( "/href=\"{$base_url_regex}\/page\/[0-9]\/\"/", $links, 'Pagination links with trailing slashes should be included.' ); - $this->assertDoesNotMatchRegularExpression( "/href=\"{$base_url_regex}\/page\/[0-9]\"/", $links, 'Pagination links without trailing slashes should not be included.' ); + $processor = new WP_HTML_Tag_Processor( $links ); + $found_links = 0; + while ( $processor->next_tag( 'A' ) ) { + ++$found_links; + $href = $processor->get_attribute( 'href' ); + $this->assertStringEndsWith( '/', $href, "Pagination links should end with a trailing slash, found: $href" ); + } + $this->assertGreaterThan( 0, $found_links, 'There should be pagination links found.' ); } /** @@ -454,13 +455,14 @@ public function test_permalinks_without_trailing_slash_produce_links_without_tra // `current` needs to be passed as it's not picked up from the query vars set by `go_to()` above. $links = paginate_links( array( 'current' => 2 ) ); - $base_url = untrailingslashit( home_url( '/category/categorized/' ) ); - $this->assertStringNotContainsString( "href=\"{$base_url}/\"", $links, 'The links should link to page one without a trailing slash.' ); - $this->assertStringContainsString( "href=\"{$base_url}\"", $links, 'The links should not link to page one with a trailing slash.' ); - - $base_url_regex = preg_quote( $base_url, '/' ); - $this->assertDoesNotMatchRegularExpression( "/href=\"$base_url_regex}\/page\/[0-9]\/\"/", $links, 'Pagination links with trailing slashes should not be included.' ); - $this->assertMatchesRegularExpression( "/href=\"{$base_url_regex}\/page\/[0-9]\"/", $links, 'Pagination links without trailing slashes should be included.' ); + $processor = new WP_HTML_Tag_Processor( $links ); + $found_links = 0; + while ( $processor->next_tag( 'A' ) ) { + ++$found_links; + $href = $processor->get_attribute( 'href' ); + $this->assertStringEndsNotWith( '/', $href, "Pagination links should end with a trailing slash, found: $href" ); + } + $this->assertGreaterThan( 0, $found_links, 'There should be pagination links found.' ); } /** @@ -474,7 +476,7 @@ public function test_permalinks_without_trailing_slash_produce_links_without_tra * @param string $query_string Query string. * @param string $unexpected Unexpected query string. */ - public function test_permalinks_with_trailing_slash_do_not_modify_query_strings( string $query_string, string $unexpected ) { + public function test_permalinks_with_trailing_slash_do_not_modify_query_strings( string $query_string ) { update_option( 'posts_per_page', 2 ); $this->set_permalink_structure( '/%postname%/' ); @@ -483,8 +485,14 @@ public function test_permalinks_with_trailing_slash_do_not_modify_query_strings( // `current` needs to be passed as it's not picked up from the query vars set by `go_to()` above. $links = paginate_links( array( 'current' => 2 ) ); - $this->assertStringContainsString( "/page/3/?{$query_string}\"", $links, 'The query string should appear in the links.' ); - $this->assertStringNotContainsString( "/page/3/?{$unexpected}\"", $links, 'The query string should not be modified.' ); + $processor = new WP_HTML_Tag_Processor( $links ); + $found_links = 0; + while ( $processor->next_tag( 'A' ) ) { + ++$found_links; + $href = $processor->get_attribute( 'href' ); + $this->assertStringEndsWith( "/?{$query_string}", $href, "Pagination links should not modify the query string, found: $href" ); + } + $this->assertGreaterThan( 0, $found_links, 'There should be pagination links found.' ); } /** @@ -498,7 +506,7 @@ public function test_permalinks_with_trailing_slash_do_not_modify_query_strings( * @param string $query_string Query string. * @param string $unexpected Unexpected query string. */ - public function test_permalinks_without_trailing_slash_do_not_modify_query_strings( string $query_string, string $unexpected ) { + public function test_permalinks_without_trailing_slash_do_not_modify_query_strings( string $query_string ) { update_option( 'posts_per_page', 2 ); $this->set_permalink_structure( '/%postname%' ); @@ -507,8 +515,15 @@ public function test_permalinks_without_trailing_slash_do_not_modify_query_strin // `current` needs to be passed as it's not picked up from the query vars set by `go_to()` above. $links = paginate_links( array( 'current' => 2 ) ); - $this->assertStringContainsString( "/page/3?{$query_string}\"", $links, 'The query string should appear in the links.' ); - $this->assertStringNotContainsString( "/page/3?{$unexpected}\"", $links, 'The query string should not be modified.' ); + $processor = new WP_HTML_Tag_Processor( $links ); + $found_links = 0; + while ( $processor->next_tag( 'A' ) ) { + ++$found_links; + $href = $processor->get_attribute( 'href' ); + $this->assertStringEndsWith( "?{$query_string}", $href, "Pagination links should not modify the query string, found: $href" ); + $this->assertStringEndsNotWith( "/?{$query_string}", $href, "Pagination links should not be slashed before the query string, found: $href" ); + } + $this->assertGreaterThan( 0, $found_links, 'There should be pagination links found.' ); } /** @@ -520,12 +535,8 @@ public function test_permalinks_without_trailing_slash_do_not_modify_query_strin */ public function data_query_strings(): array { return array( - array( 'foo=bar', 'foo=bar/' ), - array( 'foo=bar', 'foo=bar%2F' ), - array( 'foo=bar%2F', 'foo=bar' ), - - array( 's=post', 's=post/' ), - array( 's=post', 's=post%2F' ), + array( 'foo=bar' ), + array( 'foo=bar&pen=pencil' ), ); } }