1<?php 2 3namespace Test\Parsoid\Html2Wt; 4 5use DOMDocument; 6use DOMElement; 7use PHPUnit\Framework\MockObject\MockObject; 8use PHPUnit\Framework\TestCase; 9use Wikimedia\Parsoid\Core\SelserData; 10use Wikimedia\Parsoid\Html2Wt\SerializerState; 11use Wikimedia\Parsoid\Html2Wt\WikitextSerializer; 12use Wikimedia\Parsoid\Mocks\MockEnv; 13use Wikimedia\Zest\Zest; 14 15class SerializerStateTest extends TestCase { 16 17 /** 18 * A WikitextSerializer mock, with some basic methods mocked. 19 * @param array $extraMethodsToMock 20 * @return WikitextSerializer|MockObject 21 */ 22 private function getBaseSerializerMock( $extraMethodsToMock = [] ) { 23 $serializer = $this->getMockBuilder( WikitextSerializer::class ) 24 ->disableOriginalConstructor() 25 ->setMethods( array_merge( [ 'buildSep', 'trace' ], $extraMethodsToMock ) ) 26 ->getMock(); 27 $serializer->expects( $this->any() ) 28 ->method( 'buildSep' ) 29 ->willReturn( '' ); 30 $serializer->expects( $this->any() ) 31 ->method( 'trace' ) 32 ->willReturn( null ); 33 /** @var WikitextSerializer $serializer */ 34 $serializer->env = new MockEnv( [] ); 35 return $serializer; 36 } 37 38 private function getState( 39 array $options = [], MockEnv $env = null, WikitextSerializer $serializer = null 40 ) { 41 if ( !$env ) { 42 $env = new MockEnv( [] ); 43 } 44 if ( !$serializer ) { 45 $serializer = new WikitextSerializer( [ 'env' => $env ] ); 46 } 47 return new SerializerState( $serializer, $options ); 48 } 49 50 /** 51 * Create a DOM document with the given HTML body and return the given node within it. 52 * @param string $html 53 * @param string $selector 54 * @return DOMElement 55 */ 56 private function getNode( $html = '<div id="main"></div>', $selector = '#main' ) { 57 $document = new DOMDocument(); 58 $document->loadHTML( "<html><body>$html</body></html>" ); 59 return Zest::find( $selector, $document )[0]; 60 } 61 62 /** 63 * @covers \Wikimedia\Parsoid\Html2Wt\SerializerState::__construct 64 */ 65 public function testConstruct() { 66 $state = $this->getState(); 67 $this->assertTrue( $state->rtTestMode ); 68 $this->assertSame( [], $state->currLine->chunks ); 69 70 $state = $this->getState( [ 'rtTestMode' => false ] ); 71 $this->assertFalse( $state->rtTestMode ); 72 } 73 74 /** 75 * @covers \Wikimedia\Parsoid\Html2Wt\SerializerState::initMode 76 */ 77 public function testInitMode() { 78 $state = $this->getState(); 79 $state->initMode( true ); 80 $this->assertTrue( $state->selserMode ); 81 } 82 83 /** 84 * @covers \Wikimedia\Parsoid\Html2Wt\SerializerState::appendSep 85 */ 86 public function testAppendSep() { 87 $state = $this->getState(); 88 $state->appendSep( 'foo' ); 89 $state->appendSep( 'bar' ); 90 $this->assertSame( 'foobar', $state->sep->src ); 91 } 92 93 /** 94 * @covers \Wikimedia\Parsoid\Html2Wt\SerializerState::updateSep 95 */ 96 public function testUpdateSep() { 97 $state = $this->getState(); 98 $node = $this->getNode(); 99 $state->updateSep( $node ); 100 $this->assertSame( $node, $state->sep->lastSourceNode ); 101 } 102 103 /** 104 * @covers \Wikimedia\Parsoid\Html2Wt\SerializerState::getOrigSrc 105 */ 106 public function testGetOrigSrc() { 107 $env = new MockEnv( [] ); 108 $selserData = new SelserData( '0123456789' ); 109 $state = $this->getState( [ 110 'selserData' => $selserData, 111 ], $env ); 112 $state->initMode( true ); 113 $this->assertSame( '23', $state->getOrigSrc( 2, 4 ) ); 114 } 115 116 /** 117 * @covers \Wikimedia\Parsoid\Html2Wt\SerializerState::updateModificationFlags 118 */ 119 public function testUpdateModificationFlags() { 120 $state = $this->getState(); 121 $node = $this->getNode(); 122 $state->currNodeUnmodified = true; 123 $state->updateModificationFlags( $node ); 124 $this->assertFalse( $state->currNodeUnmodified ); 125 } 126 127 /** 128 * @covers \Wikimedia\Parsoid\Html2Wt\SerializerState::emitChunk 129 */ 130 public function testEmitChunk() { 131 $serializer = $this->getBaseSerializerMock(); 132 $serializer->env = new MockEnv( [] ); 133 $state = $this->getState( [], null, $serializer ); 134 $node = $this->getNode(); 135 $this->assertSame( '', $state->currLine->text ); 136 $state->emitChunk( 'foo', $node ); 137 $this->assertSame( 'foo', $state->currLine->text ); 138 $state->singleLineContext->enforce(); 139 $state->emitChunk( "\nfoo", $node ); 140 $this->assertSame( 'foo foo', $state->currLine->text ); 141 $state->singleLineContext->pop(); 142 $state->emitChunk( "\nfoo", $node ); 143 $this->assertSame( "foo foo\nfoo", $state->currLine->text ); 144 // FIXME this could be expanded a lot 145 } 146 147 /** 148 * @covers \Wikimedia\Parsoid\Html2Wt\SerializerState::serializeChildren 149 */ 150 public function testSerializeChildren() { 151 $node = $this->getNode(); 152 $serializer = $this->getBaseSerializerMock( [ 'serializeNode' ] ); 153 $serializer->expects( $this->never() ) 154 ->method( 'serializeNode' ); 155 $state = $this->getState( [], null, $serializer ); 156 $state->serializeChildren( $node ); 157 158 $node = $this->getNode( '<div id="main"><span></span><span></span></div>' ); 159 $serializer = $this->getBaseSerializerMock( [ 'serializeNode' ] ); 160 $serializer->expects( $this->exactly( 2 ) ) 161 ->method( 'serializeNode' ) 162 ->withConsecutive( 163 [ $node->firstChild ], 164 [ $node->firstChild->nextSibling ] 165 ) 166 ->willReturnCallback( function ( DOMElement $node ) { 167 return $node->nextSibling; 168 } ); 169 $state = $this->getState( [], null, $serializer ); 170 $state->serializeChildren( $node ); 171 172 $callback = function () { 173 }; 174 $node = $this->getNode( '<div id="main"><span></span></div>' ); 175 $serializer = $this->getBaseSerializerMock( [ 'serializeNode' ] ); 176 $serializer->expects( $this->once() ) 177 ->method( 'serializeNode' ) 178 ->with( $node->firstChild ) 179 ->willReturnCallback( function ( DOMElement $node ) use ( &$state, $callback ) { 180 $this->assertSame( $callback, end( $state->wteHandlerStack ) ); 181 return $node->nextSibling; 182 } ); 183 $state = $this->getState( [], null, $serializer ); 184 $this->assertEmpty( $state->wteHandlerStack ); 185 $state->serializeChildren( $node, $callback ); 186 $this->assertEmpty( $state->wteHandlerStack ); 187 } 188 189 /** 190 * @covers \Wikimedia\Parsoid\Html2Wt\SerializerState::kickOffSerialize 191 * @covers \Wikimedia\Parsoid\Html2Wt\SerializerState::serializeLinkChildrenToString 192 * @covers \Wikimedia\Parsoid\Html2Wt\SerializerState::serializeCaptionChildrenToString 193 * @covers \Wikimedia\Parsoid\Html2Wt\SerializerState::serializeIndentPreChildrenToString 194 * @dataProvider provideSerializeChildrenToString 195 */ 196 public function testSerializeChildrenToString( $method ) { 197 $serializer = $this->getBaseSerializerMock( [ 'serializeNode' ] ); 198 $serializer->expects( $this->never() ) 199 ->method( 'serializeNode' ); 200 $state = $this->getState( [], null, $serializer ); 201 $node = $this->getNode(); 202 $callback = function () { 203 }; 204 $state->$method( $node, $callback ); 205 } 206 207 public function provideSerializeChildrenToString() { 208 return [ 209 [ 'kickOffSerialize' ], 210 [ 'serializeLinkChildrenToString' ], 211 [ 'serializeCaptionChildrenToString' ], 212 [ 'serializeIndentPreChildrenToString' ], 213 ]; 214 } 215 216} 217