2019-11-08 14:46:52 +00:00
|
|
|
<?php
|
|
|
|
|
|
|
|
namespace Cite\Tests\Unit;
|
|
|
|
|
2019-11-19 14:12:11 +00:00
|
|
|
use Cite\Cite;
|
2019-12-09 16:08:57 +00:00
|
|
|
use Cite\ErrorReporter;
|
2019-12-04 16:53:05 +00:00
|
|
|
use Cite\FootnoteMarkFormatter;
|
2019-12-11 11:19:18 +00:00
|
|
|
use Cite\ReferencesFormatter;
|
2019-11-27 11:09:31 +00:00
|
|
|
use Cite\ReferenceStack;
|
2019-12-11 13:58:25 +00:00
|
|
|
use Language;
|
2019-12-03 12:39:02 +00:00
|
|
|
use Parser;
|
2019-12-11 13:58:25 +00:00
|
|
|
use ParserOptions;
|
2019-12-03 12:39:02 +00:00
|
|
|
use ParserOutput;
|
|
|
|
use StripState;
|
2019-11-08 14:46:52 +00:00
|
|
|
use Wikimedia\TestingAccessWrapper;
|
|
|
|
|
|
|
|
/**
|
2019-11-19 14:12:11 +00:00
|
|
|
* @coversDefaultClass \Cite\Cite
|
2019-11-19 10:31:08 +00:00
|
|
|
*
|
|
|
|
* @license GPL-2.0-or-later
|
2019-11-08 14:46:52 +00:00
|
|
|
*/
|
2019-12-10 15:24:49 +00:00
|
|
|
class CiteUnitTest extends \MediaWikiUnitTestCase {
|
2019-12-04 16:53:05 +00:00
|
|
|
|
2019-11-27 11:09:31 +00:00
|
|
|
/**
|
|
|
|
* @covers ::validateRef
|
2019-12-06 09:35:55 +00:00
|
|
|
* @covers ::validateRefOutsideOfReferences
|
|
|
|
* @covers ::validateRefInReferences
|
2019-12-03 20:24:10 +00:00
|
|
|
* @dataProvider provideValidateRef
|
2019-11-27 11:09:31 +00:00
|
|
|
*/
|
|
|
|
public function testValidateRef(
|
|
|
|
array $referencesStack,
|
|
|
|
?string $inReferencesGroup,
|
|
|
|
bool $isSectionPreview,
|
|
|
|
?string $text,
|
|
|
|
?string $name,
|
|
|
|
?string $group,
|
|
|
|
?string $follow,
|
|
|
|
?string $extends,
|
2019-12-16 14:33:55 +00:00
|
|
|
?string $dir,
|
2019-11-27 11:09:31 +00:00
|
|
|
$expected
|
|
|
|
) {
|
2019-12-09 16:08:57 +00:00
|
|
|
/** @var ErrorReporter $errorReporter */
|
|
|
|
$errorReporter = $this->createMock( ErrorReporter::class );
|
2019-11-27 11:09:31 +00:00
|
|
|
/** @var ReferenceStack $stack */
|
|
|
|
$stack = TestingAccessWrapper::newFromObject( new ReferenceStack( $errorReporter ) );
|
|
|
|
$stack->refs = $referencesStack;
|
|
|
|
|
|
|
|
/** @var Cite $cite */
|
2019-12-11 13:58:25 +00:00
|
|
|
$cite = TestingAccessWrapper::newFromObject( $this->newCite() );
|
2019-11-27 11:09:31 +00:00
|
|
|
$cite->referenceStack = $stack;
|
|
|
|
$cite->inReferencesGroup = $inReferencesGroup;
|
|
|
|
$cite->isSectionPreview = $isSectionPreview;
|
|
|
|
|
2019-12-19 09:16:14 +00:00
|
|
|
$status = $cite->validateRef( $text, $group, $name, $extends, $follow, $dir );
|
2019-11-27 11:09:31 +00:00
|
|
|
if ( is_string( $expected ) ) {
|
|
|
|
$this->assertSame( $expected, $status->getErrors()[0]['message'] );
|
|
|
|
} else {
|
|
|
|
$this->assertSame( $expected, $status->isOK(), $status->getErrors()[0]['message'] ?? '' );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-03 20:24:10 +00:00
|
|
|
public function provideValidateRef() {
|
2019-11-27 11:09:31 +00:00
|
|
|
return [
|
2019-12-03 20:24:10 +00:00
|
|
|
// Shared <ref> validations regardless of context
|
|
|
|
'Numeric name' => [
|
|
|
|
'referencesStack' => [],
|
|
|
|
'inReferencesGroup' => null,
|
|
|
|
'isSectionPreview' => false,
|
|
|
|
'text' => null,
|
|
|
|
'name' => '1',
|
2019-12-21 01:25:58 +00:00
|
|
|
'group' => '',
|
2019-12-03 20:24:10 +00:00
|
|
|
'follow' => null,
|
|
|
|
'extends' => null,
|
2019-12-16 14:33:55 +00:00
|
|
|
'dir' => null,
|
2019-12-03 20:24:10 +00:00
|
|
|
'expected' => 'cite_error_ref_numeric_key',
|
|
|
|
],
|
|
|
|
'Numeric follow' => [
|
|
|
|
'referencesStack' => [],
|
|
|
|
'inReferencesGroup' => null,
|
|
|
|
'isSectionPreview' => false,
|
|
|
|
'text' => 't',
|
|
|
|
'name' => null,
|
2019-12-21 01:25:58 +00:00
|
|
|
'group' => '',
|
2019-12-03 20:24:10 +00:00
|
|
|
'follow' => '1',
|
|
|
|
'extends' => null,
|
2019-12-16 14:33:55 +00:00
|
|
|
'dir' => null,
|
2019-12-03 20:24:10 +00:00
|
|
|
'expected' => 'cite_error_ref_numeric_key',
|
|
|
|
],
|
|
|
|
'Numeric extends' => [
|
|
|
|
'referencesStack' => [],
|
|
|
|
'inReferencesGroup' => null,
|
|
|
|
'isSectionPreview' => false,
|
|
|
|
'text' => 't',
|
|
|
|
'name' => null,
|
2019-12-21 01:25:58 +00:00
|
|
|
'group' => '',
|
2019-12-03 20:24:10 +00:00
|
|
|
'follow' => null,
|
|
|
|
'extends' => '1',
|
2019-12-16 14:33:55 +00:00
|
|
|
'dir' => null,
|
2019-12-03 20:24:10 +00:00
|
|
|
'expected' => 'cite_error_ref_numeric_key',
|
|
|
|
],
|
|
|
|
'Follow with name' => [
|
|
|
|
'referencesStack' => [],
|
|
|
|
'inReferencesGroup' => null,
|
|
|
|
'isSectionPreview' => false,
|
|
|
|
'text' => 't',
|
|
|
|
'name' => 'n',
|
2019-12-21 01:25:58 +00:00
|
|
|
'group' => '',
|
2019-12-03 20:24:10 +00:00
|
|
|
'follow' => 'f',
|
|
|
|
'extends' => null,
|
2019-12-16 14:33:55 +00:00
|
|
|
'dir' => null,
|
2019-12-03 20:24:10 +00:00
|
|
|
'expected' => 'cite_error_ref_too_many_keys',
|
|
|
|
],
|
|
|
|
'Follow with extends' => [
|
|
|
|
'referencesStack' => [],
|
|
|
|
'inReferencesGroup' => null,
|
|
|
|
'isSectionPreview' => false,
|
|
|
|
'text' => 't',
|
|
|
|
'name' => null,
|
2019-12-21 01:25:58 +00:00
|
|
|
'group' => '',
|
2019-12-03 20:24:10 +00:00
|
|
|
'follow' => 'f',
|
|
|
|
'extends' => 'e',
|
2019-12-16 14:33:55 +00:00
|
|
|
'dir' => null,
|
2019-12-03 20:24:10 +00:00
|
|
|
'expected' => 'cite_error_ref_too_many_keys',
|
|
|
|
],
|
2019-11-27 11:09:31 +00:00
|
|
|
// Validating <ref> outside of <references>
|
|
|
|
'text-only <ref>' => [
|
|
|
|
'referencesStack' => [],
|
|
|
|
'inReferencesGroup' => null,
|
|
|
|
'isSectionPreview' => false,
|
|
|
|
'text' => 't',
|
|
|
|
'name' => null,
|
2019-12-21 01:25:58 +00:00
|
|
|
'group' => '',
|
2019-11-27 11:09:31 +00:00
|
|
|
'follow' => null,
|
|
|
|
'extends' => null,
|
2019-12-16 14:33:55 +00:00
|
|
|
'dir' => null,
|
2019-11-27 11:09:31 +00:00
|
|
|
'expected' => true,
|
|
|
|
],
|
2019-12-03 20:24:10 +00:00
|
|
|
'Whitespace or empty text' => [
|
|
|
|
'referencesStack' => [],
|
|
|
|
'inReferencesGroup' => null,
|
|
|
|
'isSectionPreview' => false,
|
|
|
|
'text' => '',
|
|
|
|
'name' => null,
|
2019-12-21 01:25:58 +00:00
|
|
|
'group' => '',
|
2019-12-03 20:24:10 +00:00
|
|
|
'follow' => null,
|
|
|
|
'extends' => null,
|
2019-12-16 14:33:55 +00:00
|
|
|
'dir' => null,
|
2019-12-03 20:24:10 +00:00
|
|
|
'expected' => 'cite_error_ref_no_input',
|
|
|
|
],
|
2019-11-27 11:09:31 +00:00
|
|
|
'totally empty <ref>' => [
|
|
|
|
'referencesStack' => [],
|
|
|
|
'inReferencesGroup' => null,
|
|
|
|
'isSectionPreview' => false,
|
|
|
|
'text' => null,
|
|
|
|
'name' => null,
|
2019-12-21 01:25:58 +00:00
|
|
|
'group' => '',
|
2019-11-27 11:09:31 +00:00
|
|
|
'follow' => null,
|
|
|
|
'extends' => null,
|
2019-12-16 14:33:55 +00:00
|
|
|
'dir' => null,
|
2019-11-27 11:09:31 +00:00
|
|
|
'expected' => 'cite_error_ref_no_key',
|
|
|
|
],
|
2019-12-03 20:24:10 +00:00
|
|
|
'contains <ref>-like text' => [
|
|
|
|
'referencesStack' => [],
|
|
|
|
'inReferencesGroup' => null,
|
|
|
|
'isSectionPreview' => false,
|
|
|
|
'text' => 'Foo <ref name="bar">',
|
|
|
|
'name' => 'n',
|
2019-12-21 01:25:58 +00:00
|
|
|
'group' => '',
|
2019-12-03 20:24:10 +00:00
|
|
|
'follow' => null,
|
|
|
|
'extends' => null,
|
2019-12-16 14:33:55 +00:00
|
|
|
'dir' => null,
|
2019-12-03 20:24:10 +00:00
|
|
|
'expected' => 'cite_error_included_ref',
|
|
|
|
],
|
2019-11-27 11:09:31 +00:00
|
|
|
|
|
|
|
// Validating a <ref> in <references>
|
|
|
|
'most trivial <ref> in <references>' => [
|
|
|
|
'referencesStack' => [ 'g' => [ 'n' => [] ] ],
|
|
|
|
'inReferencesGroup' => 'g',
|
|
|
|
'isSectionPreview' => false,
|
|
|
|
'text' => 'not empty',
|
|
|
|
'name' => 'n',
|
|
|
|
'group' => 'g',
|
|
|
|
'follow' => null,
|
|
|
|
'extends' => null,
|
2019-12-16 14:33:55 +00:00
|
|
|
'dir' => null,
|
2019-11-27 11:09:31 +00:00
|
|
|
'expected' => true,
|
|
|
|
],
|
2019-12-03 20:24:10 +00:00
|
|
|
'Different group than <references>' => [
|
|
|
|
'referencesStack' => [ 'g' => [ 'n' => [] ] ],
|
|
|
|
'inReferencesGroup' => 'g1',
|
|
|
|
'isSectionPreview' => false,
|
|
|
|
'text' => 't',
|
|
|
|
'name' => 'n',
|
|
|
|
'group' => 'g2',
|
|
|
|
'follow' => null,
|
|
|
|
'extends' => null,
|
2019-12-16 14:33:55 +00:00
|
|
|
'dir' => null,
|
2019-12-03 20:24:10 +00:00
|
|
|
'expected' => 'cite_error_references_group_mismatch',
|
|
|
|
],
|
|
|
|
'Unnamed in <references>' => [
|
|
|
|
'referencesStack' => [ 'g' => [ 'n' => [] ] ],
|
|
|
|
'inReferencesGroup' => 'g',
|
|
|
|
'isSectionPreview' => false,
|
|
|
|
'text' => 't',
|
|
|
|
'name' => null,
|
|
|
|
'group' => 'g',
|
|
|
|
'follow' => null,
|
|
|
|
'extends' => null,
|
2019-12-16 14:33:55 +00:00
|
|
|
'dir' => null,
|
2019-12-03 20:24:10 +00:00
|
|
|
'expected' => 'cite_error_references_no_key',
|
|
|
|
],
|
|
|
|
'Empty text in <references>' => [
|
|
|
|
'referencesStack' => [ 'g' => [ 'n' => [] ] ],
|
|
|
|
'inReferencesGroup' => 'g',
|
|
|
|
'isSectionPreview' => false,
|
|
|
|
'text' => '',
|
|
|
|
'name' => 'n',
|
|
|
|
'group' => 'g',
|
|
|
|
'follow' => null,
|
|
|
|
'extends' => null,
|
2019-12-16 14:33:55 +00:00
|
|
|
'dir' => null,
|
2019-12-03 20:24:10 +00:00
|
|
|
'expected' => 'cite_error_empty_references_define',
|
|
|
|
],
|
|
|
|
'Group never used' => [
|
|
|
|
'referencesStack' => [ 'g2' => [ 'n' => [] ] ],
|
|
|
|
'inReferencesGroup' => 'g',
|
|
|
|
'isSectionPreview' => false,
|
|
|
|
'text' => 'not empty',
|
|
|
|
'name' => 'n',
|
|
|
|
'group' => 'g',
|
|
|
|
'follow' => null,
|
|
|
|
'extends' => null,
|
2019-12-16 14:33:55 +00:00
|
|
|
'dir' => null,
|
2019-12-03 20:24:10 +00:00
|
|
|
'expected' => 'cite_error_references_missing_group',
|
|
|
|
],
|
|
|
|
'Ref never used' => [
|
|
|
|
'referencesStack' => [ 'g' => [ 'n' => [] ] ],
|
|
|
|
'inReferencesGroup' => 'g',
|
|
|
|
'isSectionPreview' => false,
|
|
|
|
'text' => 'not empty',
|
|
|
|
'name' => 'n2',
|
|
|
|
'group' => 'g',
|
|
|
|
'follow' => null,
|
|
|
|
'extends' => null,
|
2019-12-16 14:33:55 +00:00
|
|
|
'dir' => null,
|
2019-12-03 20:24:10 +00:00
|
|
|
'expected' => 'cite_error_references_missing_key',
|
|
|
|
],
|
2019-12-16 14:33:55 +00:00
|
|
|
'Good dir' => [
|
|
|
|
'referencesStack' => [],
|
|
|
|
'inReferencesGroup' => null,
|
|
|
|
'isSectionPreview' => false,
|
|
|
|
'text' => 'not empty',
|
|
|
|
'name' => 'n',
|
2019-12-21 01:25:58 +00:00
|
|
|
'group' => '',
|
2019-12-16 14:33:55 +00:00
|
|
|
'follow' => null,
|
|
|
|
'extends' => null,
|
|
|
|
'dir' => 'RTL',
|
|
|
|
'expected' => true,
|
|
|
|
],
|
|
|
|
'Bad dir' => [
|
|
|
|
'referencesStack' => [],
|
|
|
|
'inReferencesGroup' => null,
|
|
|
|
'isSectionPreview' => false,
|
|
|
|
'text' => 'not empty',
|
|
|
|
'name' => 'n',
|
2019-12-21 01:25:58 +00:00
|
|
|
'group' => '',
|
2019-12-16 14:33:55 +00:00
|
|
|
'follow' => null,
|
|
|
|
'extends' => null,
|
|
|
|
'dir' => 'foobar',
|
|
|
|
'expected' => 'cite_error_ref_invalid_dir',
|
|
|
|
],
|
2019-11-27 11:09:31 +00:00
|
|
|
];
|
|
|
|
}
|
|
|
|
|
2019-12-03 20:24:10 +00:00
|
|
|
/**
|
|
|
|
* @covers ::validateRef
|
|
|
|
*/
|
|
|
|
public function testValidateRef_noExtends() {
|
|
|
|
global $wgCiteBookReferencing;
|
|
|
|
$wgCiteBookReferencing = false;
|
|
|
|
|
|
|
|
/** @var Cite $cite */
|
2019-12-11 13:58:25 +00:00
|
|
|
$cite = TestingAccessWrapper::newFromObject( $this->newCite() );
|
2019-12-19 09:16:14 +00:00
|
|
|
$status = $cite->validateRef( 'text', '', 'name', 'a', null, null );
|
2019-12-03 20:24:10 +00:00
|
|
|
$this->assertSame( 'cite_error_ref_too_many_keys', $status->getErrors()[0]['message'] );
|
|
|
|
}
|
|
|
|
|
2019-11-08 14:46:52 +00:00
|
|
|
/**
|
2019-11-27 12:50:05 +00:00
|
|
|
* @covers ::parseArguments
|
2019-12-03 22:23:49 +00:00
|
|
|
* @dataProvider provideParseArguments
|
2019-11-08 14:46:52 +00:00
|
|
|
*/
|
2019-12-03 22:23:49 +00:00
|
|
|
public function testParseArguments(
|
2019-11-26 11:05:51 +00:00
|
|
|
array $attributes,
|
|
|
|
array $expectedValue,
|
|
|
|
string $expectedError = null
|
|
|
|
) {
|
2019-11-08 14:46:52 +00:00
|
|
|
/** @var Cite $cite */
|
2019-12-11 13:58:25 +00:00
|
|
|
$cite = TestingAccessWrapper::newFromObject( $this->newCite() );
|
2019-11-26 11:05:51 +00:00
|
|
|
$status = $cite->parseArguments(
|
2019-11-27 12:50:05 +00:00
|
|
|
$attributes,
|
|
|
|
[ 'dir', 'extends', 'follow', 'group', 'name' ]
|
|
|
|
);
|
2019-11-26 11:05:51 +00:00
|
|
|
$this->assertSame( $expectedValue, array_values( $status->getValue() ) );
|
|
|
|
$this->assertSame( !$expectedError, $status->isOK() );
|
|
|
|
if ( $expectedError ) {
|
|
|
|
$this->assertSame( $expectedError, $status->getErrors()[0]['message'] );
|
|
|
|
}
|
2019-11-08 14:46:52 +00:00
|
|
|
}
|
|
|
|
|
2019-12-03 22:23:49 +00:00
|
|
|
public function provideParseArguments() {
|
2019-11-27 11:44:25 +00:00
|
|
|
// Note: Values are guaranteed to be trimmed by the parser, see
|
|
|
|
// Sanitizer::decodeTagAttributes()
|
2019-11-08 14:46:52 +00:00
|
|
|
return [
|
2019-11-22 08:49:27 +00:00
|
|
|
[ [], [ null, null, null, null, null ] ],
|
2019-11-08 14:46:52 +00:00
|
|
|
|
|
|
|
// One attribute only
|
2019-11-26 11:18:55 +00:00
|
|
|
[ [ 'dir' => 'invalid' ], [ 'invalid', null, null, null, null ] ],
|
2019-11-27 11:44:25 +00:00
|
|
|
[ [ 'dir' => 'rtl' ], [ 'rtl', null, null, null, null ] ],
|
|
|
|
[ [ 'follow' => 'f' ], [ null, null, 'f', null, null ] ],
|
|
|
|
[ [ 'group' => 'g' ], [ null, null, null, 'g', null ] ],
|
2019-11-26 11:05:51 +00:00
|
|
|
[
|
|
|
|
[ 'invalid' => 'i' ],
|
|
|
|
[ null, null, null, null, null ],
|
|
|
|
'cite_error_ref_too_many_keys'
|
|
|
|
],
|
|
|
|
[
|
|
|
|
[ 'invalid' => null ],
|
|
|
|
[ null, null, null, null, null ],
|
|
|
|
'cite_error_ref_too_many_keys'
|
|
|
|
],
|
2019-11-27 11:44:25 +00:00
|
|
|
[ [ 'name' => 'n' ], [ null, null, null, null, 'n' ] ],
|
2019-11-27 12:50:05 +00:00
|
|
|
[ [ 'name' => null ], [ null, null, null, null, null ] ],
|
2019-11-27 11:44:25 +00:00
|
|
|
[ [ 'extends' => 'e' ], [ null, 'e', null, null, null ] ],
|
2019-11-08 14:46:52 +00:00
|
|
|
|
|
|
|
// Pairs
|
2019-11-26 11:18:55 +00:00
|
|
|
[ [ 'follow' => 'f', 'name' => 'n' ], [ null, null, 'f', null, 'n' ] ],
|
2019-11-27 12:50:05 +00:00
|
|
|
[ [ 'follow' => null, 'name' => null ], [ null, null, null, null, null ] ],
|
2019-11-26 11:18:55 +00:00
|
|
|
[ [ 'follow' => 'f', 'extends' => 'e' ], [ null, 'e', 'f', null, null ] ],
|
|
|
|
[ [ 'group' => 'g', 'name' => 'n' ], [ null, null, null, 'g', 'n' ] ],
|
2019-11-08 14:46:52 +00:00
|
|
|
|
|
|
|
// Combinations of 3 or more attributes
|
|
|
|
[
|
|
|
|
[ 'group' => 'g', 'name' => 'n', 'extends' => 'e', 'dir' => 'rtl' ],
|
2019-11-26 11:18:55 +00:00
|
|
|
[ 'rtl', 'e', null, 'g', 'n' ]
|
2019-11-08 14:46:52 +00:00
|
|
|
],
|
|
|
|
];
|
|
|
|
}
|
|
|
|
|
2019-12-03 22:39:52 +00:00
|
|
|
/**
|
|
|
|
* @covers ::guardedReferences
|
|
|
|
* @dataProvider provideGuardedReferences
|
|
|
|
*/
|
|
|
|
public function testGuardedReferences(
|
|
|
|
?string $text,
|
|
|
|
array $argv,
|
|
|
|
int $expectedRollbackCount,
|
|
|
|
string $expectedInReferencesGroup,
|
|
|
|
bool $expectedResponsive,
|
|
|
|
string $expectedOutput
|
|
|
|
) {
|
|
|
|
global $wgCiteResponsiveReferences;
|
|
|
|
$wgCiteResponsiveReferences = false;
|
|
|
|
|
2019-12-09 14:07:37 +00:00
|
|
|
/** @var Parser $parser */
|
|
|
|
$parser = $this->createMock( Parser::class );
|
|
|
|
|
2019-12-11 13:58:25 +00:00
|
|
|
$cite = $this->newCite();
|
2019-12-03 22:39:52 +00:00
|
|
|
/** @var Cite $spy */
|
|
|
|
$spy = TestingAccessWrapper::newFromObject( $cite );
|
2019-12-09 16:08:57 +00:00
|
|
|
$spy->errorReporter = $this->createMock( ErrorReporter::class );
|
2019-12-03 22:39:52 +00:00
|
|
|
$spy->errorReporter->method( 'halfParsed' )->willReturnCallback(
|
2019-12-19 09:16:14 +00:00
|
|
|
function ( Parser $parser, ...$args ) {
|
2019-12-09 16:08:57 +00:00
|
|
|
return '(' . implode( '|', $args ) . ')';
|
2019-12-03 22:39:52 +00:00
|
|
|
}
|
|
|
|
);
|
2019-12-09 15:36:34 +00:00
|
|
|
$spy->referencesFormatter = $this->createMock( ReferencesFormatter::class );
|
|
|
|
$spy->referencesFormatter->method( 'formatReferences' )
|
2019-12-09 14:07:37 +00:00
|
|
|
->with( $parser, [], $expectedResponsive, false )
|
2019-12-11 15:02:49 +00:00
|
|
|
->willReturn( 'references!' );
|
2019-12-03 22:39:52 +00:00
|
|
|
$spy->isSectionPreview = false;
|
|
|
|
$spy->referenceStack = $this->createMock( ReferenceStack::class );
|
2019-12-09 14:07:37 +00:00
|
|
|
$spy->referenceStack->method( 'popGroup' )
|
2019-12-03 22:39:52 +00:00
|
|
|
->with( $expectedInReferencesGroup )->willReturn( [] );
|
2019-12-19 09:16:14 +00:00
|
|
|
$spy->referenceStack->expects( $expectedRollbackCount ? $this->once() : $this->never() )
|
|
|
|
->method( 'rollbackRefs' )
|
|
|
|
->with( $expectedRollbackCount )
|
|
|
|
->willReturn( [ [ 't', [] ] ] );
|
2019-12-03 22:39:52 +00:00
|
|
|
|
2019-12-09 14:07:37 +00:00
|
|
|
$output = $spy->guardedReferences( $text, $argv, $parser );
|
2019-12-03 22:39:52 +00:00
|
|
|
$this->assertSame( $expectedOutput, $output );
|
|
|
|
}
|
|
|
|
|
|
|
|
public function provideGuardedReferences() {
|
|
|
|
return [
|
|
|
|
'Bare references tag' => [
|
|
|
|
null,
|
|
|
|
[],
|
|
|
|
0,
|
|
|
|
'',
|
|
|
|
false,
|
|
|
|
'references!'
|
|
|
|
],
|
|
|
|
'References with group' => [
|
|
|
|
null,
|
|
|
|
[ 'group' => 'g' ],
|
|
|
|
0,
|
|
|
|
'g',
|
|
|
|
false,
|
|
|
|
'references!'
|
|
|
|
],
|
|
|
|
'Empty references tag' => [
|
|
|
|
'',
|
|
|
|
[],
|
|
|
|
0,
|
|
|
|
'',
|
|
|
|
false,
|
|
|
|
'references!'
|
|
|
|
],
|
|
|
|
'Set responsive' => [
|
|
|
|
'',
|
|
|
|
[ 'responsive' => '1' ],
|
|
|
|
0,
|
|
|
|
'',
|
|
|
|
true,
|
|
|
|
'references!'
|
|
|
|
],
|
|
|
|
'Unknown attribute' => [
|
|
|
|
'',
|
|
|
|
[ 'blargh' => '0' ],
|
|
|
|
0,
|
|
|
|
'',
|
|
|
|
false,
|
2019-12-09 16:08:57 +00:00
|
|
|
'(cite_error_references_invalid_parameters)',
|
2019-12-03 22:39:52 +00:00
|
|
|
],
|
|
|
|
'Contains refs (which are broken)' => [
|
|
|
|
Parser::MARKER_PREFIX . '-ref- and ' . Parser::MARKER_PREFIX . '-notref-',
|
|
|
|
[],
|
|
|
|
1,
|
|
|
|
'',
|
|
|
|
false,
|
2019-12-09 16:08:57 +00:00
|
|
|
'references!' . "\n" . '(cite_error_references_no_key)'
|
2019-12-03 22:39:52 +00:00
|
|
|
],
|
|
|
|
];
|
|
|
|
}
|
|
|
|
|
2019-12-03 12:48:03 +00:00
|
|
|
/**
|
|
|
|
* @covers ::guardedRef
|
|
|
|
* @dataProvider provideGuardedRef
|
|
|
|
*/
|
|
|
|
public function testGuardedRef(
|
|
|
|
string $text,
|
|
|
|
array $argv,
|
|
|
|
?string $inReferencesGroup,
|
|
|
|
array $initialRefs,
|
|
|
|
string $expectOutput,
|
|
|
|
array $expectedErrors,
|
|
|
|
array $expectedRefs
|
|
|
|
) {
|
|
|
|
/** @var (array|false)[] $pushedRefs Jumble of raw arguments, to roughly emulate
|
|
|
|
* ReferenceStack.
|
|
|
|
*/
|
|
|
|
$pushedRefs = [];
|
|
|
|
|
|
|
|
$mockParser = $this->createMock( Parser::class );
|
|
|
|
$mockParser->method( 'getStripState' )
|
|
|
|
->willReturn( $this->createMock( StripState::class ) );
|
2019-12-06 12:09:28 +00:00
|
|
|
|
2019-12-09 16:08:57 +00:00
|
|
|
$mockErrorReporter = $this->createMock( ErrorReporter::class );
|
2019-12-03 12:48:03 +00:00
|
|
|
$mockErrorReporter->method( 'halfParsed' )->willReturnCallback(
|
2019-12-11 15:05:19 +00:00
|
|
|
function ( $parser, ...$args ) {
|
2019-12-09 16:08:57 +00:00
|
|
|
return '(' . implode( '|', $args ) . ')';
|
2019-12-03 12:48:03 +00:00
|
|
|
}
|
|
|
|
);
|
|
|
|
$mockErrorReporter->method( 'plain' )->willReturnCallback(
|
2019-12-11 15:05:19 +00:00
|
|
|
function ( $parser, ...$args ) {
|
2019-12-09 16:08:57 +00:00
|
|
|
return '(' . implode( '|', $args ) . ')';
|
2019-12-03 12:48:03 +00:00
|
|
|
}
|
|
|
|
);
|
2019-12-06 12:09:28 +00:00
|
|
|
|
2019-12-03 12:48:03 +00:00
|
|
|
$mockFootnoteMarkFormatter = $this->createMock( FootnoteMarkFormatter::class );
|
|
|
|
$mockFootnoteMarkFormatter->method( 'linkRef' )->willReturn( '<foot />' );
|
2019-12-06 12:09:28 +00:00
|
|
|
|
2019-12-11 13:58:25 +00:00
|
|
|
$cite = $this->newCite();
|
2019-12-06 12:09:28 +00:00
|
|
|
/** @var Cite $spy */
|
|
|
|
$spy = TestingAccessWrapper::newFromObject( $cite );
|
|
|
|
$spy->errorReporter = $mockErrorReporter;
|
2019-12-03 12:48:03 +00:00
|
|
|
$spy->footnoteMarkFormatter = $mockFootnoteMarkFormatter;
|
|
|
|
$spy->inReferencesGroup = $inReferencesGroup;
|
|
|
|
$spy->referenceStack = $this->createMock( ReferenceStack::class );
|
|
|
|
$spy->referenceStack->method( 'getGroupRefs' )->willReturnCallback(
|
|
|
|
function ( $group ) use ( $initialRefs ) {
|
|
|
|
return $initialRefs[$group];
|
|
|
|
} );
|
|
|
|
$spy->referenceStack->method( 'hasGroup' )->willReturn( true );
|
|
|
|
$spy->referenceStack->method( 'pushInvalidRef' )->willReturnCallback(
|
|
|
|
function () use ( &$pushedRefs ) {
|
|
|
|
$pushedRefs[] = false;
|
|
|
|
}
|
|
|
|
);
|
|
|
|
$spy->referenceStack->method( 'pushRef' )->willReturnCallback(
|
2019-12-19 09:16:14 +00:00
|
|
|
function ( Parser $parser, StripState $stripState, ...$arguments ) use ( &$pushedRefs ) {
|
|
|
|
$pushedRefs[] = $arguments;
|
|
|
|
return [ 'name' => $arguments[1] ];
|
2019-12-03 12:48:03 +00:00
|
|
|
}
|
|
|
|
);
|
2019-12-16 16:11:42 +00:00
|
|
|
$spy->referenceStack->method( 'appendText' )->willReturnCallback(
|
2019-12-03 12:48:03 +00:00
|
|
|
function ( $group, $name, $text ) use ( &$pushedRefs ) {
|
2019-12-16 16:11:42 +00:00
|
|
|
$pushedRefs[] = [ 'appendText', $group, $name, $text ];
|
2019-12-03 12:48:03 +00:00
|
|
|
}
|
|
|
|
);
|
|
|
|
|
2019-12-19 09:16:14 +00:00
|
|
|
$result = $spy->guardedRef( $mockParser, $text, $argv );
|
2019-12-03 12:48:03 +00:00
|
|
|
$this->assertSame( $expectOutput, $result );
|
|
|
|
$this->assertSame( $expectedErrors, $spy->mReferencesErrors );
|
|
|
|
$this->assertSame( $expectedRefs, $pushedRefs );
|
|
|
|
}
|
|
|
|
|
|
|
|
public function provideGuardedRef() {
|
|
|
|
return [
|
|
|
|
'Whitespace text' => [
|
|
|
|
' ',
|
|
|
|
[
|
|
|
|
'name' => 'a',
|
|
|
|
],
|
|
|
|
null,
|
|
|
|
[],
|
|
|
|
'<foot />',
|
|
|
|
[],
|
|
|
|
[
|
2019-12-19 09:16:14 +00:00
|
|
|
[ null, [ 'name' => 'a' ], '', 'a', null, null, null ]
|
2019-12-03 12:48:03 +00:00
|
|
|
]
|
|
|
|
],
|
|
|
|
'Empty in default references' => [
|
|
|
|
'',
|
|
|
|
[],
|
|
|
|
'',
|
|
|
|
[],
|
|
|
|
'',
|
2019-12-09 16:08:57 +00:00
|
|
|
[ '(cite_error_references_no_key)' ],
|
2019-12-03 12:48:03 +00:00
|
|
|
[]
|
|
|
|
],
|
|
|
|
'Fallback to references group' => [
|
|
|
|
'text',
|
|
|
|
[
|
|
|
|
'name' => 'a',
|
|
|
|
],
|
|
|
|
'foo',
|
|
|
|
[
|
|
|
|
'foo' => [
|
|
|
|
'a' => []
|
|
|
|
]
|
|
|
|
],
|
|
|
|
'',
|
|
|
|
[],
|
|
|
|
[
|
2019-12-16 16:11:42 +00:00
|
|
|
[ 'appendText', 'foo', 'a', 'text' ]
|
2019-12-03 12:48:03 +00:00
|
|
|
]
|
|
|
|
],
|
|
|
|
'Successful ref' => [
|
|
|
|
'text',
|
|
|
|
[
|
|
|
|
'name' => 'a',
|
|
|
|
],
|
|
|
|
null,
|
|
|
|
[],
|
|
|
|
'<foot />',
|
|
|
|
[],
|
|
|
|
[
|
2019-12-19 09:16:14 +00:00
|
|
|
[ 'text', [ 'name' => 'a' ], '', 'a', null, null, null ]
|
2019-12-03 12:48:03 +00:00
|
|
|
]
|
|
|
|
],
|
|
|
|
'Invalid ref' => [
|
|
|
|
'text',
|
|
|
|
[
|
|
|
|
'name' => 'a',
|
|
|
|
'badkey' => 'b',
|
|
|
|
],
|
|
|
|
null,
|
|
|
|
[],
|
2019-12-09 16:08:57 +00:00
|
|
|
'(cite_error_ref_too_many_keys)',
|
2019-12-03 12:48:03 +00:00
|
|
|
[],
|
|
|
|
[ false ]
|
|
|
|
],
|
|
|
|
'Successful references ref' => [
|
|
|
|
'text',
|
|
|
|
[
|
|
|
|
'name' => 'a',
|
|
|
|
],
|
|
|
|
'',
|
|
|
|
[
|
|
|
|
'' => [
|
|
|
|
'a' => []
|
|
|
|
]
|
|
|
|
],
|
|
|
|
'',
|
|
|
|
[],
|
|
|
|
[
|
2019-12-16 16:11:42 +00:00
|
|
|
[ 'appendText', '', 'a', 'text' ]
|
2019-12-03 12:48:03 +00:00
|
|
|
]
|
|
|
|
],
|
|
|
|
'Mismatched text in references' => [
|
|
|
|
'text-2',
|
|
|
|
[
|
|
|
|
'name' => 'a',
|
|
|
|
],
|
|
|
|
'',
|
|
|
|
[
|
|
|
|
'' => [
|
|
|
|
'a' => [
|
|
|
|
'text' => 'text-1',
|
|
|
|
]
|
|
|
|
]
|
|
|
|
],
|
|
|
|
'',
|
|
|
|
[],
|
|
|
|
[
|
2019-12-16 16:11:42 +00:00
|
|
|
[ 'appendText', '', 'a', ' (cite_error_references_duplicate_key|a)' ]
|
2019-12-03 12:48:03 +00:00
|
|
|
]
|
|
|
|
],
|
|
|
|
];
|
|
|
|
}
|
|
|
|
|
2019-12-03 12:39:02 +00:00
|
|
|
/**
|
|
|
|
* @covers ::guardedRef
|
|
|
|
*/
|
|
|
|
public function testGuardedRef_extendsProperty() {
|
|
|
|
$mockOutput = $this->createMock( ParserOutput::class );
|
|
|
|
// This will be our most important assertion.
|
|
|
|
$mockOutput->expects( $this->once() )
|
|
|
|
->method( 'setProperty' )
|
|
|
|
->with( Cite::BOOK_REF_PROPERTY, true );
|
|
|
|
|
|
|
|
$mockParser = $this->createMock( Parser::class );
|
|
|
|
$mockParser->method( 'getOutput' )->willReturn( $mockOutput );
|
|
|
|
$mockParser->method( 'getStripState' )
|
|
|
|
->willReturn( $this->createMock( StripState::class ) );
|
|
|
|
/** @var Parser $mockParser */
|
|
|
|
|
2019-12-11 13:58:25 +00:00
|
|
|
$cite = $this->newCite();
|
2019-12-06 12:09:28 +00:00
|
|
|
/** @var Cite $spy */
|
2019-12-03 12:39:02 +00:00
|
|
|
$spy = TestingAccessWrapper::newFromObject( $cite );
|
2019-12-09 16:08:57 +00:00
|
|
|
$spy->errorReporter = $this->createMock( ErrorReporter::class );
|
2019-12-03 12:39:02 +00:00
|
|
|
$spy->referenceStack = $this->createMock( ReferenceStack::class );
|
|
|
|
|
2019-12-19 09:16:14 +00:00
|
|
|
$spy->guardedRef( $mockParser, 'text', [ Cite::BOOK_REF_ATTRIBUTE => 'a' ] );
|
2019-12-03 12:39:02 +00:00
|
|
|
}
|
|
|
|
|
2019-12-11 11:19:18 +00:00
|
|
|
/**
|
|
|
|
* @covers ::__clone
|
2019-12-18 15:06:53 +00:00
|
|
|
* @covers ::__construct
|
2019-12-11 11:19:18 +00:00
|
|
|
*/
|
|
|
|
public function testClone() {
|
2019-12-11 13:58:25 +00:00
|
|
|
$original = $this->newCite();
|
2019-12-11 11:19:18 +00:00
|
|
|
/** @var Cite $spy */
|
|
|
|
$spy = TestingAccessWrapper::newFromObject( $original );
|
|
|
|
$spy->referenceStack = $this->createMock( ReferenceStack::class );
|
|
|
|
|
|
|
|
$clone = clone $original;
|
|
|
|
/** @var Cite $clone */
|
|
|
|
$clone = TestingAccessWrapper::newFromObject( $clone );
|
|
|
|
$this->assertNotSame( $clone->referenceStack, $spy->referenceStack );
|
|
|
|
}
|
|
|
|
|
2019-12-11 13:58:25 +00:00
|
|
|
private function newCite(): Cite {
|
|
|
|
$mockOptions = $this->createMock( ParserOptions::class );
|
|
|
|
$mockOptions->method( 'getIsPreview' )->willReturn( false );
|
|
|
|
$mockOptions->method( 'getIsSectionPreview' )->willReturn( false );
|
|
|
|
$mockOptions->method( 'getUserLangObj' )->willReturn(
|
|
|
|
$this->createMock( Language::class ) );
|
|
|
|
$mockParser = $this->createMock( Parser::class );
|
|
|
|
$mockParser->method( 'getOptions' )->willReturn( $mockOptions );
|
|
|
|
$mockParser->method( 'getContentLanguage' )->willReturn(
|
|
|
|
$this->createMock( Language::class ) );
|
|
|
|
/** @var Parser $mockParser */
|
|
|
|
return new Cite( $mockParser );
|
|
|
|
}
|
|
|
|
|
2019-11-08 14:46:52 +00:00
|
|
|
}
|