diff options
Diffstat (limited to 'Echo/tests')
64 files changed, 713 insertions, 670 deletions
diff --git a/Echo/tests/browser/README.md b/Echo/tests/browser/README.md deleted file mode 100644 index 36319498..00000000 --- a/Echo/tests/browser/README.md +++ /dev/null @@ -1 +0,0 @@ -Please see https://github.com/wikimedia/mediawiki-selenium for instructions on how to run tests. diff --git a/Echo/tests/browser/ci.yml b/Echo/tests/browser/ci.yml deleted file mode 100644 index c2eca934..00000000 --- a/Echo/tests/browser/ci.yml +++ /dev/null @@ -1,9 +0,0 @@ -BROWSER: - - chrome - - firefox - -MEDIAWIKI_ENVIRONMENT: - - beta - -PLATFORM: - - Linux diff --git a/Echo/tests/browser/environments.yml b/Echo/tests/browser/environments.yml deleted file mode 100644 index c32e21bb..00000000 --- a/Echo/tests/browser/environments.yml +++ /dev/null @@ -1,48 +0,0 @@ -# Customize this configuration as necessary to provide defaults for various -# test environments. -# -# The set of defaults to use is determined by the MEDIAWIKI_ENVIRONMENT -# environment variable. -# -# export MEDIAWIKI_ENVIRONMENT=mw-vagrant-host -# bundle exec cucumber -# -# Additional variables set by the environment will override the corresponding -# defaults defined here. -# -# export MEDIAWIKI_ENVIRONMENT=mw-vagrant-host -# export MEDIAWIKI_USER=Selenium_user2 -# bundle exec cucumber -# -mw-vagrant-host: &default - user_factory: true - mediawiki_url: http://127.0.0.1:8080/wiki/ - -mw-vagrant-guest: - user_factory: true - mediawiki_url: http://127.0.0.1/wiki/ - -local: - mediawiki_user: Admin - mediawiki_password: vagrant - mediawiki_user_b: Selenium Echo user 2 - mediawiki_url: http://dev.wiki.local.wmftest.net:8080/wiki/ - -beta: - mediawiki_url: https://en.wikipedia.beta.wmflabs.org/wiki/ - mediawiki_user: Selenium_user - mediawiki_user_b: Selenium Echo user 2 - # mediawiki_password: SET THIS IN THE ENVIRONMENT! - -test2: - mediawiki_url: https://test2.wikipedia.org/wiki/ - mediawiki_user: Selenium_user - mediawiki_user_b: Selenium Echo user 2 - # mediawiki_password: SET THIS IN THE ENVIRONMENT! - -integration: - browser: chrome - user_factory: true - # mediawiki_url: THIS WILL BE SET BY JENKINS - -default: *default diff --git a/Echo/tests/browser/features/no_javascript.feature b/Echo/tests/browser/features/no_javascript.feature deleted file mode 100644 index a8b318ed..00000000 --- a/Echo/tests/browser/features/no_javascript.feature +++ /dev/null @@ -1,11 +0,0 @@ -@chrome @en.wikipedia.beta.wmflabs.org @firefox @localhost @vagrant -Feature: Basic features for no-js functionality - - Background: - Given I am using a nojs browser - - Scenario: Clicking alerts badge goes to Special:Notifications - Given I am logged in - When I click the alert badge - And I wait for the page to load - Then I am on Special Notifications page diff --git a/Echo/tests/browser/features/notifications.feature b/Echo/tests/browser/features/notifications.feature deleted file mode 100644 index 3d443a7d..00000000 --- a/Echo/tests/browser/features/notifications.feature +++ /dev/null @@ -1,22 +0,0 @@ -@chrome @en.wikipedia.beta.wmflabs.org @firefox @localhost @vagrant -Feature: Testing notification types - - Background: - Given I am logged in - And all my notifications are read - - Scenario: Someone mentions me - Given another user mentions me - When I refresh the page - Then the alert badge is showing unseen notifications - And the alert badge value is "Alert (1)" - - @skip - Scenario: Someone writes on my talk page - Given another user writes on my talk page - When I refresh the page - Then the alert badge is showing unseen notifications - And the alert badge value is "Alert (1)" - When I click the alert badge - And I see the alert popup - Then there are "1" unread notifications in the alert popup diff --git a/Echo/tests/browser/features/step_definition/badge_steps.rb b/Echo/tests/browser/features/step_definition/badge_steps.rb deleted file mode 100644 index 82590cb1..00000000 --- a/Echo/tests/browser/features/step_definition/badge_steps.rb +++ /dev/null @@ -1,10 +0,0 @@ -# Steps related to clicking and interacting with the badge -# Work in both nojs and js version - -Given(/^I click the alert badge$/) do - on(ArticlePage).alerts_element.when_present.click -end - -Given(/^I click the notice badge$/) do - on(ArticlePage).notices_element.when_present.click -end diff --git a/Echo/tests/browser/features/step_definition/no_javascript.rb b/Echo/tests/browser/features/step_definition/no_javascript.rb deleted file mode 100644 index 5cf97b1b..00000000 --- a/Echo/tests/browser/features/step_definition/no_javascript.rb +++ /dev/null @@ -1,21 +0,0 @@ -# This test has no javascript -# Therefore this test has no AJAX -# Therefore it should run without any "when_present" clauses -# If you need a "when_present" to make the test run, that is a bug - -Given(/^I am using a nojs browser$/) do - # The following user-agent string contains: - # SymbianOS: for RL to NOT load the modern experience - # SMART-TV-SamsungBrowser: to bypass mobile-frontend and stay on the desktop site - browser_factory.override(browser_user_agent: 'SymbianOS,SMART-TV-SamsungBrowser') -end - -Given(/^I wait for the page to load$/) do - # Wait for the page to load. We're technically clicking the <li> rather than <a>, - # so the special-case implicit wait after clicking links doesn't kick in. - browser.wait -end - -Given(/^I am on Special Notifications page$/) do - expect(on(SpecialNotificationsPage).firstHeading).to match('Notifications') -end diff --git a/Echo/tests/browser/features/step_definition/notifications_steps.rb b/Echo/tests/browser/features/step_definition/notifications_steps.rb deleted file mode 100644 index 5557c5db..00000000 --- a/Echo/tests/browser/features/step_definition/notifications_steps.rb +++ /dev/null @@ -1,76 +0,0 @@ -Given(/^all my notifications are read$/) do - clear_unread_notifications(@username) -end - -Given(/^I refresh the page$/) do - on(ArticlePage) do |page| - page.refresh - end -end - -Given(/^another user mentions me$/) do - message = '===Mention test===\nI am mentioning [[User:' + user(nil) + - ']] in this page to test Echo notifications. ~~~~' - as_user(:b) do - api.create_page( - @data_manager.get('Echo_test_page'), - message - ) - end -end - -Given(/^another user writes on my talk page$/) do - talk_page = "User_talk:#{user}" - message = '===Talk page test===\n' + - 'I am writing a message in your user page to test Echo notifications. ~~~~' - as_user(:b) do - api.create_page(talk_page, message) - end -end - -Given(/^the alert badge is showing unseen notifications$/) do - on(ArticlePage) do |page| - page.refresh_until do - page.alerts.badge_unseen_element.exists? - end - end -end - -Given(/^the notice badge is showing unseen notifications$/) do - on(ArticlePage) do |page| - page.refresh_until do - page.notices.badge_unseen_element.exists? - end - end -end - -Given(/^the alert badge value is "(.+)"$/) do |num| - on(ArticlePage) do |page| - page.refresh_until do - # `.text` doesn't work for invisible elements, and Selenium thinks the badge is invisible - page.alerts.badge_element.attribute('innerText') == num - end - end -end - -Given(/^the notice badge value is "(.+)"$/) do |num| - on(ArticlePage) do |page| - page.refresh_until do - page.notices.badge_element.attribute('innerText') == num - end - end -end - -Given(/^there are "(.+)" unread notifications in the notice popup$/) do |num| - on(ArticlePage) do |page| - page.notices.when_loaded - expect(page.notices.num_unread_notifications).to eq(num.to_i) - end -end - -Given(/^there are "(.+)" unread notifications in the alert popup$/) do |num| - on(ArticlePage) do |page| - page.alerts.when_loaded - expect(page.alerts.num_unread_notifications).to eq(num.to_i) - end -end diff --git a/Echo/tests/browser/features/step_definition/popup_steps.rb b/Echo/tests/browser/features/step_definition/popup_steps.rb deleted file mode 100644 index e6c8f0f0..00000000 --- a/Echo/tests/browser/features/step_definition/popup_steps.rb +++ /dev/null @@ -1,11 +0,0 @@ -Given(/^I see the alert popup$/) do - on(ArticlePage) do |page| - expect(page.alerts.title_element.when_present.text).to match('Alerts') - end -end - -Given(/^I see the message popup$/) do - on(ArticlePage) do |page| - expect(page.messages.title_element.when_present.text).to match('Messages') - end -end diff --git a/Echo/tests/browser/features/support/components/notifications.rb b/Echo/tests/browser/features/support/components/notifications.rb deleted file mode 100644 index 6f8d5ab8..00000000 --- a/Echo/tests/browser/features/support/components/notifications.rb +++ /dev/null @@ -1,21 +0,0 @@ -class Notifications - include PageObject - - link(:badge, css: '.mw-echo-notifications-badge') - link(:badge_unseen, css: '.mw-echo-unseen-notifications') - link(:mark_all_as_read, css: '.mw-echo-ui-notificationsListWidget-markAllReadButton > a') - div(:popup, css: '.mw-echo-ui-notificationBadgeButtonPopupWidget-popup') - span(:title, css: '.oo-ui-popupWidget-head > .oo-ui-labelElement-label') - div( - :notifications_container, - css: '.mw-echo-ui-notificationsListWidget > .mw-echo-ui-notificationItemWidget') - - def when_loaded - title_element.when_present - notifications_container_element.when_present - end - - def num_unread_notifications - div_elements(css: '.mw-echo-ui-notificationItemWidget-unread').size - end -end diff --git a/Echo/tests/browser/features/support/data_manager.rb b/Echo/tests/browser/features/support/data_manager.rb deleted file mode 100644 index 0ff6d66c..00000000 --- a/Echo/tests/browser/features/support/data_manager.rb +++ /dev/null @@ -1,11 +0,0 @@ -# Data manager for Echo tests -class DataManager - def initialize - @data = {} - end - - def get(part) - @data[part] = "#{part}_#{Random.srand}" unless @data.key? part - @data[part] - end -end diff --git a/Echo/tests/browser/features/support/echo_api_helper.rb b/Echo/tests/browser/features/support/echo_api_helper.rb deleted file mode 100644 index 77b7e5ab..00000000 --- a/Echo/tests/browser/features/support/echo_api_helper.rb +++ /dev/null @@ -1,25 +0,0 @@ -module EchoAPIHelper - def create_page_with_user(title, text, username) - as_user(username) do - api.create_page title, text - end - end - - def clear_unread_notifications(username) - as_user(username) do - api.action('echomarkread', token_type: 'csrf', all: '1') - end - end - - def update_seentime(username, notificationType) - as_user(username) do - api.action('echomarkseen', token_type: 'csrf', type: notificationType) - end - end - - def watch_page(username, pageTitle) - as_user(username) do - api.action('watch', token_type: 'watch', title: pageTitle) - end - end -end diff --git a/Echo/tests/browser/features/support/echo_pageobject_extension.rb b/Echo/tests/browser/features/support/echo_pageobject_extension.rb deleted file mode 100644 index 8e18a6e6..00000000 --- a/Echo/tests/browser/features/support/echo_pageobject_extension.rb +++ /dev/null @@ -1,9 +0,0 @@ -module PageObject - def refresh_until(timeout = PageObject.default_page_wait, message = nil) - platform.wait_until(timeout, message) do - yield.tap do |result| - refresh unless result - end - end - end -end diff --git a/Echo/tests/browser/features/support/env.rb b/Echo/tests/browser/features/support/env.rb deleted file mode 100644 index d7dbf3e0..00000000 --- a/Echo/tests/browser/features/support/env.rb +++ /dev/null @@ -1,10 +0,0 @@ -require 'mediawiki_selenium/cucumber' -require 'mediawiki_selenium/pages' -require 'mediawiki_selenium/step_definitions' - -def env_or_default(key, default) - ENV[key].nil? ? default : ENV[key].to_i -end - -PageObject.default_page_wait = env_or_default 'PAGE_WAIT_TIMEOUT', 60 -PageObject.default_element_wait = env_or_default 'ELEMENT_WAIT_TIMEOUT', 60 diff --git a/Echo/tests/browser/features/support/hooks.rb b/Echo/tests/browser/features/support/hooks.rb deleted file mode 100644 index 0d84341d..00000000 --- a/Echo/tests/browser/features/support/hooks.rb +++ /dev/null @@ -1,10 +0,0 @@ -# Allow running of bundle exec cucumber --dry-run -f stepdefs -require 'mediawiki_selenium' -require 'page-object' -require_relative 'echo_api_helper' -require_relative 'echo_pageobject_extension' -require_relative 'data_manager' - -World(EchoAPIHelper) - -Before { @data_manager = DataManager.new } diff --git a/Echo/tests/browser/features/support/pages/article_page.rb b/Echo/tests/browser/features/support/pages/article_page.rb deleted file mode 100644 index 25ffa043..00000000 --- a/Echo/tests/browser/features/support/pages/article_page.rb +++ /dev/null @@ -1,8 +0,0 @@ -class ArticlePage - include PageObject - - li(:alerts, css: '#pt-notifications-alert') - li(:notices, css: '#pt-notifications-notice') - page_section(:alerts, Notifications, css: '#pt-notifications-alert') - page_section(:notices, Notifications, css: '#pt-notifications-notice') -end diff --git a/Echo/tests/browser/features/support/pages/special_notifications_page.rb b/Echo/tests/browser/features/support/pages/special_notifications_page.rb deleted file mode 100644 index 3f6f0c25..00000000 --- a/Echo/tests/browser/features/support/pages/special_notifications_page.rb +++ /dev/null @@ -1,8 +0,0 @@ -# Special:Notifications page -class SpecialNotificationsPage - include PageObject - - page_url 'Special:Notifications' - - h1(:firstHeading, css: '.firstHeading') -end diff --git a/Echo/tests/phpunit/AttributeManagerTest.php b/Echo/tests/phpunit/AttributeManagerTest.php index f9b0c939..42ccb992 100644 --- a/Echo/tests/phpunit/AttributeManagerTest.php +++ b/Echo/tests/phpunit/AttributeManagerTest.php @@ -1,5 +1,8 @@ <?php +/** + * @covers EchoAttributeManager + */ class EchoAttributeManagerTest extends MediaWikiTestCase { public function testNewFromGlobalVars() { @@ -55,7 +58,7 @@ class EchoAttributeManagerTest extends MediaWikiTestCase { * @dataProvider getUserLocatorsProvider */ public function testGetUserLocators( $message, $expect, $type, $notifications ) { - $manager = new EchoAttributeManager( $notifications, [], [], [], [] ); + $manager = new EchoAttributeManager( $notifications, [], [], [] ); $result = $manager->getUserCallable( $type, EchoAttributeManager::ATTR_LOCATORS ); $this->assertEquals( $expect, $result, $message ); @@ -72,7 +75,7 @@ class EchoAttributeManagerTest extends MediaWikiTestCase { 'priority' => 10 ] ]; - $manager = new EchoAttributeManager( $notif, $category, [], [], [] ); + $manager = new EchoAttributeManager( $notif, $category, [], [] ); $this->assertTrue( $manager->getCategoryEligibility( $this->mockUser(), 'category_one' ) ); $category = [ 'category_one' => [ @@ -82,7 +85,7 @@ class EchoAttributeManagerTest extends MediaWikiTestCase { ] ] ]; - $manager = new EchoAttributeManager( $notif, $category, [], [], [] ); + $manager = new EchoAttributeManager( $notif, $category, [], [] ); $this->assertFalse( $manager->getCategoryEligibility( $this->mockUser(), 'category_one' ) ); } @@ -97,10 +100,10 @@ class EchoAttributeManagerTest extends MediaWikiTestCase { 'priority' => 10 ] ]; - $manager = new EchoAttributeManager( $notif, $category, [], [], [] ); + $manager = new EchoAttributeManager( $notif, $category, [], [] ); $this->assertEquals( $manager->getNotificationCategory( 'event_one' ), 'category_one' ); - $manager = new EchoAttributeManager( $notif, [], [], [], [] ); + $manager = new EchoAttributeManager( $notif, [], [], [] ); $this->assertEquals( $manager->getNotificationCategory( 'event_one' ), 'other' ); $notif = [ @@ -113,7 +116,7 @@ class EchoAttributeManagerTest extends MediaWikiTestCase { 'priority' => 10 ] ]; - $manager = new EchoAttributeManager( $notif, $category, [], [], [] ); + $manager = new EchoAttributeManager( $notif, $category, [], [] ); $this->assertEquals( $manager->getNotificationCategory( 'event_one' ), 'other' ); } @@ -135,7 +138,7 @@ class EchoAttributeManagerTest extends MediaWikiTestCase { ], 'category_four' => [] ]; - $manager = new EchoAttributeManager( $notif, $category, [], [], [] ); + $manager = new EchoAttributeManager( $notif, $category, [], [] ); $this->assertEquals( 6, $manager->getCategoryPriority( 'category_one' ) ); $this->assertEquals( 10, $manager->getCategoryPriority( 'category_two' ) ); $this->assertEquals( 10, $manager->getCategoryPriority( 'category_three' ) ); @@ -169,7 +172,7 @@ class EchoAttributeManagerTest extends MediaWikiTestCase { ], 'category_four' => [] ]; - $manager = new EchoAttributeManager( $notif, $category, [], [], [] ); + $manager = new EchoAttributeManager( $notif, $category, [], [] ); $this->assertEquals( 6, $manager->getNotificationPriority( 'event_one' ) ); $this->assertEquals( 10, $manager->getNotificationPriority( 'event_two' ) ); $this->assertEquals( 10, $manager->getNotificationPriority( 'event_three' ) ); @@ -221,7 +224,7 @@ class EchoAttributeManagerTest extends MediaWikiTestCase { * @dataProvider getEventsForSectionProvider */ public function testGetEventsForSection( $expected, $notificationTypes, $section, $message ) { - $am = new EchoAttributeManager( $notificationTypes, [], [], [], [] ); + $am = new EchoAttributeManager( $notificationTypes, [], [], [] ); $actual = $am->getEventsForSection( $section ); $this->assertEquals( $expected, $actual, $message ); } @@ -255,8 +258,9 @@ class EchoAttributeManagerTest extends MediaWikiTestCase { 'priority' => 10, ], ]; - $manager = new EchoAttributeManager( $notif, $category, [], [], [] ); - $this->assertEquals( $manager->getUserEnabledEvents( $this->mockUser(), 'web' ), [ 'event_two', 'event_three' ] ); + $manager = new EchoAttributeManager( $notif, $category, [], [] ); + $this->assertEquals( $manager->getUserEnabledEvents( $this->mockUser(), 'web' ), + [ 'event_two', 'event_three' ] ); } public function testGetUserEnabledEventsbySections() { @@ -287,7 +291,7 @@ class EchoAttributeManagerTest extends MediaWikiTestCase { 'priority' => 10 ], ]; - $manager = new EchoAttributeManager( $notif, $category, [], [], [] ); + $manager = new EchoAttributeManager( $notif, $category, [], [] ); $expected = [ 'event_one', 'event_three', 'event_four' ]; $actual = $manager->getUserEnabledEventsBySections( $this->mockUser(), 'web', [ 'alert' ] ); sort( $expected ); @@ -301,7 +305,8 @@ class EchoAttributeManagerTest extends MediaWikiTestCase { $this->assertEquals( $actual, $expected ); $expected = [ 'event_one', 'event_two', 'event_three', 'event_four' ]; - $actual = $manager->getUserEnabledEventsBySections( $this->mockUser(), 'web', [ 'message', 'alert' ] ); + $actual = $manager->getUserEnabledEventsBySections( $this->mockUser(), 'web', + [ 'message', 'alert' ] ); sort( $expected ); sort( $actual ); $this->assertEquals( $actual, $expected ); @@ -352,8 +357,13 @@ class EchoAttributeManagerTest extends MediaWikiTestCase { /** * @dataProvider getEventsByCategoryProvider */ - public function testGetEventsByCategory( $message, $expectedMapping, $categories, $notifications ) { - $am = new EchoAttributeManager( $notifications, $categories, [], [], [] ); + public function testGetEventsByCategory( + $message, + $expectedMapping, + $categories, + $notifications + ) { + $am = new EchoAttributeManager( $notifications, $categories, [], [] ); $actualMapping = $am->getEventsByCategory(); $this->assertEquals( $expectedMapping, $actualMapping, $message ); } @@ -398,8 +408,16 @@ class EchoAttributeManagerTest extends MediaWikiTestCase { /** @dataProvider isNotifyTypeAvailableForCategoryProvider */ - public function testIsNotifyTypeAvailableForCategory( $message, $expected, $categoryName, $notifyType, $defaultNotifyTypeAvailability, $notifyTypeAvailabilityByCategory ) { - $am = new EchoAttributeManager( [], [], $defaultNotifyTypeAvailability, $notifyTypeAvailabilityByCategory, [] ); + public function testIsNotifyTypeAvailableForCategory( + $message, + $expected, + $categoryName, + $notifyType, + $defaultNotifyTypeAvailability, + $notifyTypeAvailabilityByCategory + ) { + $am = new EchoAttributeManager( [], [], $defaultNotifyTypeAvailability, + $notifyTypeAvailabilityByCategory ); $actual = $am->isNotifyTypeAvailableForCategory( $categoryName, $notifyType ); $this->assertEquals( $expected, $actual, $message ); } @@ -445,8 +463,14 @@ class EchoAttributeManagerTest extends MediaWikiTestCase { /** * @dataProvider isNotifyTypeDismissableForCategoryProvider */ - public function testIsNotifyTypeDismissableForCategory( $message, $expected, $categories, $categoryName, $notifyType ) { - $am = new EchoAttributeManager( [], $categories, [], [], [] ); + public function testIsNotifyTypeDismissableForCategory( + $message, + $expected, + $categories, + $categoryName, + $notifyType + ) { + $am = new EchoAttributeManager( [], $categories, [], [] ); $actual = $am->isNotifyTypeDismissableForCategory( $categoryName, $notifyType ); $this->assertEquals( $expected, $actual, $message ); } diff --git a/Echo/tests/phpunit/BundlerTest.php b/Echo/tests/phpunit/BundlerTest.php index bd2f4499..a4a2e258 100644 --- a/Echo/tests/phpunit/BundlerTest.php +++ b/Echo/tests/phpunit/BundlerTest.php @@ -1,5 +1,8 @@ <?php +/** + * @covers Bundler + */ class BundlerTest extends MediaWikiTestCase { public function testBundle() { diff --git a/Echo/tests/phpunit/ContainmentSetTest.php b/Echo/tests/phpunit/ContainmentSetTest.php index 077e235b..1f538291 100644 --- a/Echo/tests/phpunit/ContainmentSetTest.php +++ b/Echo/tests/phpunit/ContainmentSetTest.php @@ -1,7 +1,9 @@ <?php /** + * @covers EchoContainmentSet * @group Echo + * @group Database */ class ContainmentSetTest extends MediaWikiTestCase { @@ -15,6 +17,9 @@ class ContainmentSetTest extends MediaWikiTestCase { $list->addArray( [ 'whammo' ] ); $this->assertTrue( $list->contains( 'whammo' ) ); + + $list->addArray( [ 0 ] ); + $this->assertFalse( $list->contains( 'baz' ) ); } public function testCachedListInnerListIsOnlyCalledOnce() { @@ -46,7 +51,7 @@ class ContainmentSetTest extends MediaWikiTestCase { } /** - * @Database + * @group Database */ public function testOnWikiList() { $this->editPage( 'User:Foo/Bar-baz', "abc\ndef\r\nghi\n\n\n" ); diff --git a/Echo/tests/phpunit/DiffParserTest.php b/Echo/tests/phpunit/DiffParserTest.php index 14dc8a7d..aeecef67 100644 --- a/Echo/tests/phpunit/DiffParserTest.php +++ b/Echo/tests/phpunit/DiffParserTest.php @@ -1,6 +1,7 @@ <?php /** + * @covers EchoDiffParser * @group Echo */ class EchoDiffParserTest extends MediaWikiTestCase { @@ -58,7 +59,7 @@ class EchoDiffParserTest extends MediaWikiTestCase { ], [ - 'Adding content seperated by no change must generate multiple changes', + 'Adding content separated by no change must generate multiple changes', // Expected change set [ self::mockAction( 'add', 'b1', 3 ), diff --git a/Echo/tests/phpunit/DiscussionParserTest.php b/Echo/tests/phpunit/DiscussionParserTest.php index 35da3fae..391ff078 100644 --- a/Echo/tests/phpunit/DiscussionParserTest.php +++ b/Echo/tests/phpunit/DiscussionParserTest.php @@ -1,16 +1,17 @@ <?php -use MediaWiki\MediaWikiServices; -use Wikimedia\ScopedCallback; +// phpcs:disable Generic.Files.LineLength -- Long html test examples + use Wikimedia\TestingAccessWrapper; /** + * @covers EchoDiscussionParser * @group Echo * @group Database */ class EchoDiscussionParserTest extends MediaWikiTestCase { /** - * @var array + * @var string[] */ protected $tablesUsed = [ 'user', 'revision', 'ip_changes', 'text', 'page' ]; @@ -19,7 +20,7 @@ class EchoDiscussionParserTest extends MediaWikiTestCase { * Can be setup one by one using the setupTestUser() method * Or all at once using the setupAllTestUsers() method * - * @var array [username => [user preference => preference value]] + * @var array[] [username => [user preference => preference value]] */ protected $testUsers = [ 'Werdna' => [ @@ -338,6 +339,23 @@ class EchoDiscussionParserTest extends MediaWikiTestCase { 'title' => 'MultipleSignatureMentions', 'expected' => [], ], + [ + 'new' => 1234, + 'old' => 123, + 'username' => 'Admin', + 'lang' => 'en', + 'pages' => [], + 'title' => 'Pings in summary', + 'expected' => [ + [ + 'type' => 'mention-summary', + 'agent' => 'Admin', + 'section-title' => null, + ] + ], + 'precondition' => '', + 'summary' => 'Hey [[User:Werdna|Werdna]] and [[User:Jorm]], [[User:Admin]] here', + ], ]; } @@ -345,7 +363,8 @@ class EchoDiscussionParserTest extends MediaWikiTestCase { * @dataProvider generateEventsForRevisionData */ public function testGenerateEventsForRevision( - $newId, $oldId, $username, $lang, $pages, $title, $expected, $precondition = '' + $newId, $oldId, $username, $lang, $pages, $title, $expected, $precondition = '', + $summary = '' ) { if ( $precondition !== '' ) { $result = $this->$precondition(); @@ -359,7 +378,7 @@ class EchoDiscussionParserTest extends MediaWikiTestCase { $this->setupAllTestUsers(); $revision = $this->setupTestRevisionsForEventGeneration( - $newId, $oldId, $username, $lang, $pages, $title + $newId, $oldId, $username, $lang, $pages, $title, $summary ); $events = []; $this->setupEventCallbackForEventGeneration( @@ -373,10 +392,14 @@ class EchoDiscussionParserTest extends MediaWikiTestCase { } ); - // disable mention failure and success notifications - $this->setMwGlobals( 'wgEchoMentionStatusNotifications', false ); + $this->setMwGlobals( [ + // disable mention failure and success notifications + 'wgEchoMentionStatusNotifications' => false, + // enable pings from summary + 'wgEchoMaxMentionsInEditSummary' => 5, + ] ); - EchoDiscussionParser::generateEventsForRevision( $revision ); + EchoDiscussionParser::generateEventsForRevision( $revision, false ); $this->assertEquals( $expected, $events ); } @@ -718,7 +741,7 @@ class EchoDiscussionParserTest extends MediaWikiTestCase { // enable multiple sections mentions $this->setMwGlobals( 'wgEchoMentionsOnMultipleSectionEdits', true ); - EchoDiscussionParser::generateEventsForRevision( $revision ); + EchoDiscussionParser::generateEventsForRevision( $revision, false ); $this->assertEquals( $expected, $events ); } @@ -920,39 +943,22 @@ TEXT 'wgEchoMaxMentionsCount' => 5 ] ); - EchoDiscussionParser::generateEventsForRevision( $revision ); + EchoDiscussionParser::generateEventsForRevision( $revision, false ); $this->assertEquals( $expected, $events ); } - private function setupTestRevisionsForEventGeneration( $newId, $oldId, $username, $lang, $pages, $title ) { - $langObj = Language::factory( $lang ); + private function setupTestRevisionsForEventGeneration( $newId, $oldId, $username, $lang, $pages, + $title, $summary = '' + ) { + // Content language is used by the code that interprets the namespace part of titles + // (Title::getTitleParser), so should be the fake language ;) + $this->setContentLang( $lang ); $this->setMwGlobals( [ - // this global is used by the code that interprets the namespace part of - // titles (Title::getTitleParser), so should be the fake language ;) - 'wgContLang' => $langObj, // this one allows Mediawiki:xyz pages to be set as messages 'wgUseDatabaseMessages' => true ] ); - - // Since we reset the $wgContLang global, reset the TitleParser service - $services = MediaWikiServices::getInstance(); - if ( is_callable( [ $services, 'getTitleParser' ] ) ) { - // TODO: All of this should use $this->setService() - $services->resetServiceForTesting( 'TitleParser' ); - $services->redefineService( 'TitleParser', function () use ( $langObj ) { - global $wgLocalInterwikis; - return new MediaWikiTitleCodec( - $langObj, - new GenderCache(), - $wgLocalInterwikis - ); - } ); - // Cleanup - $lock = new ScopedCallback( function () use ( $services ) { - $services->resetServiceForTesting( 'TitleParser' ); - } ); - } + $this->overrideMwServices(); // pages to be created: templates may be used to ping users (e.g. // {{u|...}}) but if we don't have that template, it just won't work! @@ -988,6 +994,7 @@ TEXT 'parent_id' => $oldId, 'text' => $newText, 'title' => $title, + 'comment' => $summary, ]; $revision = Revision::newFromRow( $row ); @@ -1501,9 +1508,13 @@ TEXT ]; } - public static function getExemplarTimestamp() { - $title = Title::newMainPage(); - $user = User::newFromName( 'Test' ); + public function getExemplarTimestamp() { + $title = $this->getMockBuilder( Title::class ) + ->getMock(); + + $user = $this->getMockBuilder( User::class ) + ->getMock(); + $options = new ParserOptions; global $wgParser; @@ -1795,7 +1806,7 @@ TEXT } protected function isParserFunctionsInstalled() { - if ( class_exists( 'ExtParserFunctions' ) ) { + if ( ExtensionRegistry::getInstance()->isLoaded( 'ParserFunctions' ) ) { return true; } else { return "ParserFunctions not enabled"; diff --git a/Echo/tests/phpunit/EchoDbFactoryTest.php b/Echo/tests/phpunit/EchoDbFactoryTest.php index 18ac1170..337c92d3 100644 --- a/Echo/tests/phpunit/EchoDbFactoryTest.php +++ b/Echo/tests/phpunit/EchoDbFactoryTest.php @@ -1,5 +1,11 @@ <?php +use Wikimedia\Rdbms\IDatabase; +use Wikimedia\Rdbms\ILoadBalancer; + +/** + * @covers MWEchoDbFactory + */ class MWEchoDbFactoryTest extends MediaWikiTestCase { public function testNewFromDefault() { @@ -13,8 +19,8 @@ class MWEchoDbFactoryTest extends MediaWikiTestCase { * @depends testNewFromDefault */ public function testGetEchoDb( MWEchoDbFactory $db ) { - $this->assertInstanceOf( 'DatabaseBase', $db->getEchoDb( DB_MASTER ) ); - $this->assertInstanceOf( 'DatabaseBase', $db->getEchoDb( DB_SLAVE ) ); + $this->assertInstanceOf( IDatabase::class, $db->getEchoDb( DB_MASTER ) ); + $this->assertInstanceOf( IDatabase::class, $db->getEchoDb( DB_REPLICA ) ); } /** @@ -24,7 +30,7 @@ class MWEchoDbFactoryTest extends MediaWikiTestCase { $reflection = new ReflectionClass( 'MWEchoDbFactory' ); $method = $reflection->getMethod( 'getLB' ); $method->setAccessible( true ); - $this->assertInstanceOf( 'LoadBalancer', $method->invoke( $db ) ); + $this->assertInstanceOf( ILoadBalancer::class, $method->invoke( $db ) ); } } diff --git a/Echo/tests/phpunit/EchoHooksTest.php b/Echo/tests/phpunit/EchoHooksTest.php new file mode 100644 index 00000000..d1f2ac56 --- /dev/null +++ b/Echo/tests/phpunit/EchoHooksTest.php @@ -0,0 +1,53 @@ +<?php + +class EchoHooksTest extends MediaWikiTestCase { + /** + * @covers \EchoHooks::onUserGetDefaultOptions() + */ + public function testOnUserGetDefaultOptions() { + $this->setMwGlobals( [ + 'wgEchoNotificationCategories' => [ + 'emailuser' => [ + 'priority' => 9, + 'tooltip' => 'echo-pref-tooltip-emailuser', + ], + 'mention' => [ + 'priority' => 4, + 'tooltip' => 'echo-pref-tooltip-mention', + ], + 'system' => [ + 'priority' => 9, + 'no-dismiss' => [ + 'all' + ], + ], + 'some-custom-category' => [ + 'priority' => 9001, + ], + ], + 'wgAllowHTMLEmail' => true, + ] ); + + $defaults = [ + 'something' => 'unrelated', + // T174220: don't overwrite defaults set elsewhere + 'echo-subscriptions-web-mention' => false, + ]; + EchoHooks::onUserGetDefaultOptions( $defaults ); + self::assertEquals( + [ + 'something' => 'unrelated', + 'echo-email-format' => 'html', + 'echo-subscriptions-email-mention' => false, + 'echo-subscriptions-web-mention' => false, + 'echo-subscriptions-email-emailuser' => false, + 'echo-subscriptions-web-emailuser' => true, + 'echo-subscriptions-email-system' => true, + 'echo-subscriptions-web-system' => true, + 'echo-subscriptions-email-some-custom-category' => false, + 'echo-subscriptions-web-some-custom-category' => true, + ], + $defaults + ); + } +} diff --git a/Echo/tests/phpunit/EchoSummaryParserTest.php b/Echo/tests/phpunit/EchoSummaryParserTest.php new file mode 100644 index 00000000..357036fc --- /dev/null +++ b/Echo/tests/phpunit/EchoSummaryParserTest.php @@ -0,0 +1,68 @@ +<?php + +/** + * @group Echo + */ +class EchoSummaryParserTest extends MediaWikiTestCase { + private $existingUsers = [ + 'Werdna', + 'Jorm', + 'Jim Carter', + ]; + /** + * @covers EchoSummaryParser::parse + * @dataProvider provideParse + * + * @param string $summary + * @param string[] $expectedUsers + */ + public function testParse( $summary, array $expectedUsers ) { + $parser = new EchoSummaryParser( function ( User $user ) { + if ( in_array( $user->getName(), $this->existingUsers ) ) { + return crc32( $user->getName() ); + } + return 0; + } ); + + $users = $parser->parse( $summary ); + foreach ( $users as $name => $user ) { + $this->assertType( User::class, $user ); + $this->assertEquals( $name, $user->getName() ); + } + + $users = array_keys( $users ); + + $this->assertArrayEquals( $expectedUsers, $users ); + } + + public function provideParse() { + return [ + [ '', [] ], + [ " \t\r\n ", [] ], + [ 'foo bar', [] ], + [ 'Werdna', [] ], + [ 'User:Werdna', [] ], + [ '[User:Werdna]', [] ], + [ '[[]]', [] ], + [ '[[:]]', [] ], + [ '[[|]]', [] ], + [ '[[:|]]', [] ], + [ '[[:|test]]', [] ], + [ '[[User:Nonexistent]]', [] ], + [ '/* [[User:Werdna */', [] ], + [ '[[User talk:Werdna]]', [] ], + [ '[[User:Werdna]]', [ 'Werdna' ] ], + [ 'this is [[ [[User:Werdna]] ]]', [ 'Werdna' ] ], + [ '[[User:Werdna|]]', [ 'Werdna' ] ], + [ '[[User:Werdna| ]]', [ 'Werdna' ] ], + [ '[[User:Werdna|Wer | d[n]a]]', [ 'Werdna' ] ], + [ '[[User:Werdna]][[User:Werdna]][[User:Werdna]]', [ 'Werdna' ] ], + [ '/**/[[User:Werdna]][[user:jorm]]', [ 'Werdna', 'Jorm' ] ], + [ '/* [[User:Werdna]] */ [[ user : jim_ Carter_]]', [ 'Jim Carter' ] ], + [ '[[User:/* Jorm */]][[User:/* remove me */Werdna]]', [] ], + [ '[[:User:Werdna]]', [] ], + [ '[[:User:Werdna|]]', [] ], + [ '[[:User:Werdna|foo]]', [] ], + ]; + } +} diff --git a/Echo/tests/phpunit/NotifUserTest.php b/Echo/tests/phpunit/NotifUserTest.php index d766a546..b25f3dc5 100644 --- a/Echo/tests/phpunit/NotifUserTest.php +++ b/Echo/tests/phpunit/NotifUserTest.php @@ -1,7 +1,9 @@ <?php + use MediaWiki\MediaWikiServices; /** + * @covers MWEchoNotifUser * @group Echo */ class MWEchoNotifUserTest extends MediaWikiTestCase { @@ -33,45 +35,6 @@ class MWEchoNotifUserTest extends MediaWikiTestCase { $this->assertInstanceOf( 'MWEchoNotifUser', $notifUser ); } - public function testFlagCacheWithNewTalkNotification() { - $notifUser = $this->newNotifUser(); - - $notifUser->flagCacheWithNewTalkNotification(); - $this->assertEquals( '1', $this->cache->get( $notifUser->getTalkNotificationCacheKey() ) ); - } - - public function testFlagCacheWithNoTalkNotification() { - $notifUser = $this->newNotifUser(); - - $notifUser->flagCacheWithNoTalkNotification(); - $this->assertEquals( '0', $this->cache->get( $notifUser->getTalkNotificationCacheKey() ) ); - } - - public function testNotifCountHasReachedMax() { - $notifUser = $this->newNotifUser(); - - if ( $notifUser->getLocalNotificationCount() > MWEchoNotifUser::MAX_BADGE_COUNT ) { - $this->assertTrue( $notifUser->notifCountHasReachedMax() ); - } else { - $this->assertFalse( $notifUser->notifCountHasReachedMax() ); - } - } - - public function testClearTalkNotification() { - $notifUser = $this->newNotifUser(); - - $notifUser->flagCacheWithNewTalkNotification(); - - $hasMax = $notifUser->notifCountHasReachedMax(); - - $notifUser->clearTalkNotification(); - if ( $hasMax ) { - $this->assertEquals( '1', $this->cache->get( $notifUser->getTalkNotificationCacheKey() ) ); - } else { - $this->assertEquals( '0', $this->cache->get( $notifUser->getTalkNotificationCacheKey() ) ); - } - } - public function testGetEmailFormat() { $user = User::newFromId( 2 ); $notifUser = MWEchoNotifUser::newFromUser( $user ); @@ -156,6 +119,12 @@ class MWEchoNotifUserTest extends MediaWikiTestCase { $gateway->expects( $this->any() ) ->method( 'markRead' ) ->will( $this->returnValue( $dbResult['markRead'] ) ); + $gateway->expects( $this->any() ) + ->method( 'getDB' ) + ->will( $this->returnValue( + $this->getMockBuilder( Database::class ) + ->disableOriginalConstructor()->getMock() + ) ); return $gateway; } @@ -171,15 +140,10 @@ class MWEchoNotifUserTest extends MediaWikiTestCase { return $mapper; } - public function mockEchoTargetPageMapper( array $result = [] ) { - $mapper = $this->getMockBuilder( 'EchoTargetPageMapper' ) + public function mockEchoTargetPageMapper() { + return $this->getMockBuilder( EchoTargetPageMapper::class ) ->disableOriginalConstructor() ->getMock(); - $mapper->expects( $this->any() ) - ->method( 'deleteByUserEvents' ) - ->will( $this->returnValue( $result ) ); - - return $mapper; } protected function mockEchoNotification() { diff --git a/Echo/tests/phpunit/NotificationStructureTest.php b/Echo/tests/phpunit/NotificationStructureTest.php new file mode 100644 index 00000000..17f5a5f3 --- /dev/null +++ b/Echo/tests/phpunit/NotificationStructureTest.php @@ -0,0 +1,40 @@ +<?php + +class NotificationStructureTest extends MediaWikiTestCase { + /** + * @coversNothing + * @dataProvider provideNotificationTypes + * + * @param string $type + * @param array $info + */ + public function testNotificationTypes( $type, array $info ) { + if ( isset( $info['presentation-model'] ) ) { + self::assertTrue( class_exists( $info['presentation-model'] ), + "Presentation model class {$info['presentation-model']} for {$type} must exist" + ); + } + + if ( isset( $info['user-locators'] ) ) { + $locators = $info['user-locators']; + $locator = reset( $locators ); + if ( is_array( $locator ) ) { + $locator = reset( $locator ); + } + self::assertTrue( is_callable( $locator ), + 'User locator ' . print_r( $locator, true ) . " for {$type} must be callable" + ); + } + } + + public function provideNotificationTypes() { + global $wgEchoNotifications; + + $result = []; + foreach ( $wgEchoNotifications as $type => $info ) { + $result[] = [ $type, $info ]; + } + + return $result; + } +} diff --git a/Echo/tests/NotificationsTest.php b/Echo/tests/phpunit/NotificationsTest.php index cc7ddfe6..641e6bd0 100644 --- a/Echo/tests/NotificationsTest.php +++ b/Echo/tests/phpunit/NotificationsTest.php @@ -24,14 +24,15 @@ class NotificationsTest extends MediaWikiTestCase { * @return EchoEvent */ public static function getLatestNotification( $user ) { - $notifs = ApiEchoNotifications::getNotifications( $user ); - $index = array_keys( $notifs ); + $notifMapper = new EchoNotificationMapper(); + $notifs = $notifMapper->fetchUnreadByUser( $user, 1, '', [ 'user-rights' ] ); + $notif = array_pop( $notifs ); - return EchoEvent::newFromID( $notifs[$index[0]]['id'] ); + return $notif->getEvent(); } /** - * @covers EchoHooks::onUserRights + * @covers EchoHooks::onUserGroupsChanged */ public function testUserRightsNotif() { $user = new User(); diff --git a/Echo/tests/phpunit/TalkPageFunctionalTest.php b/Echo/tests/phpunit/TalkPageFunctionalTest.php index 4c80f2e0..f33e5020 100644 --- a/Echo/tests/phpunit/TalkPageFunctionalTest.php +++ b/Echo/tests/phpunit/TalkPageFunctionalTest.php @@ -2,22 +2,26 @@ /** * @group Echo - * @group DataBase + * @group Database * @group medium */ class EchoTalkPageFunctionalTest extends ApiTestCase { + /** + * @var \Wikimedia\Rdbms\IDatabase + */ protected $dbr; protected function setUp() { parent::setUp(); - $this->dbr = MWEchoDbFactory::getDB( DB_SLAVE ); + $this->dbr = MWEchoDbFactory::getDB( DB_REPLICA ); } /** * Creates and updates a user talk page a few times to ensure proper events are * created. The user performing the edits is self::$users['sysop']. * @group Broken + * @covers \EchoDiscussionParser */ public function testAddCommentsToTalkPage() { $editor = self::$users['sysop']->getUser()->getName(); @@ -76,7 +80,7 @@ class EchoTalkPageFunctionalTest extends ApiTestCase { } /** - * @return array All events in db sorted from oldest to newest + * @return \stdClass[] All events in db sorted from oldest to newest */ protected function fetchAllEvents() { $res = $this->dbr->select( 'echo_event', [ '*' ], [], __METHOD__, [ 'ORDER BY' => 'event_id ASC' ] ); diff --git a/Echo/tests/phpunit/ThankYouEditTest.php b/Echo/tests/phpunit/ThankYouEditTest.php index a573bdb1..5325c5cd 100644 --- a/Echo/tests/phpunit/ThankYouEditTest.php +++ b/Echo/tests/phpunit/ThankYouEditTest.php @@ -2,6 +2,7 @@ /** * @group Echo + * @group Database */ class MWEchoThankYouEditTest extends MediaWikiTestCase { @@ -17,6 +18,9 @@ class MWEchoThankYouEditTest extends MediaWikiTestCase { $db->delete( 'echo_notification', '*', __METHOD__ ); } + /** + * @covers \EchoHooks::onPageContentSaveComplete + */ public function testFirstEdit() { // setup $this->deleteEchoData(); @@ -36,6 +40,9 @@ class MWEchoThankYouEditTest extends MediaWikiTestCase { $this->assertEquals( 1, $notification->getEvent()->getExtraParam( 'editCount', 'not found' ) ); } + /** + * @covers \EchoHooks::onPageContentSaveComplete + */ public function testTenthEdit() { // setup $this->deleteEchoData(); diff --git a/Echo/tests/phpunit/UserLocatorTest.php b/Echo/tests/phpunit/UserLocatorTest.php index f09f80a9..514c8226 100644 --- a/Echo/tests/phpunit/UserLocatorTest.php +++ b/Echo/tests/phpunit/UserLocatorTest.php @@ -1,7 +1,8 @@ <?php /** - * @Database + * @group Database + * @covers EchoUserLocator */ class EchoUserLocatorTest extends MediaWikiTestCase { diff --git a/Echo/tests/phpunit/api/ApiEchoMarkReadTest.php b/Echo/tests/phpunit/api/ApiEchoMarkReadTest.php index 135d1867..9c41ec37 100644 --- a/Echo/tests/phpunit/api/ApiEchoMarkReadTest.php +++ b/Echo/tests/phpunit/api/ApiEchoMarkReadTest.php @@ -4,15 +4,10 @@ * @group medium * @group API * @group Database - * @covers ApiQuery + * @covers ApiEchoMarkRead */ class ApiEchoMarkReadTest extends ApiTestCase { - protected function setUp() { - parent::setUp(); - $this->doLogin(); - } - function getTokens() { return $this->getTokenList( self::$users['sysop'] ); } diff --git a/Echo/tests/phpunit/api/ApiEchoNotificationsTest.php b/Echo/tests/phpunit/api/ApiEchoNotificationsTest.php index 9f86a6ef..cfcf5c90 100644 --- a/Echo/tests/phpunit/api/ApiEchoNotificationsTest.php +++ b/Echo/tests/phpunit/api/ApiEchoNotificationsTest.php @@ -3,7 +3,7 @@ /** * @group medium * @group API - * @covers ApiQuery + * @covers ApiEchoNotifications */ class ApiEchoNotificationsTest extends ApiTestCase { diff --git a/Echo/tests/phpunit/cache/TitleLocalCacheTest.php b/Echo/tests/phpunit/cache/TitleLocalCacheTest.php index af255edb..fdd722d3 100644 --- a/Echo/tests/phpunit/cache/TitleLocalCacheTest.php +++ b/Echo/tests/phpunit/cache/TitleLocalCacheTest.php @@ -1,90 +1,85 @@ <?php +use Wikimedia\TestingAccessWrapper; + +/** + * @covers EchoTitleLocalCache + * @group Database + */ class EchoTitleLocalCacheTest extends MediaWikiTestCase { public function testCreate() { $cache = EchoTitleLocalCache::create(); - $this->assertInstanceOf( 'EchoTitleLocalCache', $cache ); - - return $cache; + $this->assertInstanceOf( EchoTitleLocalCache::class, $cache ); } - /** - * @depends testCreate - */ - public function testAdd( $cache ) { - $cache->clearAll(); + public function testAdd() { + $cache = $this->getMockBuilder( EchoTitleLocalCache::class ) + ->setMethods( [ 'resolve' ] )->getMock(); + $cache->add( 1 ); - $this->assertEquals( count( $cache->getLookups() ), 1 ); - $this->assertArrayHasKey( 1, $cache->getLookups() ); + $cache->add( 9 ); + + // Resolutions should be batched + $cache->expects( $this->exactly( 1 ) )->method( 'resolve' ) + ->with( [ 1, 9 ] )->willReturn( [] ); + + // Trigger + $cache->get( 9 ); } - /** - * @depends testCreate - */ - public function testGet( $cache ) { - $map = new HashBagOStuff( [ 'maxKeys' => EchoLocalCache::TARGET_MAX_NUM ] ); - $titleIds = []; + public function testGet() { + $cache = $this->getMockBuilder( EchoTitleLocalCache::class ) + ->setMethods( [ 'resolve' ] )->getMock(); + $cachePriv = TestingAccessWrapper::newFromObject( $cache ); // First title included in cache - $res = $this->insertPage( 'EchoTitleLocalCacheTest_testGet' ); - $titleIds[$res['id']] = $res['id']; - $map->set( $res['id'], $res['title'] ); - + $res1 = $this->insertPage( 'EchoTitleLocalCacheTest_testGet1' ); + $cachePriv->targets->set( $res1['id'], $res1['title'] ); // Second title not in internal cache, resolves from db. - $res = $this->insertPage( 'EchoTitleLocalCacheTest_testGet2' ); - $titleIds[$res['id']] = $res['id']; - - $object = new \ReflectionObject( $cache ); - - // Load our generated map in as the targets (known mapping from - // title id to title object) into $cache - $targets = $object->getProperty( 'targets' ); - $targets->setAccessible( true ); - $targets->setValue( $cache, $map ); - - // Load both of the titles we are curious about into the list of titles - // to be looked up - $lookups = $object->getProperty( 'lookups' ); - $lookups->setAccessible( true ); - $lookups->setValue( $cache, $titleIds ); - - // Requesting the first object, which is within the known targets, should - // not resolve the pending lookups. - $this->assertInstanceOf( 'Title', $cache->get( reset( $titleIds ) ) ); - $this->assertGreaterThan( 0, count( $cache->getLookups() ) ); - - // Requesting the second object, which is not within the known targets, should - // resolve the pending lookups and reset the list to lookup. - $this->assertInstanceOf( 'Title', $cache->get( end( $titleIds ) ) ); - $this->assertEquals( 0, count( $cache->getLookups() ) ); + $res2 = $this->insertPage( 'EchoTitleLocalCacheTest_testGet2' ); + $cache->expects( $this->exactly( 1 ) )->method( 'resolve' ) + ->with( [ $res2['id'] ] ) + ->willReturn( [ $res2['id'] => $res2['title'] ] ); + + // Register demand for both + $cache->add( $res1['id'] ); + $cache->add( $res2['id'] ); + + // Should not call resolve() for first title + $this->assertSame( $res1['title'], $cache->get( $res1['id'] ), 'First title' ); + + // Should resolve() for second title + $this->assertSame( $res2['title'], $cache->get( $res2['id'] ), 'Second title' ); } - /** - * @depends testCreate - */ - public function testClearAll( $cache ) { - $map = new HashBagOStuff( [ 'maxKeys' => EchoLocalCache::TARGET_MAX_NUM ] ); - $map->set( 1, $this->mockTitle() ); - $object = new \ReflectionObject( $cache ); - $targets = $object->getProperty( 'targets' ); - $targets->setAccessible( true ); - $targets->setValue( $cache, $map ); - $lookups = $object->getProperty( 'lookups' ); - $lookups->setAccessible( true ); - $lookups->setValue( $cache, [ '1' => '1', '2' => '2' ] ); + public function testClearAll() { + $cache = $this->getMockBuilder( EchoTitleLocalCache::class ) + ->setMethods( [ 'resolve' ] )->getMock(); + // Add 1 to cache + $cachePriv = TestingAccessWrapper::newFromObject( $cache ); + $cachePriv->targets->set( 1, $this->mockTitle() ); + // Add 2 and 3 to demand + $cache->add( 2 ); + $cache->add( 3 ); $cache->clearAll(); - $this->assertTrue( count( $cache->getLookups() ) == 0 ); - $this->assertEquals( false, $cache->getTargets()->get( 1 ) ); - $this->assertEquals( false, $cache->getTargets()->get( '1' ) ); + + $this->assertSame( null, $cache->get( 1 ), 'Cache was cleared' ); + + // Lookups batch was cleared + $cache->expects( $this->exactly( 1 ) )->method( 'resolve' ) + ->with( [ 4 ] ) + ->willReturn( [] ); + $cache->add( 4 ); + $cache->get( 4 ); } /** - * Mock object of Title + * @return Title */ protected function mockTitle() { - $title = $this->getMockBuilder( 'Title' ) + $title = $this->getMockBuilder( Title::class ) ->disableOriginalConstructor() ->getMock(); diff --git a/Echo/tests/phpunit/controller/NotificationControllerTest.php b/Echo/tests/phpunit/controller/NotificationControllerTest.php index 8a38a7e5..9e497024 100644 --- a/Echo/tests/phpunit/controller/NotificationControllerTest.php +++ b/Echo/tests/phpunit/controller/NotificationControllerTest.php @@ -1,5 +1,8 @@ <?php +/** + * @covers EchoNotificationController + */ class NotificationControllerTest extends MediaWikiTestCase { public function evaluateUserLocatorsProvider() { diff --git a/Echo/tests/phpunit/gateway/UserNotificationGatewayTest.php b/Echo/tests/phpunit/gateway/UserNotificationGatewayTest.php index 7e656e19..2cc4fd78 100644 --- a/Echo/tests/phpunit/gateway/UserNotificationGatewayTest.php +++ b/Echo/tests/phpunit/gateway/UserNotificationGatewayTest.php @@ -1,11 +1,14 @@ <?php +/** + * @covers EchoUserNotificationGateway + */ class EchoUserNotificationGatewayTest extends MediaWikiTestCase { public function testMarkRead() { // no event ids to mark $gateway = new EchoUserNotificationGateway( User::newFromId( 1 ), $this->mockMWEchoDbFactory() ); - $this->assertNull( $gateway->markRead( [] ) ); + $this->assertFalse( $gateway->markRead( [] ) ); // successful update $gateway = new EchoUserNotificationGateway( User::newFromId( 1 ), $this->mockMWEchoDbFactory( [ 'update' => true ] ) ); @@ -29,19 +32,19 @@ class EchoUserNotificationGatewayTest extends MediaWikiTestCase { public function testGetNotificationCount() { // unsuccessful select $gateway = new EchoUserNotificationGateway( $this->mockUser(), $this->mockMWEchoDbFactory( [ 'selectRowCount' => 0 ] ) ); - $this->assertEquals( 0, $gateway->getCappedNotificationCount( DB_SLAVE, [ 'event_one' ] ) ); + $this->assertEquals( 0, $gateway->getCappedNotificationCount( DB_REPLICA, [ 'event_one' ] ) ); // successful select of alert $gateway = new EchoUserNotificationGateway( $this->mockUser(), $this->mockMWEchoDbFactory( [ 'selectRowCount' => 2 ] ) ); - $this->assertEquals( 2, $gateway->getCappedNotificationCount( DB_SLAVE, [ 'event_one', 'event_two' ] ) ); + $this->assertEquals( 2, $gateway->getCappedNotificationCount( DB_REPLICA, [ 'event_one', 'event_two' ] ) ); // there is event, should return 0 $gateway = new EchoUserNotificationGateway( $this->mockUser(), $this->mockMWEchoDbFactory( [ 'selectRowCount' => 2 ] ) ); - $this->assertEquals( 0, $gateway->getCappedNotificationCount( DB_SLAVE, [] ) ); + $this->assertEquals( 0, $gateway->getCappedNotificationCount( DB_REPLICA, [] ) ); // successful select $gateway = new EchoUserNotificationGateway( $this->mockUser(), $this->mockMWEchoDbFactory( [ 'selectRowCount' => 3 ] ) ); - $this->assertEquals( 3, $gateway->getCappedNotificationCount( DB_SLAVE, [ 'event_one' ] ) ); + $this->assertEquals( 3, $gateway->getCappedNotificationCount( DB_REPLICA, [ 'event_one' ] ) ); } public function testGetUnreadNotifications() { @@ -93,7 +96,8 @@ class EchoUserNotificationGatewayTest extends MediaWikiTestCase { } /** - * Mock object of DatabaseMysql ( DatabaseBase ) + * Returns a mock database object + * @return \Wikimedia\Rdbms\IDatabase */ protected function mockDb( array $dbResult = [] ) { $dbResult += [ @@ -102,7 +106,7 @@ class EchoUserNotificationGatewayTest extends MediaWikiTestCase { 'selectRow' => '', 'selectRowCount' => '', ]; - $db = $this->getMockBuilder( 'DatabaseMysql' ) + $db = $this->getMockBuilder( 'DatabaseMysqli' ) ->disableOriginalConstructor() ->getMock(); $db->expects( $this->any() ) @@ -117,9 +121,10 @@ class EchoUserNotificationGatewayTest extends MediaWikiTestCase { $db->expects( $this->any() ) ->method( 'selectRowCount' ) ->will( $this->returnValue( $dbResult['selectRowCount'] ) ); + $numRows = is_array( $dbResult['select'] ) ? count( $dbResult['select'] ) : 0; $db->expects( $this->any() ) ->method( 'numRows' ) - ->will( $this->returnValue( count( $dbResult['select'] ) ) ); + ->will( $this->returnValue( $numRows ) ); return $db; } diff --git a/Echo/tests/phpunit/iterator/FilteredSequentialIteratorTest.php b/Echo/tests/phpunit/iterator/FilteredSequentialIteratorTest.php index b05e3229..7042d633 100644 --- a/Echo/tests/phpunit/iterator/FilteredSequentialIteratorTest.php +++ b/Echo/tests/phpunit/iterator/FilteredSequentialIteratorTest.php @@ -1,5 +1,9 @@ <?php +/** + * @covers EchoCallbackIterator + * @covers EchoFilteredSequentialIterator + */ class FilteredSequentialIteratorTest extends MediaWikiTestCase { public function testEchoCallbackIteratorDoesntBlowUp() { diff --git a/Echo/tests/phpunit/maintenance/SupressionMaintenanceTest.php b/Echo/tests/phpunit/maintenance/SupressionMaintenanceTest.php index 0a32d8af..029be88f 100644 --- a/Echo/tests/phpunit/maintenance/SupressionMaintenanceTest.php +++ b/Echo/tests/phpunit/maintenance/SupressionMaintenanceTest.php @@ -2,6 +2,7 @@ /** * @group Echo + * @covers EchoSuppressionRowUpdateGenerator */ class SuppressionMaintenanceTest extends MediaWikiTestCase { diff --git a/Echo/tests/phpunit/mapper/AbstractMapperTest.php b/Echo/tests/phpunit/mapper/AbstractMapperTest.php index 5c3a5826..02194a02 100644 --- a/Echo/tests/phpunit/mapper/AbstractMapperTest.php +++ b/Echo/tests/phpunit/mapper/AbstractMapperTest.php @@ -1,5 +1,8 @@ <?php +/** + * @covers EchoAbstractMapper + */ class EchoAbstractMapperTest extends MediaWikiTestCase { public function testAttachListener() { @@ -65,13 +68,3 @@ class EchoAbstractMapperTest extends MediaWikiTestCase { } } - -/** - * Create a stub class for testing the abstract class - */ -class EchoAbstractMapperStub extends EchoAbstractMapper { - - public function testMethod() { - } - -} diff --git a/Echo/tests/phpunit/mapper/EchoAbstractMapperStub.php b/Echo/tests/phpunit/mapper/EchoAbstractMapperStub.php new file mode 100644 index 00000000..359e97ff --- /dev/null +++ b/Echo/tests/phpunit/mapper/EchoAbstractMapperStub.php @@ -0,0 +1,11 @@ +<?php + +/** + * Create a stub class for testing the abstract class + */ +class EchoAbstractMapperStub extends EchoAbstractMapper { + + public function testMethod() { + } + +} diff --git a/Echo/tests/phpunit/mapper/EchoExecuteFirstArgumentStub.php b/Echo/tests/phpunit/mapper/EchoExecuteFirstArgumentStub.php new file mode 100644 index 00000000..1e2889e7 --- /dev/null +++ b/Echo/tests/phpunit/mapper/EchoExecuteFirstArgumentStub.php @@ -0,0 +1,18 @@ +<?php + +class EchoExecuteFirstArgumentStub implements PHPUnit_Framework_MockObject_Stub { + public function invoke( PHPUnit_Framework_MockObject_Invocation $invocation ) { + if ( !$invocation instanceof PHPUnit_Framework_MockObject_Invocation_Static ) { + throw new PHPUnit_Framework_Exception( 'wrong invocation type' ); + } + if ( !$invocation->arguments ) { + throw new PHPUnit_Framework_Exception( 'Method call must have an argument' ); + } + + return call_user_func( reset( $invocation->arguments ) ); + } + + public function toString() { + return 'return result of call_user_func on first invocation argument'; + } +} diff --git a/Echo/tests/phpunit/mapper/EventMapperTest.php b/Echo/tests/phpunit/mapper/EventMapperTest.php index e9528ca9..e81e183d 100644 --- a/Echo/tests/phpunit/mapper/EventMapperTest.php +++ b/Echo/tests/phpunit/mapper/EventMapperTest.php @@ -1,5 +1,8 @@ <?php +/** + * @covers EchoEventMapper + */ class EchoEventMapperTest extends MediaWikiTestCase { public function provideDataTestInsert() { @@ -99,7 +102,8 @@ class EchoEventMapperTest extends MediaWikiTestCase { } /** - * Mock object of DatabaseMysql ( DatabaseBase ) + * Returns a mock database object + * @return \Wikimedia\Rdbms\IDatabase */ protected function mockDb( array $dbResult ) { $dbResult += [ @@ -108,7 +112,7 @@ class EchoEventMapperTest extends MediaWikiTestCase { 'select' => '', 'selectRow' => '' ]; - $db = $this->getMockBuilder( 'DatabaseMysql' ) + $db = $this->getMockBuilder( 'DatabaseMysqli' ) ->disableOriginalConstructor() ->getMock(); $db->expects( $this->any() ) diff --git a/Echo/tests/phpunit/mapper/NotificationMapperTest.php b/Echo/tests/phpunit/mapper/NotificationMapperTest.php index 420c0934..62359a9c 100644 --- a/Echo/tests/phpunit/mapper/NotificationMapperTest.php +++ b/Echo/tests/phpunit/mapper/NotificationMapperTest.php @@ -1,5 +1,8 @@ <?php +/** + * @covers EchoNotificationMapper + */ class EchoNotificationMapperTest extends MediaWikiTestCase { /** @@ -39,8 +42,8 @@ class EchoNotificationMapperTest extends MediaWikiTestCase { $notifMapper = new EchoNotificationMapper( $this->mockMWEchoDbFactory( [ 'select' => $dbResult ] ) ); $res = $notifMapper->fetchUnreadByUser( $this->mockUser(), 10, null, '', [ 'test_event' ] ); - $this->assertTrue( is_array( $res ) ); - $this->assertGreaterThan( 0, count( $res ) ); + $this->assertInternalType( 'array', $res ); + $this->assertNotEmpty( $res ); foreach ( $res as $row ) { $this->assertInstanceOf( 'EchoNotification', $row ); } @@ -90,8 +93,8 @@ class EchoNotificationMapperTest extends MediaWikiTestCase { ) ); $res = $notifMapper->fetchByUser( $this->mockUser(), 10, '', [ 'test_event' ] ); - $this->assertTrue( is_array( $res ) ); - $this->assertGreaterThan( 0, count( $res ) ); + $this->assertInternalType( 'array', $res ); + $this->assertNotEmpty( $res ); foreach ( $res as $row ) { $this->assertInstanceOf( 'EchoNotification', $row ); } @@ -158,13 +161,36 @@ class EchoNotificationMapperTest extends MediaWikiTestCase { } public function testDeleteByUserEventOffset() { - $dbResult = [ 'delete' => true ]; - $notifMapper = new EchoNotificationMapper( $this->mockMWEchoDbFactory( $dbResult ) ); - $this->assertTrue( $notifMapper->deleteByUserEventOffset( User::newFromId( 1 ), 500 ) ); + $this->setMwGlobals( [ 'wgUpdateRowsPerQuery' => 4 ] ); + $mockDb = $this->getMockBuilder( 'DatabaseMysqli' ) + ->disableOriginalConstructor() + ->getMock(); + $mockDb->expects( $this->any() ) + ->method( 'selectFieldValues' ) + ->will( $this->returnValue( [ 1, 2, 3, 5, 8, 13, 21, 34, 55, 89 ] ) ); + $mockDb->expects( $this->exactly( 3 ) ) + ->method( 'delete' ) + ->withConsecutive( + [ + $this->equalTo( 'echo_notification' ), + $this->equalTo( [ 'notification_user' => 1, 'notification_event' => [ 1, 2, 3, 5 ] ] ), + $this->anything() + ], + [ + $this->equalTo( 'echo_notification' ), + $this->equalTo( [ 'notification_user' => 1, 'notification_event' => [ 8, 13, 21, 34 ] ] ), + $this->anything() + ], + [ + $this->equalTo( 'echo_notification' ), + $this->equalTo( [ 'notification_user' => 1, 'notification_event' => [ 55, 89 ] ] ), + $this->anything() + ] + ) + ->will( $this->returnValue( true ) ); - $dbResult = [ 'delete' => false ]; - $notifMapper = new EchoNotificationMapper( $this->mockMWEchoDbFactory( $dbResult ) ); - $this->assertFalse( $notifMapper->deleteByUserEventOffset( User::newFromId( 1 ), 500 ) ); + $notifMapper = new EchoNotificationMapper( $this->mockMWEchoDbFactory( $mockDb ) ); + $this->assertTrue( $notifMapper->deleteByUserEventOffset( User::newFromId( 1 ), 500 ) ); } /** @@ -203,20 +229,23 @@ class EchoNotificationMapperTest extends MediaWikiTestCase { /** * Mock object of MWEchoDbFactory + * @param array|\Wikimedia\Rdbms\IDatabase $dbResultOrMockDb */ - protected function mockMWEchoDbFactory( $dbResult ) { + protected function mockMWEchoDbFactory( $dbResultOrMockDb ) { + $mockDb = is_array( $dbResultOrMockDb ) ? $this->mockDb( $dbResultOrMockDb ) : $dbResultOrMockDb; $dbFactory = $this->getMockBuilder( 'MWEchoDbFactory' ) ->disableOriginalConstructor() ->getMock(); $dbFactory->expects( $this->any() ) ->method( 'getEchoDb' ) - ->will( $this->returnValue( $this->mockDb( $dbResult ) ) ); + ->will( $this->returnValue( $mockDb ) ); return $dbFactory; } /** - * Mock object of DatabaseMysql ( DatabaseBase ) + * Returns a mock database object + * @return \Wikimedia\Rdbms\IDatabase */ protected function mockDb( array $dbResult ) { $dbResult += [ @@ -226,7 +255,7 @@ class EchoNotificationMapperTest extends MediaWikiTestCase { 'delete' => '' ]; - $db = $this->getMockBuilder( 'DatabaseMysql' ) + $db = $this->getMockBuilder( 'DatabaseMysqli' ) ->disableOriginalConstructor() ->getMock(); $db->expects( $this->any() ) @@ -249,20 +278,3 @@ class EchoNotificationMapperTest extends MediaWikiTestCase { } } - -class EchoExecuteFirstArgumentStub implements PHPUnit_Framework_MockObject_Stub { - public function invoke( PHPUnit_Framework_MockObject_Invocation $invocation ) { - if ( !$invocation instanceof PHPUnit_Framework_MockObject_Invocation_Static ) { - throw new PHPUnit_Framework_Exception( 'wrong invocation type' ); - } - if ( !$invocation->arguments ) { - throw new PHPUnit_Framework_Exception( 'Method call must have an argument' ); - } - - return call_user_func( reset( $invocation->arguments ) ); - } - - public function toString() { - return 'return result of call_user_func on first invocation argument'; - } -} diff --git a/Echo/tests/phpunit/mapper/TargetPageMapperTest.php b/Echo/tests/phpunit/mapper/TargetPageMapperTest.php index 522ee34d..aaba3e3c 100644 --- a/Echo/tests/phpunit/mapper/TargetPageMapperTest.php +++ b/Echo/tests/phpunit/mapper/TargetPageMapperTest.php @@ -1,5 +1,8 @@ <?php +/** + * @covers EchoTargetPageMapper + */ class EchoTargetPageMapperTest extends MediaWikiTestCase { public function provideDataTestInsert() { @@ -35,16 +38,13 @@ class EchoTargetPageMapperTest extends MediaWikiTestCase { * Mock object of EchoTargetPage */ protected function mockEchoTargetPage() { - $target = $this->getMockBuilder( 'EchoTargetPage' ) + $target = $this->getMockBuilder( EchoTargetPage::class ) ->disableOriginalConstructor() ->getMock(); $target->expects( $this->any() ) ->method( 'toDbArray' ) ->will( $this->returnValue( [] ) ); $target->expects( $this->any() ) - ->method( 'getUser' ) - ->will( $this->returnValue( User::newFromId( 1 ) ) ); - $target->expects( $this->any() ) ->method( 'getPageId' ) ->will( $this->returnValue( 2 ) ); $target->expects( $this->any() ) @@ -69,7 +69,8 @@ class EchoTargetPageMapperTest extends MediaWikiTestCase { } /** - * Mock object of DatabaseMysql ( DatabaseBase ) + * Returns a mock database object + * @return \Wikimedia\Rdbms\IDatabase */ protected function mockDb( array $dbResult ) { $dbResult += [ @@ -78,7 +79,7 @@ class EchoTargetPageMapperTest extends MediaWikiTestCase { 'select' => '', 'delete' => '' ]; - $db = $this->getMockBuilder( 'DatabaseMysql' ) + $db = $this->getMockBuilder( 'DatabaseMysqli' ) ->disableOriginalConstructor() ->getMock(); $db->expects( $this->any() ) diff --git a/Echo/tests/phpunit/model/NotificationTest.php b/Echo/tests/phpunit/model/NotificationTest.php index d6d45c3e..92522303 100644 --- a/Echo/tests/phpunit/model/NotificationTest.php +++ b/Echo/tests/phpunit/model/NotificationTest.php @@ -1,5 +1,8 @@ <?php +/** + * @covers EchoNotification + */ class EchoNotificationTest extends MediaWikiTestCase { public function testNewFromRow() { @@ -28,7 +31,7 @@ class EchoNotificationTest extends MediaWikiTestCase { $notif = EchoNotification::newFromRow( (object)$row, [ EchoTargetPage::newFromRow( (object)$this->mockTargetPageRow() ) ] ); - $this->assertGreaterThan( 0, count( $notif->getTargetPages() ) ); + $this->assertNotEmpty( $notif->getTargetPages() ); foreach ( $notif->getTargetPages() as $targetPage ) { $this->assertInstanceOf( 'EchoTargetPage', $targetPage ); } diff --git a/Echo/tests/phpunit/model/TargetPageTest.php b/Echo/tests/phpunit/model/TargetPageTest.php index a7f7c035..f925cfeb 100644 --- a/Echo/tests/phpunit/model/TargetPageTest.php +++ b/Echo/tests/phpunit/model/TargetPageTest.php @@ -1,5 +1,8 @@ <?php +/** + * @covers EchoTargetPage + */ class EchoTargetPageTest extends MediaWikiTestCase { public function testCreate() { diff --git a/Echo/tests/phpunit/revision_txt/123.txt b/Echo/tests/phpunit/revision_txt/123.txt new file mode 100644 index 00000000..257cc564 --- /dev/null +++ b/Echo/tests/phpunit/revision_txt/123.txt @@ -0,0 +1 @@ +foo diff --git a/Echo/tests/phpunit/revision_txt/1234.txt b/Echo/tests/phpunit/revision_txt/1234.txt new file mode 100644 index 00000000..d675fa44 --- /dev/null +++ b/Echo/tests/phpunit/revision_txt/1234.txt @@ -0,0 +1 @@ +foo bar diff --git a/Echo/tests/qunit/.eslintrc.json b/Echo/tests/qunit/.eslintrc.json new file mode 100644 index 00000000..348a1b7f --- /dev/null +++ b/Echo/tests/qunit/.eslintrc.json @@ -0,0 +1,6 @@ +{ + "extends": [ + "wikimedia/qunit", + "../../.eslintrc.json" + ] +} diff --git a/Echo/tests/qunit/model/test_mw.echo.dm.BundleNotificationItem.js b/Echo/tests/qunit/model/test_mw.echo.dm.BundleNotificationItem.js index 66e259c6..22a4d59b 100644 --- a/Echo/tests/qunit/model/test_mw.echo.dm.BundleNotificationItem.js +++ b/Echo/tests/qunit/model/test_mw.echo.dm.BundleNotificationItem.js @@ -15,13 +15,13 @@ { modelName: 'foo' } ); - assert.equal( + assert.strictEqual( bundle.getCount(), 5, 'Bundled items added to internal list' ); - assert.equal( + assert.strictEqual( bundle.getName(), 'foo', 'Bundle name stored' @@ -33,13 +33,13 @@ 'All ids present' ); - assert.equal( + assert.strictEqual( bundle.isRead(), false, 'Bundle with all unread items is unread' ); - assert.equal( + assert.strictEqual( bundle.hasUnseen(), true, 'Bundle has unseen items' @@ -74,7 +74,7 @@ } ); - assert.equal( + assert.strictEqual( bundle.hasUnseen(), true, 'Bundle has unseen' @@ -85,20 +85,20 @@ bundledItems[ i ].toggleSeen( true ); } - assert.equal( + assert.strictEqual( bundle.hasUnseen(), false, 'Bundle does not have unseen after all items marked as seen' ); - assert.equal( + assert.strictEqual( bundle.isRead(), false, 'Bundle is unread' ); // Mark one item as read bundledItems[ 0 ].toggleRead( true ); - assert.equal( + assert.strictEqual( bundle.isRead(), false, 'Bundle is still unread if it has some unread items' @@ -108,7 +108,7 @@ for ( i = 0; i < bundledItems.length; i++ ) { bundledItems[ i ].toggleRead( true ); } - assert.equal( + assert.strictEqual( bundle.isRead(), true, 'Bundle is marked as read if all items are read' diff --git a/Echo/tests/qunit/model/test_mw.echo.dm.CrossWikiNotificationItem.js b/Echo/tests/qunit/model/test_mw.echo.dm.CrossWikiNotificationItem.js index 6073a1a5..add303e2 100644 --- a/Echo/tests/qunit/model/test_mw.echo.dm.CrossWikiNotificationItem.js +++ b/Echo/tests/qunit/model/test_mw.echo.dm.CrossWikiNotificationItem.js @@ -116,7 +116,7 @@ [ 'baz', 'bar', 'foo' ], 'Model source names exist in order' ); - assert.equal( + assert.strictEqual( model.hasUnseen(), true, 'hasUnseen is true if there are unseen items in any group' @@ -129,14 +129,14 @@ } } groupDefinitions[ 0 ].items[ 0 ].toggleSeen( false ); - assert.equal( + assert.strictEqual( model.hasUnseen(), true, 'hasUnseen is true even if only one item in one group is unseen' ); groupDefinitions[ 0 ].items[ 0 ].toggleSeen( true ); - assert.equal( + assert.strictEqual( model.hasUnseen(), false, 'hasUnseen is false if there are no unseen items in any of the groups' @@ -209,7 +209,7 @@ numUnseenItems = model.getItems().filter( function ( item ) { return !item.isSeen(); } ).length; - assert.equal( + assert.strictEqual( numUnseenItems, numAllItems, 'Starting state: all items are unseen' @@ -221,7 +221,7 @@ numUnseenItems = model.getItems().filter( function ( item ) { return !item.isSeen(); } ).length; - assert.equal( + assert.strictEqual( numUnseenItems, numAllItems - groupDefinitions[ 0 ].items.length, 'Only some items are seen' @@ -233,7 +233,7 @@ numUnseenItems = model.getItems().filter( function ( item ) { return !item.isSeen(); } ).length; - assert.equal( + assert.strictEqual( numUnseenItems, 0, 'All items are seen' diff --git a/Echo/tests/qunit/model/test_mw.echo.dm.FiltersModel.js b/Echo/tests/qunit/model/test_mw.echo.dm.FiltersModel.js index c080e242..2bc8146c 100644 --- a/Echo/tests/qunit/model/test_mw.echo.dm.FiltersModel.js +++ b/Echo/tests/qunit/model/test_mw.echo.dm.FiltersModel.js @@ -52,28 +52,28 @@ QUnit.test( 'Changing filters', function ( assert ) { var model = new mw.echo.dm.FiltersModel(); - assert.equal( + assert.strictEqual( model.getReadState(), 'all', 'Initial value: all' ); model.setReadState( 'unread' ); - assert.equal( + assert.strictEqual( model.getReadState(), 'unread', 'Changing state (unread)' ); model.setReadState( 'read' ); - assert.equal( + assert.strictEqual( model.getReadState(), 'read', 'Changing state (read)' ); model.setReadState( 'foo' ); - assert.equal( + assert.strictEqual( model.getReadState(), 'read', 'Ignoring invalid state (foo)' diff --git a/Echo/tests/qunit/model/test_mw.echo.dm.NotificationGroupsList.js b/Echo/tests/qunit/model/test_mw.echo.dm.NotificationGroupsList.js index 63c381e8..c30a8adc 100644 --- a/Echo/tests/qunit/model/test_mw.echo.dm.NotificationGroupsList.js +++ b/Echo/tests/qunit/model/test_mw.echo.dm.NotificationGroupsList.js @@ -4,7 +4,7 @@ QUnit.test( 'Constructing the model', function ( assert ) { var model = new mw.echo.dm.NotificationGroupsList(); - assert.equal( + assert.strictEqual( model.getTimestamp(), 0, 'Empty group has timestamp 0' @@ -59,14 +59,14 @@ groupDefinitions[ i ].items ); - assert.equal( + assert.strictEqual( model.getItemCount(), i + 1, 'Group number increases after addGroup ("' + groupDefinitions[ i ].name + '")' ); group = model.getGroupByName( groupDefinitions[ i ].name ); - assert.equal( + assert.strictEqual( group.getName(), groupDefinitions[ i ].name, 'Group exists after addGroup ("' + groupDefinitions[ i ].name + '")' @@ -76,12 +76,12 @@ // Remove group model.removeGroup( groupDefinitions[ 0 ].name ); - assert.equal( + assert.strictEqual( model.getItemCount(), groupDefinitions.length - 1, 'Group number decreased after removeGroup' ); - assert.equal( + assert.strictEqual( model.getGroupByName( groupDefinitions[ 0 ] ), null, 'Removed group is no longer in the list' @@ -90,7 +90,7 @@ // Removing the last item from a group should remove the group group = model.getGroupByName( 'baz' ); group.discardItems( groupDefinitions[ 2 ].items ); - assert.equal( + assert.strictEqual( model.getGroupByName( 'baz' ), null, 'Empty group is no longer in the list' diff --git a/Echo/tests/qunit/model/test_mw.echo.dm.NotificationsList.js b/Echo/tests/qunit/model/test_mw.echo.dm.NotificationsList.js index 107d4fc0..02de8bc4 100644 --- a/Echo/tests/qunit/model/test_mw.echo.dm.NotificationsList.js +++ b/Echo/tests/qunit/model/test_mw.echo.dm.NotificationsList.js @@ -69,24 +69,24 @@ new mw.echo.dm.NotificationItem( 5, { type: 'message', timestamp: '201609190500', read: true, seen: false } ) ]; - assert.equal( + assert.strictEqual( model.getCount(), 0, 'Model list starts empty' ); - assert.equal( + assert.strictEqual( model.getTimestamp(), '200101010000', 'Model timestamp is its default' ); model.setItems( items ); - assert.equal( + assert.strictEqual( model.getCount(), 6, 'Item list setup' ); - assert.equal( + assert.strictEqual( model.getTimestamp(), '201609190100', 'Model timestamp is the latest unread item\'s timestamp' diff --git a/Echo/tests/qunit/model/test_mw.echo.dm.SourcePagesModel.js b/Echo/tests/qunit/model/test_mw.echo.dm.SourcePagesModel.js index 53f9b9de..7c98fc6a 100644 --- a/Echo/tests/qunit/model/test_mw.echo.dm.SourcePagesModel.js +++ b/Echo/tests/qunit/model/test_mw.echo.dm.SourcePagesModel.js @@ -82,12 +82,12 @@ model.setAllSources( sources ); - assert.equal( + assert.strictEqual( model.getCurrentSource(), 'local', 'Default source is local' ); - assert.equal( + assert.strictEqual( model.getCurrentPage(), null, 'Default page is null' @@ -97,12 +97,12 @@ [ 'local', 'hewiki' ], 'Source array includes all sources' ); - assert.equal( + assert.strictEqual( model.getSourceTitle( 'hewiki' ), 'Hebrew Wikipedia', 'Source title' ); - assert.equal( + assert.strictEqual( model.getSourceTotalCount( 'hewiki' ), 15, 'Source total count' @@ -157,12 +157,12 @@ // Change source model.setCurrentSourcePage( 'hewiki', 'User:Bar' ); - assert.equal( + assert.strictEqual( model.getCurrentSource(), 'hewiki', 'Source changed successfully' ); - assert.equal( + assert.strictEqual( model.getCurrentPage(), 'User:Bar', 'Page changed successfully' diff --git a/Echo/tests/qunit/model/test_mw.echo.dm.UnreadNotificationCounter.js b/Echo/tests/qunit/model/test_mw.echo.dm.UnreadNotificationCounter.js index 8b2662e1..42eb47aa 100644 --- a/Echo/tests/qunit/model/test_mw.echo.dm.UnreadNotificationCounter.js +++ b/Echo/tests/qunit/model/test_mw.echo.dm.UnreadNotificationCounter.js @@ -15,7 +15,7 @@ ]; for ( i = 0; i < cases.length; i++ ) { - assert.equal( + assert.strictEqual( model.getCappedNotificationCount( cases[ i ].input ), cases[ i ].output, 'Capped notifications count: ' + @@ -27,29 +27,29 @@ QUnit.test( 'Estimate change', function ( assert ) { var model = new mw.echo.dm.UnreadNotificationCounter( - null, - 'all', // type - 99 // max - ); + null, + 'all', // type + 99 // max + ); // Set initial model.setCount( 50 ); model.estimateChange( -10 ); - assert.equal( + assert.strictEqual( model.getCount(), 40, // 50-10 'Estimation within range' ); model.estimateChange( 70 ); - assert.equal( + assert.strictEqual( model.getCount(), 100, // Estimation reached above cap - cap is set 'Estimation brings count to cap' ); model.estimateChange( -10 ); - assert.equal( + assert.strictEqual( model.getCount(), 100, // We are already above cap, count will not change 'Estimation while counter is outside of cap - no change' diff --git a/Echo/tests/qunit/overlay/test_ext.echo.overlay.js b/Echo/tests/qunit/overlay/test_ext.echo.overlay.js index 213ffddd..aa0518a1 100644 --- a/Echo/tests/qunit/overlay/test_ext.echo.overlay.js +++ b/Echo/tests/qunit/overlay/test_ext.echo.overlay.js @@ -1,6 +1,6 @@ ( function ( $, mw ) { QUnit.module( 'ext.echo.overlay', { - setup: function () { + beforeEach: function () { var ApiStub; this.$badge = $( '<a class="mw-echo-notifications-badge mw-echo-unseen-notifications">1</a>' ); @@ -198,7 +198,7 @@ } } ); - QUnit.test( 'mw.echo.overlay.buildOverlay', 7, function ( assert ) { + QUnit.test( 'mw.echo.overlay.buildOverlay', function ( assert ) { var $overlay; this.sandbox.stub( mw.echo.overlay, 'api', new this.ApiStub() ); mw.echo.overlay.buildOverlay( function ( $o ) { @@ -216,7 +216,7 @@ false, 'The badge no longer indicates new messages.' ); } ); - QUnit.test( 'mw.echo.overlay.buildOverlay with messages', 5, function ( assert ) { + QUnit.test( 'mw.echo.overlay.buildOverlay with messages', function ( assert ) { var $overlay; this.sandbox.stub( mw.echo.overlay, 'api', new this.ApiStub( 'no-new-messages' ) ); mw.echo.overlay.buildOverlay( function ( $o ) { @@ -232,7 +232,7 @@ false, 'The notification button class is updated with the default switch to alert tab.' ); } ); - QUnit.test( 'Switch tabs on overlay. 1 unread alert, no unread messages.', 7, function ( assert ) { + QUnit.test( 'Switch tabs on overlay. 1 unread alert, no unread messages.', function ( assert ) { var $overlay, $tabs; this.sandbox.stub( mw.echo.overlay, 'api', new this.ApiStub( 'no-new-messages' ) ); @@ -259,7 +259,7 @@ true, 'Second tab has active class .as it is the only clickable tab' ); } ); - QUnit.test( 'Unread message behaviour', 5, function ( assert ) { + QUnit.test( 'Unread message behaviour', function ( assert ) { var $overlay; this.sandbox.stub( mw.echo.overlay, 'api', new this.ApiStub( 'with-new-messages' ) ); @@ -282,7 +282,7 @@ 'There are no notifications now so no need for button.' ); } ); - QUnit.test( 'Mark as read.', 8, function ( assert ) { + QUnit.test( 'Mark as read.', function ( assert ) { var $overlay; this.$badge.text( '8' ); this.sandbox.stub( mw.echo.overlay, 'api', new this.ApiStub( 'with-new-messages' ) ); @@ -311,7 +311,7 @@ assert.strictEqual( this.$badge.text(), '6', 'Now 6 unread notifications.' ); } ); - QUnit.test( 'Tabs when there is overflow.', 2, function ( assert ) { + QUnit.test( 'Tabs when there is overflow.', function ( assert ) { var $overlay; this.sandbox.stub( mw.echo.overlay, 'api', new this.ApiStub( 'with-new-messages', 50 ) ); mw.echo.overlay.buildOverlay( function ( $o ) { @@ -324,7 +324,7 @@ assert.strictEqual( $overlay.find( '.mw-echo-unread' ).length, 8, 'There are 8 unread notifications.' ); } ); - QUnit.test( 'Switching tabs visibility', 4, function ( assert ) { + QUnit.test( 'Switching tabs visibility', function ( assert ) { var $overlay; this.sandbox.stub( mw.echo.overlay, 'api', new this.ApiStub( 'with-new-messages' ) ); diff --git a/Echo/tests/rspec/README.md b/Echo/tests/rspec/README.md deleted file mode 100644 index 5cf77fbf..00000000 --- a/Echo/tests/rspec/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# Usage - - vagrant up - vagrant roles enable echo flow wikimediaflow - vagrant provision - bundle install - bundle exec rake spec diff --git a/Echo/tests/rspec/notification_spec.rb b/Echo/tests/rspec/notification_spec.rb deleted file mode 100644 index ff4b9a63..00000000 --- a/Echo/tests/rspec/notification_spec.rb +++ /dev/null @@ -1,55 +0,0 @@ -require 'mediawiki_api' - -describe 'Echo' do - before(:all) do - if ENV['JENKINS_HOME'] - # jenkins - @mediawiki_api = "#{ENV['MW_SERVER']}#{ENV['MW_SCRIPT_PATH']}/api.php" - @admin_username = 'WikiAdmin' - @admin_password = 'testpass' - else - # mediawiki-vagrant - @mediawiki_api = 'http://127.0.0.1:8080/w/api.php' - @admin_username = 'Admin' - @admin_password = 'vagrant' - end - - @client = MediawikiApi::Client.new @mediawiki_api - end - - before(:each) do - @client.log_in @admin_username, @admin_password - - require 'securerandom' - @random_username = "U#{SecureRandom.hex(5)}" - @random_password = SecureRandom.hex(5) - end - - it 'should notify a new user with welcome message' do - @client.create_account(@random_username, @random_password) - - @client.log_in @random_username, @random_password - notifications = @client.query(meta: 'notifications').data['notifications']['list'] - - welcome_notification = notifications.first - expect(welcome_notification['type']).to eq 'welcome' - expect(welcome_notification['agent']['name']).to eq @random_username - expect(welcome_notification['timestamp']['date']).to eq 'Today' - end - - it 'should notify user about mention on wikitext page' do - @client.create_account(@random_username, @random_password) - - page = SecureRandom.hex(5).capitalize - @client.edit(title: page, text: "[[User:#{@random_username}]] ~~~~") - - @client.log_in @random_username, @random_password - notifications = @client.query(meta: 'notifications').data['notifications']['list'] - - mention_notification = notifications.last - expect(mention_notification['type']).to eq 'mention' - expect(mention_notification['agent']['name']).to eq @admin_username - expect(mention_notification['title']['full']).to eq page - end - -end diff --git a/Echo/tests/selenium/.eslintrc.json b/Echo/tests/selenium/.eslintrc.json new file mode 100644 index 00000000..5efe15d0 --- /dev/null +++ b/Echo/tests/selenium/.eslintrc.json @@ -0,0 +1,14 @@ +{ + "extends": "wikimedia", + "env": { + "es6": true, + "mocha": true, + "node": true + }, + "globals": { + "browser": false + }, + "rules": { + "no-console": 0 + } +} diff --git a/Echo/tests/selenium/README.md b/Echo/tests/selenium/README.md new file mode 100644 index 00000000..59fa8eb4 --- /dev/null +++ b/Echo/tests/selenium/README.md @@ -0,0 +1,42 @@ +# Selenium tests + +Please see tests/selenium/README.md file in mediawiki/core repository, usually at mediawiki/vagrant/mediawiki folder. + +## Setup + +Set up MediaWiki-Vagrant: + + cd mediawiki/vagrant + vagrant up + vagrant roles enable echo + vagrant provision + cd mediawiki + npm install + +## Start Chromedriver and run all tests + +Run both mediawiki/core and extension tests from mediawiki/core repository (usually at mediawiki/vagrant/mediawiki folder): + + npm run selenium + +## Start Chromedriver + +To run only some tests, you first have to start Chromedriver in one terminal tab (or window): + + chromedriver --url-base=wd/hub --port=4444 + +## Run test(s) from one file + +Then, in another terminal tab (or window) run this from mediawiki/core repository (usually at mediawiki/vagrant/mediawiki folder): + + ./node_modules/.bin/wdio tests/selenium/wdio.conf.js --spec extensions/EXTENSION-NAME/tests/selenium/specs/FILE-NAME.js + +`wdio` is a dependency of mediawiki/core that you have installed with `npm install`. + +## Run specific test(s) + +To run only test(s) which name contains string TEST-NAME, run this from mediawiki/core repository (usually at mediawiki/vagrant/mediawiki folder): + + ./node_modules/.bin/wdio tests/selenium/wdio.conf.js --spec extensions/EXTENSION-NAME/tests/selenium/specs/FILE-NAME.js --mochaOpts.grep TEST-NAME + +Make sure Chromedriver is running when executing the above command. diff --git a/Echo/tests/selenium/pageobjects/echo.page.js b/Echo/tests/selenium/pageobjects/echo.page.js new file mode 100644 index 00000000..13a68e97 --- /dev/null +++ b/Echo/tests/selenium/pageobjects/echo.page.js @@ -0,0 +1,10 @@ +'use strict'; +const Page = require( 'wdio-mediawiki/Page' ); + +class EchoPage extends Page { + + get alerts() { return browser.element( '#pt-notifications-alert' ); } + get notices() { return browser.element( '#pt-notifications-notice' ); } + +} +module.exports = new EchoPage(); diff --git a/Echo/tests/selenium/specs/echo.js b/Echo/tests/selenium/specs/echo.js new file mode 100644 index 00000000..790109d0 --- /dev/null +++ b/Echo/tests/selenium/specs/echo.js @@ -0,0 +1,18 @@ +'use strict'; + +var assert = require( 'assert' ), + EchoPage = require( '../pageobjects/echo.page' ), + UserLoginPage = require( 'wdio-mediawiki/LoginPage' ); + +describe( 'Echo', function () { + + it( 'alerts and notices are visible after logging in', function () { + + UserLoginPage.login( browser.options.username, browser.options.password ); + + assert( EchoPage.alerts.isExisting() ); + assert( EchoPage.notices.isExisting() ); + + } ); + +} ); diff --git a/Echo/tests/selenium/wdio.conf.js b/Echo/tests/selenium/wdio.conf.js new file mode 100644 index 00000000..3b58beb6 --- /dev/null +++ b/Echo/tests/selenium/wdio.conf.js @@ -0,0 +1,88 @@ +/** + * See also: http://webdriver.io/guide/testrunner/configurationfile.html + */ +const fs = require( 'fs' ), + saveScreenshot = require( 'wdio-mediawiki' ).saveScreenshot; + +exports.config = { + // ====== + // Custom WDIO config specific to MediaWiki + // ====== + // Use in a test as `browser.options.<key>`. + // Defaults are for convenience with MediaWiki-Vagrant + + // Wiki admin + username: process.env.MEDIAWIKI_USER || 'Admin', + password: process.env.MEDIAWIKI_PASSWORD || 'vagrant', + + // Base for browser.url() and Page#openTitle() + baseUrl: ( process.env.MW_SERVER || 'http://127.0.0.1:8080' ) + ( + process.env.MW_SCRIPT_PATH || '/w' + ), + + // ================== + // Test Files + // ================== + specs: [ + __dirname + '/specs/*.js' + ], + + // ============ + // Capabilities + // ============ + capabilities: [ { + // https://sites.google.com/a/chromium.org/chromedriver/capabilities + browserName: 'chrome', + maxInstances: 1, + chromeOptions: { + // If DISPLAY is set, assume developer asked non-headless or CI with Xvfb. + // Otherwise, use --headless (added in Chrome 59) + // https://chromium.googlesource.com/chromium/src/+/59.0.3030.0/headless/README.md + args: [ + ...( process.env.DISPLAY ? [] : [ '--headless' ] ), + // Chrome sandbox does not work in Docker + ...( fs.existsSync( '/.dockerenv' ) ? [ '--no-sandbox' ] : [] ) + ] + } + } ], + + // =================== + // Test Configurations + // =================== + + // Level of verbosity: silent | verbose | command | data | result | error + logLevel: 'error', + + // Setting this enables automatic screenshots for when a browser command fails + // It is also used by afterTest for capturig failed assertions. + screenshotPath: process.env.LOG_DIR || __dirname + '/log', + + // Default timeout for each waitFor* command. + waitforTimeout: 10 * 1000, + + // See also: http://webdriver.io/guide/testrunner/reporters.html + reporters: [ 'spec' ], + + // See also: http://mochajs.org + mochaOpts: { + ui: 'bdd', + timeout: 60 * 1000 + }, + + // ===== + // Hooks + // ===== + + /** + * Save a screenshot when test fails. + * + * @param {Object} test Mocha Test object + */ + afterTest: function ( test ) { + var filePath; + if ( !test.passed ) { + filePath = saveScreenshot( test.title ); + console.log( '\n\tScreenshot: ' + filePath + '\n' ); + } + } +}; |