3 use MediaWiki\Block\DatabaseBlock
;
4 use MediaWiki\Context\DerivativeContext
;
5 use MediaWiki\Context\IContextSource
;
6 use MediaWiki\Context\RequestContext
;
7 use MediaWiki\DAO\WikiAwareEntity
;
8 use MediaWiki\MainConfigNames
;
9 use MediaWiki\Permissions\PermissionManager
;
10 use MediaWiki\Request\FauxRequest
;
11 use MediaWiki\Title\Title
;
12 use MediaWiki\User\User
;
20 * @license GPL-2.0-or-later
21 * @author Thiemo Kreuz
23 class ActionTest
extends MediaWikiIntegrationTestCase
{
25 protected function setUp(): void
{
28 $context = $this->getContext();
29 $this->overrideConfigValue(
30 MainConfigNames
::Actions
,
37 'access' => 'ControlledAccessDummyAction',
38 'unblock' => 'RequiresUnblockDummyAction',
39 'string' => 'NamedDummyAction',
40 'declared' => 'NonExistingClassName',
41 'callable' => [ $this, 'dummyActionCallback' ],
42 'object' => new InstantiatedDummyAction(
51 * @param string $requestedAction
52 * @param WikiPage|null $wikiPage
53 * @return Action|bool|null
55 private function getAction(
56 string $requestedAction,
57 ?WikiPage
$wikiPage = null
59 $context = $this->getContext( $requestedAction );
61 return Action
::factory(
63 $this->getArticle( $wikiPage, $context ),
69 * @param WikiPage|null $wikiPage
70 * @param IContextSource|null $context
73 private function getArticle(
74 ?WikiPage
$wikiPage = null,
75 ?IContextSource
$context = null
77 $context ??
= $this->getContext();
78 if ( $wikiPage !== null ) {
79 $context->setWikiPage( $wikiPage );
80 $context->setTitle( $wikiPage->getTitle() );
82 $wikiPage = $this->getPage();
85 return Article
::newFromWikiPage( $wikiPage, $context );
88 private function getPage(): WikiPage
{
89 $title = Title
::makeTitle( 0, 'Title' );
90 return $this->getServiceContainer()->getWikiPageFactory()->newFromTitle( $title );
94 * @param string|null $requestedAction
95 * @return IContextSource
97 private function getContext(
98 ?
string $requestedAction = null
100 $request = new FauxRequest( [ 'action' => $requestedAction ] );
102 $context = new DerivativeContext( RequestContext
::getMain() );
103 $context->setRequest( $request );
104 $context->setWikiPage( $this->getPage() );
109 public static function provideActions() {
111 [ 'dummy', 'DummyAction' ],
112 [ 'string', 'NamedDummyAction' ],
113 [ 'callable', 'CalledDummyAction' ],
114 [ 'object', 'InstantiatedDummyAction' ],
116 // Capitalization is ignored
117 [ 'DUMMY', 'DummyAction' ],
118 [ 'STRING', 'NamedDummyAction' ],
120 // non-existing values
122 [ 'undeclared', null ],
125 // disabled action exists but cannot be created
126 [ 'disabled', false ],
130 public static function provideGetActionName() {
132 'dummy' => [ 'dummy', 'DummyAction' ],
133 'string' => [ 'string', 'NamedDummyAction' ],
134 'callable' => [ 'callable', 'CalledDummyAction' ],
135 'object' => [ 'object', 'InstantiatedDummyAction' ],
137 // Capitalization is ignored
138 'dummy (caps)' => [ 'DUMMY', 'DummyAction' ],
139 'string (caps)' => [ 'STRING', 'NamedDummyAction' ],
141 // non-existing values
142 'null (string)' => [ 'null', 'nosuchaction' ],
143 'undeclared' => [ 'undeclared', 'nosuchaction' ],
144 'empty' => [ '', 'nosuchaction' ],
147 'null (value)' => [ null, 'view' ],
148 'false' => [ false, 'nosuchaction' ],
150 // Compatibility with old URLs
151 'editredlink' => [ 'editredlink', 'edit' ],
152 'historysubmit' => [ 'historysubmit', 'view' ],
154 'disabled not resolvable' => [ 'disabled', 'nosuchaction' ],
159 * @dataProvider provideGetActionName
160 * @param string $requestedAction
161 * @param string $expected
163 public function testGetActionName( $requestedAction, $expected ) {
164 $actionName = Action
::getActionName(
165 $this->getContext( $requestedAction )
167 $this->assertEquals( $expected, $actionName );
170 public function testGetActionName_whenCanNotUseWikiPage_defaultsToView() {
171 $request = new FauxRequest( [ 'action' => 'edit' ] );
172 $context = new DerivativeContext( RequestContext
::getMain() );
173 $context->setRequest( $request );
174 $actionName = Action
::getActionName( $context );
176 $this->assertEquals( 'view', $actionName );
180 * @covers \Action::factory
182 * @dataProvider provideActions
183 * @param string $requestedAction
184 * @param string|false|null $expected
186 public function testActionFactory( string $requestedAction, $expected ) {
187 $action = $this->getAction( $requestedAction );
189 if ( is_string( $expected ) ) {
190 $this->assertInstanceOf( $expected, $action );
192 $this->assertSame( $expected, $action );
196 public function dummyActionCallback() {
197 $article = $this->getArticle();
198 return new CalledDummyAction(
200 $article->getContext()
204 public function testCanExecute() {
205 $user = $this->getTestUser()->getUser();
206 $this->overrideUserPermissions( $user, 'access' );
207 $action = $this->getAction( 'access' );
208 $this->assertNull( $action->canExecute( $user ) );
211 public function testCanExecuteNoRight() {
212 $user = $this->getTestUser()->getUser();
213 $this->overrideUserPermissions( $user, [] );
214 $action = $this->getAction( 'access' );
215 $this->expectException( PermissionsError
::class );
216 $action->canExecute( $user );
219 public function testCanExecuteRequiresUnblock() {
220 $page = $this->getExistingTestPage();
221 $action = $this->getAction( 'unblock', $page );
223 $user = $this->createMock( User
::class );
225 $user->method( 'getWikiId' )->willReturn( WikiAwareEntity
::LOCAL
);
227 $block = new DatabaseBlock( [
229 'by' => $this->getTestSysop()->getUser(),
230 'expiry' => 'infinity',
234 $user->expects( $this->once() )
235 ->method( 'getBlock' )
236 ->willReturn( $block );
238 $permissionManager = $this->createMock( PermissionManager
::class );
239 $permissionManager->method( 'isBlockedFrom' )->willReturn( true );
240 $this->setService( 'PermissionManager', $permissionManager );
242 $this->expectException( UserBlockedError
::class );
243 $action->canExecute( $user );
248 class DummyAction
extends Action
{
250 public function getName() {
251 return static::class;
254 public function show() {
257 public function execute() {
260 public function canExecute( User
$user ) {
261 $this->checkCanExecute( $user );
265 class NamedDummyAction
extends DummyAction
{
268 class CalledDummyAction
extends DummyAction
{
271 class InstantiatedDummyAction
extends DummyAction
{
274 class ControlledAccessDummyAction
extends DummyAction
{
275 public function getRestriction() {
280 class RequiresUnblockDummyAction
extends DummyAction
{
281 public function requiresUnblock() {