9e18eb0985afc694a6d0250a861821c14a3443ab
[lhc/web/wiklou.git] / tests / phpunit / includes / api / ApiComparePagesTest.php
1 <?php
2
3 /**
4 * @group API
5 * @group Database
6 * @group medium
7 * @covers ApiComparePages
8 */
9 class ApiComparePagesTest extends ApiTestCase {
10
11 protected static $repl = [];
12
13 protected function setUp() {
14 parent::setUp();
15
16 // Set $wgExternalDiffEngine to something bogus to try to force use of
17 // the PHP engine rather than wikidiff2.
18 $this->setMwGlobals( [
19 'wgExternalDiffEngine' => '/dev/null',
20 ] );
21 }
22
23 protected function addPage( $page, $text, $model = CONTENT_MODEL_WIKITEXT ) {
24 $title = Title::newFromText( 'ApiComparePagesTest ' . $page );
25 $content = ContentHandler::makeContent( $text, $title, $model );
26
27 $page = WikiPage::factory( $title );
28 $user = static::getTestSysop()->getUser();
29 $status = $page->doEditContent(
30 $content, 'Test for ApiComparePagesTest: ' . $text, 0, false, $user
31 );
32 if ( !$status->isOK() ) {
33 $this->fail( "Failed to create $title: " . $status->getWikiText( false, false, 'en' ) );
34 }
35 return $status->value['revision']->getId();
36 }
37
38 public function addDBDataOnce() {
39 $user = static::getTestSysop()->getUser();
40 self::$repl['creator'] = $user->getName();
41 self::$repl['creatorid'] = $user->getId();
42
43 self::$repl['revA1'] = $this->addPage( 'A', 'A 1' );
44 self::$repl['revA2'] = $this->addPage( 'A', 'A 2' );
45 self::$repl['revA3'] = $this->addPage( 'A', 'A 3' );
46 self::$repl['revA4'] = $this->addPage( 'A', 'A 4' );
47 self::$repl['pageA'] = Title::newFromText( 'ApiComparePagesTest A' )->getArticleID();
48
49 self::$repl['revB1'] = $this->addPage( 'B', 'B 1' );
50 self::$repl['revB2'] = $this->addPage( 'B', 'B 2' );
51 self::$repl['revB3'] = $this->addPage( 'B', 'B 3' );
52 self::$repl['revB4'] = $this->addPage( 'B', 'B 4' );
53 self::$repl['pageB'] = Title::newFromText( 'ApiComparePagesTest B' )->getArticleID();
54
55 self::$repl['revC1'] = $this->addPage( 'C', 'C 1' );
56 self::$repl['revC2'] = $this->addPage( 'C', 'C 2' );
57 self::$repl['revC3'] = $this->addPage( 'C', 'C 3' );
58 self::$repl['pageC'] = Title::newFromText( 'ApiComparePagesTest C' )->getArticleID();
59
60 $id = $this->addPage( 'D', 'D 1' );
61 self::$repl['pageD'] = Title::newFromText( 'ApiComparePagesTest D' )->getArticleID();
62 wfGetDB( DB_MASTER )->delete( 'revision', [ 'rev_id' => $id ] );
63
64 self::$repl['revE1'] = $this->addPage( 'E', 'E 1' );
65 self::$repl['revE2'] = $this->addPage( 'E', 'E 2' );
66 self::$repl['revE3'] = $this->addPage( 'E', 'E 3' );
67 self::$repl['revE4'] = $this->addPage( 'E', 'E 4' );
68 self::$repl['pageE'] = Title::newFromText( 'ApiComparePagesTest E' )->getArticleID();
69 wfGetDB( DB_MASTER )->update(
70 'page', [ 'page_latest' => 0 ], [ 'page_id' => self::$repl['pageE'] ]
71 );
72
73 self::$repl['revF1'] = $this->addPage( 'F', "== Section 1 ==\nF 1.1\n\n== Section 2 ==\nF 1.2" );
74 self::$repl['pageF'] = Title::newFromText( 'ApiComparePagesTest F' )->getArticleID();
75
76 self::$repl['revG1'] = $this->addPage( 'G', "== Section 1 ==\nG 1.1", CONTENT_MODEL_TEXT );
77 self::$repl['pageG'] = Title::newFromText( 'ApiComparePagesTest G' )->getArticleID();
78
79 WikiPage::factory( Title::newFromText( 'ApiComparePagesTest C' ) )
80 ->doDeleteArticleReal( 'Test for ApiComparePagesTest' );
81
82 RevisionDeleter::createList(
83 'revision',
84 RequestContext::getMain(),
85 Title::newFromText( 'ApiComparePagesTest B' ),
86 [ self::$repl['revB2'] ]
87 )->setVisibility( [
88 'value' => [
89 Revision::DELETED_TEXT => 1,
90 Revision::DELETED_USER => 1,
91 Revision::DELETED_COMMENT => 1,
92 ],
93 'comment' => 'Test for ApiComparePages',
94 ] );
95
96 RevisionDeleter::createList(
97 'revision',
98 RequestContext::getMain(),
99 Title::newFromText( 'ApiComparePagesTest B' ),
100 [ self::$repl['revB3'] ]
101 )->setVisibility( [
102 'value' => [
103 Revision::DELETED_USER => 1,
104 Revision::DELETED_COMMENT => 1,
105 Revision::DELETED_RESTRICTED => 1,
106 ],
107 'comment' => 'Test for ApiComparePages',
108 ] );
109
110 Title::clearCaches(); // Otherwise it has the wrong latest revision for some reason
111 }
112
113 protected function doReplacements( &$value ) {
114 if ( is_string( $value ) ) {
115 if ( preg_match( '/^{{REPL:(.+?)}}$/', $value, $m ) ) {
116 $value = self::$repl[$m[1]];
117 } else {
118 $value = preg_replace_callback( '/{{REPL:(.+?)}}/', function ( $m ) {
119 return self::$repl[$m[1]] ?? $m[0];
120 }, $value );
121 }
122 } elseif ( is_array( $value ) || is_object( $value ) ) {
123 foreach ( $value as &$v ) {
124 $this->doReplacements( $v );
125 }
126 unset( $v );
127 }
128 }
129
130 /**
131 * @dataProvider provideDiff
132 */
133 public function testDiff( $params, $expect, $exceptionCode = false, $sysop = false ) {
134 $this->doReplacements( $params );
135
136 $params += [
137 'action' => 'compare',
138 'errorformat' => 'none',
139 ];
140
141 $user = $sysop
142 ? static::getTestSysop()->getUser()
143 : static::getTestUser()->getUser();
144 if ( $exceptionCode ) {
145 try {
146 $this->doApiRequest( $params, null, false, $user );
147 $this->fail( 'Expected exception not thrown' );
148 } catch ( ApiUsageException $ex ) {
149 $this->assertTrue( $this->apiExceptionHasCode( $ex, $exceptionCode ),
150 "Exception with code $exceptionCode" );
151 }
152 } else {
153 $apiResult = $this->doApiRequest( $params, null, false, $user );
154 $apiResult = $apiResult[0];
155 $this->doReplacements( $expect );
156 $this->assertEquals( $expect, $apiResult );
157 }
158 }
159
160 private static function makeDeprecationWarnings( ...$params ) {
161 $warn = [];
162 foreach ( $params as $p ) {
163 $warn[] = [
164 'code' => 'deprecation',
165 'data' => [ 'feature' => "action=compare&{$p}" ],
166 'module' => 'compare',
167 ];
168 if ( count( $warn ) === 1 ) {
169 $warn[] = [
170 'code' => 'deprecation-help',
171 'module' => 'main',
172 ];
173 }
174 }
175
176 return $warn;
177 }
178
179 public static function provideDiff() {
180 // phpcs:disable Generic.Files.LineLength.TooLong
181 return [
182 'Basic diff, titles' => [
183 [
184 'fromtitle' => 'ApiComparePagesTest A',
185 'totitle' => 'ApiComparePagesTest B',
186 ],
187 [
188 'compare' => [
189 'fromid' => '{{REPL:pageA}}',
190 'fromrevid' => '{{REPL:revA4}}',
191 'fromns' => 0,
192 'fromtitle' => 'ApiComparePagesTest A',
193 'toid' => '{{REPL:pageB}}',
194 'torevid' => '{{REPL:revB4}}',
195 'tons' => 0,
196 'totitle' => 'ApiComparePagesTest B',
197 'body' => '<tr><td colspan="2" class="diff-lineno" id="mw-diff-left-l1" >Line 1:</td>' . "\n"
198 . '<td colspan="2" class="diff-lineno">Line 1:</td></tr>' . "\n"
199 . '<tr><td class=\'diff-marker\'>−</td><td class=\'diff-deletedline\'><div><del class="diffchange diffchange-inline">A </del>4</div></td><td class=\'diff-marker\'>+</td><td class=\'diff-addedline\'><div><ins class="diffchange diffchange-inline">B </ins>4</div></td></tr>' . "\n",
200 ]
201 ],
202 ],
203 'Basic diff, page IDs' => [
204 [
205 'fromid' => '{{REPL:pageA}}',
206 'toid' => '{{REPL:pageB}}',
207 ],
208 [
209 'compare' => [
210 'fromid' => '{{REPL:pageA}}',
211 'fromrevid' => '{{REPL:revA4}}',
212 'fromns' => 0,
213 'fromtitle' => 'ApiComparePagesTest A',
214 'toid' => '{{REPL:pageB}}',
215 'torevid' => '{{REPL:revB4}}',
216 'tons' => 0,
217 'totitle' => 'ApiComparePagesTest B',
218 'body' => '<tr><td colspan="2" class="diff-lineno" id="mw-diff-left-l1" >Line 1:</td>' . "\n"
219 . '<td colspan="2" class="diff-lineno">Line 1:</td></tr>' . "\n"
220 . '<tr><td class=\'diff-marker\'>−</td><td class=\'diff-deletedline\'><div><del class="diffchange diffchange-inline">A </del>4</div></td><td class=\'diff-marker\'>+</td><td class=\'diff-addedline\'><div><ins class="diffchange diffchange-inline">B </ins>4</div></td></tr>' . "\n",
221 ]
222 ],
223 ],
224 'Basic diff, revision IDs' => [
225 [
226 'fromrev' => '{{REPL:revA2}}',
227 'torev' => '{{REPL:revA3}}',
228 ],
229 [
230 'compare' => [
231 'fromid' => '{{REPL:pageA}}',
232 'fromrevid' => '{{REPL:revA2}}',
233 'fromns' => 0,
234 'fromtitle' => 'ApiComparePagesTest A',
235 'toid' => '{{REPL:pageA}}',
236 'torevid' => '{{REPL:revA3}}',
237 'tons' => 0,
238 'totitle' => 'ApiComparePagesTest A',
239 'body' => '<tr><td colspan="2" class="diff-lineno" id="mw-diff-left-l1" >Line 1:</td>' . "\n"
240 . '<td colspan="2" class="diff-lineno">Line 1:</td></tr>' . "\n"
241 . '<tr><td class=\'diff-marker\'>−</td><td class=\'diff-deletedline\'><div>A <del class="diffchange diffchange-inline">2</del></div></td><td class=\'diff-marker\'>+</td><td class=\'diff-addedline\'><div>A <ins class="diffchange diffchange-inline">3</ins></div></td></tr>' . "\n",
242 ]
243 ],
244 ],
245 'Basic diff, deleted revision ID as sysop' => [
246 [
247 'fromrev' => '{{REPL:revA2}}',
248 'torev' => '{{REPL:revC2}}',
249 ],
250 [
251 'compare' => [
252 'fromid' => '{{REPL:pageA}}',
253 'fromrevid' => '{{REPL:revA2}}',
254 'fromns' => 0,
255 'fromtitle' => 'ApiComparePagesTest A',
256 'toid' => 0,
257 'torevid' => '{{REPL:revC2}}',
258 'tons' => 0,
259 'totitle' => 'ApiComparePagesTest C',
260 'toarchive' => true,
261 'body' => '<tr><td colspan="2" class="diff-lineno" id="mw-diff-left-l1" >Line 1:</td>' . "\n"
262 . '<td colspan="2" class="diff-lineno">Line 1:</td></tr>' . "\n"
263 . '<tr><td class=\'diff-marker\'>−</td><td class=\'diff-deletedline\'><div><del class="diffchange diffchange-inline">A </del>2</div></td><td class=\'diff-marker\'>+</td><td class=\'diff-addedline\'><div><ins class="diffchange diffchange-inline">C </ins>2</div></td></tr>' . "\n",
264 ]
265 ],
266 false, true
267 ],
268 'Basic diff, revdel as sysop' => [
269 [
270 'fromrev' => '{{REPL:revA2}}',
271 'torev' => '{{REPL:revB2}}',
272 ],
273 [
274 'compare' => [
275 'fromid' => '{{REPL:pageA}}',
276 'fromrevid' => '{{REPL:revA2}}',
277 'fromns' => 0,
278 'fromtitle' => 'ApiComparePagesTest A',
279 'toid' => '{{REPL:pageB}}',
280 'torevid' => '{{REPL:revB2}}',
281 'tons' => 0,
282 'totitle' => 'ApiComparePagesTest B',
283 'totexthidden' => true,
284 'touserhidden' => true,
285 'tocommenthidden' => true,
286 'body' => '<tr><td colspan="2" class="diff-lineno" id="mw-diff-left-l1" >Line 1:</td>' . "\n"
287 . '<td colspan="2" class="diff-lineno">Line 1:</td></tr>' . "\n"
288 . '<tr><td class=\'diff-marker\'>−</td><td class=\'diff-deletedline\'><div><del class="diffchange diffchange-inline">A </del>2</div></td><td class=\'diff-marker\'>+</td><td class=\'diff-addedline\'><div><ins class="diffchange diffchange-inline">B </ins>2</div></td></tr>' . "\n",
289 ]
290 ],
291 false, true
292 ],
293 'Basic diff, text' => [
294 [
295 'fromslots' => 'main',
296 'fromtext-main' => 'From text',
297 'fromcontentmodel-main' => 'wikitext',
298 'toslots' => 'main',
299 'totext-main' => 'To text {{subst:PAGENAME}}',
300 'tocontentmodel-main' => 'wikitext',
301 ],
302 [
303 'compare' => [
304 'body' => '<tr><td colspan="2" class="diff-lineno" id="mw-diff-left-l1" >Line 1:</td>' . "\n"
305 . '<td colspan="2" class="diff-lineno">Line 1:</td></tr>' . "\n"
306 . '<tr><td class=\'diff-marker\'>−</td><td class=\'diff-deletedline\'><div><del class="diffchange diffchange-inline">From </del>text</div></td><td class=\'diff-marker\'>+</td><td class=\'diff-addedline\'><div><ins class="diffchange diffchange-inline">To </ins>text <ins class="diffchange diffchange-inline">{{subst:PAGENAME}}</ins></div></td></tr>' . "\n",
307 ]
308 ],
309 ],
310 'Basic diff, text 2' => [
311 [
312 'fromslots' => 'main',
313 'fromtext-main' => 'From text',
314 'toslots' => 'main',
315 'totext-main' => 'To text {{subst:PAGENAME}}',
316 'tocontentmodel-main' => 'wikitext',
317 ],
318 [
319 'compare' => [
320 'body' => '<tr><td colspan="2" class="diff-lineno" id="mw-diff-left-l1" >Line 1:</td>' . "\n"
321 . '<td colspan="2" class="diff-lineno">Line 1:</td></tr>' . "\n"
322 . '<tr><td class=\'diff-marker\'>−</td><td class=\'diff-deletedline\'><div><del class="diffchange diffchange-inline">From </del>text</div></td><td class=\'diff-marker\'>+</td><td class=\'diff-addedline\'><div><ins class="diffchange diffchange-inline">To </ins>text <ins class="diffchange diffchange-inline">{{subst:PAGENAME}}</ins></div></td></tr>' . "\n",
323 ]
324 ],
325 ],
326 'Basic diff, guessed model' => [
327 [
328 'fromslots' => 'main',
329 'fromtext-main' => 'From text',
330 'toslots' => 'main',
331 'totext-main' => 'To text',
332 ],
333 [
334 'warnings' => [ [ 'code' => 'compare-nocontentmodel', 'module' => 'compare' ] ],
335 'compare' => [
336 'body' => '<tr><td colspan="2" class="diff-lineno" id="mw-diff-left-l1" >Line 1:</td>' . "\n"
337 . '<td colspan="2" class="diff-lineno">Line 1:</td></tr>' . "\n"
338 . '<tr><td class=\'diff-marker\'>−</td><td class=\'diff-deletedline\'><div><del class="diffchange diffchange-inline">From </del>text</div></td><td class=\'diff-marker\'>+</td><td class=\'diff-addedline\'><div><ins class="diffchange diffchange-inline">To </ins>text</div></td></tr>' . "\n",
339 ]
340 ],
341 ],
342 'Basic diff, text with title and PST' => [
343 [
344 'fromslots' => 'main',
345 'fromtext-main' => 'From text',
346 'totitle' => 'Test',
347 'toslots' => 'main',
348 'totext-main' => 'To text {{subst:PAGENAME}}',
349 'topst' => true,
350 ],
351 [
352 'compare' => [
353 'body' => '<tr><td colspan="2" class="diff-lineno" id="mw-diff-left-l1" >Line 1:</td>' . "\n"
354 . '<td colspan="2" class="diff-lineno">Line 1:</td></tr>' . "\n"
355 . '<tr><td class=\'diff-marker\'>−</td><td class=\'diff-deletedline\'><div><del class="diffchange diffchange-inline">From </del>text</div></td><td class=\'diff-marker\'>+</td><td class=\'diff-addedline\'><div><ins class="diffchange diffchange-inline">To </ins>text <ins class="diffchange diffchange-inline">Test</ins></div></td></tr>' . "\n",
356 ]
357 ],
358 ],
359 'Basic diff, text with page ID and PST' => [
360 [
361 'fromslots' => 'main',
362 'fromtext-main' => 'From text',
363 'toid' => '{{REPL:pageB}}',
364 'toslots' => 'main',
365 'totext-main' => 'To text {{subst:PAGENAME}}',
366 'topst' => true,
367 ],
368 [
369 'compare' => [
370 'body' => '<tr><td colspan="2" class="diff-lineno" id="mw-diff-left-l1" >Line 1:</td>' . "\n"
371 . '<td colspan="2" class="diff-lineno">Line 1:</td></tr>' . "\n"
372 . '<tr><td class=\'diff-marker\'>−</td><td class=\'diff-deletedline\'><div><del class="diffchange diffchange-inline">From </del>text</div></td><td class=\'diff-marker\'>+</td><td class=\'diff-addedline\'><div><ins class="diffchange diffchange-inline">To </ins>text <ins class="diffchange diffchange-inline">ApiComparePagesTest B</ins></div></td></tr>' . "\n",
373 ]
374 ],
375 ],
376 'Basic diff, text with revision and PST' => [
377 [
378 'fromslots' => 'main',
379 'fromtext-main' => 'From text',
380 'torev' => '{{REPL:revB2}}',
381 'toslots' => 'main',
382 'totext-main' => 'To text {{subst:PAGENAME}}',
383 'topst' => true,
384 ],
385 [
386 'compare' => [
387 'body' => '<tr><td colspan="2" class="diff-lineno" id="mw-diff-left-l1" >Line 1:</td>' . "\n"
388 . '<td colspan="2" class="diff-lineno">Line 1:</td></tr>' . "\n"
389 . '<tr><td class=\'diff-marker\'>−</td><td class=\'diff-deletedline\'><div><del class="diffchange diffchange-inline">From </del>text</div></td><td class=\'diff-marker\'>+</td><td class=\'diff-addedline\'><div><ins class="diffchange diffchange-inline">To </ins>text <ins class="diffchange diffchange-inline">ApiComparePagesTest B</ins></div></td></tr>' . "\n",
390 ]
391 ],
392 ],
393 'Basic diff, text with deleted revision and PST' => [
394 [
395 'fromslots' => 'main',
396 'fromtext-main' => 'From text',
397 'torev' => '{{REPL:revC2}}',
398 'toslots' => 'main',
399 'totext-main' => 'To text {{subst:PAGENAME}}',
400 'topst' => true,
401 ],
402 [
403 'compare' => [
404 'body' => '<tr><td colspan="2" class="diff-lineno" id="mw-diff-left-l1" >Line 1:</td>' . "\n"
405 . '<td colspan="2" class="diff-lineno">Line 1:</td></tr>' . "\n"
406 . '<tr><td class=\'diff-marker\'>−</td><td class=\'diff-deletedline\'><div><del class="diffchange diffchange-inline">From </del>text</div></td><td class=\'diff-marker\'>+</td><td class=\'diff-addedline\'><div><ins class="diffchange diffchange-inline">To </ins>text <ins class="diffchange diffchange-inline">ApiComparePagesTest C</ins></div></td></tr>' . "\n",
407 ]
408 ],
409 false, true
410 ],
411 'Basic diff, test with sections' => [
412 [
413 'fromtitle' => 'ApiComparePagesTest F',
414 'fromslots' => 'main',
415 'fromtext-main' => "== Section 2 ==\nFrom text?",
416 'fromsection-main' => 2,
417 'totitle' => 'ApiComparePagesTest F',
418 'toslots' => 'main',
419 'totext-main' => "== Section 1 ==\nTo text?",
420 'tosection-main' => 1,
421 ],
422 [
423 'compare' => [
424 'body' => '<tr><td colspan="2" class="diff-lineno" id="mw-diff-left-l1" >Line 1:</td>' . "\n"
425 . '<td colspan="2" class="diff-lineno">Line 1:</td></tr>' . "\n"
426 . '<tr><td class=\'diff-marker\'> </td><td class=\'diff-context\'><div>== Section 1 ==</div></td><td class=\'diff-marker\'> </td><td class=\'diff-context\'><div>== Section 1 ==</div></td></tr>' . "\n"
427 . '<tr><td class=\'diff-marker\'>−</td><td class=\'diff-deletedline\'><div><del class="diffchange diffchange-inline">F 1.1</del></div></td><td class=\'diff-marker\'>+</td><td class=\'diff-addedline\'><div><ins class="diffchange diffchange-inline">To text?</ins></div></td></tr>' . "\n"
428 . '<tr><td class=\'diff-marker\'> </td><td class=\'diff-context\'></td><td class=\'diff-marker\'> </td><td class=\'diff-context\'></td></tr>' . "\n"
429 . '<tr><td class=\'diff-marker\'> </td><td class=\'diff-context\'><div>== Section 2 ==</div></td><td class=\'diff-marker\'> </td><td class=\'diff-context\'><div>== Section 2 ==</div></td></tr>' . "\n"
430 . '<tr><td class=\'diff-marker\'>−</td><td class=\'diff-deletedline\'><div><del class="diffchange diffchange-inline">From text?</del></div></td><td class=\'diff-marker\'>+</td><td class=\'diff-addedline\'><div><ins class="diffchange diffchange-inline">F 1.2</ins></div></td></tr>' . "\n",
431 ]
432 ],
433 ],
434 'Diff with all props' => [
435 [
436 'fromrev' => '{{REPL:revB1}}',
437 'torev' => '{{REPL:revB3}}',
438 'totitle' => 'ApiComparePagesTest B',
439 'prop' => 'diff|diffsize|rel|ids|title|user|comment|parsedcomment|size'
440 ],
441 [
442 'compare' => [
443 'fromid' => '{{REPL:pageB}}',
444 'fromrevid' => '{{REPL:revB1}}',
445 'fromns' => 0,
446 'fromtitle' => 'ApiComparePagesTest B',
447 'fromsize' => 3,
448 'fromuser' => '{{REPL:creator}}',
449 'fromuserid' => '{{REPL:creatorid}}',
450 'fromcomment' => 'Test for ApiComparePagesTest: B 1',
451 'fromparsedcomment' => 'Test for ApiComparePagesTest: B 1',
452 'toid' => '{{REPL:pageB}}',
453 'torevid' => '{{REPL:revB3}}',
454 'tons' => 0,
455 'totitle' => 'ApiComparePagesTest B',
456 'tosize' => 3,
457 'touserhidden' => true,
458 'tocommenthidden' => true,
459 'tosuppressed' => true,
460 'next' => '{{REPL:revB4}}',
461 'diffsize' => 391,
462 'body' => '<tr><td colspan="2" class="diff-lineno" id="mw-diff-left-l1" >Line 1:</td>' . "\n"
463 . '<td colspan="2" class="diff-lineno">Line 1:</td></tr>' . "\n"
464 . '<tr><td class=\'diff-marker\'>−</td><td class=\'diff-deletedline\'><div>B <del class="diffchange diffchange-inline">1</del></div></td><td class=\'diff-marker\'>+</td><td class=\'diff-addedline\'><div>B <ins class="diffchange diffchange-inline">3</ins></div></td></tr>' . "\n",
465 ]
466 ],
467 ],
468 'Diff with all props as sysop' => [
469 [
470 'fromrev' => '{{REPL:revB2}}',
471 'torev' => '{{REPL:revB3}}',
472 'totitle' => 'ApiComparePagesTest B',
473 'prop' => 'diff|diffsize|rel|ids|title|user|comment|parsedcomment|size'
474 ],
475 [
476 'compare' => [
477 'fromid' => '{{REPL:pageB}}',
478 'fromrevid' => '{{REPL:revB2}}',
479 'fromns' => 0,
480 'fromtitle' => 'ApiComparePagesTest B',
481 'fromsize' => 3,
482 'fromtexthidden' => true,
483 'fromuserhidden' => true,
484 'fromuser' => '{{REPL:creator}}',
485 'fromuserid' => '{{REPL:creatorid}}',
486 'fromcommenthidden' => true,
487 'fromcomment' => 'Test for ApiComparePagesTest: B 2',
488 'fromparsedcomment' => 'Test for ApiComparePagesTest: B 2',
489 'toid' => '{{REPL:pageB}}',
490 'torevid' => '{{REPL:revB3}}',
491 'tons' => 0,
492 'totitle' => 'ApiComparePagesTest B',
493 'tosize' => 3,
494 'touserhidden' => true,
495 'tocommenthidden' => true,
496 'tosuppressed' => true,
497 'prev' => '{{REPL:revB1}}',
498 'next' => '{{REPL:revB4}}',
499 'diffsize' => 391,
500 'body' => '<tr><td colspan="2" class="diff-lineno" id="mw-diff-left-l1" >Line 1:</td>' . "\n"
501 . '<td colspan="2" class="diff-lineno">Line 1:</td></tr>' . "\n"
502 . '<tr><td class=\'diff-marker\'>−</td><td class=\'diff-deletedline\'><div>B <del class="diffchange diffchange-inline">2</del></div></td><td class=\'diff-marker\'>+</td><td class=\'diff-addedline\'><div>B <ins class="diffchange diffchange-inline">3</ins></div></td></tr>' . "\n",
503 ]
504 ],
505 false, true
506 ],
507 'Relative diff, cur' => [
508 [
509 'fromrev' => '{{REPL:revA2}}',
510 'torelative' => 'cur',
511 'prop' => 'ids',
512 ],
513 [
514 'compare' => [
515 'fromid' => '{{REPL:pageA}}',
516 'fromrevid' => '{{REPL:revA2}}',
517 'toid' => '{{REPL:pageA}}',
518 'torevid' => '{{REPL:revA4}}',
519 ]
520 ],
521 ],
522 'Relative diff, next' => [
523 [
524 'fromrev' => '{{REPL:revE2}}',
525 'torelative' => 'next',
526 'prop' => 'ids|rel',
527 ],
528 [
529 'compare' => [
530 'fromid' => '{{REPL:pageE}}',
531 'fromrevid' => '{{REPL:revE2}}',
532 'toid' => '{{REPL:pageE}}',
533 'torevid' => '{{REPL:revE3}}',
534 'prev' => '{{REPL:revE1}}',
535 'next' => '{{REPL:revE4}}',
536 ]
537 ],
538 ],
539 'Relative diff, prev' => [
540 [
541 'fromrev' => '{{REPL:revE3}}',
542 'torelative' => 'prev',
543 'prop' => 'ids|rel',
544 ],
545 [
546 'compare' => [
547 'fromid' => '{{REPL:pageE}}',
548 'fromrevid' => '{{REPL:revE2}}',
549 'toid' => '{{REPL:pageE}}',
550 'torevid' => '{{REPL:revE3}}',
551 'prev' => '{{REPL:revE1}}',
552 'next' => '{{REPL:revE4}}',
553 ]
554 ],
555 ],
556 'Relative diff, no prev' => [
557 [
558 'fromrev' => '{{REPL:revA1}}',
559 'torelative' => 'prev',
560 'prop' => 'ids|rel|diff|title|user|comment',
561 ],
562 [
563 'warnings' => [
564 [
565 'code' => 'compare-no-prev',
566 'module' => 'compare',
567 ],
568 ],
569 'compare' => [
570 'toid' => '{{REPL:pageA}}',
571 'torevid' => '{{REPL:revA1}}',
572 'tons' => 0,
573 'totitle' => 'ApiComparePagesTest A',
574 'touser' => '{{REPL:creator}}',
575 'touserid' => '{{REPL:creatorid}}',
576 'tocomment' => 'Test for ApiComparePagesTest: A 1',
577 'toparsedcomment' => 'Test for ApiComparePagesTest: A 1',
578 'next' => '{{REPL:revA2}}',
579 'body' => '<tr><td colspan="2" class="diff-lineno" id="mw-diff-left-l1" >Line 1:</td>' . "\n"
580 . '<td colspan="2" class="diff-lineno">Line 1:</td></tr>' . "\n"
581 . '<tr><td class=\'diff-marker\'>−</td><td class=\'diff-deletedline\'><div> </div></td><td class=\'diff-marker\'>+</td><td class=\'diff-addedline\'><div><ins class="diffchange diffchange-inline">A 1</ins></div></td></tr>' . "\n",
582 ],
583 ],
584 ],
585 'Relative diff, no next' => [
586 [
587 'fromrev' => '{{REPL:revA4}}',
588 'torelative' => 'next',
589 'prop' => 'ids|rel|diff|title|user|comment',
590 ],
591 [
592 'warnings' => [
593 [
594 'code' => 'compare-no-next',
595 'module' => 'compare',
596 ],
597 ],
598 'compare' => [
599 'fromid' => '{{REPL:pageA}}',
600 'fromrevid' => '{{REPL:revA4}}',
601 'fromns' => 0,
602 'fromtitle' => 'ApiComparePagesTest A',
603 'fromuser' => '{{REPL:creator}}',
604 'fromuserid' => '{{REPL:creatorid}}',
605 'fromcomment' => 'Test for ApiComparePagesTest: A 4',
606 'fromparsedcomment' => 'Test for ApiComparePagesTest: A 4',
607 'prev' => '{{REPL:revA3}}',
608 'body' => '',
609 ],
610 ],
611 ],
612 'Diff for specific slots' => [
613 // @todo Use a page with multiple slots here
614 [
615 'fromrev' => '{{REPL:revA1}}',
616 'torev' => '{{REPL:revA3}}',
617 'prop' => 'diff',
618 'slots' => 'main',
619 ],
620 [
621 'compare' => [
622 'bodies' => [
623 'main' => '<tr><td colspan="2" class="diff-lineno" id="mw-diff-left-l1" >Line 1:</td>' . "\n"
624 . '<td colspan="2" class="diff-lineno">Line 1:</td></tr>' . "\n"
625 . '<tr><td class=\'diff-marker\'>−</td><td class=\'diff-deletedline\'><div>A <del class="diffchange diffchange-inline">1</del></div></td><td class=\'diff-marker\'>+</td><td class=\'diff-addedline\'><div>A <ins class="diffchange diffchange-inline">3</ins></div></td></tr>' . "\n",
626 ],
627 ],
628 ],
629 ],
630 // @todo Add a test for diffing with a deleted slot. Deleting 'main' doesn't work.
631
632 'Basic diff, deprecated text' => [
633 [
634 'fromtext' => 'From text',
635 'fromcontentmodel' => 'wikitext',
636 'totext' => 'To text {{subst:PAGENAME}}',
637 'tocontentmodel' => 'wikitext',
638 ],
639 [
640 'warnings' => self::makeDeprecationWarnings( 'fromtext', 'fromcontentmodel', 'totext', 'tocontentmodel' ),
641 'compare' => [
642 'body' => '<tr><td colspan="2" class="diff-lineno" id="mw-diff-left-l1" >Line 1:</td>' . "\n"
643 . '<td colspan="2" class="diff-lineno">Line 1:</td></tr>' . "\n"
644 . '<tr><td class=\'diff-marker\'>−</td><td class=\'diff-deletedline\'><div><del class="diffchange diffchange-inline">From </del>text</div></td><td class=\'diff-marker\'>+</td><td class=\'diff-addedline\'><div><ins class="diffchange diffchange-inline">To </ins>text <ins class="diffchange diffchange-inline">{{subst:PAGENAME}}</ins></div></td></tr>' . "\n",
645 ]
646 ],
647 ],
648 'Basic diff, deprecated text 2' => [
649 [
650 'fromtext' => 'From text',
651 'totext' => 'To text {{subst:PAGENAME}}',
652 'tocontentmodel' => 'wikitext',
653 ],
654 [
655 'warnings' => self::makeDeprecationWarnings( 'fromtext', 'totext', 'tocontentmodel' ),
656 'compare' => [
657 'body' => '<tr><td colspan="2" class="diff-lineno" id="mw-diff-left-l1" >Line 1:</td>' . "\n"
658 . '<td colspan="2" class="diff-lineno">Line 1:</td></tr>' . "\n"
659 . '<tr><td class=\'diff-marker\'>−</td><td class=\'diff-deletedline\'><div><del class="diffchange diffchange-inline">From </del>text</div></td><td class=\'diff-marker\'>+</td><td class=\'diff-addedline\'><div><ins class="diffchange diffchange-inline">To </ins>text <ins class="diffchange diffchange-inline">{{subst:PAGENAME}}</ins></div></td></tr>' . "\n",
660 ]
661 ],
662 ],
663 'Basic diff, deprecated text, guessed model' => [
664 [
665 'fromtext' => 'From text',
666 'totext' => 'To text',
667 ],
668 [
669 'warnings' => array_merge( self::makeDeprecationWarnings( 'fromtext', 'totext' ), [
670 [ 'code' => 'compare-nocontentmodel', 'module' => 'compare' ],
671 ] ),
672 'compare' => [
673 'body' => '<tr><td colspan="2" class="diff-lineno" id="mw-diff-left-l1" >Line 1:</td>' . "\n"
674 . '<td colspan="2" class="diff-lineno">Line 1:</td></tr>' . "\n"
675 . '<tr><td class=\'diff-marker\'>−</td><td class=\'diff-deletedline\'><div><del class="diffchange diffchange-inline">From </del>text</div></td><td class=\'diff-marker\'>+</td><td class=\'diff-addedline\'><div><ins class="diffchange diffchange-inline">To </ins>text</div></td></tr>' . "\n",
676 ]
677 ],
678 ],
679 'Basic diff, deprecated text with title and PST' => [
680 [
681 'fromtext' => 'From text',
682 'totitle' => 'Test',
683 'totext' => 'To text {{subst:PAGENAME}}',
684 'topst' => true,
685 ],
686 [
687 'warnings' => self::makeDeprecationWarnings( 'fromtext', 'totext' ),
688 'compare' => [
689 'body' => '<tr><td colspan="2" class="diff-lineno" id="mw-diff-left-l1" >Line 1:</td>' . "\n"
690 . '<td colspan="2" class="diff-lineno">Line 1:</td></tr>' . "\n"
691 . '<tr><td class=\'diff-marker\'>−</td><td class=\'diff-deletedline\'><div><del class="diffchange diffchange-inline">From </del>text</div></td><td class=\'diff-marker\'>+</td><td class=\'diff-addedline\'><div><ins class="diffchange diffchange-inline">To </ins>text <ins class="diffchange diffchange-inline">Test</ins></div></td></tr>' . "\n",
692 ]
693 ],
694 ],
695 'Basic diff, deprecated text with page ID and PST' => [
696 [
697 'fromtext' => 'From text',
698 'toid' => '{{REPL:pageB}}',
699 'totext' => 'To text {{subst:PAGENAME}}',
700 'topst' => true,
701 ],
702 [
703 'warnings' => self::makeDeprecationWarnings( 'fromtext', 'totext' ),
704 'compare' => [
705 'body' => '<tr><td colspan="2" class="diff-lineno" id="mw-diff-left-l1" >Line 1:</td>' . "\n"
706 . '<td colspan="2" class="diff-lineno">Line 1:</td></tr>' . "\n"
707 . '<tr><td class=\'diff-marker\'>−</td><td class=\'diff-deletedline\'><div><del class="diffchange diffchange-inline">From </del>text</div></td><td class=\'diff-marker\'>+</td><td class=\'diff-addedline\'><div><ins class="diffchange diffchange-inline">To </ins>text <ins class="diffchange diffchange-inline">ApiComparePagesTest B</ins></div></td></tr>' . "\n",
708 ]
709 ],
710 ],
711 'Basic diff, deprecated text with revision and PST' => [
712 [
713 'fromtext' => 'From text',
714 'torev' => '{{REPL:revB2}}',
715 'totext' => 'To text {{subst:PAGENAME}}',
716 'topst' => true,
717 ],
718 [
719 'warnings' => self::makeDeprecationWarnings( 'fromtext', 'totext' ),
720 'compare' => [
721 'body' => '<tr><td colspan="2" class="diff-lineno" id="mw-diff-left-l1" >Line 1:</td>' . "\n"
722 . '<td colspan="2" class="diff-lineno">Line 1:</td></tr>' . "\n"
723 . '<tr><td class=\'diff-marker\'>−</td><td class=\'diff-deletedline\'><div><del class="diffchange diffchange-inline">From </del>text</div></td><td class=\'diff-marker\'>+</td><td class=\'diff-addedline\'><div><ins class="diffchange diffchange-inline">To </ins>text <ins class="diffchange diffchange-inline">ApiComparePagesTest B</ins></div></td></tr>' . "\n",
724 ]
725 ],
726 ],
727 'Basic diff, deprecated text with deleted revision and PST' => [
728 [
729 'fromtext' => 'From text',
730 'torev' => '{{REPL:revC2}}',
731 'totext' => 'To text {{subst:PAGENAME}}',
732 'topst' => true,
733 ],
734 [
735 'warnings' => self::makeDeprecationWarnings( 'fromtext', 'totext' ),
736 'compare' => [
737 'body' => '<tr><td colspan="2" class="diff-lineno" id="mw-diff-left-l1" >Line 1:</td>' . "\n"
738 . '<td colspan="2" class="diff-lineno">Line 1:</td></tr>' . "\n"
739 . '<tr><td class=\'diff-marker\'>−</td><td class=\'diff-deletedline\'><div><del class="diffchange diffchange-inline">From </del>text</div></td><td class=\'diff-marker\'>+</td><td class=\'diff-addedline\'><div><ins class="diffchange diffchange-inline">To </ins>text <ins class="diffchange diffchange-inline">ApiComparePagesTest C</ins></div></td></tr>' . "\n",
740 ]
741 ],
742 false, true
743 ],
744 'Basic diff, test with deprecated sections' => [
745 [
746 'fromtitle' => 'ApiComparePagesTest F',
747 'fromsection' => 1,
748 'totext' => "== Section 1 ==\nTo text\n\n== Section 2 ==\nTo text?",
749 'tosection' => 2,
750 ],
751 [
752 'warnings' => self::makeDeprecationWarnings( 'fromsection', 'totext', 'tosection' ),
753 'compare' => [
754 'body' => '<tr><td colspan="2" class="diff-lineno" id="mw-diff-left-l1" >Line 1:</td>' . "\n"
755 . '<td colspan="2" class="diff-lineno">Line 1:</td></tr>' . "\n"
756 . '<tr><td class=\'diff-marker\'>−</td><td class=\'diff-deletedline\'><div>== Section <del class="diffchange diffchange-inline">1 </del>==</div></td><td class=\'diff-marker\'>+</td><td class=\'diff-addedline\'><div>== Section <ins class="diffchange diffchange-inline">2 </ins>==</div></td></tr>' . "\n"
757 . '<tr><td class=\'diff-marker\'>−</td><td class=\'diff-deletedline\'><div><del class="diffchange diffchange-inline">F 1.1</del></div></td><td class=\'diff-marker\'>+</td><td class=\'diff-addedline\'><div><ins class="diffchange diffchange-inline">To text?</ins></div></td></tr>' . "\n",
758 'fromid' => '{{REPL:pageF}}',
759 'fromrevid' => '{{REPL:revF1}}',
760 'fromns' => '0',
761 'fromtitle' => 'ApiComparePagesTest F',
762 ]
763 ],
764 ],
765 'Basic diff, test with deprecated sections and revdel, non-sysop' => [
766 [
767 'fromrev' => '{{REPL:revB2}}',
768 'fromsection' => 0,
769 'torev' => '{{REPL:revB4}}',
770 'tosection' => 0,
771 ],
772 [],
773 'missingcontent'
774 ],
775 'Basic diff, test with deprecated sections and revdel, sysop' => [
776 [
777 'fromrev' => '{{REPL:revB2}}',
778 'fromsection' => 0,
779 'torev' => '{{REPL:revB4}}',
780 'tosection' => 0,
781 ],
782 [
783 'warnings' => self::makeDeprecationWarnings( 'fromsection', 'tosection' ),
784 'compare' => [
785 'body' => '<tr><td colspan="2" class="diff-lineno" id="mw-diff-left-l1" >Line 1:</td>' . "\n"
786 . '<td colspan="2" class="diff-lineno">Line 1:</td></tr>' . "\n"
787 . '<tr><td class=\'diff-marker\'>−</td><td class=\'diff-deletedline\'><div>B <del class="diffchange diffchange-inline">2</del></div></td><td class=\'diff-marker\'>+</td><td class=\'diff-addedline\'><div>B <ins class="diffchange diffchange-inline">4</ins></div></td></tr>' . "\n",
788 'fromid' => '{{REPL:pageB}}',
789 'fromrevid' => '{{REPL:revB2}}',
790 'fromns' => 0,
791 'fromtitle' => 'ApiComparePagesTest B',
792 'fromtexthidden' => true,
793 'fromuserhidden' => true,
794 'fromcommenthidden' => true,
795 'toid' => '{{REPL:pageB}}',
796 'torevid' => '{{REPL:revB4}}',
797 'tons' => 0,
798 'totitle' => 'ApiComparePagesTest B',
799 ]
800 ],
801 false, true,
802 ],
803
804 'Error, missing title' => [
805 [
806 'fromtitle' => 'ApiComparePagesTest X',
807 'totitle' => 'ApiComparePagesTest B',
808 ],
809 [],
810 'missingtitle',
811 ],
812 'Error, invalid title' => [
813 [
814 'fromtitle' => '<bad>',
815 'totitle' => 'ApiComparePagesTest B',
816 ],
817 [],
818 'invalidtitle',
819 ],
820 'Error, missing page ID' => [
821 [
822 'fromid' => 8817900,
823 'totitle' => 'ApiComparePagesTest B',
824 ],
825 [],
826 'nosuchpageid',
827 ],
828 'Error, page with missing revision' => [
829 [
830 'fromtitle' => 'ApiComparePagesTest D',
831 'totitle' => 'ApiComparePagesTest B',
832 ],
833 [],
834 'nosuchrevid',
835 ],
836 'Error, page with no revision' => [
837 [
838 'fromtitle' => 'ApiComparePagesTest E',
839 'totitle' => 'ApiComparePagesTest B',
840 ],
841 [],
842 'nosuchrevid',
843 ],
844 'Error, bad rev ID' => [
845 [
846 'fromrev' => 8817900,
847 'totitle' => 'ApiComparePagesTest B',
848 ],
849 [],
850 'nosuchrevid',
851 ],
852 'Error, deleted revision ID, non-sysop' => [
853 [
854 'fromrev' => '{{REPL:revA2}}',
855 'torev' => '{{REPL:revC2}}',
856 ],
857 [],
858 'nosuchrevid',
859 ],
860 'Error, deleted revision ID and torelative=prev' => [
861 [
862 'fromrev' => '{{REPL:revC2}}',
863 'torelative' => 'prev',
864 ],
865 [],
866 'compare-relative-to-deleted', true
867 ],
868 'Error, deleted revision ID and torelative=next' => [
869 [
870 'fromrev' => '{{REPL:revC2}}',
871 'torelative' => 'next',
872 ],
873 [],
874 'compare-relative-to-deleted', true
875 ],
876 'Deleted revision ID and torelative=cur' => [
877 [
878 'fromrev' => '{{REPL:revC2}}',
879 'torelative' => 'cur',
880 ],
881 [],
882 'nosuchrevid', true
883 ],
884 'Error, revision-deleted content' => [
885 [
886 'fromrev' => '{{REPL:revA2}}',
887 'torev' => '{{REPL:revB2}}',
888 ],
889 [],
890 'missingcontent',
891 ],
892 'Error, text with no title and PST' => [
893 [
894 'fromtext' => 'From text',
895 'totext' => 'To text {{subst:PAGENAME}}',
896 'topst' => true,
897 ],
898 [],
899 'compare-no-title',
900 ],
901 'Error, test with invalid from section ID' => [
902 [
903 'fromtitle' => 'ApiComparePagesTest F',
904 'fromsection' => 5,
905 'totext' => "== Section 1 ==\nTo text\n\n== Section 2 ==\nTo text?",
906 'tosection' => 2,
907 ],
908 [],
909 'nosuchfromsection',
910 ],
911 'Error, test with invalid to section ID' => [
912 [
913 'fromtitle' => 'ApiComparePagesTest F',
914 'fromsection' => 1,
915 'totext' => "== Section 1 ==\nTo text\n\n== Section 2 ==\nTo text?",
916 'tosection' => 5,
917 ],
918 [],
919 'nosuchtosection',
920 ],
921 'Error, Relative diff, no from revision' => [
922 [
923 'fromtext' => 'Foo',
924 'torelative' => 'cur',
925 'prop' => 'ids',
926 ],
927 [],
928 'compare-relative-to-nothing'
929 ],
930 'Error, Relative diff, cur with no current revision' => [
931 [
932 'fromrev' => '{{REPL:revE2}}',
933 'torelative' => 'cur',
934 'prop' => 'ids',
935 ],
936 [],
937 'nosuchrevid'
938 ],
939 'Error, Relative diff, next revdeleted' => [
940 [
941 'fromrev' => '{{REPL:revB1}}',
942 'torelative' => 'next',
943 'prop' => 'ids',
944 ],
945 [],
946 'missingcontent'
947 ],
948 'Error, Relative diff, prev revdeleted' => [
949 [
950 'fromrev' => '{{REPL:revB3}}',
951 'torelative' => 'prev',
952 'prop' => 'ids',
953 ],
954 [],
955 'missingcontent'
956 ],
957 'Error, section diff with no revision' => [
958 [
959 'fromtitle' => 'ApiComparePagesTest F',
960 'toslots' => 'main',
961 'totext-main' => "== Section 1 ==\nTo text?",
962 'tosection-main' => 1,
963 ],
964 [],
965 'compare-notorevision',
966 ],
967 'Error, section diff with revdeleted revision' => [
968 [
969 'fromtitle' => 'ApiComparePagesTest F',
970 'torev' => '{{REPL:revB2}}',
971 'toslots' => 'main',
972 'totext-main' => "== Section 1 ==\nTo text?",
973 'tosection-main' => 1,
974 ],
975 [],
976 'missingcontent',
977 ],
978 'Error, section diff with a content model not supporting sections' => [
979 [
980 'fromtitle' => 'ApiComparePagesTest G',
981 'torev' => '{{REPL:revG1}}',
982 'toslots' => 'main',
983 'totext-main' => "== Section 1 ==\nTo text?",
984 'tosection-main' => 1,
985 ],
986 [],
987 'sectionsnotsupported',
988 ],
989 'Error, section diff with bad content model' => [
990 [
991 'fromtitle' => 'ApiComparePagesTest F',
992 'torev' => '{{REPL:revF1}}',
993 'toslots' => 'main',
994 'totext-main' => "== Section 1 ==\nTo text?",
995 'tosection-main' => 1,
996 'tocontentmodel-main' => CONTENT_MODEL_TEXT,
997 ],
998 [],
999 'sectionreplacefailed',
1000 ],
1001 'Error, deleting the main slot' => [
1002 [
1003 'fromtitle' => 'ApiComparePagesTest A',
1004 'totitle' => 'ApiComparePagesTest A',
1005 'toslots' => 'main',
1006 ],
1007 [],
1008 'compare-maintextrequired',
1009 ],
1010 // @todo Add a test for using 'tosection-foo' without 'totext-foo' (can't do it with main)
1011 ];
1012 // phpcs:enable
1013 }
1014 }