createNoOpMock( Parser::class, [ 'recursiveTagParse' ] ); $mockParser->method( 'recursiveTagParse' )->willReturnArgument( 0 ); $mockErrorReporter = $this->createMock( ErrorReporter::class ); $mockErrorReporter->method( 'plain' )->willReturnCallback( static fn ( $parser, ...$args ) => '(' . implode( '|', $args ) . ')' ); $mockMessageLocalizer = $this->createMock( ReferenceMessageLocalizer::class ); $mockMessageLocalizer->method( 'msg' )->willReturnCallback( function ( ...$args ) { $msg = $this->createMock( Message::class ); $msg->method( 'plain' )->willReturn( '
t\n
" ], 'Count one' => [ 'ref' => [ 'count' => 1, 'key' => 5, 'name' => 'a', 'text' => 't', ], 'expectedOutput' => '(cite_references_link_one|1-5|1+5-0|t' . "\n|)" ], 'Anonymous' => [ 'ref' => [ 'count' => 1, 'key' => 5, 'number' => 3, 'text' => 't', ], 'expectedOutput' => '(cite_references_link_one|5|5+|t' . "\n|)" ], 'Count many' => [ 'ref' => [ 'count' => 3, 'key' => 5, 'name' => 'a', 'number' => 3, 'text' => 't', ], 'expectedOutput' => '(cite_references_link_many|1-5|(cite_references_link_many_format|1+5-0|3.0|' . '(cite_references_link_many_format_backlink_labels))' . '(cite_references_link_many_sep)(cite_references_link_many_format|1+5-1|3.1|' . 'cite_error_references_no_backlink_label)(cite_references_link_many_and)' . '(cite_references_link_many_format|1+5-2|3.2|cite_error_references_no_backlink_label' . ')|t' . "\n|)" ], ]; } /** * @dataProvider provideReferenceText */ public function testReferenceText( ?string $text, bool $isSectionPreview, string $expectedOutput ) { $mockErrorReporter = $this->createMock( ErrorReporter::class ); $mockErrorReporter->method( 'plain' )->willReturnCallback( static fn ( $parser, ...$args ) => '(' . implode( '|', $args ) . ')' ); /** @var ReferenceListFormatter $formatter */ $formatter = TestingAccessWrapper::newFromObject( new ReferenceListFormatter( $mockErrorReporter, $this->createNoOpMock( AnchorFormatter::class ), $this->createNoOpMock( ReferenceMessageLocalizer::class ) ) ); $parser = $this->createNoOpMock( Parser::class ); $ref = TestUtils::refFromArray( [ 'text' => $text ] ); $output = $formatter->renderTextAndWarnings( $parser, 1, $ref, $isSectionPreview ); $this->assertSame( $expectedOutput, $output ); } public static function provideReferenceText() { return [ 'No text, not preview' => [ 'text' => null, 'isSectionPreview' => false, 'expectedOutput' => '(cite_error_references_no_text|1)' ], 'No text, is preview' => [ 'text' => null, 'isSectionPreview' => true, 'expectedOutput' => '(cite_warning_sectionpreview_no_text|1)' ], 'Has text' => [ 'text' => 'text', 'isSectionPreview' => true, 'expectedOutput' => 'text' . "\n" ], 'Trims text' => [ 'text' => "text\n\n", 'isSectionPreview' => true, 'expectedOutput' => 'text' . "\n" ], ]; } /** * @dataProvider provideReferencesFormatEntryAlternateBacklinkLabel */ public function testReferencesFormatEntryAlternateBacklinkLabel( ?string $expectedLabel, string $labelList, int $offset ) { $mockMessage = $this->createNoOpMock( Message::class, [ 'isDisabled', 'plain' ] ); $mockMessage->method( 'isDisabled' )->willReturn( !$labelList ); $mockMessage->method( 'plain' )->willReturn( $labelList ); $mockMessageLocalizer = $this->createMock( ReferenceMessageLocalizer::class ); $mockMessageLocalizer->method( 'msg' ) ->willReturn( $mockMessage ); $errorReporter = $this->createMock( ErrorReporter::class ); $errorReporter->method( 'plain' )->willReturnArgument( 1 ); /** @var ReferenceListFormatter $formatter */ $formatter = TestingAccessWrapper::newFromObject( new ReferenceListFormatter( $errorReporter, $this->createNoOpMock( AnchorFormatter::class ), $mockMessageLocalizer ) ); $parser = $this->createNoOpMock( Parser::class ); $label = $formatter->referencesFormatEntryAlternateBacklinkLabel( $parser, $offset ); $this->assertSame( $expectedLabel, $label ); } public static function provideReferencesFormatEntryAlternateBacklinkLabel() { yield [ 'aa', 'aa ab ac', 0 ]; yield [ 'ab', 'aa ab ac', 1 ]; yield [ 'å', 'å b c', 0 ]; yield [ 'cite_error_references_no_backlink_label', 'a b c', 10 ]; yield [ null, '', 0 ]; } /** * @dataProvider provideReferencesFormatEntryNumericBacklinkLabel */ public function testReferencesFormatEntryNumericBacklinkLabel( string $expectedLabel, string $base, int $offset, int $max ) { $mockMessageLocalizer = $this->createMock( ReferenceMessageLocalizer::class ); $mockMessageLocalizer->method( 'localizeSeparators' )->willReturn( ',' ); $mockMessageLocalizer->method( 'localizeDigits' )->willReturnArgument( 0 ); /** @var ReferenceListFormatter $formatter */ $formatter = TestingAccessWrapper::newFromObject( new ReferenceListFormatter( $this->createNoOpMock( ErrorReporter::class ), $this->createNoOpMock( AnchorFormatter::class ), $mockMessageLocalizer ) ); $label = $formatter->referencesFormatEntryNumericBacklinkLabel( $base, $offset, $max ); $this->assertSame( $expectedLabel, $label ); } public static function provideReferencesFormatEntryNumericBacklinkLabel() { yield [ '1,2', '1', 2, 9 ]; yield [ '1,02', '1', 2, 99 ]; yield [ '1,002', '1', 2, 100 ]; yield [ '1,50005', '1', 50005, 50005 ]; yield [ '2,1', '2', 1, 1 ]; yield [ '3.2,1', '3.2', 1, 1 ]; } /** * @dataProvider provideLists */ public function testListToText( array $list, $expected ) { $mockMessageLocalizer = $this->createMock( ReferenceMessageLocalizer::class ); $mockMessageLocalizer->method( 'msg' )->willReturnCallback( function ( ...$args ) { $msg = $this->createMock( Message::class ); $msg->method( 'plain' )->willReturn( '(' . implode( '|', $args ) . ')' ); return $msg; } ); /** @var ReferenceListFormatter $formatter */ $formatter = TestingAccessWrapper::newFromObject( new ReferenceListFormatter( $this->createNoOpMock( ErrorReporter::class ), $this->createNoOpMock( AnchorFormatter::class ), $mockMessageLocalizer ) ); $this->assertSame( $expected, $formatter->listToText( $list ) ); } public static function provideLists() { return [ [ 'list' => [], 'expected' => '' ], [ // This is intentionally using numbers to test the to-string cast 'list' => [ 1 ], 'expected' => '1' ], [ 'list' => [ 1, 2 ], 'expected' => '1(cite_references_link_many_and)2' ], [ 'list' => [ 1, 2, 3 ], 'expected' => '1(cite_references_link_many_sep)2(cite_references_link_many_and)3' ], ]; } }