10 class ApiOptionsTest
extends MediaWikiLangTestCase
{
12 /** @var PHPUnit_Framework_MockObject_MockObject */
14 /** @var ApiOptions */
17 /** @var DerivativeContext */
20 private static $Success = [ 'options' => 'success' ];
22 protected function setUp() {
25 $this->mUserMock
= $this->getMockBuilder( User
::class )
26 ->disableOriginalConstructor()
29 // Set up groups and rights
30 $this->mUserMock
->expects( $this->any() )
31 ->method( 'getEffectiveGroups' )->will( $this->returnValue( [ '*', 'user' ] ) );
32 $this->mUserMock
->expects( $this->any() )
33 ->method( 'isAllowedAny' )->will( $this->returnValue( true ) );
35 // Set up callback for User::getOptionKinds
36 $this->mUserMock
->expects( $this->any() )
37 ->method( 'getOptionKinds' )->will( $this->returnCallback( [ $this, 'getOptionKinds' ] ) );
40 $this->mUserMock
->expects( $this->any() )
41 ->method( 'getInstanceForUpdate' )->will( $this->returnValue( $this->mUserMock
) );
43 // Needs to return something
44 $this->mUserMock
->method( 'getOptions' )
47 // Create a new context
48 $this->mContext
= new DerivativeContext( new RequestContext() );
49 $this->mContext
->getContext()->setTitle( Title
::newFromText( 'Test' ) );
50 $this->mContext
->setUser( $this->mUserMock
);
52 $main = new ApiMain( $this->mContext
);
57 $this->mTested
= new ApiOptions( $main, 'options' );
59 $this->mergeMwGlobalArrayValue( 'wgHooks', [
61 [ $this, 'hookGetPreferences' ]
64 $this->mergeMwGlobalArrayValue( 'wgDefaultUserOptions', [
65 'testradio' => 'option1',
67 // Workaround for static caching in User::getDefaultOptions()
68 $this->setContentLang( Language
::factory( 'qqq' ) );
71 public function hookGetPreferences( $user, &$preferences ) {
74 foreach ( [ 'name', 'willBeNull', 'willBeEmpty', 'willBeHappy' ] as $k ) {
78 'label' => "\u{00A0}",
82 $preferences['testmultiselect'] = [
83 'type' => 'multiselect',
86 '<span dir="auto">Some HTML here for option 1</span>' => 'opt1',
87 '<span dir="auto">Some HTML here for option 2</span>' => 'opt2',
88 '<span dir="auto">Some HTML here for option 3</span>' => 'opt3',
89 '<span dir="auto">Some HTML here for option 4</span>' => 'opt4',
93 'label' => "\u{00A0}",
94 'prefix' => 'testmultiselect-',
98 $preferences['testradio'] = [
100 'options' => [ 'Option 1' => 'option1', 'Option 2' => 'option2' ],
106 * @param IContextSource $context
107 * @param array|null $options
111 public function getOptionKinds( IContextSource
$context, $options = null ) {
114 'name' => 'registered',
115 'willBeNull' => 'registered',
116 'willBeEmpty' => 'registered',
117 'willBeHappy' => 'registered',
118 'testradio' => 'registered',
119 'testmultiselect-opt1' => 'registered-multiselect',
120 'testmultiselect-opt2' => 'registered-multiselect',
121 'testmultiselect-opt3' => 'registered-multiselect',
122 'testmultiselect-opt4' => 'registered-multiselect',
123 'special' => 'special',
126 if ( $options === null ) {
131 foreach ( $options as $key => $value ) {
132 if ( isset( $kinds[$key] ) ) {
133 $mapping[$key] = $kinds[$key];
134 } elseif ( substr( $key, 0, 7 ) === 'userjs-' ) {
135 $mapping[$key] = 'userjs';
137 $mapping[$key] = 'unused';
144 private function getSampleRequest( $custom = [] ) {
148 'optionname' => null,
149 'optionvalue' => null,
152 return array_merge( $request, $custom );
155 private function executeQuery( $request ) {
156 $this->mContext
->setRequest( new FauxRequest( $request, true, $this->mSession
) );
157 $this->mTested
->execute();
159 return $this->mTested
->getResult()->getResultData( null, [ 'Strip' => 'all' ] );
163 * @expectedException ApiUsageException
165 public function testNoToken() {
166 $request = $this->getSampleRequest( [ 'token' => null ] );
168 $this->executeQuery( $request );
171 public function testAnon() {
172 $this->mUserMock
->expects( $this->once() )
174 ->will( $this->returnValue( true ) );
177 $request = $this->getSampleRequest();
179 $this->executeQuery( $request );
180 } catch ( ApiUsageException
$e ) {
181 $this->assertTrue( ApiTestCase
::apiExceptionHasCode( $e, 'notloggedin' ) );
184 $this->fail( "ApiUsageException was not thrown" );
187 public function testNoOptionname() {
189 $request = $this->getSampleRequest( [ 'optionvalue' => '1' ] );
191 $this->executeQuery( $request );
192 } catch ( ApiUsageException
$e ) {
193 $this->assertTrue( ApiTestCase
::apiExceptionHasCode( $e, 'nooptionname' ) );
196 $this->fail( "ApiUsageException was not thrown" );
199 public function testNoChanges() {
200 $this->mUserMock
->expects( $this->never() )
201 ->method( 'resetOptions' );
203 $this->mUserMock
->expects( $this->never() )
204 ->method( 'setOption' );
206 $this->mUserMock
->expects( $this->never() )
207 ->method( 'saveSettings' );
210 $request = $this->getSampleRequest();
212 $this->executeQuery( $request );
213 } catch ( ApiUsageException
$e ) {
214 $this->assertTrue( ApiTestCase
::apiExceptionHasCode( $e, 'nochanges' ) );
217 $this->fail( "ApiUsageException was not thrown" );
220 public function testReset() {
221 $this->mUserMock
->expects( $this->once() )
222 ->method( 'resetOptions' )
223 ->with( $this->equalTo( [ 'all' ] ) );
225 $this->mUserMock
->expects( $this->never() )
226 ->method( 'setOption' );
228 $this->mUserMock
->expects( $this->once() )
229 ->method( 'saveSettings' );
231 $request = $this->getSampleRequest( [ 'reset' => '' ] );
233 $response = $this->executeQuery( $request );
235 $this->assertEquals( self
::$Success, $response );
238 public function testResetKinds() {
239 $this->mUserMock
->expects( $this->once() )
240 ->method( 'resetOptions' )
241 ->with( $this->equalTo( [ 'registered' ] ) );
243 $this->mUserMock
->expects( $this->never() )
244 ->method( 'setOption' );
246 $this->mUserMock
->expects( $this->once() )
247 ->method( 'saveSettings' );
249 $request = $this->getSampleRequest( [ 'reset' => '', 'resetkinds' => 'registered' ] );
251 $response = $this->executeQuery( $request );
253 $this->assertEquals( self
::$Success, $response );
256 public function testResetChangeOption() {
257 $this->mUserMock
->expects( $this->once() )
258 ->method( 'resetOptions' );
260 $this->mUserMock
->expects( $this->exactly( 2 ) )
261 ->method( 'setOption' )
263 [ $this->equalTo( 'willBeHappy' ), $this->equalTo( 'Happy' ) ],
264 [ $this->equalTo( 'name' ), $this->equalTo( 'value' ) ]
267 $this->mUserMock
->expects( $this->once() )
268 ->method( 'saveSettings' );
272 'change' => 'willBeHappy=Happy',
273 'optionname' => 'name',
274 'optionvalue' => 'value'
277 $response = $this->executeQuery( $this->getSampleRequest( $args ) );
279 $this->assertEquals( self
::$Success, $response );
283 * @dataProvider provideOptionManupulation
284 * @param array $params
285 * @param array $setOptions
286 * @param array|null $result
288 public function testOptionManupulation( array $params, array $setOptions, array $result = null,
291 $this->mUserMock
->expects( $this->never() )
292 ->method( 'resetOptions' );
294 $this->mUserMock
->expects( $this->exactly( count( $setOptions ) ) )
295 ->method( 'setOption' )
296 ->withConsecutive( ...$setOptions );
299 $this->mUserMock
->expects( $this->once() )
300 ->method( 'saveSettings' );
302 $this->mUserMock
->expects( $this->never() )
303 ->method( 'saveSettings' );
306 $request = $this->getSampleRequest( $params );
307 $response = $this->executeQuery( $request );
310 $result = self
::$Success;
312 $this->assertEquals( $result, $response, $message );
315 public function provideOptionManupulation() {
318 [ 'change' => 'userjs-option=1' ],
319 [ [ 'userjs-option', '1' ] ],
321 'Setting userjs options',
324 [ 'change' => 'willBeNull|willBeEmpty=|willBeHappy=Happy' ],
326 [ 'willBeNull', null ],
327 [ 'willBeEmpty', '' ],
328 [ 'willBeHappy', 'Happy' ],
331 'Basic option setting',
334 [ 'change' => 'testradio=option2' ],
335 [ [ 'testradio', 'option2' ] ],
337 'Changing radio options',
340 [ 'change' => 'testradio' ],
341 [ [ 'testradio', null ] ],
343 'Resetting radio options',
346 [ 'change' => 'unknownOption=1' ],
349 'options' => 'success',
352 'warnings' => "Validation error for \"unknownOption\": not a valid preference."
356 'Unrecognized options should be rejected',
359 [ 'change' => 'special=1' ],
362 'options' => 'success',
365 'warnings' => "Validation error for \"special\": cannot be set by this module."
369 'Refuse setting special options',
373 'change' => 'testmultiselect-opt1=1|testmultiselect-opt2|'
374 . 'testmultiselect-opt3=|testmultiselect-opt4=0'
377 [ 'testmultiselect-opt1', true ],
378 [ 'testmultiselect-opt2', null ],
379 [ 'testmultiselect-opt3', false ],
380 [ 'testmultiselect-opt4', false ],
383 'Setting multiselect options',
386 [ 'optionname' => 'name', 'optionvalue' => 'value' ],
387 [ [ 'name', 'value' ] ],
389 'Setting options via optionname/optionvalue'
392 [ 'optionname' => 'name' ],
393 [ [ 'name', null ] ],
395 'Resetting options via optionname without optionvalue',