Merge "Remove LogEventsList::showHeader() (deprecated since 1.19)"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Wed, 25 Jun 2014 09:06:25 +0000 (09:06 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Wed, 25 Jun 2014 09:06:26 +0000 (09:06 +0000)
45 files changed:
RELEASE-NOTES-1.24
includes/page/Article.php
languages/Language.php
languages/i18n/bar.json
languages/i18n/be-tarask.json
languages/i18n/be.json
languages/i18n/cs.json
languages/i18n/egl.json
languages/i18n/es.json
languages/i18n/hr.json
languages/i18n/ia.json
languages/i18n/id.json
languages/i18n/pl.json
languages/i18n/qqq.json
languages/i18n/ro.json
languages/i18n/sr-ec.json
languages/i18n/uk.json
languages/i18n/yi.json
resources/src/mediawiki.page/mediawiki.page.patrol.ajax.js
tests/.gitignore [new file with mode: 0644]
tests/browser/Gemfile [new file with mode: 0755]
tests/browser/Gemfile.lock [new file with mode: 0644]
tests/browser/README.mediawiki [new file with mode: 0644]
tests/browser/environment_variables [new file with mode: 0644]
tests/browser/features/create_account.feature [new file with mode: 0644]
tests/browser/features/create_and_follow_wiki_link.feature [new file with mode: 0644]
tests/browser/features/edit_page.feature [new file with mode: 0644]
tests/browser/features/main_page_links.feature [new file with mode: 0644]
tests/browser/features/step_definitions/create_account_steps.rb [new file with mode: 0644]
tests/browser/features/step_definitions/create_and_follow_wiki_link_steps.rb [new file with mode: 0644]
tests/browser/features/step_definitions/edit_page_steps.rb [new file with mode: 0644]
tests/browser/features/step_definitions/main_page_links_steps.rb [new file with mode: 0644]
tests/browser/features/step_definitions/view_history_steps.rb [new file with mode: 0644]
tests/browser/features/support/env.rb [new file with mode: 0644]
tests/browser/features/support/hooks.rb [new file with mode: 0644]
tests/browser/features/support/modules/url_module.rb [new file with mode: 0644]
tests/browser/features/support/pages/create_account_page.rb [new file with mode: 0644]
tests/browser/features/support/pages/edit_page.rb [new file with mode: 0644]
tests/browser/features/support/pages/main_page.rb [new file with mode: 0644]
tests/browser/features/support/pages/view_history_page.rb [new file with mode: 0644]
tests/browser/features/support/pages/ztargetpage.rb [new file with mode: 0644]
tests/browser/features/view_history.feature [new file with mode: 0644]
tests/parser/parserTests.txt
tests/phpunit/includes/ArticleTest.php
tests/phpunit/languages/LanguageTest.php

index 129b691..ee45799 100644 (file)
@@ -86,6 +86,8 @@ production.
   to 'name', to allow for the name to be localizable. 'name' should still be
   specified for backwards-compatibility and to define the path Special:Version
   uses to find extension license information.
+* Browser tests are now included to verify basic wiki functionality in developer
+  environments. For details on running tests, see tests/browser/README.mediawiki.
 
 === Bug fixes in 1.24 ===
 * (bug 49116) Footer copyright notice is now always displayed in user language
index 3244d87..967ca07 100644 (file)
@@ -2060,29 +2060,41 @@ class Article implements Page {
 
        /**
         * @return array
+        *
+        * @deprecated since 1.24, use WikiPage::selectFields() instead
         */
        public static function selectFields() {
+               wfDeprecated( __METHOD__, '1.24' );
                return WikiPage::selectFields();
        }
 
        /**
         * @param Title $title
+        *
+        * @deprecated since 1.24, use WikiPage::onArticleCreate() instead
         */
        public static function onArticleCreate( $title ) {
+               wfDeprecated( __METHOD__, '1.24' );
                WikiPage::onArticleCreate( $title );
        }
 
        /**
         * @param Title $title
+        *
+        * @deprecated since 1.24, use WikiPage::onArticleDelete() instead
         */
        public static function onArticleDelete( $title ) {
+               wfDeprecated( __METHOD__, '1.24' );
                WikiPage::onArticleDelete( $title );
        }
 
        /**
         * @param Title $title
+        *
+        * @deprecated since 1.24, use WikiPage::onArticleEdit() instead
         */
        public static function onArticleEdit( $title ) {
+               wfDeprecated( __METHOD__, '1.24' );
                WikiPage::onArticleEdit( $title );
        }
 
index e505b28..bf30455 100644 (file)
@@ -1030,6 +1030,18 @@ class Language {
                return $this->getMessageFromDB( self::$mHijriCalendarMonthMsgs[$key - 1] );
        }
 
+       /**
+        * Pass through result from $dateTimeObj->format()
+        */
+       private static function dateTimeObjFormat( &$dateTimeObj, $ts, $zone, $code ) {
+               if ( !$dateTimeObj ) {
+                       $dateTimeObj = DateTime::createFromFormat(
+                               'YmdHis', $ts, $zone ?: new DateTimeZone( 'UTC' )
+                       );
+               }
+               return $dateTimeObj->format( $code );
+       }
+
        /**
         * This is a workalike of PHP's date() function, but with better
         * internationalisation, a reduced set of format characters, and a better
@@ -1090,12 +1102,14 @@ class Language {
         *      YYYYMMDDHHMMSS
         *      01234567890123
         * @param DateTimeZone $zone Timezone of $ts
+        * @param[out] int $ttl The amount of time (in seconds) the output may be cached for.
+        * Only makes sense if $ts is the current time.
         * @todo handling of "o" format character for Iranian, Hebrew, Hijri & Thai?
         *
         * @throws MWException
         * @return string
         */
-       function sprintfDate( $format, $ts, DateTimeZone $zone = null ) {
+       function sprintfDate( $format, $ts, DateTimeZone $zone = null, &$ttl = null ) {
                $s = '';
                $raw = false;
                $roman = false;
@@ -1109,6 +1123,25 @@ class Language {
                $minguo = false;
                $tenno = false;
 
+               $usedSecond = false;
+               $usedMinute = false;
+               $usedHour = false;
+               $usedAMPM = false;
+               $usedDay = false;
+               $usedWeek = false;
+               $usedMonth = false;
+               $usedYear = false;
+               $usedISOYear = false;
+               $usedIsLeapYear = false;
+
+               $usedHebrewMonth = false;
+               $usedIranianMonth = false;
+               $usedHijriMonth = false;
+               $usedHebrewYear = false;
+               $usedIranianYear = false;
+               $usedHijriYear = false;
+               $usedTennoYear = false;
+
                if ( strlen( $ts ) !== 14 ) {
                        throw new MWException( __METHOD__ . ": The timestamp $ts should have 14 characters" );
                }
@@ -1152,213 +1185,247 @@ class Language {
                                        $hebrewNum = true;
                                        break;
                                case 'xg':
+                                       $usedMonth = true;
                                        $s .= $this->getMonthNameGen( substr( $ts, 4, 2 ) );
                                        break;
                                case 'xjx':
+                                       $usedHebrewMonth = true;
                                        if ( !$hebrew ) {
                                                $hebrew = self::tsToHebrew( $ts );
                                        }
                                        $s .= $this->getHebrewCalendarMonthNameGen( $hebrew[1] );
                                        break;
                                case 'd':
+                                       $usedDay = true;
                                        $num = substr( $ts, 6, 2 );
                                        break;
                                case 'D':
-                                       if ( !$dateTimeObj ) {
-                                               $dateTimeObj = DateTime::createFromFormat(
-                                                       'YmdHis', $ts, $zone ?: new DateTimeZone( 'UTC' )
-                                               );
-                                       }
-                                       $s .= $this->getWeekdayAbbreviation( $dateTimeObj->format( 'w' ) + 1 );
+                                       $usedDay = true;
+                                       $s .= $this->getWeekdayAbbreviation( Language::dateTimeObjFormat( $dateTimeObj, $ts, $zone, 'w' ) + 1 );
                                        break;
                                case 'j':
+                                       $usedDay = true;
                                        $num = intval( substr( $ts, 6, 2 ) );
                                        break;
                                case 'xij':
+                                       $usedDay = true;
                                        if ( !$iranian ) {
                                                $iranian = self::tsToIranian( $ts );
                                        }
                                        $num = $iranian[2];
                                        break;
                                case 'xmj':
+                                       $usedDay = true;
                                        if ( !$hijri ) {
                                                $hijri = self::tsToHijri( $ts );
                                        }
                                        $num = $hijri[2];
                                        break;
                                case 'xjj':
+                                       $usedDay = true;
                                        if ( !$hebrew ) {
                                                $hebrew = self::tsToHebrew( $ts );
                                        }
                                        $num = $hebrew[2];
                                        break;
                                case 'l':
-                                       if ( !$dateTimeObj ) {
-                                               $dateTimeObj = DateTime::createFromFormat(
-                                                       'YmdHis', $ts, $zone ?: new DateTimeZone( 'UTC' )
-                                               );
-                                       }
-                                       $s .= $this->getWeekdayName( $dateTimeObj->format( 'w' ) + 1 );
+                                       $usedDay = true;
+                                       $s .= $this->getWeekdayName( Language::dateTimeObjFormat( $dateTimeObj, $ts, $zone, 'w' ) + 1 );
                                        break;
                                case 'F':
+                                       $usedMonth = true;
                                        $s .= $this->getMonthName( substr( $ts, 4, 2 ) );
                                        break;
                                case 'xiF':
+                                       $usedIranianMonth = true;
                                        if ( !$iranian ) {
                                                $iranian = self::tsToIranian( $ts );
                                        }
                                        $s .= $this->getIranianCalendarMonthName( $iranian[1] );
                                        break;
                                case 'xmF':
+                                       $usedHijriMonth = true;
                                        if ( !$hijri ) {
                                                $hijri = self::tsToHijri( $ts );
                                        }
                                        $s .= $this->getHijriCalendarMonthName( $hijri[1] );
                                        break;
                                case 'xjF':
+                                       $usedHebrewMonth = true;
                                        if ( !$hebrew ) {
                                                $hebrew = self::tsToHebrew( $ts );
                                        }
                                        $s .= $this->getHebrewCalendarMonthName( $hebrew[1] );
                                        break;
                                case 'm':
+                                       $usedMonth = true;
                                        $num = substr( $ts, 4, 2 );
                                        break;
                                case 'M':
+                                       $usedMonth = true;
                                        $s .= $this->getMonthAbbreviation( substr( $ts, 4, 2 ) );
                                        break;
                                case 'n':
+                                       $usedMonth = true;
                                        $num = intval( substr( $ts, 4, 2 ) );
                                        break;
                                case 'xin':
+                                       $usedIranianMonth = true;
                                        if ( !$iranian ) {
                                                $iranian = self::tsToIranian( $ts );
                                        }
                                        $num = $iranian[1];
                                        break;
                                case 'xmn':
+                                       $usedHijriMonth = true;
                                        if ( !$hijri ) {
                                                $hijri = self::tsToHijri ( $ts );
                                        }
                                        $num = $hijri[1];
                                        break;
                                case 'xjn':
+                                       $usedHebrewMonth = true;
                                        if ( !$hebrew ) {
                                                $hebrew = self::tsToHebrew( $ts );
                                        }
                                        $num = $hebrew[1];
                                        break;
                                case 'xjt':
+                                       $usedHebrewMonth = true;
                                        if ( !$hebrew ) {
                                                $hebrew = self::tsToHebrew( $ts );
                                        }
                                        $num = $hebrew[3];
                                        break;
                                case 'Y':
+                                       $usedYear = true;
                                        $num = substr( $ts, 0, 4 );
                                        break;
                                case 'xiY':
+                                       $usedIranianYear = true;
                                        if ( !$iranian ) {
                                                $iranian = self::tsToIranian( $ts );
                                        }
                                        $num = $iranian[0];
                                        break;
                                case 'xmY':
+                                       $usedHijriYear = true;
                                        if ( !$hijri ) {
                                                $hijri = self::tsToHijri( $ts );
                                        }
                                        $num = $hijri[0];
                                        break;
                                case 'xjY':
+                                       $usedHebrewYear = true;
                                        if ( !$hebrew ) {
                                                $hebrew = self::tsToHebrew( $ts );
                                        }
                                        $num = $hebrew[0];
                                        break;
                                case 'xkY':
+                                       $usedYear = true;
                                        if ( !$thai ) {
                                                $thai = self::tsToYear( $ts, 'thai' );
                                        }
                                        $num = $thai[0];
                                        break;
                                case 'xoY':
+                                       $usedYear = true;
                                        if ( !$minguo ) {
                                                $minguo = self::tsToYear( $ts, 'minguo' );
                                        }
                                        $num = $minguo[0];
                                        break;
                                case 'xtY':
+                                       $usedTennoYear = true;
                                        if ( !$tenno ) {
                                                $tenno = self::tsToYear( $ts, 'tenno' );
                                        }
                                        $num = $tenno[0];
                                        break;
                                case 'y':
+                                       $usedYear = true;
                                        $num = substr( $ts, 2, 2 );
                                        break;
                                case 'xiy':
+                                       $usedIranianYear = true;
                                        if ( !$iranian ) {
                                                $iranian = self::tsToIranian( $ts );
                                        }
                                        $num = substr( $iranian[0], -2 );
                                        break;
                                case 'a':
+                                       $usedAMPM = true;
                                        $s .= intval( substr( $ts, 8, 2 ) ) < 12 ? 'am' : 'pm';
                                        break;
                                case 'A':
+                                       $usedAMPM = true;
                                        $s .= intval( substr( $ts, 8, 2 ) ) < 12 ? 'AM' : 'PM';
                                        break;
                                case 'g':
+                                       $usedHour = true;
                                        $h = substr( $ts, 8, 2 );
                                        $num = $h % 12 ? $h % 12 : 12;
                                        break;
                                case 'G':
+                                       $usedHour = true;
                                        $num = intval( substr( $ts, 8, 2 ) );
                                        break;
                                case 'h':
+                                       $usedHour = true;
                                        $h = substr( $ts, 8, 2 );
                                        $num = sprintf( '%02d', $h % 12 ? $h % 12 : 12 );
                                        break;
                                case 'H':
+                                       $usedHour = true;
                                        $num = substr( $ts, 8, 2 );
                                        break;
                                case 'i':
+                                       $usedMinute = true;
                                        $num = substr( $ts, 10, 2 );
                                        break;
                                case 's':
+                                       $usedSecond = true;
                                        $num = substr( $ts, 12, 2 );
                                        break;
                                case 'c':
                                case 'r':
+                                       $usedSecond = true;
+                                       // fall through
                                case 'e':
                                case 'O':
                                case 'P':
                                case 'T':
-                                       // Pass through string from $dateTimeObj->format()
-                                       if ( !$dateTimeObj ) {
-                                               $dateTimeObj = DateTime::createFromFormat(
-                                                       'YmdHis', $ts, $zone ?: new DateTimeZone( 'UTC' )
-                                               );
-                                       }
-                                       $s .= $dateTimeObj->format( $code );
+                                       $s .= Language::dateTimeObjFormat( $dateTimeObj, $ts, $zone, $code );
                                        break;
                                case 'w':
                                case 'N':
                                case 'z':
+                                       $usedDay = true;
+                                       $num = Language::dateTimeObjFormat( $dateTimeObj, $ts, $zone, $code );
+                                       break;
                                case 'W':
+                                       $usedWeek = true;
+                                       $num = Language::dateTimeObjFormat( $dateTimeObj, $ts, $zone, $code );
+                                       break;
                                case 't':
+                                       $usedMonth = true;
+                                       $num = Language::dateTimeObjFormat( $dateTimeObj, $ts, $zone, $code );
+                                       break;
                                case 'L':
+                                       $usedIsLeapYear = true;
+                                       $num = Language::dateTimeObjFormat( $dateTimeObj, $ts, $zone, $code );
+                                       break;
                                case 'o':
+                                       $usedISOYear = true;
+                                       $num = Language::dateTimeObjFormat( $dateTimeObj, $ts, $zone, $code );
+                                       break;
                                case 'U':
+                                       $usedSecond = true;
+                                       // fall through
                                case 'I':
                                case 'Z':
-                                       // Pass through number from $dateTimeObj->format()
-                                       if ( !$dateTimeObj ) {
-                                               $dateTimeObj = DateTime::createFromFormat(
-                                                       'YmdHis', $ts, $zone ?: new DateTimeZone( 'UTC' )
-                                               );
-                                       }
-                                       $num = $dateTimeObj->format( $code );
+                                       $num = Language::dateTimeObjFormat( $dateTimeObj, $ts, $zone, $code );
                                        break;
                                case '\\':
                                        # Backslash escaping
@@ -1403,6 +1470,62 @@ class Language {
                        }
                }
 
+               if ( $usedSecond ) {
+                       $ttl = 1;
+               } elseif ( $usedMinute ) {
+                       $ttl = 60 - substr( $ts, 12, 2 );
+               } elseif ( $usedHour ) {
+                       $ttl = 3600 - substr( $ts, 10, 2 ) * 60 - substr( $ts, 12, 2 );
+               } elseif ( $usedAMPM ) {
+                       $ttl = 43200 - ( substr( $ts, 8, 2 ) % 12 ) * 3600 - substr( $ts, 10, 2 ) * 60 - substr( $ts, 12, 2 );
+               } elseif ( $usedDay || $usedHebrewMonth || $usedIranianMonth || $usedHijriMonth || $usedHebrewYear || $usedIranianYear || $usedHijriYear || $usedTennoYear ) {
+                       // @todo Someone who understands the non-Gregorian calendars should write proper logic for them
+                       // so that they don't need purged every day.
+                       $ttl = 86400 - substr( $ts, 8, 2 ) * 3600 - substr( $ts, 10, 2 ) * 60 - substr( $ts, 12, 2 );
+               } else {
+                       $possibleTtls = array();
+                       $timeRemainingInDay = 86400 - substr( $ts, 8, 2 ) * 3600 - substr( $ts, 10, 2 ) * 60 - substr( $ts, 12, 2 );
+                       if ( $usedWeek ) {
+                               $possibleTtls[] = ( 7 - Language::dateTimeObjFormat( $dateTimeObj, $ts, $zone, 'N' ) ) * 86400 + $timeRemainingInDay;
+                       } elseif ( $usedISOYear ) {
+                               // December 28th falls on the last ISO week of the year, every year.
+                               // The last ISO week of a year can be 52 or 53.
+                               $lastWeekOfISOYear = DateTime::createFromFormat( 'Ymd', substr( $ts, 0, 4 ) . '1228', $zone ?: new DateTimeZone( 'UTC' ) )->format( 'W' );
+                               $currentISOWeek = Language::dateTimeObjFormat( $dateTimeObj, $ts, $zone, 'W' );
+                               $weeksRemaining = $lastWeekOfISOYear - $currentISOWeek;
+                               $timeRemainingInWeek = ( 7 - Language::dateTimeObjFormat( $dateTimeObj, $ts, $zone, 'N' ) ) * 86400 + $timeRemainingInDay;
+                               $possibleTtls[] = $weeksRemaining * 604800 + $timeRemainingInWeek;
+                       }
+
+                       if ( $usedMonth ) {
+                               $possibleTtls[] = ( Language::dateTimeObjFormat( $dateTimeObj, $ts, $zone, 't' ) - substr( $ts, 6, 2 ) ) * 86400 + $timeRemainingInDay;
+                       } elseif ( $usedYear ) {
+                               $possibleTtls[] = ( Language::dateTimeObjFormat( $dateTimeObj, $ts, $zone, 'L' ) + 364 - Language::dateTimeObjFormat( $dateTimeObj, $ts, $zone, 'z' ) ) * 86400
+                                       + $timeRemainingInDay;
+                       } elseif ( $usedIsLeapYear ) {
+                               $year = substr( $ts, 0, 4 );
+                               $timeRemainingInYear = ( Language::dateTimeObjFormat( $dateTimeObj, $ts, $zone, 'L' ) + 364 - Language::dateTimeObjFormat( $dateTimeObj, $ts, $zone, 'z' ) ) * 86400
+                                       + $timeRemainingInDay;
+                               $mod = $year % 4;
+                               if ( $mod || ( !( $year % 100 ) && $year % 400 ) ) {
+                                       // this isn't a leap year. see when the next one starts
+                                       $nextCandidate = $year - $mod + 4;
+                                       if ( $nextCandidate % 100 || !( $nextCandidate % 400 ) ) {
+                                               $possibleTtls[] = ( $nextCandidate - $year - 1 ) * 365 * 86400 + $timeRemainingInYear;
+                                       } else {
+                                               $possibleTtls[] = ( $nextCandidate - $year + 3 ) * 365 * 86400 + $timeRemainingInYear;
+                                       }
+                               } else {
+                                       // this is a leap year, so the next year isn't
+                                       $possibleTtls[] = $timeRemainingInYear;
+                               }
+                       }
+
+                       if ( $possibleTtls ) {
+                               $ttl = min( $possibleTtls );
+                       }
+               }
+
                return $s;
        }
 
index c0e0311..68d1029 100644 (file)
        "qbmyoptions": "Meine Seiten",
        "faq": "Oft gstejte Frong",
        "faqpage": "Project:FAQ",
-       "vector-action-addsection": "An Obschnitt dazua doa",
-       "vector-action-delete": "Leschn",
-       "vector-action-move": "Vaschiabm",
-       "vector-action-protect": "Schitzn",
-       "vector-action-undelete": "Wiederherstön",
-       "vector-action-unprotect": "freigeem",
-       "vector-view-create": "Aufbaun",
-       "vector-view-edit": "Werkln",
-       "vector-view-history": "Gschicht oschaugn",
-       "vector-view-view": "Lesn",
-       "vector-view-viewsource": "Quejtext ozoagn",
        "actions": "Aktiona",
        "namespaces": "Namasramm",
        "variants": "Variantn",
        "nologinlink": "A neichs Nutzakonto olegn",
        "createaccount": "Nutzakonto olegn",
        "gotaccount": "Hosd scho a Nutzakonto? '''$1'''.",
-       "gotaccountlink": "Omejdn",
+       "gotaccountlink": "Eilogga",
        "userlogin-resetlink": "Hosd de Datn zan Eilogga vagessn?",
        "createaccountmail": "per E-Mail",
        "createaccountreason": "Grund",
index 76040e8..51f7900 100644 (file)
        "version-hook-name": "Назва працэдуры-перахопніка",
        "version-hook-subscribedby": "Падпісаны на",
        "version-version": "(Вэрсія $1)",
+       "version-no-ext-name": "[бяз назвы]",
        "version-svn-revision": "(r$2)",
        "version-license": "Ліцэнзія MediaWiki",
        "version-ext-license": "Ліцэнзія",
index 9959ded..1579ea7 100644 (file)
        "copyrightwarning": "Заўважце, што ўсе ўклады на {{SITENAME}} лічацца выданымі на ўмовах $2 (бач падрабязнасці на $1). Калі вы не жадаеце, каб вашыя матэрыялы бязлітасна правіліся, і свабодна распаўсюджваліся, то і не аддавайце іх сюды.<br />\nТаксама вы нам абяцаеце, што напісалі гэта самі, або скапіравалі з рэсурсу, які знаходзіцца ў публічнай уласнасці, або з аналагічнага свабоднага рэсурсу.\n'''НЕ КЛАДЗІЦЕ СЮДЫ, БЕЗ АДПАВЕДНАГА ДАЗВОЛУ, МАТЭРЫЯЛУ, ЯКІ АХОЎВАЕЦЦА АЎТАРСКІМ ПРАВАМ!'''",
        "copyrightwarning2": "Заўважце, што кожны ўклад на {{SITENAME}} можа быць папраўлены, зменены або выдалены іншымі ўдзельнікамі. Калі вы не жадаеце, каб вашыя матэрыялы бязлітасна правіліся, то і не давайце іх сюды.<br />\nТаксама вы нам абяцаеце, што напісалі гэта самі, або скапіравалі з рэсурсу, які знаходзіцца ў публічнай уласнасці, або з аналагічнага свабоднага рэсурсу (бач падрабязнасці на $1).\n'''НЕ КЛАДЗІЦЕ СЮДЫ, БЕЗ АДПАВЕДНАГА ДАЗВОЛУ, МАТЭРЫЯЛУ, ЯКІ АХОЎВАЕЦЦА АЎТАРСКІМ ПРАВАМ!'''",
        "longpageerror": "'''Памылка: Аб’ём тэксту, які Вы спрабуеце запісаць складае $1 {{PLURAL:$1|кілабайт|кілабайты|кілабайтаў}}, што болей устаноўленага абмежавання на $2 {{PLURAL:$2|кілабайт|кілабайты|кілабайтаў}}.'''\nСтаронка не можа быць захаваная.",
-       "readonlywarning": "'''УВАГА: зараз вы не можаце запісаць свае праўкі, таму што база даных зачынена на абслугоўванне. Магчыма, варта перанесці ваш тэкст у асобны файл і запісаць на потым.\n\nАдміністратар, які зачыніў базу, растлумачыў гэта так: $1'''",
+       "readonlywarning": "<strong>Увага: зараз вы не можаце запісаць свае праўкі, таму што база звестак зачынена на абслугоўванне.</strong>\nМагчыма, варта перанесці ваш тэкст у асобны файл і запісаць на потым.\n\nАдміністратар, які зачыніў базу, растлумачыў гэта так: $1",
        "protectedpagewarning": "'''УВАГА: старонка пастаўленая пад ахову, таму яе могуць правіць толькі адміністратары.'''\nНіжэй паказаны апошні запіс з адпаведнага журналу:",
        "semiprotectedpagewarning": "'''Увага:''' старонка пастаўленая пад ахову, таму яе могуць правіць толькі рэгістраваныя ўдзельнікі («паў-ахова»). Ніжэй паказаны апошні запіс з адпаведнага журналу:",
        "cascadeprotectedwarning": "'''Увага:''' гэтая старонка ахоўваецца, таму яе могуць правіць толькі ўдзельнікі з правамі адміністратара. Прычына аховы: улучэнне гэтай старонкі ў {{PLURAL:$1|старонку, якая стаіць|старонкі, якія стаяць}} пад каскаднай аховай:",
        "postedit-confirmation-saved": "Ваша праўка запісана.",
        "edit-already-exists": "Не ўдалося стварыць новую старонку.\nТакая ўжо існуе.",
        "defaultmessagetext": "Прадвызначаны тэкст",
+       "content-failed-to-parse": "Не ўдалося прааналізаваць змест \"$2\" як $1: $3",
+       "invalid-content-data": "Недапушчальнае змесціва",
        "content-not-allowed-here": "\"$1\" не дазволены на старонцы [[$2]]",
        "editwarning-warning": "Пераход на іншую старонку можа прывесці да страты правак, зробленых Вамі. \nКалі Вы ўвайшлі ў сістэму, Вы можаце адключыць гэта папярэджанне ў раздзеле \"{{int:prefs-editing}}\" Вашых настроек.",
        "editpage-notsupportedcontentformat-title": "Фармат змесціва не падтрымліваецца",
        "rclinks": "Паказаць апошнія $1 змен за мінулыя $2 дзён<br />$3",
        "diff": "розн.",
        "hist": "гіст.",
-       "hide": "без Ñ\83лÑ\96кÑ\83",
-       "show": "з Ñ\83лÑ\96кам",
+       "hide": "Ð\9dе Ñ\9eлÑ\96Ñ\87ваÑ\86Ñ\8c",
+       "show": "УлÑ\96Ñ\87ваÑ\86Ñ\8c",
        "minoreditletter": "д",
        "newpageletter": "Н",
        "boteditletter": "р",
        "windows-nonascii-filename": "Дадзеная вікі не падтрымлівае імёны файлаў са спецыяльнымі знакамі.",
        "fileexists": "Ужо ёсць файл з такой назвай, праверце <strong>[[:$1]]</strong>, калі не ўпэўнены, што жадаеце мяняць яго змесціва.\n[[$1|thumb]]",
        "filepageexists": "Для файла з такой назвай існуе старонка апісання <strong>[[:$1]]</strong>, але сам файл зараз не існуе.\nТаму вашае апісанне не з'явіцца на адпаведнай старонцы, пакуль вы самастойна яе не паправіце.\n[[$1|thumb]]",
-       "fileexists-extension": "Ужо Ñ\91Ñ\81Ñ\86Ñ\8c Ñ\84айл Ð· Ð¿Ð°Ð´Ð¾Ð±Ð½Ð°Ð¹ Ð½Ð°Ð·Ð²Ð°Ð¹: [[$2|thumb]]\n* Ð\9dазва Ñ\9eкладанага Ñ\84айла: <strong>[[:$1]]</strong>\n* Ð\9dазва Ð½Ð°Ñ\8fÑ\9eнага Ñ\84айла: <strong>[[:$2]]</strong>\nÐ\92Ñ\8bбеÑ\80Ñ\8bÑ\86е Ñ\96нÑ\88Ñ\83Ñ\8e Ð½Ð°Ð·Ð²Ñ\83.",
+       "fileexists-extension": "Ужо Ñ\91Ñ\81Ñ\86Ñ\8c Ñ\84айл Ð· Ð¿Ð°Ð´Ð¾Ð±Ð½Ð°Ð¹ Ð½Ð°Ð·Ð²Ð°Ð¹: [[$2|thumb]]\n* Ð\9dазва Ñ\9eкладанага Ñ\84айла: <strong>[[:$1]]</strong>\n* Ð\9dазва Ð½Ð°Ñ\8fÑ\9eнага Ñ\84айла: <strong>[[:$2]]</strong>\nÐ\9cожа, Ñ\85оÑ\87аÑ\86е Ð²Ñ\8bкаÑ\80Ñ\8bÑ\81Ñ\82аÑ\86Ñ\8c Ð°Ð´Ñ\80ознÑ\83Ñ\8e Ð½Ð°Ð·Ð²Ñ\83?",
        "fileexists-thumbnail-yes": "Файл падобны на выяву скарочанага памеру ''(драбніца)''. [[$1|thumb]]\nПраверце файл <strong>[[:$1]]</strong>.\nКалі правераны файл мае змест і памеры, аднолькавыя з гэтым, то дадатковае ўкладанне драбніцы непатрэбнае.",
        "file-thumbnail-no": "Назва файла пачынаецца з <strong>$1</strong>.\nТак можа называцца выява зменшанага памеру ''(драбніца)''.\nКалі гэтая выява сапраўды запісаная ў найлепшым разрозненні, якое ёсць, то ўкладайце яе, а іначай лепей памяняць назву файла.",
        "fileexists-forbidden": "Файл з такой назвай ужо ёсць, і нельга запісаць паўзверх яго. Калі вы жадаеце абавязкова ўкласці свой файл, то выберыце новую назву. [[File:$1|thumb|center|$1]]",
        "mostlinkedtemplates": "Шаблоны ў частым выкарыстанні",
        "mostcategories": "Артыкулы ў найбольшай кольк. катэгорый",
        "mostimages": "Выявы ў частым выкарыстанні",
-       "mostinterwikis": "Артыкулы з найбольш. кольк. інтэрвкікі",
+       "mostinterwikis": "Артыкулы з найбольш. кольк. інтэрвікі",
        "mostrevisions": "Артыкулы з найбольшай колькасцю версій",
        "prefixindex": "Старонкі з назвамі на ўзор",
        "prefixindex-namespace": "Усе старонкі з прэфіксам ( $1 прастора імёнаў)",
        "protect-cascadeon": "Старонка зараз ахоўваецца, таму што ўлучана ў наступн{{PLURAL:$1|ую старонку, на якую|ыя старонкі, на якія}} пастаўлена каскадная ахова. Можна змяніць узровень аховы гэтай старонкі, але вынікаў каскаднай аховы гэта не пераможа.",
        "protect-default": "Дазваляць усім удзельнікам",
        "protect-fallback": "Патрабуецца дазвол \"$1\"",
-       "protect-level-autoconfirmed": "Ð\97абаÑ\80анÑ\8fÑ\86Ñ\8c Ð½Ð¾Ð²Ñ\8bм Ñ\96 Ð½ÐµÑ\80Ñ\8dгÑ\96Ñ\81Ñ\82Ñ\80аванÑ\8bм Ñ\83дзелÑ\8cнÑ\96кам",
+       "protect-level-autoconfirmed": "ТолÑ\8cкÑ\96 Ð°Ñ\9eÑ\82а-паÑ\86веÑ\80джанÑ\8bÑ\8f Ñ\83дзелÑ\8cнÑ\96кÑ\96",
        "protect-level-sysop": "Толькі для адміністратараў",
        "protect-summary-cascade": "каскад",
        "protect-expiring": "скончыцца $1 (UTC)",
        "blockipsuccesstext": "[[Special:Contributions/$1|$1]] быў заблакаваны.<br />\nБлокі пералічаны ў [[Special:BlockList|спісе блокаў]].",
        "ipb-blockingself": "Вы спрабуеце заблакіраваць сябе самога! Вы ўпэўнены, што хочаце гэта зрабіць?",
        "ipb-confirmhideuser": "Вы намерваецеся заблакіраваць удзельніка і схаваць яго імя. Яно не будзе адлюстроўвацца ў спісах і журналах. Вы ўпэўнены, што жадаеце гэта зрабіць?",
+       "ipb-confirmaction": "Калі вы ўпэўнены, што сапраўды хочаце гэта зрабіць, калі ласка, адзначце поле \"{{int:ipb-confirm}}\" ніжэй.",
        "ipb-edit-dropdown": "Прычыны пастаноўкі блока",
        "ipb-unblock-addr": "Зняць блок з $1",
        "ipb-unblock": "Зняць блок з імя ўдзельніка або адрасу IP",
        "change-blocklink": "змяніць блок",
        "contribslink": "уклад",
        "emaillink": "адправіць ліст",
-       "autoblocker": "Аўтаматычны блок таму што вашым адрасам IP нядаўна карыстаўся \"[[User:$1|$1]]\". Блакаванне $1's патлумачана так: \"'''$2'''\"",
+       "autoblocker": "Аўтаматычны блок, таму што вашым адрасам IP нядаўна карыстаўся \"[[User:$1|$1]]\".\nБлакаванне $1 патлумачана так: \"$2\"",
        "blocklogpage": "Журнал блокаў",
        "blocklog-showlog": "{{GENDER:$1|Гэты ўдзельнік ужо блакаваўся|Гэта ўдзельніца ўжо блакавалася}} раней.\nЖурнал блакіровак прыведзены ніжэй:",
        "blocklog-showsuppresslog": "Гэты ўдзельнік ужо заблакаваны і скрыты. Журнал утойвання прыведзены ніжэй:",
        "proxyblockreason": "Ваш адрас IP заблакаваны, таму што ён належыць да ліку адкрытых проксі.\nГэта сур'ёзная праблема бяспекі; паведамце пра гэта свайму Інтэрнет-правайдэру або ў службу тэхнічнай падтрымкі.",
        "sorbsreason": "Ваш адрас IP знаходзіцца ў спісе забароненых адкрытых проксі, якім карыстаецца {{SITENAME}}.",
        "sorbs_create_account_reason": "Ваш адрас IP знаходзіцца ў спісе забароненых адкрытых проксі, якім карыстаецца {{SITENAME}}.\nВы не можаце рэгістравацца",
+       "xffblockreason": "IP-адрас, прыведзены ў загалоўку X-Forwarded-For, або ваш, або проксі-сервера, які вы выкарыстоўваеце, быў заблакаваны. Першапачаткова блок патлумачаны так: $1",
        "cant-see-hidden-user": "Удзельнік, якога вы спрабуеце заблакіраваць, ужо заблакіраваны і схаваны. Паколькі ў вас няма дазволу на працу па схаванні ўдзельнікаў, вы не можаце прагледзець ці змяніць гэту блакіроўку.",
        "ipbblocked": "Вы не можаце блакіраваць ці разблакіраваць іншых удзельнікаў, бо вы самі заблакіраваны",
        "ipbnounblockself": "Вы не можаце разблакіраваць самога сябе",
        "movepagetalktext": "Звязаная старонка размовы будзе аўтаматычна перанесена разам з асноўнай, '''апроч тых выпадкаў, калі:'''\n*Існуе непустая старонка размовы звязаная з новай назвай, або\n*З боксу, што ніжэй, знятая адзнака.\n\nУ такіх выпадках, калі гэта неабходна, трэба пераносіць або аб'ядноўваць старонку размовы самастойна.",
        "movearticle": "Перанесці старонку:",
        "moveuserpage-warning": "'''Увага.''' Вы збіраецеся пераназваць старонку ўдзельніка. Калі ласка, звернеце ўвагу, што пераназвана будзе толькі старонка, удзельнік '''не''' будзе пераназваны.",
+       "movecategorypage-warning": "<strong>Увага:</strong> Вы збіраецеся перанесці старонку катэгорыі. Заўважце, што толькі гэта старонка будзе перанесена, і ніводная старонка са старой катэгорыі <em>не будзе</em> катэгарызавана ў новай.",
        "movenologintext": "Вы павінны быць зарэгістраваным удзельнікам, і [[Special:UserLogin|ўвайсці ў сістэму]], каб пераносіць старонкі.",
        "movenotallowed": "Вам не дазволена пераносіць старонкі.",
        "movenotallowedfile": "Вы не маеце дазволу пераносіць файлы.",
        "cant-move-user-page": "Вам не дазволена пераносіць старонак карыстальнікаў (не лічачы пад-старонак).",
        "cant-move-to-user-page": "Вам не дазволена пераносіць старонку ў старонку карыстальніка (не лічачы пад-старонак карыстальніка).",
+       "cant-move-category-page": "Вам не дазволена пераносіць старонкі катэгорый.",
+       "cant-move-to-category-page": "Вам не дазволена пераносіць старонку ў старонку катэгорыі.",
        "newtitle": "Пад новую назву:",
        "move-watch": "Назіраць за старонкай",
        "movepagebtn": "Перанесці старонку",
        "allmessagesname": "Назва",
        "allmessagesdefault": "Прадвызначаны тэкст",
        "allmessagescurrent": "Актуальны тэкст",
-       "allmessagestext": "Пералік сістэмных паведамленняў, наяўных у прасторы назваў MediaWiki.",
+       "allmessagestext": "Пералік сістэмных паведамленняў, наяўных у прасторы назваў MediaWiki.\nКалі ласка, наведайце пляцоўкі [https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation MediaWiki Localisation] і [//translatewiki.net translatewiki.net], калі жадаеце ўдзельнічаць у перакладзе MediaWiki.",
        "allmessagesnotsupportedDB": "Немагчыма паказаць '''{{ns:special}}:{{:{{ns:mediawiki}}:Allmessages}}''', таму што не працуе '''$wgUseDatabaseMessages'''.",
        "allmessages-filter-legend": "Фільтры",
        "allmessages-filter": "Са станам апрацоўкі:",
        "thumbnail-more": "Павялічыць",
        "filemissing": "Адсутны файл",
        "thumbnail_error": "Памылка пры стварэнні драбніцы: $1",
+       "thumbnail_error_remote": "Паведамленне пра памылку з $1:\n$2",
        "djvu_page_error": "Старонка DjVu па-за інтэрвалам",
        "djvu_no_xml": "Не ўдалося ўзяць XML для файла DjVu",
        "thumbnail-temp-create": "Не ўдаецца стварыць часовы файл эскіза",
        "pageinfo-lasttime": "Дата апошняй праўкі",
        "pageinfo-edits": "Агульная колькасць правак",
        "pageinfo-authors": "Агульная колькасць розных аўтараў",
+       "pageinfo-toolboxlink": "Звесткі пра старонку",
        "pageinfo-redirectsto": "Перасылае да",
+       "pageinfo-redirectsto-info": "звесткі",
+       "pageinfo-contentpage": "Улічваецца як змястоўная старонка",
+       "pageinfo-contentpage-yes": "Так",
+       "pageinfo-protect-cascading": "Каскадная ахова пачынаецца адсюль",
+       "pageinfo-protect-cascading-yes": "Да",
+       "pageinfo-protect-cascading-from": "Каскадная ахова ад",
        "pageinfo-category-info": "Звесткі аб катэгорыі",
        "pageinfo-category-pages": "Колькасць старонак",
        "pageinfo-category-subcats": "Колькасць падкатэгорый",
        "markedaspatrollederror": "Немагчыма пазначыць як вартае",
        "markedaspatrollederrortext": "Трэба паказаць тую версію, якую жадаеце пазначыць ухваленай.",
        "markedaspatrollederror-noautopatrol": "Вам не дазволена значыць уласныя праўкі як ухваленыя.",
+       "markedaspatrollednotify": "Змена ў $1 пазначана як ухваленая.",
        "patrol-log-page": "Журнал ухваленых",
        "patrol-log-header": "Журнал ухваленых версій",
        "log-show-hide-patrol": "$1 журнал ухваленняў",
        "newimages-summary": "Тут паказаныя нядаўна ўкладзеныя файлы.",
        "newimages-legend": "Фільтр",
        "newimages-label": "Назва файла (або яе частка):",
+       "newimages-showbots": "Паказваць укладанні ботамі",
        "noimages": "Тут нічога няма.",
        "ilsubmit": "Знайсці",
        "bydate": "п. датаў",
        "redirect-submit": "Перайсці",
        "redirect-lookup": "Шукаць:",
        "redirect-value": "Значэнне:",
-       "redirect-user": "ID удзельніка:",
+       "redirect-user": "ID удзельніка",
        "redirect-page": "Ідэнтыфікатар старонкі",
        "redirect-revision": "Версія старонкі",
        "redirect-file": "Назва файла",
        "htmlform-selectorother-other": "Рознае",
        "htmlform-no": "Не",
        "htmlform-cloner-create": "Дадаць яшчэ",
+       "htmlform-cloner-delete": "Сцерці",
+       "htmlform-cloner-required": "Неабходна хаця б адно значэнне.",
        "sqlite-has-fts": "$1 з падтрымкай поўна-тэкставага пошуку",
        "sqlite-no-fts": "$1 без падтрымкі поўна-тэкставага пошуку",
        "logentry-delete-delete": "$1 {{GENDER:$2|сцёр|сцёрла}} старонку $3",
        "logentry-move-move-noredirect": "$1 {{GENDER:$2|перанёс|перанесла}} старонку $3 у $4, не пакінуўшы перасылкі",
        "logentry-move-move_redir": "$1 {{GENDER:$2|перанёс|перанесла}} старонку $3 у $4 па-над перасылкаю",
        "logentry-move-move_redir-noredirect": "$1 {{GENDER:$2|перанёс|перанесла}} старонку $3 у $4 па-над перасылкаю, не пакінуўшы перасылкі",
-       "logentry-patrol-patrol": "$1 адпатруляваў версію $4 старонкі $3",
-       "logentry-patrol-patrol-auto": "$1 аўтаматычна адпатруляваў версію $4 старонкі $3",
+       "logentry-patrol-patrol": "$1 {{GENDER:$2|адзначыў|адзначыла}} версію $4 старонкі $3 як ухваленую",
+       "logentry-patrol-patrol-auto": "$1 аўтаматычна {{GENDER:$2|адзначыў|адзначыла}} версію $4 старонкі $3 як ухваленую",
        "logentry-newusers-newusers": "Створаны ўліковы запіс {{GENDER:$2|удзельніка}} $1",
        "logentry-newusers-create": "Створаны ўліковы запіс {{GENDER:$4|удзельніка|удзельніцы}} $1",
        "logentry-newusers-create2": "$1 {{GENDER:$2|стварыў|стварыла}} уліковы запіс {{GENDER:$4|удзельніка|удзельніцы}} $3",
        "logentry-newusers-byemail": "$1 {{GENDER:$2|стварыў|стварыла}} ўліковы запіс {{GENDER:$4|удзельніка|удзельніцы}} $3; пароль адпраўлены на адрас эл.пошты.",
        "logentry-newusers-autocreate": "Аўтаматычна створаны ўліковы запіс удзельніка $1",
+       "logentry-rights-rights": "$1 {{GENDER:$2|змяніў|змяніла}} прыналежнасць $3 да групы з $4 на $5",
+       "logentry-rights-rights-legacy": "$1 {{GENDER:$2|змяніў|змяніла}} прыналежнасць $3 да групы",
+       "logentry-rights-autopromote": "$1 аўтаматычна {{GENDER:$2|пераведзены|пераведзена}} з $4 у $5",
        "rightsnone": "(няма)",
        "feedback-bugornote": "Калі вы гатовыя падрабязна апісаць тэхнічную праблему, калі ласка, [$1 паведаміце пра памылку].\nУ адваротным выпадку вы можаце выкарыстоўваць гэтую простую форму. Ваш каментар будзе дададзены на старонку «[$3 $2]» разам з вашым імем удзельніка і выкарыстоўваемым браўзерам.",
        "feedback-subject": "Тэма:",
        "api-error-empty-file": "Дасланы Вамі файл быў пусты.",
        "api-error-emptypage": "Стварэнне новых пустых старонак забаронена.",
        "api-error-fetchfileerror": "Унутраная памылка: падчас атрымання файла штосьці здарылася.",
+       "api-error-fileexists-forbidden": "Файл з назвай \"$1\" ужо ёсць і не можа быць перапісаны.",
+       "api-error-fileexists-shared-forbidden": "Файл з назвай \"$1\" ужо ёсць у супольным сховішчы файлаў, ён не можа быць перапісаны.",
        "api-error-file-too-large": "Дасланы Вамі файл занадта вялікі.",
        "api-error-filename-tooshort": "Імя файла занадта кароткае.",
        "api-error-filetype-banned": "Гэты тып файлаў забаронены.",
        "api-error-ok-but-empty": "Унутраная памылка: няма адказу ад сервера.",
        "api-error-overwrite": "Замена існуючага файла не дапускаецца.",
        "api-error-stashfailed": "Унутраная памылка: сервер не змог захаваць часовы файл.",
+       "api-error-publishfailed": "Унутраная памылка: сервер не змог апублікаваць тымчасовы файл.",
        "api-error-timeout": "Сервер не адказаў у чаканы тэрмін.",
        "api-error-unclassified": "Узнікла невядомая памылка",
        "api-error-unknown-code": "Невядомая памылка: «$1».",
        "duration-millennia": "$1 {{PLURAL:$1|тысячагоддзе|тысячагоддзі|тысячагоддзяў}}",
        "limitreport-cputime-value": "$1 {{PLURAL:$1|секунда|секунды|секундаў}}",
        "limitreport-walltime-value": "$1 {{PLURAL:$1|секунда|секунды|секундаў}}",
+       "expand_templates_output": "Вынік",
        "expand_templates_remove_comments": "Сцерці каментарыі",
        "expand_templates_preview": "Перадпаказ"
 }
index 099815b..77c706e 100644 (file)
        "mergehistory-empty": "Nelze sloučit žádnou verzi.",
        "mergehistory-success": "$3 {{PLURAL:$3|verze|verze|verzí}} stránky [[:$1]] {{PLURAL:$3|byla úspěšně sloučena|byly úspěšně sloučeny|bylo úspěšně sloučeno}} do stránky [[:$2]].",
        "mergehistory-fail": "Sloučení historií nelze provést. Překontrolujte zadané stránky a jejich historii.",
+       "mergehistory-fail-toobig": "Nelze provést sloučení historie, protože by se přesouvalo více revizí, než je limit $1.",
        "mergehistory-no-source": "Zdrojová stránka $1 neexistuje.",
        "mergehistory-no-destination": "Cílová stránka $1 neexistuje.",
        "mergehistory-invalid-source": "Zdrojová stránka musí mít platný název.",
        "windows-nonascii-filename": "Tato wiki nepodporuje názvy souborů obsahující zvláštní znaky.",
        "fileexists": "Soubor s tímto jménem již existuje, prosím podívejte se na <strong>[[:$1]]</strong>, pokud nevíte jistě, zda chcete tento soubor nahradit.\n[[$1|thumb]]",
        "filepageexists": "Popisná stránka pro soubor s tímto jménem již byla na <strong>[[:$1]]</strong> založena, avšak odpovídající soubor dosud neexistuje.\nShrnutí, které zde uvedete, se na popisné stránce nezobrazí.\nPokud tam chcete své shrnutí zobrazit, budete muset příslušnou stránku editovat ručně. [[$1|thumb]]",
-       "fileexists-extension": "Již existuje soubor s podobným jménem: [[$2|thumb]]\n* Jméno načítaného souboru: <strong>[[:$1]]</strong>\n* Jméno existujícího souboru: <strong>[[:$2]]</strong>\nVyberte jiné jméno.",
+       "fileexists-extension": "Již existuje soubor s podobným jménem: [[$2|thumb]]\n* Jméno načítaného souboru: <strong>[[:$1]]</strong>\n* Jméno existujícího souboru: <strong>[[:$2]]</strong>\nNechcete raději použít odlišnější jméno?",
        "fileexists-thumbnail-yes": "Tento soubor je zřejmě obrázek ve zmenšené velikosti ''(náhled)''. [[$1|thumb]]\nZkontrolujte soubor <strong>[[:$1]]</strong>.\nPokud je zmiňovaný soubor větší, ale jinak stejný, není potřeba zvlášť načítat jeho zmenšenou verzi.",
        "file-thumbnail-no": "Jméno souboru začíná na <strong>$1</strong>.\nMožná to je obrázek ve zmenšené velikosti ''(náhled)''.\nNačtěte soubor v plném rozlišením, pokud je k dispozici, nebo změňte jméno souboru.",
        "fileexists-forbidden": "Soubor s tímto názvem již existuje a není dovoleno ho přepsat.\nPokud chcete přesto soubor načíst, vraťte se a zvolte jiný název.\n[[File:$1|thumb|center|$1]]",
        "filedelete-maintenance": "Mazání a obnovování souborů je kvůli údržbě dočasně vypnuto.",
        "filedelete-maintenance-title": "Soubor nelze smazat",
        "mimesearch": "Hledání podle MIME typu",
-       "mimesearch-summary": "Tato stránka umožňuje filtrovat soubory podle MIME typu.<br />\nVstup: <code>typ obsahu/podtyp</code>, např. <code>image/jpeg</code>.",
+       "mimesearch-summary": "Tato stránka umožňuje filtrovat soubory podle MIME typu.<br />\nVstup: <code>typ obsahu/podtyp</code> nebo <code>typ obsahu/*</code>, např. <code>image/jpeg</code>.",
        "mimetype": "MIME typ:",
        "download": "stažení",
        "unwatchedpages": "Nesledované stránky",
        "version-hook-name": "Název přípojného bodu",
        "version-hook-subscribedby": "Volán z",
        "version-version": "($1)",
+       "version-no-ext-name": "[bez názvu]",
        "version-license": "Licence MediaWiki",
        "version-ext-license": "Licence",
        "version-ext-colheader-name": "Rozšíření",
index 9fa2617..286f720 100644 (file)
        "mergehistory-empty": "Nisòna versiòun da unîr",
        "mergehistory-success": "{{PLURAL:$3|'Na versiòun ed [[:$1]] l'é stêda unîda|$3 versiòn ed [[:$1]] în stêdi unîdi}} al la stòria ed [[:$2]].",
        "mergehistory-fail": "Impusébil unîr al stòri. Verifichêr la pàgina e al règoli dal mumèint.",
+       "mergehistory-fail-toobig": "Imposébil fêr l'uniòun ed la stòria cun pió 'd $1{{PLURAL:$1|revisiòun}} da spustêr",
        "mergehistory-no-source": "La pàgina 'd urégin $1 l'an gh'é mìa.",
        "mergehistory-no-destination": "La pàgina 'd arîv $1 l'an gh'é mìa.",
        "mergehistory-invalid-source": "La pàgina 'd urégin la gh'à 'd avèir un tétol curèt.",
        "showingresultsnum": "Ed sègvit {{PLURAL:$1|a vîn preşentê <strong>1</strong> rişultêt| a vînen preşentê  <strong>$3</strong> rişultêt}} a partîr dal nómer #<strong>$2</strong>.",
        "showingresultsheader": "{{PLURAL:$5|Al risultêt '''$1''' ed '''$3'''|I risultêt '''$1 - $2''' ed '''$3'''}} per '''$4'''",
        "search-nonefound": "La sērca an n'à mìa dê di rişultê.",
+       "powersearch-legend": "Sèirca specêla",
+       "powersearch-ns": "Sērca int al spâsi di nòm:",
+       "powersearch-togglelabel": "Sernés:",
        "powersearch-toggleall": "Tót",
        "powersearch-togglenone": "Nisûn",
+       "powersearch-remember": "Arcôrda la siēlta per êtri sèirchi ch'é vō fêr",
+       "search-external": "Sèirca fâta fōra",
        "preferences": "Preferèinsa",
        "mypreferences": "Preferèinsi",
        "prefs-edits": "Mudéfichi fâti:",
index bdeb540..1bb9b51 100644 (file)
        "windows-nonascii-filename": "Este wiki no admite nombres de archivo con caracteres especiales.",
        "fileexists": "Ya existe un archivo con este nombre, por favor comprueba <strong>[[:$1]]</strong> si {{GENDER:|tú}} no estás seguro de querer cambiarlo.\n[[$1|thumb]]",
        "filepageexists": "La página de descripción de este archivo ya ha sido creada en <strong>[[:$1]]</strong>, pero no existe actualmente ningún fichero con este nombre.\nEl resumen que ha ingresado no aparecerá en la página de descripción. Para que el sumario aparezca, deberá editarlo manualmente.\n[[$1|thumb]]",
-       "fileexists-extension": "Existe un archivo con un nombre similar: [[$2|thumb]]\n* Nombre del archivo que se está subiendo: <strong>[[:$1]]</strong>\n* Nombre del archivo ya existente: <strong>[[:$2]]</strong>\nPor favor, elige un nombre diferente.",
+       "fileexists-extension": "Existe un archivo con un nombre similar: [[$2|thumb]]\n* Nombre del archivo que se está subiendo: <strong>[[:$1]]</strong>\n* Nombre del archivo ya existente: <strong>[[:$2]]</strong>\n¿Quieres cambiar el nombre para que sea más distintivo?",
        "fileexists-thumbnail-yes": "El archivo parece ser una imagen de tamaño reducido ''(thumbnail)''. [[$1|thumb]]\nPor favor comprueba el archivo <strong>[[:$1]]</strong>.\nSi el archivo comprobado es la misma imagen a tamaño original no es necesario subir un thumbnail más.",
        "file-thumbnail-no": "El nombre del archivo comienza con <strong>$1</strong>.\nParece ser una imagen de tamaño reducido ''(thumbnail)''.\nSi tiene esta imagen a toda resolución súbala, si no, por favor cambie el nombre del archivo.",
        "fileexists-forbidden": "Ya existe un archivo con este nombre, y no puede ser grabado encima de otro. Si quiere subir su archivo de todos modos, por favor vuelva atrás y utilice otro nombre. [[File:$1|thumb|center|$1]]",
        "filedelete-maintenance": "Borrado y restauración de archivos temporalmente deshabilitados durante el mantenimiento.",
        "filedelete-maintenance-title": "No se puede eliminar el archivo",
        "mimesearch": "Búsqueda por MIME",
-       "mimesearch-summary": "Esta página permite el filtrado de ficheros por su tipo MIME.\nEntrada: contenttype/subtype, p. ej. <code>image/jpeg</code>.",
+       "mimesearch-summary": "Esta página permite el filtrado de ficheros por su tipo MIME.\nEntrada: contenttype/subtype o contenttype/*, p. ej. <code>image/jpeg</code>.",
        "mimetype": "Tipo MIME:",
        "download": "descargar",
        "unwatchedpages": "Páginas no vigiladas",
        "version-hook-name": "Nombre de la extensión",
        "version-hook-subscribedby": "Suscrito por",
        "version-version": "($1)",
+       "version-no-ext-name": "[sin nombre]",
        "version-license": "Licencia de MediaWiki",
        "version-ext-license": "Licencia",
        "version-ext-colheader-name": "Extensión",
index 64f8c2d..568b852 100644 (file)
        "right-bot": "Izmjene su tretirane kao automatski proces (bot)",
        "right-nominornewtalk": "Bez manjih izmjena na novim stranicama za razgovor",
        "right-apihighlimits": "Korištenje viših granica kod API upita",
-       "right-writeapi": "Mogućnost pisanja API",
+       "right-writeapi": "Mogućnost pisanja API-ja",
        "right-delete": "Brisanje stranica",
        "right-bigdelete": "Brisanje stranica koje imaju veliku povijest",
        "right-deletelogentry": "Brisanje i vraćanje određenih zapisa u evidenciji",
        "right-block": "Blokiranje suradnika u uređivanju",
        "right-blockemail": "Blokiranje suradnika u slanju elektroničke pošte",
        "right-hideuser": "Blokiranje suradničkog imena, skrivajući ga od javnosti",
-       "right-ipblock-exempt": "Imunitet na IP blokiranje, auto-blok i blokiranje opsega",
-       "right-proxyunbannable": "Imunitet na automatska blokiranja posrednika (proxya)",
+       "right-ipblock-exempt": "Iznimka od blokiranja IP adresa, auto-bloka i blokiranja opsega",
+       "right-proxyunbannable": "Iznimka od automatskih blokiranja posrednika (proxya)",
        "right-unblockself": "Odblokirati se",
        "right-protect": "Mijenjanje razina zaštićivanja i uređivanje zaštićenih stranica",
        "right-editprotected": "Uređivanje zaštićenih stranica (bez prenosive zaštite)",
+       "right-editsemiprotected": "Uređivanje zaštićenih stranica kao \"{{int: zaštititi-nivo-autoconfirmed}}\"",
        "right-editinterface": "Uređivanje suradničkog sučelja",
        "right-editusercssjs": "Uređivanje CSS i JS stranica drugih suradnika",
        "right-editusercss": "Uređivanje CSS stranica drugih suradnika",
        "right-edituserjs": "Uređivanje JS stranica drugih suradnika",
+       "right-editmyusercss": "Uređivanje vlastitih CSS stranica",
+       "right-editmyuserjs": "Uređivanje vlastitih JavaScript stranica",
        "right-viewmywatchlist": "Pregled svojeg popisa praćenih stranica",
+       "right-editmywatchlist": "Uređivanje vlastitog popisa praćenja. Određenim djelovanjima moguće je dodavati stranice i bez ovoga dopuštenja.",
        "right-viewmyprivateinfo": "Vidite svoje privatne podatke (npr. adresu e-pošte, stvarno ime)",
        "right-editmyprivateinfo": "Uredite svoje privatne podatke (npr. adresa e-pošte, stvarno ime)",
        "right-editmyoptions": "Uredite svoje postavke",
index 9e9b2f6..c294d59 100644 (file)
        "mergehistory-empty": "Nulle versiones pote esser fusionate.",
        "mergehistory-success": "$3 {{PLURAL:$3|version|versiones}} de [[:$1]] fusionate in [[:$2]] con successo.",
        "mergehistory-fail": "Impossibile executar le fusion del historia. Per favor reverifica le parametros del pagina e del tempore.",
+       "mergehistory-fail-toobig": "Le historias de versiones non pote esser fusionate con plus de $1 {{PLURAL:$1|version|versiones}} a displaciar.",
        "mergehistory-no-source": "Le pagina de origine $1 non existe.",
        "mergehistory-no-destination": "Le pagina de destination $1 non existe.",
        "mergehistory-invalid-source": "Le pagina de origine debe esser un titulo valide.",
        "windows-nonascii-filename": "Iste wiki non supporta nomines de file con characteres special.",
        "fileexists": "Un file con iste nomine existe jam.\nPer favor verifica <strong>[[:$1]]</strong> si {{GENDER:|tu}} non es secur de voler cambiar lo.\n[[$1|thumb]]",
        "filepageexists": "Le pagina de description correspondente a iste file ha ja essite create a <strong>[[:$1]]</strong>, ma nulle file con iste nomine existe al momento.\nLe summario que tu entra non apparera in le pagina de description.\nSi tu vole que illo appare, tu debe inserer lo manualmente.\n[[$1|thumb]]",
-       "fileexists-extension": "Un file con un nomine similar existe ja: [[$2|thumb]]\n* Nomine del file a incargar: <strong>[[:$1]]</strong>\n* Nomine del file existente: <strong>[[:$2]]</strong>\nPer favor selige un altere nomine.",
+       "fileexists-extension": "Un file con un nomine similar existe ja: [[$2|thumb]]\n* Nomine del file a incargar: <strong>[[:$1]]</strong>\n* Nomine del file existente: <strong>[[:$2]]</strong>\nConsidera usar un nomine plus distincte.",
        "fileexists-thumbnail-yes": "Iste file pare esser un imagine a grandor reducite ''(miniatura)''. [[$1|thumb]]\nPer favor verifica le file <strong>[[:$1]]</strong>.\nSi le file verificate es le mesme imagine a grandor original, non es necessari incargar un miniatura additional.",
        "file-thumbnail-no": "Le nomine del file comencia con <strong>$1</strong>.\nIllo pare esser un imagine a grandor reducite ''(miniatura)''.\nSi tu possede iste imagine in plen resolution, incarga lo, alteremente cambia le nomine del file per favor.",
        "fileexists-forbidden": "Un file con iste nomine existe ja, e non pote esser superscribite.\nSi tu vole ancora incargar iste file, per favor retorna e usa un nove nomine. [[File:$1|thumb|center|$1]]",
        "filedelete-maintenance": "Deletion e restauration de files temporarimente disactivate durante mantenentia.",
        "filedelete-maintenance-title": "Non pote deler file",
        "mimesearch": "Recerca de typo MIME",
-       "mimesearch-summary": "Iste pagina permitte filtrar le files a base de lor typos MIME.\nSyntaxe: typo/subtypo, p.ex. <code>image/jpeg</code>.",
+       "mimesearch-summary": "Iste pagina permitte filtrar le files a base de lor typos MIME.\nSyntaxe: typodecontento/subtypo o typodecontento/*, p.ex. <code>image/jpeg</code>.",
        "mimetype": "Typo MIME:",
        "download": "discargar",
        "unwatchedpages": "Paginas non observate",
        "version-hook-name": "Nomine del uncino",
        "version-hook-subscribedby": "Subscribite per",
        "version-version": "(Version $1)",
+       "version-no-ext-name": "[sin nomine]",
        "version-license": "Licentia pro MediaWiki",
        "version-ext-license": "Licentia",
        "version-ext-colheader-name": "Extension",
index 0082cb0..c8b84de 100644 (file)
        "sp-contributions-newbies-sub": "Untuk pengguna baru",
        "sp-contributions-newbies-title": "Kontribusi pengguna baru",
        "sp-contributions-blocklog": "Log pemblokiran",
-       "sp-contributions-suppresslog": "kontribusi pengguna yang dihapus",
+       "sp-contributions-suppresslog": "kontribusi pengguna yang disembunyikan",
        "sp-contributions-deleted": "kontribusi pengguna yang dihapus",
        "sp-contributions-uploads": "unggahan",
        "sp-contributions-logs": "log",
index 372dadf..83a5b24 100644 (file)
        "version-hook-name": "Nazwa haka (ang. hook name)",
        "version-hook-subscribedby": "Zapotrzebowany przez",
        "version-version": "(Wersja $1)",
+       "version-no-ext-name": "[bez nazwy]",
        "version-license": "Licencja MediaWiki",
        "version-ext-license": "Licencja",
        "version-ext-colheader-name": "Rozszerzenie",
index fe58c57..9d019f9 100644 (file)
        "days-abbrev": "{{optional}}\nAbbreviation for \"days\". $1 is the number of days.\n\nSee also {{msg-mw|days}}",
        "seconds": "Full word for \"seconds\". $1 is the number of seconds.\n\nSee also {{msg-mw|seconds-abbrev}}, {{msg-mw|seconds-ago}}.\n{{Identical|Second}}",
        "minutes": "Full word for \"minutes\". $1 is the number of minutes.\n\nSee also {{msg-mw|minutes-abbrev}}, {{msg-mw|minutes-ago}}.\n\n{{Identical|Minute}}",
-       "hours": "Full word for \"hours\". $1 is the number of hours.\n\nSee also {{msg-mw|hours-abbrev}}, {{msg-mw|hours-ago}}.",
+       "hours": "Full word for \"hours\". $1 is the number of hours.\n\nSee also {{msg-mw|hours-abbrev}}, {{msg-mw|hours-ago}}.\n{{Identical|Hour}}",
        "days": "Full word for \"days\". $1 is the number of days.\n\nSee also {{msg-mw|Days-abbrev}}\n\n{{Identical|Day}}",
-       "weeks": "Full word for \"weeks\". Parameters:\n* $1 is the number of weeks",
-       "months": "Full word for \"months\". $1 is the number of months.",
-       "years": "Full word for \"years\".\n\nParameters:\n* $1 - the number of years",
+       "weeks": "Full word for \"weeks\". Parameters:\n* $1 is the number of weeks\n{{Identical|Week}}",
+       "months": "Full word for \"months\".\n* $1 - the number of months.\n{{Identical|Month}}",
+       "years": "Full word for \"years\".\n\nParameters:\n* $1 - the number of years\n{{Identical|Year}}",
        "ago": "Phrase for indicating how long ago something happened. Parameters:\n* $1 - some kind of timestamp\n{{Identical|Ago}}",
        "just-now": "Phrase for indicating something happened just now.\n{{Identical|Just now}}",
        "hours-ago": "Phrase for indicating that something occurred a certain number of hours ago.\n\nParameters:\n* $1 - number of hours",
index 9b370c6..662a388 100644 (file)
        "version-hook-name": "Nume hook",
        "version-hook-subscribedby": "Subscris de",
        "version-version": "($1)",
+       "version-no-ext-name": "[fără nume]",
        "version-license": "Licență MediaWiki",
        "version-ext-license": "Licență",
        "version-ext-colheader-name": "Extensie",
index ccf418b..86d2892 100644 (file)
        "mergehistory-empty": "Нема измена за спајање.",
        "mergehistory-success": "$3 {{PLURAL:$3|измена странице [[:$1]] је спојена|измене странице [[:$1]] су спојене|измена странице [[:$1]] је спојено}} у [[:$2]].",
        "mergehistory-fail": "Не могу да спојим историје. Проверите страницу и временске параметре.",
+       "mergehistory-fail-toobig": "Није могуће спојити историје јер више од $1 {{PLURAL:$1|измене|измена}} ће бити премештено.",
        "mergehistory-no-source": "Изворна страница $1 не постоји.",
        "mergehistory-no-destination": "Одредишна страница $1 не постоји.",
        "mergehistory-invalid-source": "Изворна страница мора имати исправан наслов.",
index 7afcfeb..6b7d813 100644 (file)
        "duplicatesoffile": "{{PLURAL:$1|Дублікатом цього файлу є файл|Такі $1 файли є дублікатами цього файлу|Такі $1 файлів є дублікатами цього файлу}}\n([[Special:FileDuplicateSearch/$2|докладніше]]):",
        "sharedupload": "Цей файл із $1 і є доступним для інших проектів.",
        "sharedupload-desc-there": "Цей файл з $1 і може використовуватися в інших проектах.\nДодаткову інформацію можна знайти на [$2 сторінці опису файлу].",
-       "sharedupload-desc-here": "Цей файл з $1 і може використовуватися в інших проектах.\nДалі наведена інформація з його [$2 сторінки опису].",
+       "sharedupload-desc-here": "Цей файл з {{GRAMMAR:genitive|$1}} і може використовуватися в інших проектах.\nДалі наведена інформація з його [$2 сторінки опису].",
        "sharedupload-desc-edit": "Цей файл з $1 і може використовуватися в інших проектах. \nЙого опис можна відредагувати [$2 на відповідній сторінці тут].",
        "sharedupload-desc-create": "Цей файл з $1 і може використовуватися в інших проектах. \nЙого опис можна зробити [$2 на відповідній сторінці тут].",
        "filepage-nofile": "Не існує файлу з такою назвою.",
index d83dade..bdbe3de 100644 (file)
        "windows-nonascii-filename": "די וויקי שטיצט נישט טעקע־נעמען מיט ספעציעלע צייכענען.",
        "fileexists": "א טעקע מיט דעם נאָמען עקזיסטירט שוין, ביטע זײַט בודק <strong>[[:$1]]</strong> ווען {{GENDER:|איר}} זענט נישט זיכער אַז איר ווילט אים ענדערן.\n[[$1|thumb]]",
        "filepageexists": "דער באשרייבונג בלאט פאר דער דאזיקער טעקע האט מען שוין געשאפן ביי <strong>[[:$1]]</strong>, אבער ס'עקזיסטירט נישט קיין טעקע מיט דעם נאמען.\nדי רעזומע וואס איר קלאפט אריין וועט זיך נישט באווייזן אויפן באשרייבונג בלאט.\nכדי צו שאפן אז אייער רעזומע וועט זיך טאקע באווייזן דארט, דארפט איר רעדאקטירן זי האנטווייז.\n[[$1|thumb]]",
-       "fileexists-extension": "×\90 ×\98עקע ×\9e×\99×\98 ×\90×\9f ×¢× ×\9c×¢×\9b×\9f × ×\90×\9e×¢×\9f ×¢×§×\96×\99ס×\98×\99ר×\98 ×©×\95×\99×\9f: [[$2|thumb]]\n* × ×\90×\9e×¢×\9f ×¤×\95×\9f ×\93ער ×\98עקע ×\95×\95×\90ס ×\95×\95ער×\98 ×\90ר×\95×\99פ×\92×¢×\9c×\90Ö¸×\93×\9f: <strong>[[:$1]]</strong>\n* × ×\90×\9e×¢×\9f ×¤×\95×\9f ×\93ער ×¤Ö¿×\90ר×\90נענער ×\98עקע: <strong>[[:$2]]</strong>\n×\96ײַ×\98 ×\90×\96×\95×\99 ×\92×\95×\98 ×\90×\95×\9f ×§×\9c×\95×\99×\91×\98 ×\90×\9f ×\90× ×\93ער נאמען.",
+       "fileexists-extension": "×\90 ×\98עקע ×\9e×\99×\98 ×\90×\9f ×¢× ×\9c×¢×\9b×\9f × ×\90×\9e×¢×\9f ×¢×§×\96×\99ס×\98×\99ר×\98 ×©×\95×\99×\9f: [[$2|thumb]]\n* × ×\90×\9e×¢×\9f ×¤×\95×\9f ×\93ער ×\98עקע ×\95×\95×\90ס ×\95×\95ער×\98 ×\90ר×\95×\99פ×\92×¢×\9c×\90Ö¸×\93×\9f: <strong>[[:$1]]</strong>\n* × ×\90×\9e×¢×\9f ×¤×\95×\9f ×\93ער ×¤Ö¿×\90ר×\90נענער ×\98עקע: <strong>[[:$2]]</strong>\n×\90פשר ×\95×\95×\99×\9c×\98 ×\90×\99ר × ×\99צ×\9f ×\90 ×\9eער ×\90פש×\99×\99×\93× ×\93×\99ק×\9f נאמען.",
        "fileexists-thumbnail-yes": "די טעקע זעט אויס צו זײַן א פארקלענערט בילד  ''(קליין)''.\n[[$1|thumb]]\nזײַט אזוי גוט קאנטראלירט די טעקע <strong>[[:$1]]</strong>.\nווען די קאנטראלירטע טעקע איז דאס זעלבע בילד אין דער אריגינעלער גרייס ברויכט מען נישט ארויפלאדן נאך א פארקלענערט בילד.",
        "file-thumbnail-no": "דער טעקע־נאמען הייבט אן מיט <strong>$1</strong>.\nזי זעט אויס ווי א פארקלענערט בילד ''(מיניאטור)''.\nטאמער האט איר דאס בילד אין פולער רעזאלוציע טוט עס ארויפלאדן, אנדערשט זייט אזוי גוט און ענדערט דעם טעקע־נאמען.",
        "fileexists-forbidden": "א טעקע מיט דעם נאָמען עקזיסטירט שוין, און מען קען זי נישט אַריבערשרײַבן.\nאויב איר ווילט דאך אַרויפֿלאָדן אײַער טעקע, ביטע גיין צוריק און ניצן אַן אַנדער נאָמען.\n[[File:$1|thumb|center|$1]]",
index 1fe0e26..cc72e16 100644 (file)
@@ -30,9 +30,8 @@
                        rcid = mw.util.getParamValue( 'rcid', href );
                        apiRequest = new mw.Api();
 
-                       apiRequest.post( {
+                       apiRequest.postWithToken( 'patrol', {
                                action: 'patrol',
-                               token: mw.user.tokens.get( 'patrolToken' ),
                                rcid: rcid
                        } )
                        .done( function ( data ) {
diff --git a/tests/.gitignore b/tests/.gitignore
new file mode 100644 (file)
index 0000000..eb56bcc
--- /dev/null
@@ -0,0 +1,4 @@
+# Puppet-managed dependencies for browser tests
+.bundle
+.gem
+.ruby-version
diff --git a/tests/browser/Gemfile b/tests/browser/Gemfile
new file mode 100755 (executable)
index 0000000..fd6803e
--- /dev/null
@@ -0,0 +1,7 @@
+#ruby=ruby-2.1.1
+
+source "https://rubygems.org"
+
+gem "chunky_png"
+gem "mediawiki_api"
+gem "mediawiki_selenium"
diff --git a/tests/browser/Gemfile.lock b/tests/browser/Gemfile.lock
new file mode 100644 (file)
index 0000000..d78c9a1
--- /dev/null
@@ -0,0 +1,83 @@
+GEM
+  remote: https://rubygems.org/
+  specs:
+    builder (3.2.2)
+    childprocess (0.5.2)
+      ffi (~> 1.0, >= 1.0.11)
+    chunky_png (1.3.0)
+    cucumber (1.3.14)
+      builder (>= 2.1.2)
+      diff-lcs (>= 1.1.3)
+      gherkin (~> 2.12)
+      multi_json (>= 1.7.5, < 2.0)
+      multi_test (>= 0.1.1)
+    data_magic (0.18)
+      faker (>= 1.1.2)
+      yml_reader (>= 0.2)
+    diff-lcs (1.2.5)
+    domain_name (0.5.18)
+      unf (>= 0.0.5, < 1.0.0)
+    faker (1.3.0)
+      i18n (~> 0.5)
+    faraday (0.9.0)
+      multipart-post (>= 1.2, < 3)
+    faraday-cookie_jar (0.0.6)
+      faraday (>= 0.7.4)
+      http-cookie (~> 1.0.0)
+    ffi (1.9.3)
+    gherkin (2.12.2)
+      multi_json (~> 1.3)
+    headless (1.0.1)
+    http-cookie (1.0.2)
+      domain_name (~> 0.5)
+    i18n (0.6.9)
+    json (1.8.1)
+    mediawiki_api (0.1.2)
+      faraday (~> 0.9, >= 0.9.0)
+      faraday-cookie_jar (~> 0.0, >= 0.0.6)
+    mediawiki_selenium (0.2.20)
+      cucumber (~> 1.3, >= 1.3.10)
+      headless (~> 1.0, >= 1.0.1)
+      json (~> 1.8, >= 1.8.1)
+      net-http-persistent (~> 2.9, >= 2.9.1)
+      page-object (~> 0.9, >= 0.9.5)
+      rest-client (~> 1.6, >= 1.6.7)
+      rspec-expectations (~> 2.14, >= 2.14.4)
+      syntax (~> 1.2, >= 1.2.0)
+    mime-types (2.2)
+    multi_json (1.9.2)
+    multi_test (0.1.1)
+    multipart-post (2.0.0)
+    net-http-persistent (2.9.4)
+    page-object (0.9.8)
+      page_navigation (>= 0.9)
+      selenium-webdriver (>= 2.40.0)
+      watir-webdriver (>= 0.6.8)
+    page_navigation (0.9)
+      data_magic (>= 0.14)
+    rest-client (1.6.7)
+      mime-types (>= 1.16)
+    rspec-expectations (2.14.5)
+      diff-lcs (>= 1.1.3, < 2.0)
+    rubyzip (1.1.3)
+    selenium-webdriver (2.41.0)
+      childprocess (>= 0.5.0)
+      multi_json (~> 1.0)
+      rubyzip (~> 1.0)
+      websocket (~> 1.0.4)
+    syntax (1.2.0)
+    unf (0.1.4)
+      unf_ext
+    unf_ext (0.0.6)
+    watir-webdriver (0.6.9)
+      selenium-webdriver (>= 2.18.0)
+    websocket (1.0.7)
+    yml_reader (0.2)
+
+PLATFORMS
+  ruby
+
+DEPENDENCIES
+  chunky_png
+  mediawiki_api
+  mediawiki_selenium
diff --git a/tests/browser/README.mediawiki b/tests/browser/README.mediawiki
new file mode 100644 (file)
index 0000000..8921fb6
--- /dev/null
@@ -0,0 +1,64 @@
+Purpose:
+
+The purpose of these tests is to validate that a newly installed (or updated, or hacked, or whatever) mediawiki instance presents to the user a set of expected features, regardless of what language the wiki is in, or where it is installed, or what extensions it might have.
+
+The tests are based on the basic definition of a wiki, a website where anyone
+
+* can read a page
+* can create a page
+* can edit a page
+* can link one page to another page
+
+Install:
+
+Ruby 1.9.3 or higher is required
+Firefox browser is required
+
+cd /tests/browser
+gem update --system
+gem install bundler
+bundle install
+
+Run the tests:
+
+Edit the environment_variables file with appropriate values for your wiki
+$source environment_variables (example shown in bash shell)
+
+bundle exec cucumber features/
+
+Note that the acceptance tests will create three pages in your wiki entitled "Editing Test Page", "Link Source Test Page", and "Link Target Test Page".  These pages may be deleted at any time.  If you wish to re-run the tests at any time, these test pages will be re-created or reset to their original contents at the time that the tests run.
+
+For more information about running Selenium tests please see
+https://github.com/wikimedia/mediawiki-selenium
+
+Details:
+
+create_account.feature
+* Checks three different ways to arrive on page allowing the user to create an account
+
+create_and_follow_wiki_link.feature:
+* uses the mediawiki API to create a link target page
+* uses the mediawiki API to create a link source page
+* navigates a browser to the link source page
+* clicks the link in that page to the link target page
+* validates that the browser has in fact followed the link to the target page correctly
+
+edit_page.feature:
+* uses the mediawiki API to create an editable page on the wiki
+* navigates a browser to the page
+* clicks the Edit button to invoke the basic editor
+* edits the page with a particular string containing a static part and also a quasi-unique random part
+* saves the edited page
+* checks that the saved page contains the particular string with which the page was edited
+
+main_page.feature:
+* navigates a browser to the default landing page of the wiki
+* checks for the View History link on the landing page
+* checks for the full set of of sidebar links that should exist on every mediawiki wiki
+
+view_history.feature
+* similar to edit_page.feature but checks for an older version of the edited page
+
+Notes:
+
+Tested on beta labs hewiki, dewiki, enwiki, and on a local installation of mediawiki
\ No newline at end of file
diff --git a/tests/browser/environment_variables b/tests/browser/environment_variables
new file mode 100644 (file)
index 0000000..25c4577
--- /dev/null
@@ -0,0 +1,5 @@
+export MEDIAWIKI_URL=http://localhost/wiki/
+export MEDIAWIKI_API_URL=http://localhost/w/api.php
+export MEDIAWIKI_USER=Selenium_user
+export MEDIAWIKI_PASSWORD=Selenium_password
+export BROWSER=firefox
diff --git a/tests/browser/features/create_account.feature b/tests/browser/features/create_account.feature
new file mode 100644 (file)
index 0000000..a8a96d6
--- /dev/null
@@ -0,0 +1,11 @@
+Feature: Create account
+
+  Scenario Outline: Go to Create account page
+    Given I go to Create account page at <path>
+    Then form has Create account button
+
+  Examples:
+    | path                          |
+    | Special:CreateAccount         |
+    | Special:UserLogin/signup      |
+    | Special:UserLogin?type=signup |
diff --git a/tests/browser/features/create_and_follow_wiki_link.feature b/tests/browser/features/create_and_follow_wiki_link.feature
new file mode 100644 (file)
index 0000000..f5f0ce0
--- /dev/null
@@ -0,0 +1,9 @@
+
+Feature: Create Page With Wiki Link
+
+  Scenario: Create Page With Wiki Link
+    Given I create page "Link Target Test Page" with content "Link Target Test Page"
+      And I go to the "Link Source Test Page" page with content "This is a [[Link Target Test Page|link to the test target page]] right here."
+    When I click the Link Target link
+    Then I should be on the Link Target Test Page
+      And the page content should contain "Link Target Test Page"
\ No newline at end of file
diff --git a/tests/browser/features/edit_page.feature b/tests/browser/features/edit_page.feature
new file mode 100644 (file)
index 0000000..53efd6b
--- /dev/null
@@ -0,0 +1,8 @@
+Feature: Edit Page
+
+  Scenario: Create and edit page
+    Given I go to the "Editing Test Page" page with content "This is a page to test editing"
+    When I click Edit
+      And I edit the page with "Edited and a random string"
+      And I save the edit
+    Then the edited page content should contain "Edited and a random string"
diff --git a/tests/browser/features/main_page_links.feature b/tests/browser/features/main_page_links.feature
new file mode 100644 (file)
index 0000000..053ee62
--- /dev/null
@@ -0,0 +1,18 @@
+Feature: Main Page View History Links
+
+  Background:
+  Given I open the main wiki URL
+
+  Scenario: Main Page View History links exist
+    Then I should see a link for View History
+
+  Scenario: Main Page Sidebar Links
+    Then I should see a link for Recent changes
+      And I should see a link for Random page
+      And I should see a link for Help
+      And I should see a link for What links here
+      And I should see a link for Related changes
+      And I should see a link for Special pages
+      And I should see a link for Printable version
+      And I should see a link for Permanent link
+      And I should see a link for Page information
\ No newline at end of file
diff --git a/tests/browser/features/step_definitions/create_account_steps.rb b/tests/browser/features/step_definitions/create_account_steps.rb
new file mode 100644 (file)
index 0000000..7fa2984
--- /dev/null
@@ -0,0 +1,18 @@
+#
+# This file is subject to the license terms in the LICENSE file found in the
+# qa-browsertests top-level directory and at
+# https://git.wikimedia.org/blob/qa%2Fbrowsertests/HEAD/LICENSE. No part of
+# qa-browsertests, including this file, may be copied, modified, propagated, or
+# distributed except according to the terms contained in the LICENSE file.
+#
+# Copyright 2012-2014 by the Mediawiki developers. See the CREDITS file in the
+# qa-browsertests top-level directory and at
+# https://git.wikimedia.org/blob/qa%2Fbrowsertests/HEAD/CREDITS
+#
+Given(/^I go to Create account page at (.+)$/) do |path|
+  visit(CreateAccountPage, :using_params => {:page_title => path})
+end
+
+Then(/^form has Create account button$/) do
+  on(CreateAccountPage).create_account_element.should exist
+end
diff --git a/tests/browser/features/step_definitions/create_and_follow_wiki_link_steps.rb b/tests/browser/features/step_definitions/create_and_follow_wiki_link_steps.rb
new file mode 100644 (file)
index 0000000..ba41f7f
--- /dev/null
@@ -0,0 +1,28 @@
+Given(/^I go to the "(.+)" page with content "(.+)"$/) do |page_title, page_content|
+  @wikitext = page_content
+  on(APIPage).create page_title, page_content
+  step "I am on the #{page_title} page"
+end
+
+Given(/^I am on the (.+) page$/) do |article|
+  article = article.gsub(/ /, '_')
+  visit(ZtargetPage, :using_params => {:article_name => article})
+end
+
+Given(/^I create page "(.*?)" with content "(.*?)"$/) do |page_title, page_content|
+  on(APIPage).create page_title, page_content
+end
+
+
+When(/^I click the Link Target link$/) do
+  on(ZtargetPage).link_target_page_link
+end
+
+Then(/^I should be on the Link Target Test Page$/) do
+  @browser.url.should match /Link_Target_Test_Page/
+end
+
+Then(/^the page content should contain "(.*?)"$/) do |content|
+  on(ZtargetPage).page_content.should match content
+end
+
diff --git a/tests/browser/features/step_definitions/edit_page_steps.rb b/tests/browser/features/step_definitions/edit_page_steps.rb
new file mode 100644 (file)
index 0000000..5af097b
--- /dev/null
@@ -0,0 +1,16 @@
+When(/^I click Edit$/) do
+  on(MainPage).edit_link
+end
+
+When(/^I edit the page with "(.*?)"$/) do |edit_content|
+  on(EditPage).edit_page_content_element.send_keys(edit_content + @random_string)
+end
+
+When(/^I save the edit$/) do
+  on(EditPage).save_button
+end
+
+Then(/^the edited page content should contain "(.*?)"$/) do |content|
+  on(MainPage).page_content.should match(content + @random_string)
+end
+
diff --git a/tests/browser/features/step_definitions/main_page_links_steps.rb b/tests/browser/features/step_definitions/main_page_links_steps.rb
new file mode 100644 (file)
index 0000000..c76fd2b
--- /dev/null
@@ -0,0 +1,47 @@
+Given(/^I open the main wiki URL$/) do
+  visit(MainPage)
+end
+
+Then(/^I should see a link for View History$/) do
+  on(MainPage).view_history_link_element.should be_visible
+end
+
+Then(/^I should see a link for Edit$/) do
+  on(MainPage).edit_link_element.should be_visible
+end
+
+Then(/^I should see a link for Recent changes$/) do
+  on(MainPage).recent_changes_link_element.should be_visible
+end
+
+Then(/^I should see a link for Random page$/) do
+  on(MainPage).random_page_link_element.should be_visible
+end
+
+Then(/^I should see a link for Help$/) do
+  on(MainPage).help_link_element.should be_visible
+end
+
+Then(/^I should see a link for What links here$/) do
+  on(MainPage).what_links_here_link_element.should be_visible
+end
+
+Then(/^I should see a link for Related changes$/) do
+  on(MainPage).related_changes_link_element.should be_visible
+end
+
+Then(/^I should see a link for Special pages$/) do
+  on(MainPage).special_pages_link_element.should be_visible
+end
+
+Then(/^I should see a link for Printable version$/) do
+  on(MainPage).printable_version_link_element.should be_visible
+end
+
+Then(/^I should see a link for Permanent link$/) do
+  on(MainPage).permanent_link_link_element.should be_visible
+end
+
+Then(/^I should see a link for Page information$/) do
+  on(MainPage).page_information_link_element.should be_visible
+end
diff --git a/tests/browser/features/step_definitions/view_history_steps.rb b/tests/browser/features/step_definitions/view_history_steps.rb
new file mode 100644 (file)
index 0000000..1ecc008
--- /dev/null
@@ -0,0 +1,8 @@
+When(/^I click View History$/) do
+  on(ViewHistoryPage).view_history_link
+end
+
+Then(/^I should see a link to a previous version of the page$/) do
+  on(ViewHistoryPage).old_version_link_element.should be_visible
+end
+
diff --git a/tests/browser/features/support/env.rb b/tests/browser/features/support/env.rb
new file mode 100644 (file)
index 0000000..4ffef5e
--- /dev/null
@@ -0,0 +1,2 @@
+require "mediawiki_selenium"
+require "mediawiki_api"
diff --git a/tests/browser/features/support/hooks.rb b/tests/browser/features/support/hooks.rb
new file mode 100644 (file)
index 0000000..85309f3
--- /dev/null
@@ -0,0 +1,2 @@
+# Needed for cucumber --dry-run -f stepdefs
+require 'page-object'
diff --git a/tests/browser/features/support/modules/url_module.rb b/tests/browser/features/support/modules/url_module.rb
new file mode 100644 (file)
index 0000000..6c329e8
--- /dev/null
@@ -0,0 +1,10 @@
+module URL
+  def self.url(name)
+    if ENV["MEDIAWIKI_URL"]
+      mediawiki_url = ENV["MEDIAWIKI_URL"]
+    else
+      mediawiki_url = "http://127.0.0.1:80/w/index.php"
+    end
+    "#{mediawiki_url}#{name}"
+  end
+end
diff --git a/tests/browser/features/support/pages/create_account_page.rb b/tests/browser/features/support/pages/create_account_page.rb
new file mode 100644 (file)
index 0000000..380bccb
--- /dev/null
@@ -0,0 +1,19 @@
+#
+# This file is subject to the license terms in the LICENSE file found in the
+# qa-browsertests top-level directory and at
+# https://git.wikimedia.org/blob/qa%2Fbrowsertests/HEAD/LICENSE. No part of
+# qa-browsertests, including this file, may be copied, modified, propagated, or
+# distributed except according to the terms contained in the LICENSE file.
+#
+# Copyright 2012-2014 by the Mediawiki developers. See the CREDITS file in the
+# qa-browsertests top-level directory and at
+# https://git.wikimedia.org/blob/qa%2Fbrowsertests/HEAD/CREDITS
+#
+class CreateAccountPage
+  include PageObject
+
+  include URL
+  page_url URL.url("<%=params[:page_title]%>")
+
+  button(:create_account, id: "wpCreateaccount")
+end
diff --git a/tests/browser/features/support/pages/edit_page.rb b/tests/browser/features/support/pages/edit_page.rb
new file mode 100644 (file)
index 0000000..594ac73
--- /dev/null
@@ -0,0 +1,7 @@
+class EditPage
+  include PageObject
+
+  text_area(:edit_page_content, id: "wpTextbox1")
+  button(:save_button, id: "wpSave")
+
+end
\ No newline at end of file
diff --git a/tests/browser/features/support/pages/main_page.rb b/tests/browser/features/support/pages/main_page.rb
new file mode 100644 (file)
index 0000000..7d96c2b
--- /dev/null
@@ -0,0 +1,19 @@
+class MainPage
+  include PageObject
+
+  include URL
+  page_url URL.url("")
+
+  a(:edit_link, href: /action=edit/)
+  li(:help_link, id: "n-help")
+  div(:page_content, id: "content")
+  li(:page_information_link, id: "t-info")
+  li(:permanent_link_link, id: "t-permalink")
+  a(:printable_version_link, href: /printable=yes/)
+  li(:random_page_link, id: "n-randompage")
+  li(:recent_changes_link, id: "n-recentchanges")
+  li(:related_changes_link, id: "t-recentchangeslinked")
+  li(:special_pages_link, id: "t-specialpages")
+  a(:view_history_link, href: /action=history/)
+  li(:what_links_here_link, id: "t-whatlinkshere")
+end
\ No newline at end of file
diff --git a/tests/browser/features/support/pages/view_history_page.rb b/tests/browser/features/support/pages/view_history_page.rb
new file mode 100644 (file)
index 0000000..6689598
--- /dev/null
@@ -0,0 +1,7 @@
+class ViewHistoryPage
+  include PageObject
+
+  a(:view_history_link, href: /action=history/)
+  a(:old_version_link, href: /oldid=/)
+
+end
\ No newline at end of file
diff --git a/tests/browser/features/support/pages/ztargetpage.rb b/tests/browser/features/support/pages/ztargetpage.rb
new file mode 100644 (file)
index 0000000..c1f46ec
--- /dev/null
@@ -0,0 +1,7 @@
+class ZtargetPage < MainPage
+  include URL
+  page_url URL.url("<%=params[:article_name]%>")
+  include PageObject
+
+  a(:link_target_page_link, text: "link to the test target page")
+end
\ No newline at end of file
diff --git a/tests/browser/features/view_history.feature b/tests/browser/features/view_history.feature
new file mode 100644 (file)
index 0000000..82bc813
--- /dev/null
@@ -0,0 +1,10 @@
+Feature: View History
+
+  Scenario: Edit page and view history
+    Given I go to the "History Test Page" page with content "This is a page that will have history"
+    When I click Edit
+      And I edit the page with "Edited and a random string"
+      And I save the edit
+      And the edited page content should contain "Edited and a random string"
+      And I click View History
+    Then I should see a link to a previous version of the page
\ No newline at end of file
index b3e9c51..03e4959 100644 (file)
@@ -1093,6 +1093,59 @@ Non-html5 tags should be accepted
 </p>
 !! end
 
+## a,rtc not permitted
+## i,b,br omitted
+!! test
+Text-level semantic html elements in wikitext
+!! wikitext
+<em>text</em>
+<strong>text</strong>
+<small>text</small>
+<s>text</s>
+<cite>text</cite>
+<q>text</q>
+<dfn>text</dfn>
+<abbr>text</abbr>
+<data>text</data>
+<time>text</time>
+<code>text</code>
+<var>text</var>
+<samp>text</samp>
+<kbd>text</kbd>
+<sub>text</sub>
+<u>text</u>
+<mark>text</mark>
+<ruby><rb>明日<rp>(</rp><rt>Ashita</rt><rp>)</rp></rb></ruby>
+<bdi>text</bdi>
+<bdo>text</bdo>
+<span>text</span>
+<wbr />
+!! html
+<p><em>text</em>
+<strong>text</strong>
+<small>text</small>
+<s>text</s>
+<cite>text</cite>
+<q>text</q>
+<dfn>text</dfn>
+<abbr>text</abbr>
+<data>text</data>
+<time>text</time>
+<code>text</code>
+<var>text</var>
+<samp>text</samp>
+<kbd>text</kbd>
+<sub>text</sub>
+<u>text</u>
+<mark>text</mark>
+<ruby><rb>明日<rp>(</rp><rt>Ashita</rt><rp>)</rp></rb></ruby>
+<bdi>text</bdi>
+<bdo>text</bdo>
+<span>text</span>
+<wbr />
+</p>
+!! end
+
 !! test
 Non-word characters don't terminate tag names (bug 17663, 40670, 52022)
 !! wikitext
@@ -1223,6 +1276,16 @@ parsoid
 </p>
 !! end
 
+!! test
+Properly escape nowiki when combined with other wiki markup
+!! options
+parsoid=html2wt
+!! wikitext
+<nowiki>* &lt;/nowiki&gt;</nowiki> tag
+!! html
+<p>* &lt;/nowiki&gt; tag</p>
+!! end
+
 ###
 ### Comments
 ###
@@ -4458,6 +4521,16 @@ Parenthesis in external links, w/ transclusion or comment
 <p>(<a rel="mw:ExtLink" href="http://example.com" data-parsoid='{"stx":"url","a":{"href":"http://example.com"},"sa":{"href":"http://example.com&lt;!-- hi -->"}}'>http://example.com</a>)</p>
 !! end
 
+!! test
+Replace invalid link targets when serializing
+!! options
+parsoid=html2wt
+!! html
+<a rel="mw:WikiLink" href="./]] foo [[bar">Manual</a>
+!! wikitext
+[[MediaWiki:Badtitletext|Manual]]
+!! end
+
 ###
 ### Quotes
 ###
@@ -5185,6 +5258,23 @@ Table cell with a single comment
 
 !! end
 
+!! test
+Table-cell after a comment-only-empty-line
+!! wikitext
+{|
+|a
+<!--c1-->
+<!--c2-->| b
+|}
+!! html/parsoid
+<table>
+<tbody><tr data-parsoid='{"autoInsertedEnd":true,"autoInsertedStart":true}'><td data-parsoid='{"autoInsertedEnd":true}'>a</td>
+<!--c1-->
+<!--c2--><td data-parsoid='{"autoInsertedEnd":true}'> b</td></tr>
+</tbody></table>
+
+!! end
+
 # The expected HTML structure in this test is debatable. The PHP parser does
 # not parse this kind of table at all. The main focus for Parsoid is on
 # round-tripping, so this output is ok for now. TODO: revisit!
@@ -5349,6 +5439,54 @@ foo
 </tbody></table>
 !!end
 
+!! test
+Strip unsupported table tags
+!! options
+parsoid=html2wt
+!! html
+<table>
+<thead>
+<tr>
+<th>Month</th>
+<th>Savings</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td>January</td>
+<td>$100</td>
+</tr>
+<tr>
+<td>February</td>
+<td>$80</td>
+</tr>
+</tbody>
+<tfoot>
+<tr>
+<td>Sum</td>
+<td>$180</td>
+</tr>
+</tfoot>
+</table>
+!! wikitext
+{|
+
+!Month
+!Savings
+
+|January
+|$100
+
+|-
+|February
+|$80
+
+|Sum
+|$180
+
+|}
+!! end
+
 ###
 ### Internal links
 ###
         b</p>
 !! end
 
+!! test
+2. Leading whitespace in non-indent-pre contexts should not be escaped
+!! options
+parsoid
+!! wikitext
+foo <ref>''a''
+ b</ref>
+!! html
+<p>foo <span about="#mwt2" class="reference" data-mw='{"name":"ref","body":{"html":"&lt;i data-parsoid=&#39;{\"dsr\":[9,14,2,2]}&#39;>a&lt;/i>\n b"},"attrs":{}}' id="cite_ref-1-0" rel="dc:references" typeof="mw:Extension/ref"><a href="#cite_note-1">[1]</a></span></p>
+!! end
+
+!! test
+3. Leading whitespace in indent-pre suppressing contexts should not be escaped
+!! options
+parsoid
+!! wikitext
+<blockquote>
+ a
+ <span>b</span>
+ c
+</blockquote>
+!! html
+<blockquote>
+<p>
+ a
+ <span>b</span>
+ c</p>
+</blockquote>
+!! end
+
+!! test
+4. Leading whitespace in indent-pre suppressing contexts should not be escaped
+!! options
+parsoid
+!! wikitext
+ [[File:Foobar.jpg|thumb|caption]]
+!! html
+!! html/parsoid
+ <figure class="mw-default-size" typeof="mw:Image/Thumb"><a href="File:Foobar.jpg"><img resource="./File:Foobar.jpg" src="//example.com/images/3/3a/Foobar.jpg" height="25" width="220"/></a><figcaption>caption</figcaption></figure>
+!! end
+
 #### --------------- Behavior Switches --------------------
 !! test
 1. Valid behavior switches should be escaped
 parsoid=html2wt
 !! wikitext
 <nowiki>__TOC__</nowiki>
+''<nowiki>__TOC__</nowiki>''
 !! html
 __TOC__
+<i>__TOC__</i>
 !! end
 
 !! test
@@ -19799,6 +19980,20 @@ Indented block & table
  </tbody></table>
 !!end
 
+!! test
+Indent and comment before table row
+!! wikitext
+{|
+ <!--hi-->|-
+ | there
+|}
+!! html/parsoid
+<table data-parsoid='{}'>
+ <!--hi--><tbody data-parsoid='{}'><tr data-parsoid='{"startTagSrc":"|-","autoInsertedEnd":true}'>
+ <td data-parsoid='{"autoInsertedEnd":true}'> there</td></tr>
+</tbody></table>
+!! end
+
 !!test
 Empty TR followed by a template-generated TR
 (Parsoid-specific since PHP parser doesn't handle this mixed tbl-wikitext)
@@ -20470,21 +20665,21 @@ foo
 !! end
 
 !! test
-Strip leading whitespace when handling indent-pre inducing tags
+Nowiki-wrap leading whitespace when handling indent-pre inducing tags
 !! options
 parsoid=html2wt
 !! wikitext
 foo
-<span>bar</span>
+<nowiki> </nowiki><span>bar</span>
 
 <span>foo2
-</span>bar2
+<nowiki> </nowiki></span>bar2
 
 <div>foo</div>
-<span>bar</span>
+<nowiki> </nowiki><span>bar</span>
 
 <div>
-<span>foo</span>
+<nowiki> </nowiki><span>foo</span>
 </div>
 !! html
 <p>foo</p>
index b07c013..ae069ea 100644 (file)
@@ -76,6 +76,7 @@ class ArticleTest extends MediaWikiTestCase {
         * @covers Article::getAutosummary
         */
        public function testStaticFunctions() {
+               $this->hideDeprecated( 'Article::selectFields' );
                $this->hideDeprecated( 'Article::getAutosummary' );
                $this->hideDeprecated( 'WikiPage::getAutosummary' );
                $this->hideDeprecated( 'CategoryPage::getAutosummary' ); // Inherited from Article
index a4ef06d..ef670df 100644 (file)
@@ -573,11 +573,30 @@ class LanguageTest extends LanguageClassesTestCase {
         * @covers Language::sprintfDate
         */
        public function testSprintfDate( $format, $ts, $expected, $msg ) {
+               $ttl = null;
                $this->assertEquals(
                        $expected,
-                       $this->getLang()->sprintfDate( $format, $ts ),
+                       $this->getLang()->sprintfDate( $format, $ts, null, $ttl ),
                        "sprintfDate('$format', '$ts'): $msg"
                );
+               if ( $ttl ) {
+                       $dt = new DateTime( $ts );
+                       $lastValidTS = $dt->add( new DateInterval( 'PT' . ( $ttl - 1 ) . 'S' ) )->format( 'YmdHis' );
+                       $this->assertEquals(
+                               $expected,
+                               $this->getLang()->sprintfDate( $format, $lastValidTS, null ),
+                               "sprintfDate('$format', '$ts'): TTL $ttl too high (output was different at $lastValidTS)"
+                       );
+               } else {
+                       // advance the time enough to make all of the possible outputs different (except possibly L)
+                       $dt = new DateTime( $ts );
+                       $newTS = $dt->add( new DateInterval( 'P1Y1M8DT13H1M1S' ) )->format( 'YmdHis' );
+                       $this->assertEquals(
+                               $expected,
+                               $this->getLang()->sprintfDate( $format, $newTS, null ),
+                               "sprintfDate('$format', '$ts'): Missing TTL (output was different at $newTS)"
+                       );
+               }
        }
 
        /**