mirror of
https://github.com/Frumph/comic-easel.git
synced 2026-08-24 21:41:31 -04:00
d867fdb60c
Covers the trust matrix for the comic HTML fields -- author capability, last editor capability, missing post -- and asserts that decoding happens before the trust check. Also pins the meta box round trip: a value stored by the meta box renders identically to before, while a value stored raw is inert. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
93 lines
3.2 KiB
PHP
93 lines
3.2 KiB
PHP
<?php
|
|
|
|
use PHPUnit\Framework\Attributes\DataProvider;
|
|
|
|
/**
|
|
* ceo_meta_for_editor() — functions/admin-meta.php
|
|
*
|
|
* Normalises a stored comic meta value before it is re-displayed in the post editor. Comic
|
|
* meta arrives in two shapes -- entity-encoded when written through the plugin's meta boxes,
|
|
* which escape on save, and raw when written through WordPress's Custom Fields panel, which
|
|
* does not. Decoding first is what makes a single escape at the output sink correct for both.
|
|
*/
|
|
class MetaForEditorTest extends CE_TestCase {
|
|
|
|
protected function setUp(): void {
|
|
parent::setUp();
|
|
self::loadPluginFile( 'functions/admin-meta.php' );
|
|
}
|
|
|
|
/**
|
|
* The property the meta boxes depend on: a value stored the way the save handler stores
|
|
* it, then normalised and re-escaped for a textarea, must come back byte-identical. If
|
|
* this fails, every existing comic shows mangled text in the editor.
|
|
*/
|
|
#[DataProvider( 'authoredTextProvider' )]
|
|
public function testTextareaRoundTripIsLossless( $typed ) {
|
|
$stored = esc_textarea( $typed );
|
|
$this->assertSame( $stored, esc_textarea( ceo_meta_for_editor( $stored ) ) );
|
|
}
|
|
|
|
/** And the value the author actually typed is what they see again. */
|
|
#[DataProvider( 'authoredTextProvider' )]
|
|
public function testTheAuthorSeesWhatTheyTyped( $typed ) {
|
|
$this->assertSame( $typed, ceo_meta_for_editor( esc_textarea( $typed ) ) );
|
|
}
|
|
|
|
public static function authoredTextProvider() {
|
|
return array(
|
|
'markup' => array( '<b>bold</b>' ),
|
|
'an entity typed literally' => array( '<b>' ),
|
|
'a bare ampersand' => array( 'Tom & Jerry' ),
|
|
'quotes' => array( 'she said "hi"' ),
|
|
'an apostrophe' => array( "it's fine" ),
|
|
'a url with a query string' => array( 'https://x.test/?a=1&b=2' ),
|
|
);
|
|
}
|
|
|
|
/**
|
|
* A raw value written past the meta boxes must not survive as live markup once the sink
|
|
* escapes it. This is the Custom Fields path.
|
|
*/
|
|
public function testRawMarkupFromCustomFieldsBecomesInert() {
|
|
$raw = '</textarea><script>alert(1)</script>';
|
|
$out = esc_textarea( ceo_meta_for_editor( $raw ) );
|
|
$this->assertStringNotContainsString( '</textarea>', $out );
|
|
$this->assertStringNotContainsString( '<script', $out );
|
|
}
|
|
|
|
public function testAttributeBreakingValueBecomesInert() {
|
|
$out = esc_attr( ceo_meta_for_editor( '" autofocus onfocus="alert(1)' ) );
|
|
$this->assertStringNotContainsString( '"', str_replace( '"', '', $out ) );
|
|
}
|
|
|
|
#[DataProvider( 'decodingProvider' )]
|
|
public function testKnownEntitiesAreDecodedOnce( $stored, $expected ) {
|
|
$this->assertSame( $expected, ceo_meta_for_editor( $stored ) );
|
|
}
|
|
|
|
public static function decodingProvider() {
|
|
return array(
|
|
array( '<b>', '<b>' ),
|
|
array( '&amp;', '&' ),
|
|
array( '"x"', '"x"' ),
|
|
array( ''', "'" ),
|
|
array( '<b>', '<b>' ),
|
|
array( '¬anentity;', '¬anentity;' ),
|
|
);
|
|
}
|
|
|
|
#[DataProvider( 'nonStringProvider' )]
|
|
public function testNonStringInputIsHandled( $input, $expected ) {
|
|
$this->assertSame( $expected, ceo_meta_for_editor( $input ) );
|
|
}
|
|
|
|
public static function nonStringProvider() {
|
|
return array(
|
|
array( null, '' ),
|
|
array( false, '' ),
|
|
array( 123, '123' ),
|
|
);
|
|
}
|
|
}
|