Skip to content

Commit fdd1135

Browse files
dd32claude
andcommitted
Add WordPress-dependent CI tests and Plugin Directory unit tests
Add the php-wordpress CI job for Handbook and Plugin Directory plugins, wp-env support, PHPUnit 11 compatibility fixes, and comprehensive Plugin Directory test coverage for templates, trademarks, readme parsing, markdown, readme validation, and plugin registration. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 8cb99d9 commit fdd1135

17 files changed

Lines changed: 1562 additions & 12 deletions

.github/workflows/unit-tests.yml

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,53 @@ jobs:
5151
- name: Run PHPUnit
5252
working-directory: ${{ matrix.working-directory }}
5353
run: phpunit ${{ matrix.phpunit-args }}
54+
55+
# WordPress-dependent PHP tests — require the WP test framework and MySQL.
56+
php-wordpress:
57+
name: "WP PHP: ${{ matrix.name }}"
58+
runs-on: ubuntu-latest
59+
services:
60+
mysql:
61+
image: mysql:8.0
62+
env:
63+
MYSQL_ROOT_PASSWORD: root
64+
MYSQL_DATABASE: wordpress_test
65+
ports:
66+
- 3306:3306
67+
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
68+
strategy:
69+
fail-fast: false
70+
matrix:
71+
include:
72+
- name: Handbook Plugin
73+
working-directory: wordpress.org/public_html/wp-content/plugins/handbook
74+
phpunit-args: "--configuration phpunit.xml.dist"
75+
- name: Plugin Directory
76+
working-directory: wordpress.org/public_html/wp-content/plugins/plugin-directory
77+
phpunit-args: "--configuration phpunit.xml --group plugin-directory"
78+
steps:
79+
- uses: actions/checkout@v4
80+
81+
- name: Set up PHP
82+
uses: shivammathur/setup-php@v2
83+
with:
84+
php-version: "8.4"
85+
extensions: mysqli
86+
tools: phpunit:^11
87+
88+
- name: Install WordPress test suite
89+
run: |
90+
git clone --depth=1 https://github.com/WordPress/wordpress-develop.git /tmp/wp-develop
91+
cd /tmp/wp-develop && composer install --no-interaction
92+
cp wp-tests-config-sample.php tests/phpunit/wp-tests-config.php
93+
sed -i "s|dirname( __FILE__ ) . '/src/'|'/tmp/wp-develop/src/'|" tests/phpunit/wp-tests-config.php
94+
sed -i "s/youremptytestdbnamehere/wordpress_test/" tests/phpunit/wp-tests-config.php
95+
sed -i "s/yourusernamehere/root/" tests/phpunit/wp-tests-config.php
96+
sed -i "s/yourpasswordhere/root/" tests/phpunit/wp-tests-config.php
97+
sed -i "s/'localhost'/'127.0.0.1'/" tests/phpunit/wp-tests-config.php
98+
99+
- name: Run PHPUnit
100+
working-directory: ${{ matrix.working-directory }}
101+
env:
102+
WP_TESTS_DIR: /tmp/wp-develop/tests/phpunit
103+
run: phpunit ${{ matrix.phpunit-args }}

wordpress.org/public_html/wp-content/plugins/handbook/phpunit/bootstrap.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@
1414
if ( ! $_tests_dir && false !== ( $pos = stripos( __FILE__, '/src/wp-content/plugins/' ) ) ) {
1515
$_tests_dir = substr( __FILE__, 0, $pos ) . '/tests/phpunit/';
1616
}
17+
// Check for wp-env test directory.
18+
elseif ( ! $_tests_dir && file_exists( '/wordpress-phpunit/includes/functions.php' ) ) {
19+
$_tests_dir = '/wordpress-phpunit/';
20+
}
1721
// Elseif no path yet, assume a temp directory path.
1822
elseif ( ! $_tests_dir ) {
1923
$_tests_dir = rtrim( sys_get_temp_dir(), '/\\' ) . '/wordpress-tests-lib/tests/phpunit/';

wordpress.org/public_html/wp-content/plugins/handbook/phpunit/tests/handbook.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,15 @@ class WPorg_Handbook_Handbook_Test extends WP_UnitTestCase {
1717

1818
protected $handbook;
1919

20-
public function setUp() {
20+
public function setUp(): void {
2121
parent::setup();
2222
WPorg_Handbook_Init::init();
2323

2424
$handbooks = WPorg_Handbook_Init::get_handbook_objects();
2525
$this->handbook = reset( $handbooks );
2626
}
2727

28-
public function tearDown() {
28+
public function tearDown(): void {
2929
parent::tearDown();
3030

3131
foreach ( WPorg_Handbook_Init::get_handbook_objects() as $obj ) {
@@ -455,7 +455,7 @@ public function test_handbook_sidebar() {
455455

456456
$this->assertTrue( isset( $wp_registered_sidebars[ 'handbook' ] ) );
457457
$this->assertSame(
458-
[ 'name', 'id', 'description', 'class', 'before_widget', 'after_widget', 'before_title', 'after_title', 'before_sidebar', 'after_sidebar' ],
458+
[ 'name', 'id', 'description', 'class', 'before_widget', 'after_widget', 'before_title', 'after_title', 'before_sidebar', 'after_sidebar', 'show_in_rest' ],
459459
array_keys( $wp_registered_sidebars[ 'handbook' ] )
460460
);
461461
$this->assertEquals( 'handbook', $wp_registered_sidebars[ 'handbook' ]['id'] );

wordpress.org/public_html/wp-content/plugins/handbook/phpunit/tests/init.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@
44

55
class WPorg_Handbook_Init_Test extends WP_UnitTestCase {
66

7-
public function setUp() {
7+
public function setUp(): void {
88
parent::setup();
99

1010
WPorg_Handbook_Init::init();
1111
}
1212

13-
public function tearDown() {
13+
public function tearDown(): void {
1414
parent::tearDown();
1515

1616
WPorg_Handbook_Init::reset( true );

wordpress.org/public_html/wp-content/plugins/handbook/phpunit/tests/template-tags.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@
44

55
class WPorg_Handbook_Template_Tags_Test extends WP_UnitTestCase {
66

7-
public function setUp() {
7+
public function setUp(): void {
88
parent::setup();
99

1010
WPorg_Handbook_Init::init();
1111
}
1212

13-
public function tearDown() {
13+
public function tearDown(): void {
1414
parent::tearDown();
1515

1616
WPorg_Handbook_Init::reset( true );
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"core": "WordPress/WordPress#master",
3+
"plugins": [
4+
".",
5+
"https://downloads.wordpress.org/plugin/jetpack.latest-stable.zip",
6+
"https://downloads.wordpress.org/plugin/advanced-post-cache.latest-stable.zip",
7+
"https://github.com/WordPress/plugin-check"
8+
],
9+
"themes": [
10+
"../../../themes/pub/wporg-plugins-2024",
11+
"https://github.com/WordPress/wporg-parent-2021"
12+
],
13+
"mappings": {
14+
"wp-content/mu-plugins/wporg-ratings-stub.php": "./tests/stubs/wporg-ratings-stub.php"
15+
}
16+
}

wordpress.org/public_html/wp-content/plugins/plugin-directory/tests/bootstrap.php

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,29 @@
66
return;
77
}
88

9+
// Find the WordPress PHPUnit test framework.
10+
$_tests_dir = getenv( 'WP_TESTS_DIR' );
11+
12+
if ( ! $_tests_dir ) {
13+
// wp-env test directory.
14+
if ( file_exists( '/wordpress-phpunit/includes/functions.php' ) ) {
15+
$_tests_dir = '/wordpress-phpunit/';
16+
} else {
17+
$_tests_dir = rtrim( sys_get_temp_dir(), '/\\' ) . '/wordpress-tests-lib/tests/phpunit/';
18+
}
19+
}
20+
21+
if ( ! file_exists( $_tests_dir . '/includes/functions.php' ) ) {
22+
echo "Could not find {$_tests_dir}/includes/functions.php\n";
23+
exit( 1 );
24+
}
25+
26+
// Give access to tests_add_filter() function.
27+
require_once $_tests_dir . '/includes/functions.php';
28+
29+
// Load Jetpack Search stub if Jetpack is not installed.
30+
require_once __DIR__ . '/stubs/jetpack-search-stub.php';
31+
932
/**
1033
* Manually load the plugin being tested.
1134
*/
@@ -14,3 +37,6 @@ function manually_load_plugin() {
1437
}
1538

1639
tests_add_filter( 'muplugins_loaded', __NAMESPACE__ . '\manually_load_plugin' );
40+
41+
// Start up the WP testing environment.
42+
require $_tests_dir . '/includes/bootstrap.php';
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
<?php
2+
3+
use WordPressdotorg\Plugin_Directory\Markdown;
4+
5+
/**
6+
* @group plugin-directory
7+
* @group markdown
8+
*/
9+
class Tests_Markdown extends WP_UnitTestCase {
10+
11+
function test_transform_basic_paragraph(): void {
12+
$md = new Markdown();
13+
$result = $md->transform( 'Hello World' );
14+
$this->assertSame( '<p>Hello World</p>', $result );
15+
}
16+
17+
function test_transform_empty_string(): void {
18+
$md = new Markdown();
19+
$result = $md->transform( '' );
20+
$this->assertSame( '', $result );
21+
}
22+
23+
function test_transform_trims_output(): void {
24+
$md = new Markdown();
25+
$result = $md->transform( " \n Hello \n " );
26+
$this->assertSame( $result, trim( $result ) );
27+
}
28+
29+
/**
30+
* Test the custom `= Section Title =` header syntax.
31+
*
32+
* This is WordPress plugin readme specific — converts `= Title =` to <h4>.
33+
*/
34+
function test_transform_equals_header(): void {
35+
$md = new Markdown();
36+
$result = $md->transform( '= Section Title =' );
37+
$this->assertStringContainsString( '<h4>Section Title</h4>', $result );
38+
}
39+
40+
function test_transform_multiple_equals_headers(): void {
41+
$md = new Markdown();
42+
$result = $md->transform( "= First =\n\nContent\n\n= Second =" );
43+
$this->assertStringContainsString( '<h4>First</h4>', $result );
44+
$this->assertStringContainsString( '<h4>Second</h4>', $result );
45+
}
46+
47+
function test_equals_header_with_leading_whitespace(): void {
48+
$md = new Markdown();
49+
$result = $md->transform( ' = Indented Header =' );
50+
$this->assertStringContainsString( '<h4>Indented Header</h4>', $result );
51+
}
52+
53+
/**
54+
* Test code_trick: <pre><code> blocks preserve underscores in code.
55+
*
56+
* This is custom logic in Markdown::code_trick() — pre-existing HTML code blocks
57+
* are converted to backtick format before markdown processing, so markdown
58+
* does not mangle underscores and other special characters inside code.
59+
*
60+
* The block needs surrounding content so trim() does not strip indentation.
61+
*/
62+
function test_code_trick_preserves_underscores_in_pre_code(): void {
63+
$md = new Markdown();
64+
$input = "Some text before.\n\n<pre><code>\$my_var = some_function();\n\$other_var = 1;</code></pre>\n\nSome text after.";
65+
$result = $md->transform( $input );
66+
67+
// Underscores should NOT be converted to <em> tags inside code blocks.
68+
$this->assertStringNotContainsString( '<em>', $result );
69+
$this->assertStringContainsString( 'my_var', $result );
70+
$this->assertStringContainsString( 'some_function', $result );
71+
}
72+
73+
function test_code_trick_inline_code_preserves_underscores(): void {
74+
$md = new Markdown();
75+
$input = "Use <code>my_var_name</code> for the setting.";
76+
$result = $md->transform( $input );
77+
78+
// Inline code should also preserve underscores.
79+
$this->assertStringNotContainsString( '<em>', $result );
80+
$this->assertStringContainsString( 'my_var_name', $result );
81+
}
82+
83+
/**
84+
* Test code_trick: bbPress-style backtick code blocks at line start are
85+
* converted to indented code (4 spaces) for markdown processing.
86+
*/
87+
function test_code_trick_bbpress_backtick_block(): void {
88+
$md = new Markdown();
89+
$input = "Some text.\n\n`some_code_here`\n\nMore text.";
90+
$result = $md->transform( $input );
91+
92+
$this->assertStringContainsString( 'some_code_here', $result );
93+
}
94+
95+
/**
96+
* Test that inline markdown code (backticks) in mid-line is preserved.
97+
*/
98+
function test_inline_backtick_code_preserved(): void {
99+
$md = new Markdown();
100+
$result = $md->transform( 'Use `add_filter()` to modify output.' );
101+
$this->assertStringContainsString( '<code>add_filter()</code>', $result );
102+
}
103+
104+
/**
105+
* Test standard markdown features (these verify the upstream MarkdownExtra
106+
* library works correctly through our transform() wrapper).
107+
*/
108+
function test_transform_bold(): void {
109+
$md = new Markdown();
110+
$result = $md->transform( '**bold text**' );
111+
$this->assertStringContainsString( '<strong>bold text</strong>', $result );
112+
}
113+
114+
function test_transform_italic(): void {
115+
$md = new Markdown();
116+
$result = $md->transform( '*italic text*' );
117+
$this->assertStringContainsString( '<em>italic text</em>', $result );
118+
}
119+
120+
function test_transform_link(): void {
121+
$md = new Markdown();
122+
$result = $md->transform( '[Example](https://example.com)' );
123+
$this->assertStringContainsString( '<a href="https://example.com">Example</a>', $result );
124+
}
125+
126+
function test_transform_unordered_list(): void {
127+
$md = new Markdown();
128+
$result = $md->transform( "* Item 1\n* Item 2\n* Item 3" );
129+
$this->assertStringContainsString( '<li>Item 1</li>', $result );
130+
$this->assertStringContainsString( '<ul>', $result );
131+
}
132+
}

0 commit comments

Comments
 (0)