Page Menu
Home
WickedGov Phorge
Search
Configure Global Search
Log In
Files
F6000066
SpecialWatchlistTest.php
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
8 KB
Referenced Files
None
Subscribers
None
SpecialWatchlistTest.php
View Options
<?php
use
MediaWiki\Context\DerivativeContext
;
use
MediaWiki\Exception\UserNotLoggedIn
;
use
MediaWiki\HookContainer\HookContainer
;
use
MediaWiki\MainConfigNames
;
use
MediaWiki\Request\FauxRequest
;
use
MediaWiki\Specials\SpecialWatchlist
;
use
MediaWiki\User\StaticUserOptionsLookup
;
use
Wikimedia\Rdbms\LBFactorySingle
;
use
Wikimedia\TestingAccessWrapper
;
use
Wikimedia\Timestamp\ConvertibleTimestamp
;
/**
* @author Addshore
*
* @group Database
*
* @covers \MediaWiki\Specials\SpecialWatchlist
*/
class
SpecialWatchlistTest
extends
SpecialPageTestBase
{
protected
function
setUp
():
void
{
parent
::
setUp
();
$this
->
setTemporaryHook
(
'ChangesListSpecialPageQuery'
,
HookContainer
::
NOOP
);
$this
->
overrideConfigValues
(
[
MainConfigNames
::
DefaultUserOptions
=>
[
'extendwatchlist'
=>
1
,
'watchlistdays'
=>
3.0
,
'watchlisthideanons'
=>
0
,
'watchlisthidebots'
=>
0
,
'watchlisthideliu'
=>
0
,
'watchlisthideminor'
=>
0
,
'watchlisthideown'
=>
0
,
'watchlisthidepatrolled'
=>
1
,
'watchlisthidecategorization'
=>
0
,
'watchlistreloadautomatically'
=>
0
,
'watchlistunwatchlinks'
=>
0
,
'timecorrection'
=>
'0'
],
MainConfigNames
::
WatchlistExpiry
=>
true
]
);
}
/**
* Returns a new instance of the special page under test.
*
* @return SpecialWatchlist
*/
protected
function
newSpecialPage
()
{
$services
=
$this
->
getServiceContainer
();
return
new
SpecialWatchlist
(
$services
->
getWatchedItemStore
(),
$services
->
getWatchlistManager
(),
$services
->
getUserOptionsLookup
(),
$services
->
getUserIdentityUtils
(),
$services
->
getTempUserConfig
(),
$services
->
getRecentChangeFactory
(),
$services
->
getChangesListQueryFactory
(),
);
}
public
function
testNotLoggedIn_throwsException
()
{
$this
->
expectException
(
UserNotLoggedIn
::
class
);
$this
->
executeSpecialPage
();
}
public
function
testUserWithNoWatchedItems_displaysNoWatchlistMessage
()
{
$user
=
new
TestUser
(
__METHOD__
);
[
$html
,
]
=
$this
->
executeSpecialPage
(
''
,
null
,
'qqx'
,
$user
->
getUser
()
);
$this
->
assertStringContainsString
(
'(nowatchlist)'
,
$html
);
}
/**
* @dataProvider provideFetchOptionsFromRequest
*/
public
function
testFetchOptionsFromRequest
(
$expectedValuesDefaults
,
$expectedValues
,
$preferences
,
$inputParams
)
{
// $defaults and $allFalse are just to make the expected values below
// shorter by hiding the background.
/** @var SpecialWatchlist $page */
$page
=
TestingAccessWrapper
::
newFromObject
(
$this
->
newSpecialPage
()
);
$page
->
registerFilters
();
// Does not consider $preferences, just wiki's defaults
$wikiDefaults
=
$page
->
getDefaultOptions
()->
getAllValues
();
switch
(
$expectedValuesDefaults
)
{
case
'allFalse'
:
$allFalse
=
$wikiDefaults
;
foreach
(
$allFalse
as
$key
=>
$value
)
{
if
(
$value
===
true
)
{
$allFalse
[
$key
]
=
false
;
}
}
// This is not exposed on the form (only in preferences) so it
// respects the preference.
$allFalse
[
'extended'
]
=
true
;
$expectedValues
+=
$allFalse
;
break
;
case
'wikiDefaults'
:
$expectedValues
+=
$wikiDefaults
;
break
;
default
:
$this
->
fail
(
"Unknown
\$
expectedValuesDefaults: $expectedValuesDefaults"
);
}
$page
=
TestingAccessWrapper
::
newFromObject
(
$this
->
newSpecialPage
()
);
$context
=
new
DerivativeContext
(
$page
->
getContext
()
);
$fauxRequest
=
new
FauxRequest
(
$inputParams
,
/* $wasPosted= */
false
);
$user
=
$this
->
getTestUser
()->
getUser
();
$userOptionsManager
=
$this
->
getServiceContainer
()->
getUserOptionsManager
();
foreach
(
$preferences
as
$key
=>
$value
)
{
$userOptionsManager
->
setOption
(
$user
,
$key
,
$value
);
}
$context
->
setRequest
(
$fauxRequest
);
$context
->
setUser
(
$user
);
$page
->
setContext
(
$context
);
$page
->
registerFilters
();
$formOptions
=
$page
->
getDefaultOptions
();
$page
->
fetchOptionsFromRequest
(
$formOptions
);
$this
->
assertArrayEquals
(
$expectedValues
,
$formOptions
->
getAllValues
(),
/* $ordered= */
false
,
/* $named= */
true
);
}
public
static
function
provideFetchOptionsFromRequest
()
{
return
[
'ignores casing'
=>
[
'expectedValuesDefaults'
=>
'wikiDefaults'
,
'expectedValues'
=>
[
'hideminor'
=>
true
,
],
'preferences'
=>
[],
'inputParams'
=>
[
'hideMinor'
=>
1
,
],
],
'first two same as prefs, second two overridden'
=>
[
'expectedValuesDefaults'
=>
'wikiDefaults'
,
'expectedValues'
=>
[
// First two same as prefs
'hideminor'
=>
true
,
'hidebots'
=>
false
,
// Second two overridden
'hideanons'
=>
false
,
'hideliu'
=>
true
,
'userExpLevel'
=>
'registered'
],
'preferences'
=>
[
'watchlisthideminor'
=>
1
,
'watchlisthidebots'
=>
0
,
'watchlisthideanons'
=>
1
,
'watchlisthideliu'
=>
0
,
],
'inputParams'
=>
[
'hideanons'
=>
0
,
'hideliu'
=>
1
,
],
],
'Defaults/preferences for form elements are entirely ignored for '
.
'action=submit and omitted elements become false'
=>
[
'expectedValuesDefaults'
=>
'allFalse'
,
'expectedValues'
=>
[
'hideminor'
=>
false
,
'hidebots'
=>
true
,
'hideanons'
=>
false
,
'hideliu'
=>
true
,
'userExpLevel'
=>
'unregistered'
],
'preferences'
=>
[
'watchlisthideminor'
=>
0
,
'watchlisthidebots'
=>
1
,
'watchlisthideanons'
=>
0
,
'watchlisthideliu'
=>
1
,
],
'inputParams'
=>
[
'hidebots'
=>
1
,
'hideliu'
=>
1
,
'action'
=>
'submit'
,
],
],
];
}
/**
* Check the exact SQL used in the main query so that we can see if
* refactoring changes it.
*
* We could probably delete this when we are done with refactoring.
*/
public
function
testDoMainQuery
()
{
ConvertibleTimestamp
::
setFakeTime
(
'20250101000000'
);
$user
=
$this
->
getTestUser
()->
getUser
();
$db
=
new
DatabaseTestHelper
(
SpecialWatchlist
::
class
);
$this
->
setService
(
'ConnectionProvider'
,
LBFactorySingle
::
newFromConnection
(
$db
)
);
$userOptionsLookup
=
new
StaticUserOptionsLookup
(
[],
[
'wllimit'
=>
50
]
);
$this
->
setService
(
'UserOptionsLookup'
,
$userOptionsLookup
);
$page
=
$this
->
newSpecialPage
();
TestingAccessWrapper
::
newFromObject
(
$page
)->
userOptionsLookup
=
$userOptionsLookup
;
$page
->
getContext
()->
setUser
(
$user
);
$page
->
getRows
();
// Warning: significant line-ending whitespace
$expected
=
<<<SQL
SELECT
recentchanges_actor.actor_user AS rc_user,
recentchanges_actor.actor_name AS rc_user_text,
rc_bot,
rc_minor,
rc_this_oldid,
page_latest,
rc_source,
rc_log_type,
rc_timestamp,
rc_namespace,
rc_title,
wl_notificationtimestamp,
(
SELECT GROUP_CONCAT(ctd_name SEPARATOR ',')
FROM change_tag JOIN change_tag_def ON ((ct_tag_id=ctd_id))
WHERE (ct_rc_id=rc_id)
) AS ts_tags,
rc_id,
rc_cur_id,
rc_last_oldid,
rc_type,
rc_patrolled,
rc_ip,
rc_old_len,
rc_new_len,
rc_deleted,
rc_logid,
rc_log_action,
rc_params,
rc_actor,
recentchanges_comment.comment_text AS rc_comment_text,
recentchanges_comment.comment_data AS rc_comment_data,
recentchanges_comment.comment_id AS rc_comment_id,
we_expiry
FROM recentchanges
JOIN actor recentchanges_actor ON ((actor_id=rc_actor))
JOIN comment recentchanges_comment ON ((comment_id=rc_comment_id))
LEFT JOIN page ON ((page_id=rc_cur_id))
JOIN watchlist ON ((wl_namespace=rc_namespace) AND (wl_title=rc_title) AND wl_user = 1)
LEFT JOIN watchlist_expiry ON ((we_item=wl_id))
WHERE ((rc_this_oldid = page_latest OR rc_this_oldid = 0))
AND ((we_expiry IS NULL OR we_expiry > '20250101000000'))
AND ((rc_source != 'mw.log' OR (rc_deleted & 1) != 1))
AND (rc_timestamp >= '20250101000000')
ORDER BY rc_timestamp DESC,rc_id DESC
LIMIT 50
SQL;
$this
->
assertSame
(
$this
->
normalizeSql
(
$expected
),
$this
->
normalizeSql
(
$db
->
getLastSqls
()
)
);
}
private
function
normalizeSql
(
$sql
)
{
$sql
=
preg_replace
(
'/^
\t
*/m'
,
''
,
$sql
);
$sql
=
str_replace
(
"
\n
"
,
''
,
$sql
);
$sql
=
preg_replace
(
'/(?!
\'
),/'
,
",
\n
"
,
$sql
);
$sql
=
preg_replace
(
'/SELECT|FROM|LEFT JOIN|JOIN|AND|WHERE|ORDER BY|LIMIT/'
,
"
\n
$0"
,
$sql
);
return
$sql
;
}
/**
* Regression test for T407996
*/
public
function
testUnstructuredFilters
()
{
$user
=
$this
->
getTestUser
()->
getUser
();
$userOptionsLookup
=
new
StaticUserOptionsLookup
(
[],
[
'wlenhancedfilters-disable'
=>
true
,
'extendwatchlist'
=>
false
,
]
);
$this
->
setService
(
'UserOptionsLookup'
,
$userOptionsLookup
);
$page
=
$this
->
newSpecialPage
();
TestingAccessWrapper
::
newFromObject
(
$page
)->
userOptionsLookup
=
$userOptionsLookup
;
$page
->
getContext
()->
setUser
(
$user
);
$page
->
getRows
();
$this
->
assertTrue
(
$page
->
getFilterGroup
(
'extended-group'
)->
getFilter
(
'extended'
)
->
isActive
(
$page
->
getOptions
(),
false
)
);
}
}
File Metadata
Details
Attached
Mime Type
text/x-php
Expires
Oct 4 2026, 05:52 (5 w, 1 d ago)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
bf/9c/d3bbdd521b0e0ac1b0c7d38a1751
Default Alt Text
SpecialWatchlistTest.php (8 KB)
Attached To
Mode
rMWPROD MediaWiki Production
Attached
Detach File
Event Timeline
Log In to Comment