Skip to content
Open
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
123 changes: 123 additions & 0 deletions .github/workflows/unit-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
name: Unit Tests

on:
push:
branches: [ trunk ]
pull_request:
branches: [ trunk ]

jobs:
# Standalone PHP tests — no WordPress or database dependency.
php-standalone:
name: "PHP: ${{ matrix.name }}"
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- name: Serve Happy API
working-directory: api.wordpress.org/public_html/core/serve-happy/1.0
phpunit-args: "--configuration phpunit.xml --exclude-group serve-happy-live-http"
bootstrap: compat
- name: Browse Happy API
working-directory: api.wordpress.org/public_html/core/browse-happy/1.0
phpunit-args: "--configuration phpunit.xml"
bootstrap: none
- name: Slack Trac Bot
working-directory: common/includes/tests/slack/trac
phpunit-args: "bot.php"
bootstrap: compat
- name: Slack Props Library
working-directory: common/includes/slack/props/tests
phpunit-args: "."
bootstrap: wpdb-stub
steps:
- uses: actions/checkout@v4

- name: Set up PHP
uses: shivammathur/setup-php@v2
with:
php-version: "8.0"
tools: phpunit:^9

- name: Create PHPUnit compatibility bootstrap
if: matrix.bootstrap == 'compat'
run: |
cat > /tmp/phpunit-bootstrap.php << 'PHPEOF'
<?php
// Polyfill for tests using the pre-PHPUnit 8 class name.
if ( ! class_exists( 'PHPUnit_Framework_TestCase' ) && class_exists( 'PHPUnit\Framework\TestCase' ) ) {
class_alias( 'PHPUnit\Framework\TestCase', 'PHPUnit_Framework_TestCase' );
}
PHPEOF

- name: Create wpdb stub bootstrap
if: matrix.bootstrap == 'wpdb-stub'
run: |
cat > /tmp/phpunit-bootstrap.php << 'PHPEOF'
<?php
// Stub for $wpdb used by PHPUnit's createStub() in the props tests.
class wpdbStub {
public function prepare( $query, ...$args ) { return $query; }
public function get_results( $query, $output = 'OBJECT' ) { return []; }
}
PHPEOF

- name: Run PHPUnit
working-directory: ${{ matrix.working-directory }}
run: |
if [ "${{ matrix.bootstrap }}" != "none" ]; then
phpunit --bootstrap /tmp/phpunit-bootstrap.php ${{ matrix.phpunit-args }}
else
phpunit ${{ matrix.phpunit-args }}
fi

# WordPress-dependent PHP tests — require the WP test framework and MySQL.
php-wordpress:
name: "WP PHP: ${{ matrix.name }}"
runs-on: ubuntu-latest
services:
mysql:
image: mysql:8.0
env:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: wordpress_test
ports:
- 3306:3306
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
strategy:
fail-fast: false
matrix:
include:
- name: Handbook Plugin
working-directory: wordpress.org/public_html/wp-content/plugins/handbook
phpunit-args: "--configuration phpunit.xml.dist"
- name: Plugin Directory
working-directory: wordpress.org/public_html/wp-content/plugins/plugin-directory
phpunit-args: "--configuration phpunit.xml --group plugin-directory"
steps:
- uses: actions/checkout@v4

- name: Set up PHP
uses: shivammathur/setup-php@v2
with:
php-version: "8.0"
extensions: mysqli
tools: phpunit:^9

- name: Install WordPress test suite
run: |
git clone --depth=1 https://github.com/WordPress/wordpress-develop.git /tmp/wp-develop
cd /tmp/wp-develop && composer install --no-interaction
cp wp-tests-config-sample.php tests/phpunit/wp-tests-config.php
sed -i "s|dirname( __FILE__ ) . '/src/'|'/tmp/wp-develop/src/'|" tests/phpunit/wp-tests-config.php
sed -i "s/youremptytestdbnamehere/wordpress_test/" tests/phpunit/wp-tests-config.php
sed -i "s/yourusernamehere/root/" tests/phpunit/wp-tests-config.php
sed -i "s/yourpasswordhere/root/" tests/phpunit/wp-tests-config.php
sed -i "s/'localhost'/'127.0.0.1'/" tests/phpunit/wp-tests-config.php

- name: Run PHPUnit
working-directory: ${{ matrix.working-directory }}
env:
WP_TESTS_DIR: /tmp/wp-develop/tests/phpunit
run: phpunit ${{ matrix.phpunit-args }}
Original file line number Diff line number Diff line change
Expand Up @@ -853,6 +853,12 @@ function test_upgrade_browsers( $header ) {
return;
}

// Internet Explorer is always flagged as needing an upgrade.
if ( 'Internet Explorer' === $parsed['name'] ) {
$this->assertTrue( $parsed['upgrade'] );
return;
}

$versions = get_browser_current_versions();

if ( ! empty( $versions[ $parsed['name'] ] ) ) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@
if ( ! $_tests_dir && false !== ( $pos = stripos( __FILE__, '/src/wp-content/plugins/' ) ) ) {
$_tests_dir = substr( __FILE__, 0, $pos ) . '/tests/phpunit/';
}
// Check for wp-env test directory.
elseif ( ! $_tests_dir && file_exists( '/wordpress-phpunit/includes/functions.php' ) ) {
$_tests_dir = '/wordpress-phpunit/';
}
// Elseif no path yet, assume a temp directory path.
elseif ( ! $_tests_dir ) {
$_tests_dir = rtrim( sys_get_temp_dir(), '/\\' ) . '/wordpress-tests-lib/tests/phpunit/';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,15 @@ class WPorg_Handbook_Handbook_Test extends WP_UnitTestCase {

protected $handbook;

public function setUp() {
public function setUp(): void {
parent::setup();
WPorg_Handbook_Init::init();

$handbooks = WPorg_Handbook_Init::get_handbook_objects();
$this->handbook = reset( $handbooks );
}

public function tearDown() {
public function tearDown(): void {
parent::tearDown();

foreach ( WPorg_Handbook_Init::get_handbook_objects() as $obj ) {
Expand Down Expand Up @@ -455,7 +455,7 @@ public function test_handbook_sidebar() {

$this->assertTrue( isset( $wp_registered_sidebars[ 'handbook' ] ) );
$this->assertSame(
[ 'name', 'id', 'description', 'class', 'before_widget', 'after_widget', 'before_title', 'after_title', 'before_sidebar', 'after_sidebar' ],
[ 'name', 'id', 'description', 'class', 'before_widget', 'after_widget', 'before_title', 'after_title', 'before_sidebar', 'after_sidebar', 'show_in_rest' ],
array_keys( $wp_registered_sidebars[ 'handbook' ] )
);
$this->assertEquals( 'handbook', $wp_registered_sidebars[ 'handbook' ]['id'] );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@

class WPorg_Handbook_Init_Test extends WP_UnitTestCase {

public function setUp() {
public function setUp(): void {
parent::setup();

WPorg_Handbook_Init::init();
}

public function tearDown() {
public function tearDown(): void {
parent::tearDown();

WPorg_Handbook_Init::reset( true );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@

class WPorg_Handbook_Template_Tags_Test extends WP_UnitTestCase {

public function setUp() {
public function setUp(): void {
parent::setup();

WPorg_Handbook_Init::init();
}

public function tearDown() {
public function tearDown(): void {
parent::tearDown();

WPorg_Handbook_Init::reset( true );
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"core": "WordPress/WordPress#master",
"plugins": [
".",
"https://downloads.wordpress.org/plugin/jetpack.latest-stable.zip",
"https://downloads.wordpress.org/plugin/advanced-post-cache.latest-stable.zip",
"https://github.com/WordPress/plugin-check"
],
"themes": [
"../../../themes/pub/wporg-plugins-2024",
"https://github.com/WordPress/wporg-parent-2021"
],
"mappings": {
"wp-content/mu-plugins/wporg-ratings-stub.php": "./tests/stubs/wporg-ratings-stub.php"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,29 @@
return;
}

// Find the WordPress PHPUnit test framework.
$_tests_dir = getenv( 'WP_TESTS_DIR' );

if ( ! $_tests_dir ) {
// wp-env test directory.
if ( file_exists( '/wordpress-phpunit/includes/functions.php' ) ) {
$_tests_dir = '/wordpress-phpunit/';
} else {
$_tests_dir = rtrim( sys_get_temp_dir(), '/\\' ) . '/wordpress-tests-lib/tests/phpunit/';
}
}

if ( ! file_exists( $_tests_dir . '/includes/functions.php' ) ) {
echo "Could not find {$_tests_dir}/includes/functions.php\n";
exit( 1 );
}

// Give access to tests_add_filter() function.
require_once $_tests_dir . '/includes/functions.php';

// Load Jetpack Search stub if Jetpack is not installed.
require_once __DIR__ . '/stubs/jetpack-search-stub.php';

/**
* Manually load the plugin being tested.
*/
Expand All @@ -14,3 +37,6 @@ function manually_load_plugin() {
}

tests_add_filter( 'muplugins_loaded', __NAMESPACE__ . '\manually_load_plugin' );

// Start up the WP testing environment.
require $_tests_dir . '/includes/bootstrap.php';
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
<?php

use WordPressdotorg\Plugin_Directory\Markdown;

/**
* @group plugin-directory
* @group markdown
*/
class Tests_Markdown extends WP_UnitTestCase {

function test_transform_basic_paragraph(): void {
$md = new Markdown();
$result = $md->transform( 'Hello World' );
$this->assertSame( '<p>Hello World</p>', $result );
}

function test_transform_empty_string(): void {
$md = new Markdown();
$result = $md->transform( '' );
$this->assertSame( '', $result );
}

function test_transform_trims_output(): void {
$md = new Markdown();
$result = $md->transform( " \n Hello \n " );
$this->assertSame( $result, trim( $result ) );
}

/**
* Test the custom `= Section Title =` header syntax.
*
* This is WordPress plugin readme specific — converts `= Title =` to <h4>.
*/
function test_transform_equals_header(): void {
$md = new Markdown();
$result = $md->transform( '= Section Title =' );
$this->assertStringContainsString( '<h4>Section Title</h4>', $result );
}

function test_transform_multiple_equals_headers(): void {
$md = new Markdown();
$result = $md->transform( "= First =\n\nContent\n\n= Second =" );
$this->assertStringContainsString( '<h4>First</h4>', $result );
$this->assertStringContainsString( '<h4>Second</h4>', $result );
}

function test_equals_header_with_leading_whitespace(): void {
$md = new Markdown();
$result = $md->transform( ' = Indented Header =' );
$this->assertStringContainsString( '<h4>Indented Header</h4>', $result );
}

/**
* Test code_trick: <pre><code> blocks preserve underscores in code.
*
* This is custom logic in Markdown::code_trick() — pre-existing HTML code blocks
* are converted to backtick format before markdown processing, so markdown
* does not mangle underscores and other special characters inside code.
*
* The block needs surrounding content so trim() does not strip indentation.
*/
function test_code_trick_preserves_underscores_in_pre_code(): void {
$md = new Markdown();
$input = "Some text before.\n\n<pre><code>\$my_var = some_function();\n\$other_var = 1;</code></pre>\n\nSome text after.";
$result = $md->transform( $input );

// Underscores should NOT be converted to <em> tags inside code blocks.
$this->assertStringNotContainsString( '<em>', $result );
$this->assertStringContainsString( 'my_var', $result );
$this->assertStringContainsString( 'some_function', $result );
}

function test_code_trick_inline_code_preserves_underscores(): void {
$md = new Markdown();
$input = "Use <code>my_var_name</code> for the setting.";
$result = $md->transform( $input );

// Inline code should also preserve underscores.
$this->assertStringNotContainsString( '<em>', $result );
$this->assertStringContainsString( 'my_var_name', $result );
}

/**
* Test code_trick: bbPress-style backtick code blocks at line start are
* converted to indented code (4 spaces) for markdown processing.
*/
function test_code_trick_bbpress_backtick_block(): void {
$md = new Markdown();
$input = "Some text.\n\n`some_code_here`\n\nMore text.";
$result = $md->transform( $input );

$this->assertStringContainsString( 'some_code_here', $result );
}

/**
* Test that inline markdown code (backticks) in mid-line is preserved.
*/
function test_inline_backtick_code_preserved(): void {
$md = new Markdown();
$result = $md->transform( 'Use `add_filter()` to modify output.' );
$this->assertStringContainsString( '<code>add_filter()</code>', $result );
}

/**
* Test standard markdown features (these verify the upstream MarkdownExtra
* library works correctly through our transform() wrapper).
*/
function test_transform_bold(): void {
$md = new Markdown();
$result = $md->transform( '**bold text**' );
$this->assertStringContainsString( '<strong>bold text</strong>', $result );
}

function test_transform_italic(): void {
$md = new Markdown();
$result = $md->transform( '*italic text*' );
$this->assertStringContainsString( '<em>italic text</em>', $result );
}

function test_transform_link(): void {
$md = new Markdown();
$result = $md->transform( '[Example](https://example.com)' );
$this->assertStringContainsString( '<a href="https://example.com">Example</a>', $result );
}

function test_transform_unordered_list(): void {
$md = new Markdown();
$result = $md->transform( "* Item 1\n* Item 2\n* Item 3" );
$this->assertStringContainsString( '<li>Item 1</li>', $result );
$this->assertStringContainsString( '<ul>', $result );
}
}
Loading