2010-12-14 16:26:35 +00:00
|
|
|
<?php
|
|
|
|
|
|
2019-08-26 12:24:37 +00:00
|
|
|
use MediaWiki\MediaWikiServices;
|
|
|
|
|
|
2013-10-21 21:09:13 +00:00
|
|
|
/**
|
|
|
|
|
* @group Xml
|
|
|
|
|
*/
|
2020-06-30 15:09:24 +00:00
|
|
|
class XmlTest extends MediaWikiIntegrationTestCase {
|
2011-05-01 19:32:49 +00:00
|
|
|
|
2019-10-20 18:11:08 +00:00
|
|
|
protected function setUp() : void {
|
2012-10-08 10:59:55 +00:00
|
|
|
parent::setUp();
|
2011-12-01 00:40:56 +00:00
|
|
|
|
2019-08-26 12:24:37 +00:00
|
|
|
$langObj = MediaWikiServices::getInstance()->getLanguageFactory()->getLanguage( 'en' );
|
2016-02-17 09:09:32 +00:00
|
|
|
$langObj->setNamespaces( [
|
2012-01-21 22:26:14 +00:00
|
|
|
-2 => 'Media',
|
|
|
|
|
-1 => 'Special',
|
2013-02-14 11:56:23 +00:00
|
|
|
0 => '',
|
|
|
|
|
1 => 'Talk',
|
|
|
|
|
2 => 'User',
|
|
|
|
|
3 => 'User_talk',
|
|
|
|
|
4 => 'MyWiki',
|
|
|
|
|
5 => 'MyWiki_Talk',
|
|
|
|
|
6 => 'File',
|
|
|
|
|
7 => 'File_talk',
|
|
|
|
|
8 => 'MediaWiki',
|
|
|
|
|
9 => 'MediaWiki_talk',
|
|
|
|
|
10 => 'Template',
|
|
|
|
|
11 => 'Template_talk',
|
|
|
|
|
100 => 'Custom',
|
|
|
|
|
101 => 'Custom_talk',
|
2016-02-17 09:09:32 +00:00
|
|
|
] );
|
2011-12-01 00:40:56 +00:00
|
|
|
|
2016-02-17 09:09:32 +00:00
|
|
|
$this->setMwGlobals( [
|
2012-10-08 10:59:55 +00:00
|
|
|
'wgLang' => $langObj,
|
2016-03-24 14:51:13 +00:00
|
|
|
'wgUseMediaWikiUIEverywhere' => false,
|
2016-02-17 09:09:32 +00:00
|
|
|
] );
|
2011-05-01 19:32:49 +00:00
|
|
|
}
|
2010-12-14 16:26:35 +00:00
|
|
|
|
2013-10-21 08:46:11 +00:00
|
|
|
/**
|
|
|
|
|
* @covers Xml::expandAttributes
|
|
|
|
|
*/
|
2010-12-14 16:26:35 +00:00
|
|
|
public function testExpandAttributes() {
|
2013-02-14 11:56:23 +00:00
|
|
|
$this->assertNull( Xml::expandAttributes( null ),
|
2010-12-14 16:26:35 +00:00
|
|
|
'Converting a null list of attributes'
|
|
|
|
|
);
|
2019-09-17 14:28:35 +00:00
|
|
|
$this->assertSame( '', Xml::expandAttributes( [] ),
|
2010-12-14 16:26:35 +00:00
|
|
|
'Converting an empty list of attributes'
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2013-10-21 08:46:11 +00:00
|
|
|
/**
|
|
|
|
|
* @covers Xml::expandAttributes
|
|
|
|
|
*/
|
2010-12-14 16:26:35 +00:00
|
|
|
public function testExpandAttributesException() {
|
2019-10-05 15:39:46 +00:00
|
|
|
$this->expectException( MWException::class );
|
2013-02-14 11:56:23 +00:00
|
|
|
Xml::expandAttributes( 'string' );
|
2010-12-14 16:26:35 +00:00
|
|
|
}
|
|
|
|
|
|
2020-12-21 02:15:17 +00:00
|
|
|
public function provideElement() {
|
|
|
|
|
// $expect, $element, $attribs, $contents
|
|
|
|
|
yield 'Opening element with no attributes' => [ '<element>', 'element', null, null ];
|
|
|
|
|
yield 'Terminated empty element' => [ '<element />', 'element', null, '' ];
|
|
|
|
|
yield 'Element with no attributes and content that needs escaping' => [
|
|
|
|
|
'<element>"hello <there> your\'s & you"</element>',
|
|
|
|
|
'element',
|
|
|
|
|
null,
|
|
|
|
|
'"hello <there> your\'s & you"'
|
|
|
|
|
];
|
|
|
|
|
yield 'Element attributes, keys are not escaped' => [
|
|
|
|
|
'<element key="value" <>="<>">',
|
|
|
|
|
'element',
|
|
|
|
|
[ 'key' => 'value', '<>' => '<>' ],
|
|
|
|
|
null
|
|
|
|
|
];
|
2010-12-14 16:26:35 +00:00
|
|
|
}
|
|
|
|
|
|
2013-10-21 08:46:11 +00:00
|
|
|
/**
|
|
|
|
|
* @covers Xml::element
|
2020-12-21 02:15:17 +00:00
|
|
|
* @dataProvider provideElement
|
2013-10-21 08:46:11 +00:00
|
|
|
*/
|
2020-12-21 02:15:17 +00:00
|
|
|
public function testElement( string $expect, string $element, $attribs, $content ) {
|
2010-12-14 16:26:35 +00:00
|
|
|
$this->assertEquals(
|
2020-12-21 02:15:17 +00:00
|
|
|
$expect,
|
|
|
|
|
Xml::element( $element, $attribs, $content )
|
2010-12-14 16:26:35 +00:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2013-10-21 08:46:11 +00:00
|
|
|
/**
|
|
|
|
|
* @covers Xml::input
|
|
|
|
|
*/
|
2013-10-23 22:51:31 +00:00
|
|
|
public function testElementInputCanHaveAValueOfZero() {
|
2010-12-14 16:26:35 +00:00
|
|
|
$this->assertEquals(
|
2015-03-13 18:49:15 +00:00
|
|
|
'<input name="name" value="0" />',
|
2010-12-14 16:26:35 +00:00
|
|
|
Xml::input( 'name', false, 0 ),
|
2017-02-20 23:45:58 +00:00
|
|
|
'Input with a value of 0 (T25797)'
|
2010-12-14 16:26:35 +00:00
|
|
|
);
|
|
|
|
|
}
|
2013-02-14 11:56:23 +00:00
|
|
|
|
2013-10-21 08:46:11 +00:00
|
|
|
/**
|
|
|
|
|
* @covers Xml::escapeTagsOnly
|
|
|
|
|
*/
|
2010-12-14 16:26:35 +00:00
|
|
|
public function testEscapeTagsOnly() {
|
|
|
|
|
$this->assertEquals( '"><', Xml::escapeTagsOnly( '"><' ),
|
|
|
|
|
'replace " > and < with their HTML entitites'
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2013-10-21 08:46:11 +00:00
|
|
|
/**
|
|
|
|
|
* @covers Xml::openElement
|
|
|
|
|
*/
|
2013-10-23 22:51:31 +00:00
|
|
|
public function testOpenElement() {
|
2010-12-14 16:26:35 +00:00
|
|
|
$this->assertEquals(
|
|
|
|
|
'<element k="v">',
|
2016-02-17 09:09:32 +00:00
|
|
|
Xml::openElement( 'element', [ 'k' => 'v' ] ),
|
2010-12-14 16:26:35 +00:00
|
|
|
'openElement() shortcut'
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2013-10-21 08:46:11 +00:00
|
|
|
/**
|
|
|
|
|
* @covers Xml::closeElement
|
|
|
|
|
*/
|
2013-10-23 22:51:31 +00:00
|
|
|
public function testCloseElement() {
|
2010-12-14 16:26:35 +00:00
|
|
|
$this->assertEquals( '</element>', Xml::closeElement( 'element' ), 'closeElement() shortcut' );
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-25 01:54:25 +00:00
|
|
|
public function provideMonthSelector() {
|
|
|
|
|
global $wgLang;
|
|
|
|
|
|
|
|
|
|
$header = '<select name="month" id="month" class="mw-month-selector">';
|
|
|
|
|
$header2 = '<select name="month" id="monthSelector" class="mw-month-selector">';
|
|
|
|
|
$monthsString = '';
|
|
|
|
|
for ( $i = 1; $i < 13; $i++ ) {
|
|
|
|
|
$monthName = $wgLang->getMonthName( $i );
|
|
|
|
|
$monthsString .= "<option value=\"{$i}\">{$monthName}</option>";
|
|
|
|
|
if ( $i !== 12 ) {
|
|
|
|
|
$monthsString .= "\n";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
$monthsString2 = str_replace(
|
|
|
|
|
'<option value="12">December</option>',
|
|
|
|
|
'<option value="12" selected="">December</option>',
|
|
|
|
|
$monthsString
|
|
|
|
|
);
|
|
|
|
|
$end = '</select>';
|
|
|
|
|
|
|
|
|
|
$allMonths = "<option value=\"AllMonths\">all</option>\n";
|
|
|
|
|
return [
|
|
|
|
|
[ $header . $monthsString . $end, '', null, 'month' ],
|
|
|
|
|
[ $header . $monthsString2 . $end, 12, null, 'month' ],
|
|
|
|
|
[ $header2 . $monthsString . $end, '', null, 'monthSelector' ],
|
|
|
|
|
[ $header . $allMonths . $monthsString . $end, '', 'AllMonths', 'month' ],
|
|
|
|
|
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @covers Xml::monthSelector
|
|
|
|
|
* @dataProvider provideMonthSelector
|
|
|
|
|
*/
|
|
|
|
|
public function testMonthSelector( $expected, $selected, $allmonths, $id ) {
|
|
|
|
|
$this->assertEquals(
|
|
|
|
|
$expected,
|
|
|
|
|
Xml::monthSelector( $selected, $allmonths, $id )
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @covers Xml::span
|
|
|
|
|
*/
|
|
|
|
|
public function testSpan() {
|
|
|
|
|
$this->assertEquals(
|
|
|
|
|
'<span class="foo" id="testSpan">element</span>',
|
|
|
|
|
Xml::span( 'element', 'foo', [ 'id' => 'testSpan' ] )
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2013-10-21 08:46:11 +00:00
|
|
|
/**
|
|
|
|
|
* @covers Xml::dateMenu
|
|
|
|
|
*/
|
2013-02-14 11:56:23 +00:00
|
|
|
public function testDateMenu() {
|
|
|
|
|
$curYear = intval( gmdate( 'Y' ) );
|
|
|
|
|
$prevYear = $curYear - 1;
|
2011-02-06 22:10:48 +00:00
|
|
|
|
2013-02-14 11:56:23 +00:00
|
|
|
$curMonth = intval( gmdate( 'n' ) );
|
2014-01-25 13:26:03 +00:00
|
|
|
|
2011-02-06 22:10:48 +00:00
|
|
|
$nextMonth = $curMonth + 1;
|
2013-02-14 11:56:23 +00:00
|
|
|
if ( $nextMonth == 13 ) {
|
|
|
|
|
$nextMonth = 1;
|
|
|
|
|
}
|
2011-02-06 22:10:48 +00:00
|
|
|
|
2011-02-06 21:23:12 +00:00
|
|
|
$this->assertEquals(
|
2014-04-24 15:05:10 +00:00
|
|
|
'<label for="year">From year (and earlier):</label> ' .
|
2016-02-18 16:33:15 +00:00
|
|
|
'<input id="year" maxlength="4" size="7" type="number" value="2011" name="year"/> ' .
|
2014-04-24 15:05:10 +00:00
|
|
|
'<label for="month">From month (and earlier):</label> ' .
|
2015-06-07 00:26:57 +00:00
|
|
|
'<select name="month" id="month" class="mw-month-selector">' .
|
2014-04-24 15:05:10 +00:00
|
|
|
'<option value="-1">all</option>' . "\n" .
|
2013-02-14 11:56:23 +00:00
|
|
|
'<option value="1">January</option>' . "\n" .
|
|
|
|
|
'<option value="2" selected="">February</option>' . "\n" .
|
|
|
|
|
'<option value="3">March</option>' . "\n" .
|
|
|
|
|
'<option value="4">April</option>' . "\n" .
|
|
|
|
|
'<option value="5">May</option>' . "\n" .
|
|
|
|
|
'<option value="6">June</option>' . "\n" .
|
|
|
|
|
'<option value="7">July</option>' . "\n" .
|
|
|
|
|
'<option value="8">August</option>' . "\n" .
|
|
|
|
|
'<option value="9">September</option>' . "\n" .
|
|
|
|
|
'<option value="10">October</option>' . "\n" .
|
|
|
|
|
'<option value="11">November</option>' . "\n" .
|
|
|
|
|
'<option value="12">December</option></select>',
|
2011-02-06 21:23:12 +00:00
|
|
|
Xml::dateMenu( 2011, 02 ),
|
|
|
|
|
"Date menu for february 2011"
|
|
|
|
|
);
|
|
|
|
|
$this->assertEquals(
|
2014-04-24 15:05:10 +00:00
|
|
|
'<label for="year">From year (and earlier):</label> ' .
|
2016-02-18 16:33:15 +00:00
|
|
|
'<input id="year" maxlength="4" size="7" type="number" value="2011" name="year"/> ' .
|
2014-04-24 15:05:10 +00:00
|
|
|
'<label for="month">From month (and earlier):</label> ' .
|
2015-06-07 00:26:57 +00:00
|
|
|
'<select name="month" id="month" class="mw-month-selector">' .
|
2014-04-24 15:05:10 +00:00
|
|
|
'<option value="-1">all</option>' . "\n" .
|
2013-02-14 11:56:23 +00:00
|
|
|
'<option value="1">January</option>' . "\n" .
|
|
|
|
|
'<option value="2">February</option>' . "\n" .
|
|
|
|
|
'<option value="3">March</option>' . "\n" .
|
|
|
|
|
'<option value="4">April</option>' . "\n" .
|
|
|
|
|
'<option value="5">May</option>' . "\n" .
|
|
|
|
|
'<option value="6">June</option>' . "\n" .
|
|
|
|
|
'<option value="7">July</option>' . "\n" .
|
|
|
|
|
'<option value="8">August</option>' . "\n" .
|
|
|
|
|
'<option value="9">September</option>' . "\n" .
|
|
|
|
|
'<option value="10">October</option>' . "\n" .
|
|
|
|
|
'<option value="11">November</option>' . "\n" .
|
|
|
|
|
'<option value="12">December</option></select>',
|
|
|
|
|
Xml::dateMenu( 2011, -1 ),
|
2011-02-06 21:23:12 +00:00
|
|
|
"Date menu with negative month for 'All'"
|
|
|
|
|
);
|
2011-02-06 22:10:48 +00:00
|
|
|
$this->assertEquals(
|
|
|
|
|
Xml::dateMenu( $curYear, $curMonth ),
|
2013-02-14 11:56:23 +00:00
|
|
|
Xml::dateMenu( '', $curMonth ),
|
2011-02-06 22:10:48 +00:00
|
|
|
"Date menu year is the current one when not specified"
|
|
|
|
|
);
|
2011-12-01 00:40:56 +00:00
|
|
|
|
2012-09-23 08:15:20 +00:00
|
|
|
$wantedYear = $nextMonth == 1 ? $curYear : $prevYear;
|
2011-12-01 14:40:11 +00:00
|
|
|
$this->assertEquals(
|
2012-09-23 08:15:20 +00:00
|
|
|
Xml::dateMenu( $wantedYear, $nextMonth ),
|
2011-02-06 22:10:48 +00:00
|
|
|
Xml::dateMenu( '', $nextMonth ),
|
|
|
|
|
"Date menu next month is 11 months ago"
|
2011-12-01 14:40:11 +00:00
|
|
|
);
|
2011-02-06 22:10:48 +00:00
|
|
|
|
|
|
|
|
$this->assertEquals(
|
2014-04-24 15:05:10 +00:00
|
|
|
'<label for="year">From year (and earlier):</label> ' .
|
2016-02-18 16:33:15 +00:00
|
|
|
'<input id="year" maxlength="4" size="7" type="number" name="year"/> ' .
|
2014-04-24 15:05:10 +00:00
|
|
|
'<label for="month">From month (and earlier):</label> ' .
|
2015-06-07 00:26:57 +00:00
|
|
|
'<select name="month" id="month" class="mw-month-selector">' .
|
2014-04-24 17:51:33 +00:00
|
|
|
'<option value="-1">all</option>' . "\n" .
|
2013-02-14 11:56:23 +00:00
|
|
|
'<option value="1">January</option>' . "\n" .
|
|
|
|
|
'<option value="2">February</option>' . "\n" .
|
|
|
|
|
'<option value="3">March</option>' . "\n" .
|
|
|
|
|
'<option value="4">April</option>' . "\n" .
|
|
|
|
|
'<option value="5">May</option>' . "\n" .
|
|
|
|
|
'<option value="6">June</option>' . "\n" .
|
|
|
|
|
'<option value="7">July</option>' . "\n" .
|
|
|
|
|
'<option value="8">August</option>' . "\n" .
|
|
|
|
|
'<option value="9">September</option>' . "\n" .
|
|
|
|
|
'<option value="10">October</option>' . "\n" .
|
|
|
|
|
'<option value="11">November</option>' . "\n" .
|
|
|
|
|
'<option value="12">December</option></select>',
|
2011-12-01 00:40:56 +00:00
|
|
|
Xml::dateMenu( '', '' ),
|
2011-02-06 22:10:48 +00:00
|
|
|
"Date menu with neither year or month"
|
|
|
|
|
);
|
2011-02-06 21:23:12 +00:00
|
|
|
}
|
|
|
|
|
|
2013-10-21 08:46:11 +00:00
|
|
|
/**
|
|
|
|
|
* @covers Xml::textarea
|
|
|
|
|
*/
|
2013-10-23 22:51:31 +00:00
|
|
|
public function testTextareaNoContent() {
|
2010-12-14 16:26:35 +00:00
|
|
|
$this->assertEquals(
|
2015-03-13 18:49:15 +00:00
|
|
|
'<textarea name="name" id="name" cols="40" rows="5"></textarea>',
|
2010-12-14 16:26:35 +00:00
|
|
|
Xml::textarea( 'name', '' ),
|
|
|
|
|
'textarea() with not content'
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2013-10-21 08:46:11 +00:00
|
|
|
/**
|
|
|
|
|
* @covers Xml::textarea
|
|
|
|
|
*/
|
2013-10-23 22:51:31 +00:00
|
|
|
public function testTextareaAttribs() {
|
2010-12-14 16:26:35 +00:00
|
|
|
$this->assertEquals(
|
2015-03-13 18:49:15 +00:00
|
|
|
'<textarea name="name" id="name" cols="20" rows="10"><txt></textarea>',
|
2010-12-14 16:26:35 +00:00
|
|
|
Xml::textarea( 'name', '<txt>', 20, 10 ),
|
|
|
|
|
'textarea() with custom attribs'
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2013-10-21 08:46:11 +00:00
|
|
|
/**
|
|
|
|
|
* @covers Xml::label
|
|
|
|
|
*/
|
2013-10-23 22:51:31 +00:00
|
|
|
public function testLabelCreation() {
|
2010-12-14 16:26:35 +00:00
|
|
|
$this->assertEquals(
|
|
|
|
|
'<label for="id">name</label>',
|
|
|
|
|
Xml::label( 'name', 'id' ),
|
|
|
|
|
'label() with no attribs'
|
|
|
|
|
);
|
|
|
|
|
}
|
2013-02-14 11:56:23 +00:00
|
|
|
|
2013-10-21 08:46:11 +00:00
|
|
|
/**
|
|
|
|
|
* @covers Xml::label
|
|
|
|
|
*/
|
2013-10-23 22:51:31 +00:00
|
|
|
public function testLabelAttributeCanOnlyBeClassOrTitle() {
|
2010-12-14 16:26:35 +00:00
|
|
|
$this->assertEquals(
|
|
|
|
|
'<label for="id">name</label>',
|
2016-02-17 09:09:32 +00:00
|
|
|
Xml::label( 'name', 'id', [ 'generated' => true ] ),
|
2010-12-14 16:26:35 +00:00
|
|
|
'label() can not be given a generated attribute'
|
|
|
|
|
);
|
|
|
|
|
$this->assertEquals(
|
|
|
|
|
'<label for="id" class="nice">name</label>',
|
2016-02-17 09:09:32 +00:00
|
|
|
Xml::label( 'name', 'id', [ 'class' => 'nice' ] ),
|
2010-12-14 16:26:35 +00:00
|
|
|
'label() can get a class attribute'
|
|
|
|
|
);
|
|
|
|
|
$this->assertEquals(
|
2011-06-23 20:05:32 +00:00
|
|
|
'<label for="id" title="nice tooltip">name</label>',
|
2016-02-17 09:09:32 +00:00
|
|
|
Xml::label( 'name', 'id', [ 'title' => 'nice tooltip' ] ),
|
2011-06-23 20:05:32 +00:00
|
|
|
'label() can get a title attribute'
|
|
|
|
|
);
|
|
|
|
|
$this->assertEquals(
|
|
|
|
|
'<label for="id" class="nice" title="nice tooltip">name</label>',
|
2016-02-17 09:09:32 +00:00
|
|
|
Xml::label( 'name', 'id', [
|
2013-02-14 11:56:23 +00:00
|
|
|
'generated' => true,
|
|
|
|
|
'class' => 'nice',
|
|
|
|
|
'title' => 'nice tooltip',
|
|
|
|
|
'anotherattr' => 'value',
|
2016-02-17 09:09:32 +00:00
|
|
|
]
|
2010-12-14 16:26:35 +00:00
|
|
|
),
|
2011-06-23 20:05:32 +00:00
|
|
|
'label() skip all attributes but "class" and "title"'
|
2010-12-14 16:26:35 +00:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2013-10-21 08:46:11 +00:00
|
|
|
/**
|
|
|
|
|
* @covers Xml::languageSelector
|
|
|
|
|
*/
|
2013-10-23 22:51:31 +00:00
|
|
|
public function testLanguageSelector() {
|
2012-05-10 19:46:14 +00:00
|
|
|
$select = Xml::languageSelector( 'en', true, null,
|
2016-02-17 09:09:32 +00:00
|
|
|
[ 'id' => 'testlang' ], wfMessage( 'yourlanguage' ) );
|
2012-05-10 19:46:14 +00:00
|
|
|
$this->assertEquals(
|
|
|
|
|
'<label for="testlang">Language:</label>',
|
|
|
|
|
$select[0]
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-21 02:15:17 +00:00
|
|
|
public function provideEncodeJsVar() {
|
|
|
|
|
// $expected, $input
|
|
|
|
|
yield 'boolean' => [ 'true', true ];
|
|
|
|
|
yield 'null' => [ 'null', null ];
|
|
|
|
|
yield 'array' => [ '["a",1]', [ 'a', 1 ] ];
|
|
|
|
|
yield 'associative arary' => [ '{"a":"a","b":1}', [ 'a' => 'a', 'b' => 1 ] ];
|
|
|
|
|
yield 'object' => [ '{"a":"a","b":1}', (object)[ 'a' => 'a', 'b' => 1 ] ];
|
|
|
|
|
yield 'int' => [ '123456', 123456 ];
|
|
|
|
|
yield 'float' => [ '1.5', 1.5 ];
|
|
|
|
|
yield 'int-like string' => [ '"123456"', '123456' ];
|
2011-02-14 00:54:40 +00:00
|
|
|
|
2020-12-21 02:15:17 +00:00
|
|
|
$code = 'function () { foo( 42 ); }';
|
|
|
|
|
yield 'code' => [ $code, new XmlJsCode( $code ) ];
|
2011-02-14 00:54:40 +00:00
|
|
|
}
|
2017-03-18 01:25:12 +00:00
|
|
|
|
ResourceLoader: Add support for packageFiles
Package files are files that are part of a module, but are not
immediately executed when the module executes. Instead, they are
lazy-excecuted when require() is called on them. Package files can be
scripts (JS) or data (JSON), and can be real files on the file system,
or virtual files generated by a callback.
Using virtual data files, server-side data and config variables can be
bundled with a module. Support for file-based require() allows us to
import npm modules into ResourceLoader more easily.
The require function passed to each script execution context, which was
previously a reference to the global mw.loader.require() function, is
changed to one that is scoped to the module and the file being executed.
This is needed to support relative paths: require( '../foo.js' ) can
mean a different file depending on the path of the calling file.
The results of require()ing each file (i.e. the value of module.exports
after executing it) are stored, and calling require() on the same file a
second time won't execute it again, but will return the stored value.
Miscellaneous changes:
- Add XmlJsCode::encodeObject(), which combines an associative array of
XmlJsCode objects into one larger XmlJsCode object. This is needed for
encoding the packageFiles parameter in mw.loader.implement() calls.
Bug: T133462
Change-Id: I78cc86e626de0720397718cd2bed8ed279579112
2018-11-03 00:53:17 +00:00
|
|
|
/**
|
|
|
|
|
* @covers Xml::encodeJsVar
|
2020-12-21 02:15:17 +00:00
|
|
|
* @dataProvider provideEncodeJsVar
|
ResourceLoader: Add support for packageFiles
Package files are files that are part of a module, but are not
immediately executed when the module executes. Instead, they are
lazy-excecuted when require() is called on them. Package files can be
scripts (JS) or data (JSON), and can be real files on the file system,
or virtual files generated by a callback.
Using virtual data files, server-side data and config variables can be
bundled with a module. Support for file-based require() allows us to
import npm modules into ResourceLoader more easily.
The require function passed to each script execution context, which was
previously a reference to the global mw.loader.require() function, is
changed to one that is scoped to the module and the file being executed.
This is needed to support relative paths: require( '../foo.js' ) can
mean a different file depending on the path of the calling file.
The results of require()ing each file (i.e. the value of module.exports
after executing it) are stored, and calling require() on the same file a
second time won't execute it again, but will return the stored value.
Miscellaneous changes:
- Add XmlJsCode::encodeObject(), which combines an associative array of
XmlJsCode objects into one larger XmlJsCode object. This is needed for
encoding the packageFiles parameter in mw.loader.implement() calls.
Bug: T133462
Change-Id: I78cc86e626de0720397718cd2bed8ed279579112
2018-11-03 00:53:17 +00:00
|
|
|
*/
|
2020-12-21 02:15:17 +00:00
|
|
|
public function testEncodeJsVar( string $expect, $input ) {
|
ResourceLoader: Add support for packageFiles
Package files are files that are part of a module, but are not
immediately executed when the module executes. Instead, they are
lazy-excecuted when require() is called on them. Package files can be
scripts (JS) or data (JSON), and can be real files on the file system,
or virtual files generated by a callback.
Using virtual data files, server-side data and config variables can be
bundled with a module. Support for file-based require() allows us to
import npm modules into ResourceLoader more easily.
The require function passed to each script execution context, which was
previously a reference to the global mw.loader.require() function, is
changed to one that is scoped to the module and the file being executed.
This is needed to support relative paths: require( '../foo.js' ) can
mean a different file depending on the path of the calling file.
The results of require()ing each file (i.e. the value of module.exports
after executing it) are stored, and calling require() on the same file a
second time won't execute it again, but will return the stored value.
Miscellaneous changes:
- Add XmlJsCode::encodeObject(), which combines an associative array of
XmlJsCode objects into one larger XmlJsCode object. This is needed for
encoding the packageFiles parameter in mw.loader.implement() calls.
Bug: T133462
Change-Id: I78cc86e626de0720397718cd2bed8ed279579112
2018-11-03 00:53:17 +00:00
|
|
|
$this->assertEquals(
|
2020-12-21 02:15:17 +00:00
|
|
|
$expect,
|
|
|
|
|
Xml::encodeJsVar( $input )
|
ResourceLoader: Add support for packageFiles
Package files are files that are part of a module, but are not
immediately executed when the module executes. Instead, they are
lazy-excecuted when require() is called on them. Package files can be
scripts (JS) or data (JSON), and can be real files on the file system,
or virtual files generated by a callback.
Using virtual data files, server-side data and config variables can be
bundled with a module. Support for file-based require() allows us to
import npm modules into ResourceLoader more easily.
The require function passed to each script execution context, which was
previously a reference to the global mw.loader.require() function, is
changed to one that is scoped to the module and the file being executed.
This is needed to support relative paths: require( '../foo.js' ) can
mean a different file depending on the path of the calling file.
The results of require()ing each file (i.e. the value of module.exports
after executing it) are stored, and calling require() on the same file a
second time won't execute it again, but will return the stored value.
Miscellaneous changes:
- Add XmlJsCode::encodeObject(), which combines an associative array of
XmlJsCode objects into one larger XmlJsCode object. This is needed for
encoding the packageFiles parameter in mw.loader.implement() calls.
Bug: T133462
Change-Id: I78cc86e626de0720397718cd2bed8ed279579112
2018-11-03 00:53:17 +00:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @covers Xml::encodeJsVar
|
|
|
|
|
* @covers XmlJsCode::encodeObject
|
|
|
|
|
*/
|
|
|
|
|
public function testEncodeObject() {
|
|
|
|
|
$codeA = 'function () { foo( 42 ); }';
|
|
|
|
|
$codeB = 'function ( jQuery ) { bar( 142857 ); }';
|
|
|
|
|
$obj = XmlJsCode::encodeObject( [
|
|
|
|
|
'a' => new XmlJsCode( $codeA ),
|
|
|
|
|
'b' => new XmlJsCode( $codeB )
|
|
|
|
|
] );
|
|
|
|
|
$this->assertEquals(
|
|
|
|
|
"{\"a\":$codeA,\"b\":$codeB}",
|
|
|
|
|
Xml::encodeJsVar( $obj )
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-18 01:25:12 +00:00
|
|
|
/**
|
|
|
|
|
* @covers Xml::listDropDown
|
|
|
|
|
*/
|
|
|
|
|
public function testListDropDown() {
|
|
|
|
|
$this->assertEquals(
|
2017-07-12 19:31:33 +00:00
|
|
|
'<select name="test-name" id="test-name" class="test-css" tabindex="2">' .
|
|
|
|
|
'<option value="other">other reasons</option>' . "\n" .
|
|
|
|
|
'<optgroup label="Foo">' .
|
|
|
|
|
'<option value="Foo 1">Foo 1</option>' . "\n" .
|
|
|
|
|
'<option value="Example" selected="">Example</option>' . "\n" .
|
|
|
|
|
'</optgroup>' . "\n" .
|
|
|
|
|
'<optgroup label="Bar">' .
|
|
|
|
|
'<option value="Bar 1">Bar 1</option>' . "\n" .
|
|
|
|
|
'</optgroup>' .
|
2017-03-18 01:25:12 +00:00
|
|
|
'</select>',
|
|
|
|
|
Xml::listDropDown(
|
|
|
|
|
// name
|
|
|
|
|
'test-name',
|
|
|
|
|
// source list
|
|
|
|
|
"* Foo\n** Foo 1\n** Example\n* Bar\n** Bar 1",
|
|
|
|
|
// other
|
|
|
|
|
'other reasons',
|
|
|
|
|
// selected
|
|
|
|
|
'Example',
|
|
|
|
|
// class
|
|
|
|
|
'test-css',
|
|
|
|
|
// tabindex
|
|
|
|
|
2
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
}
|
2017-07-12 19:31:33 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @covers Xml::listDropDownOptions
|
|
|
|
|
*/
|
|
|
|
|
public function testListDropDownOptions() {
|
|
|
|
|
$this->assertEquals(
|
|
|
|
|
[
|
|
|
|
|
'other reasons' => 'other',
|
|
|
|
|
'Foo' => [
|
|
|
|
|
'Foo 1' => 'Foo 1',
|
|
|
|
|
'Example' => 'Example',
|
|
|
|
|
],
|
|
|
|
|
'Bar' => [
|
|
|
|
|
'Bar 1' => 'Bar 1',
|
|
|
|
|
],
|
|
|
|
|
],
|
|
|
|
|
Xml::listDropDownOptions(
|
|
|
|
|
"* Foo\n** Foo 1\n** Example\n* Bar\n** Bar 1",
|
|
|
|
|
[ 'other' => 'other reasons' ]
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-10 18:24:38 +00:00
|
|
|
/**
|
|
|
|
|
* @covers Xml::listDropDownOptions
|
|
|
|
|
*/
|
|
|
|
|
public function testListDropDownOptionsOthers() {
|
|
|
|
|
// Do not use the value for 'other' as option group - T251351
|
|
|
|
|
$this->assertEquals(
|
|
|
|
|
[
|
|
|
|
|
'other reasons' => 'other',
|
|
|
|
|
'Foo 1' => 'Foo 1',
|
|
|
|
|
'Example' => 'Example',
|
|
|
|
|
'Bar' => [
|
|
|
|
|
'Bar 1' => 'Bar 1',
|
|
|
|
|
],
|
|
|
|
|
],
|
|
|
|
|
Xml::listDropDownOptions(
|
|
|
|
|
"* other reasons\n** Foo 1\n** Example\n* Bar\n** Bar 1",
|
|
|
|
|
[ 'other' => 'other reasons' ]
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2017-07-12 19:31:33 +00:00
|
|
|
/**
|
|
|
|
|
* @covers Xml::listDropDownOptionsOoui
|
|
|
|
|
*/
|
|
|
|
|
public function testListDropDownOptionsOoui() {
|
|
|
|
|
$this->assertEquals(
|
|
|
|
|
[
|
|
|
|
|
[ 'data' => 'other', 'label' => 'other reasons' ],
|
|
|
|
|
[ 'optgroup' => 'Foo' ],
|
|
|
|
|
[ 'data' => 'Foo 1', 'label' => 'Foo 1' ],
|
|
|
|
|
[ 'data' => 'Example', 'label' => 'Example' ],
|
|
|
|
|
[ 'optgroup' => 'Bar' ],
|
|
|
|
|
[ 'data' => 'Bar 1', 'label' => 'Bar 1' ],
|
|
|
|
|
],
|
|
|
|
|
Xml::listDropDownOptionsOoui( [
|
|
|
|
|
'other reasons' => 'other',
|
|
|
|
|
'Foo' => [
|
|
|
|
|
'Foo 1' => 'Foo 1',
|
|
|
|
|
'Example' => 'Example',
|
|
|
|
|
],
|
|
|
|
|
'Bar' => [
|
|
|
|
|
'Bar 1' => 'Bar 1',
|
|
|
|
|
],
|
|
|
|
|
] )
|
|
|
|
|
);
|
|
|
|
|
}
|
2017-09-17 17:05:40 +00:00
|
|
|
|
2020-12-21 02:15:17 +00:00
|
|
|
public function provideFieldset() {
|
|
|
|
|
// $expect, [ $arg1, $arg2, ... ]
|
|
|
|
|
yield 'Opening tag' => [ "<fieldset>\n", [] ];
|
|
|
|
|
yield 'Opening tag (false means no legend)' => [ "<fieldset>\n", [ false ] ];
|
|
|
|
|
yield 'Opening tag (empty string means no legend)' => [ "<fieldset>\n", [ '' ] ];
|
|
|
|
|
yield 'Opening tag with legend' => [
|
2017-09-17 17:05:40 +00:00
|
|
|
"<fieldset>\n<legend>Foo</legend>\n",
|
2020-12-21 02:15:17 +00:00
|
|
|
[ 'Foo' ]
|
|
|
|
|
];
|
|
|
|
|
yield 'Entire element with legend' => [
|
2017-09-17 17:05:40 +00:00
|
|
|
"<fieldset>\n<legend>Foo</legend>\nBar\n</fieldset>\n",
|
2020-12-21 02:15:17 +00:00
|
|
|
[ 'Foo', 'Bar' ]
|
|
|
|
|
];
|
|
|
|
|
yield 'Opening tag with legend (false means no content and no closing tag)' => [
|
2017-09-17 17:05:40 +00:00
|
|
|
"<fieldset>\n<legend>Foo</legend>\n",
|
2020-12-21 02:15:17 +00:00
|
|
|
[ 'Foo', false ]
|
|
|
|
|
];
|
|
|
|
|
yield 'Entire element with legend but no content (empty string generates a closing tag)' => [
|
2017-09-17 17:05:40 +00:00
|
|
|
"<fieldset>\n<legend>Foo</legend>\n\n</fieldset>\n",
|
2020-12-21 02:15:17 +00:00
|
|
|
[ 'Foo', '' ]
|
|
|
|
|
];
|
|
|
|
|
yield 'Opening tag with legend and attributes' => [
|
2017-09-17 17:05:40 +00:00
|
|
|
"<fieldset class=\"bar\">\n<legend>Foo</legend>\nBar\n</fieldset>\n",
|
2020-12-21 02:15:17 +00:00
|
|
|
[ 'Foo', 'Bar', [ 'class' => 'bar' ] ]
|
|
|
|
|
];
|
|
|
|
|
yield 'Entire element with legend and attributes' => [
|
2017-09-17 17:05:40 +00:00
|
|
|
"<fieldset class=\"bar\">\n<legend>Foo</legend>\n",
|
2020-12-21 02:15:17 +00:00
|
|
|
[ 'Foo', false, [ 'class' => 'bar' ] ]
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @covers Xml::fieldset
|
|
|
|
|
* @dataProvider provideFieldset
|
|
|
|
|
*/
|
|
|
|
|
public function testFieldset( string $expect, array $args ) {
|
|
|
|
|
$this->assertEquals(
|
|
|
|
|
$expect,
|
|
|
|
|
Xml::fieldset( ...$args )
|
2017-09-17 17:05:40 +00:00
|
|
|
);
|
|
|
|
|
}
|
2017-12-25 01:54:25 +00:00
|
|
|
|
|
|
|
|
/**
|
2017-12-25 03:16:26 +00:00
|
|
|
* @covers Xml::buildTable
|
2017-12-25 01:54:25 +00:00
|
|
|
*/
|
|
|
|
|
public function testBuildTable() {
|
|
|
|
|
$firstRow = [ 'foo', 'bar' ];
|
|
|
|
|
$secondRow = [ 'Berlin', 'Tehran' ];
|
|
|
|
|
$headers = [ 'header1', 'header2' ];
|
|
|
|
|
$expected = '<table id="testTable"><thead id="testTable"><th>header1</th>' .
|
|
|
|
|
'<th>header2</th></thead><tr><td>foo</td><td>bar</td></tr><tr><td>Berlin</td>' .
|
|
|
|
|
'<td>Tehran</td></tr></table>';
|
|
|
|
|
$this->assertEquals(
|
|
|
|
|
$expected,
|
|
|
|
|
Xml::buildTable(
|
|
|
|
|
[ $firstRow, $secondRow ],
|
|
|
|
|
[ 'id' => 'testTable' ],
|
|
|
|
|
$headers
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2017-12-25 03:16:26 +00:00
|
|
|
* @covers Xml::buildTableRow
|
2017-12-25 01:54:25 +00:00
|
|
|
*/
|
|
|
|
|
public function testBuildTableRow() {
|
|
|
|
|
$this->assertEquals(
|
|
|
|
|
'<tr id="testRow"><td>foo</td><td>bar</td></tr>',
|
|
|
|
|
Xml::buildTableRow( [ 'id' => 'testRow' ], [ 'foo', 'bar' ] )
|
|
|
|
|
);
|
|
|
|
|
}
|
2010-12-14 16:26:35 +00:00
|
|
|
}
|