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
Original file line number Diff line number Diff line change
Expand Up @@ -1297,7 +1297,7 @@ private function get_parsely_stats_response(

// Replace the original API with the mock, using reflection.
$api_reflection = new ReflectionProperty( $obj, 'content_api' );
$api_reflection->setAccessible( true );
self::make_accessible( $api_reflection );
$api_reflection->setValue( $obj, $api );

return $obj->get_parsely_stats_response();
Expand Down
59 changes: 42 additions & 17 deletions tests/Integration/HeadlineTestingScriptsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,6 @@ final class HeadlineTestingScriptsTest extends TestCase {
*/
private static $headline_testing;

/**
* One-line script string used in assertions.
*
* @since 3.21.0
*
* @var string $one_line_script_string Holds the one-line script string.
*/
private static $one_line_script_string = 'src="https://experiments.parsely.com/vip-experiments.js?apiKey=demoaccount.parsely.com&ver='
. PARSELY_VERSION . '" id="parsely-headline-testing-one-line-js"';

/**
* Advanced script string used in assertions.
*
Expand Down Expand Up @@ -262,7 +252,7 @@ public function test_markup_with_one_line_script_installation_method(): void {
$output = (string) ob_get_clean();

// Markup should contain the one-line script and not the advanced script.
self::assertStringContainsString( self::$one_line_script_string, $output );
$this->assert_one_line_script_in_output( $output );
self::assertStringNotContainsString( self::$advanced_script_string, $output );
}

Expand Down Expand Up @@ -307,10 +297,7 @@ public function test_markup_with_one_line_script_and_additional_option(): void {
$output = (string) ob_get_clean();

// Markup should contain the one-line script with the option's attribute.
self::assertStringContainsString(
self::$one_line_script_string . ' data-enable-live-updates="true"',
$output
);
$this->assert_one_line_script_in_output( $output, array( 'data-enable-live-updates' => 'true' ) );
self::assertStringNotContainsString( self::$advanced_script_string, $output );
}

Expand Down Expand Up @@ -410,7 +397,7 @@ public function test_markup_with_advanced_script_installation_method(): void {

// Markup should contain the advanced script and not the one-line script.
self::assertStringContainsString( self::$advanced_script_string, $output );
self::assertStringNotContainsString( self::$one_line_script_string, $output );
$this->assert_one_line_script_not_in_output( $output );
}

/**
Expand Down Expand Up @@ -460,7 +447,7 @@ public function test_markup_with_advanced_script_and_additional_option(): void {
// Markup should contain the advanced script with the option's config.
self::assertStringContainsString( self::$advanced_script_string, $output );
self::assertStringContainsString( 'enableLiveUpdates: true', $output );
self::assertStringNotContainsString( self::$one_line_script_string, $output );
$this->assert_one_line_script_not_in_output( $output );
}

/**
Expand All @@ -484,4 +471,42 @@ private function enable_headline_testing( array $settings = array() ): void {
)
);
}

/**
* Asserts that the one-line headline testing script tag is present in the
* given HTML output.
*
* Uses a regex with lookaheads to verify each attribute independently,
* making the assertion robust against WordPress changes to attribute order
* or HTML entity encoding variants (e.g. & vs &).
*
* @since 3.22.1
*
* @param string $output The HTML output to search.
* @param array<string,string> $extra_attrs Optional extra attributes to assert on the tag.
*/
private function assert_one_line_script_in_output( string $output, array $extra_attrs = array() ): void {
$version = preg_quote( PARSELY_VERSION, '~' );
$lookaheads = '(?=[^>]*\bid="parsely-headline-testing-one-line-js")'
. '(?=[^>]*\bsrc="https://experiments\.parsely\.com/vip-experiments\.js'
. '\?apiKey=demoaccount\.parsely\.com(?:&amp;|&#038;)ver=' . $version . '")';

foreach ( $extra_attrs as $attr => $value ) {
$lookaheads .= '(?=[^>]*\b' . preg_quote( $attr, '~' ) . '="' . preg_quote( $value, '~' ) . '")';
}

self::assertMatchesRegularExpression( '~<script' . $lookaheads . '~', $output );
}

/**
* Asserts that the one-line headline testing script tag is not present in
* the given HTML output.
*
* @since 3.22.1
*
* @param string $output The HTML output to search.
*/
private function assert_one_line_script_not_in_output( string $output ): void {
self::assertStringNotContainsString( 'id="parsely-headline-testing-one-line-js"', $output );
}
}
2 changes: 1 addition & 1 deletion tests/Integration/Integrations/IntegrationsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ function ( $integrations ) {

// Use Reflection to look inside the collection.
$reflector_property = ( new ReflectionClass( $integrations ) )->getProperty( 'integrations' );
$reflector_property->setAccessible( true );
self::make_accessible( $reflector_property );
/**
* Variable.
*
Expand Down
4 changes: 2 additions & 2 deletions tests/Integration/TestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -653,7 +653,7 @@ public function get_private_property( string $class_name, string $property_name
$reflector = new ReflectionClass( $class_name );
$property = $reflector->getProperty( $property_name );

$property->setAccessible( true );
self::make_accessible( $property );

return $property;
}
Expand All @@ -669,7 +669,7 @@ public function get_private_method( string $class_name, string $method ): Reflec
$reflector = new ReflectionClass( $class_name );
$method = $reflector->getMethod( $method );

$method->setAccessible( true );
self::make_accessible( $method );

return $method;
}
Expand Down
29 changes: 19 additions & 10 deletions tests/Traits/TestsReflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,21 @@
use ReflectionProperty;

trait TestsReflection {
/**
* Makes a ReflectionMethod or ReflectionProperty accessible on PHP below
* 8.1, where setAccessible() is required. It is a no-op since PHP 8.1
* and deprecated since PHP 8.5.
*
* @since 3.22.1
*
* @param ReflectionMethod|ReflectionProperty $reflection The reflection object.
*/
public static function make_accessible( object $reflection ): void {
if ( PHP_VERSION_ID < 80100 ) {
$reflection->setAccessible( true );
}
}

/**
* Gets a method from a class. This should be used when trying to access a
* private method for testing.
Expand All @@ -26,7 +41,7 @@ trait TestsReflection {
*/
public static function get_method( string $method_name, $class_name = Parsely::class ): \ReflectionMethod {
$method = ( new \ReflectionClass( $class_name ) )->getMethod( $method_name );
$method->setAccessible( true );
self::make_accessible( $method );

return $method;
}
Expand All @@ -44,7 +59,7 @@ public static function get_method( string $method_name, $class_name = Parsely::c
*/
public static function get_property( string $property_name, $class_name = Parsely::class ) {
$property = ( new \ReflectionClass( $class_name ) )->getProperty( $property_name );
$property->setAccessible( true );
self::make_accessible( $property );

return $property;
}
Expand All @@ -53,9 +68,6 @@ public static function get_property( string $property_name, $class_name = Parsel
* Overrides the value of a private property on a given object. This is
* useful when mocking the internals of a class.
*
* Note that the property will no longer be private after setAccessible is
* called.
*
* @since 3.17.0 Changed the method signature.
*
* @param object $obj The object instance on which to set the value.
Expand All @@ -66,17 +78,14 @@ public static function get_property( string $property_name, $class_name = Parsel
*/
public static function set_private_property( $obj, string $property_name, $value ): void {
$property = ( new \ReflectionClass( $obj ) )->getProperty( $property_name );
$property->setAccessible( true );
self::make_accessible( $property );
$property->setValue( $obj, $value );
}

/**
* Overrides the value of a protected property on a given object. This is
* useful when mocking the internals of a class.
*
* Note that the property will no longer be protected after setAccessible is
* called.
*
* @since 3.17.0
*
* @param object $obj The object instance on which to set the value.
Expand All @@ -88,7 +97,7 @@ public static function set_private_property( $obj, string $property_name, $value
public static function set_protected_property( $obj, string $property_name, $value ): void {
$reflection = new \ReflectionClass( $obj );
$property = $reflection->getProperty( $property_name );
$property->setAccessible( true );
self::make_accessible( $property );
$property->setValue( $obj, $value );
}
}
5 changes: 4 additions & 1 deletion tests/Unit/Integrations/IntegrationsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,16 @@
use Parsely\Integrations\Integration;
use Parsely\Integrations\Integrations;
use Parsely\Parsely;
use Parsely\Tests\Traits\TestsReflection;
use ReflectionClass;
use Yoast\WPTestUtils\BrainMonkey\TestCase;

/**
* Unit Tests for the Integrations collection.
*/
final class IntegrationsTest extends TestCase {
use TestsReflection;

/**
* Internal variable.
*
Expand Down Expand Up @@ -49,7 +52,7 @@ public function test_an_integration_can_be_registered_to_a_new_Integrations_obje
// Use Reflection to look inside the collection.
$reflector = new ReflectionClass( $integrations );
$reflector_property = $reflector->getProperty( 'integrations' );
$reflector_property->setAccessible( true );
self::make_accessible( $reflector_property );
/**
* Variable.
*
Expand Down
Loading