jenkins-bot has submitted this change and it was merged.

Change subject: Model internal and external surveys
......................................................................


Model internal and external surveys

* Add the Survey abstract base class, and the InternalSurvey and
  ExternalSurvey classes, which model internal and external surveys
  respectively.
* Add the SurveyFactory class that creates an instance of a model based
  on some specification or throws an exception - with an informative
  message - if it is invalid
* Replace most of the logic in the ResourceLoaderGetConfigVars and
  ResourceLoaderRegisterModules handlers with simple manipulations of
  models

Bug: T110196
Change-Id: I90b3ce5c7fb0ef06802cd9e0abe429e67bc742cc
---
M extension.json
A includes/ExternalSurvey.php
A includes/InternalSurvey.php
M includes/QuickSurveys.hooks.php
A includes/Survey.php
A includes/SurveyFactory.php
M tests/browser/LocalSettings.php
A tests/phpunit/SurveyFactoryTest.php
8 files changed, 501 insertions(+), 50 deletions(-)

Approvals:
  Jdlrobson: Looks good to me, approved
  jenkins-bot: Verified



diff --git a/extension.json b/extension.json
index 072d0aa..306de24 100644
--- a/extension.json
+++ b/extension.json
@@ -74,7 +74,11 @@
                }
        },
        "AutoloadClasses": {
-               "QuickSurveys\\Hooks": "includes/QuickSurveys.hooks.php"
+               "QuickSurveys\\Hooks": "includes/QuickSurveys.hooks.php",
+               "QuickSurveys\\SurveyFactory": "includes/SurveyFactory.php",
+               "QuickSurveys\\Survey": "includes/Survey.php",
+               "QuickSurveys\\InternalSurvey": "includes/InternalSurvey.php",
+               "QuickSurveys\\ExternalSurvey": "includes/ExternalSurvey.php"
        },
        "manifest_version": 1,
        "Hooks": {
@@ -101,7 +105,7 @@
                                "@question": "survey question message key",
                                "question": 
"ext-quicksurveys-example-internal-survey-question",
                                "@description": "The message key of the 
description of the survey. Displayed immediately below the survey question.",
-                               "description": "",
+                               "description": 
"ext-quicksurveys-example-internal-survey-description",
                                "@answers": "possible answer message keys for 
positive, neutral, and negative",
                                "answers": {
                                        "positive": 
"ext-quicksurveys-example-internal-survey-answer-positive",
@@ -113,7 +117,7 @@
                                "@enabled": "whether the survey is enabled",
                                "enabled": false,
                                "@coverage": "percentage of users that will see 
the survey",
-                               "coverage": "50",
+                               "coverage": 0.5,
                                "@platform": "for each platform (desktop, 
mobile), which version of it is targeted (stable, beta, alpha)",
                                "platform": {
                                        "desktop": ["stable"],
@@ -135,7 +139,7 @@
                                "@enabled": "whether the survey is enabled",
                                "enabled": false,
                                "@coverage": "percentage of users that will see 
the survey",
-                               "coverage": "50",
+                               "coverage": 0.5,
                                "@platform": "for each platform (desktop, 
mobile), which version of it is targeted (stable, beta, alpha)",
                                "platform": {
                                        "desktop": ["stable"],
diff --git a/includes/ExternalSurvey.php b/includes/ExternalSurvey.php
new file mode 100644
index 0000000..568de8f
--- /dev/null
+++ b/includes/ExternalSurvey.php
@@ -0,0 +1,43 @@
+<?php
+
+namespace QuickSurveys;
+
+class ExternalSurvey extends Survey
+{
+       /**
+        * @var string The URL of the external survey.
+        */
+       private $link;
+
+       /**
+        * @var string The description of the privacy policy of the website 
that hosts the external survey.
+        */
+       private $privacyPolicy;
+
+       public function __construct(
+               $name,
+               $question,
+               $description,
+               $isEnabled,
+               $coverage,
+               $link,
+               $privacyPolicy
+       ) {
+               parent::__construct( $name, $question, $description, 
$isEnabled, $coverage );
+
+               $this->link = $link;
+               $this->privacyPolicy = $privacyPolicy;
+       }
+
+       public function getMessages() {
+               return array_merge( parent::getMessages(), array( 
$this->privacyPolicy ) );
+       }
+
+       public function toArray() {
+               return parent::toArray() + array(
+                       'type' => 'external',
+                       'link' => $this->link,
+                       'privacyPolicy' => $this->privacyPolicy,
+               );
+       }
+}
diff --git a/includes/InternalSurvey.php b/includes/InternalSurvey.php
new file mode 100644
index 0000000..5ce74d6
--- /dev/null
+++ b/includes/InternalSurvey.php
@@ -0,0 +1,35 @@
+<?php
+
+namespace QuickSurveys;
+
+class InternalSurvey extends Survey
+{
+       /**
+        * @var array A map of internal key, e.g. "positive", to a i18n message 
key
+        */
+       private $answers;
+
+       public function __construct(
+               $name,
+               $question,
+               $description,
+               $isEnabled,
+               $coverage,
+               array $answers
+       ) {
+               parent::__construct( $name, $question, $description, 
$isEnabled, $coverage );
+
+               $this->answers = $answers;
+       }
+
+       public function getMessages() {
+               return array_merge( parent::getMessages(), array_values( 
$this->answers ) );
+       }
+
+       public function toArray() {
+               return parent::toArray() + array(
+                       'type' => 'internal',
+                       'answers' => $this->answers,
+               );
+       }
+}
diff --git a/includes/QuickSurveys.hooks.php b/includes/QuickSurveys.hooks.php
index 190219c..dbde460 100644
--- a/includes/QuickSurveys.hooks.php
+++ b/includes/QuickSurveys.hooks.php
@@ -20,7 +20,11 @@
         * @return boolean
         */
        public static function onResourceLoaderGetConfigVars( &$vars ) {
-               $vars['wgEnabledQuickSurveys'] = self::getEnabledSurveys();
+               $surveys = self::getEnabledSurveys();
+               $vars['wgEnabledQuickSurveys']= array_map( function ( Survey 
$survey ) {
+                       return $survey->toArray();
+               }, $surveys );
+
                return true;
        }
 
@@ -49,34 +53,17 @@
         */
        public static function onResourceLoaderRegisterModules( ResourceLoader 
&$resourceLoader ) {
                $enabledSurveys = self::getEnabledSurveys();
-               // Register enabled surveys as their own modules
+
                foreach ( $enabledSurveys as $survey ) {
-                       $messages = array();
+                       $moduleName = $survey->getResourceLoaderModuleName();
+                       $module = array(
+                               $moduleName => array(
+                                       'messages' => $survey->getMessages(),
+                                       'targets' => array( 'desktop', 'mobile' 
),
+                               ),
+                       );
 
-                       // All surveys have a question and description
-                       $messages[] = $survey['question'];
-                       $messages[] = $survey['description'];
-
-                       // Add messages that are specific the survey type
-                       if ( $survey['type'] === 'internal' ) {
-                               $messages[] = $survey['answers']['positive'];
-                               $messages[] = $survey['answers']['neutral'];
-                               $messages[] = $survey['answers']['negative'];
-
-                       } elseif ( $survey['type'] === 'external' ) {
-                               $messages[] = $survey['link'];
-                               // camelCase because the key will be a property 
of a JavaScript object.
-                               // Also this is not the key of the i18n 
message, it's the key that denotes the key of
-                               // the i18n message. Basically, key of a PHP 
array.
-                               $messages[] = $survey['privacyPolicy'];
-                       }
-
-                       $surveyModule = array( $survey['module'] => array(
-                               'messages' => $messages,
-                               'targets' => array( 'desktop', 'mobile' ),
-                       ) );
-
-                       $resourceLoader->register( $surveyModule );
+                       $resourceLoader->register( $module );
                }
 
                return true;
@@ -89,28 +76,15 @@
         */
        private static function getEnabledSurveys() {
                $config = ConfigFactory::getDefaultInstance()->makeConfig( 
'quicksurveys' );
-               // Get configured surveys
                $configuredSurveys = $config->has( 'QuickSurveysConfig' )
                        ? $config->get( 'QuickSurveysConfig' )
                        : array();
-               $enabledQuickSurveys = array();
-               // Make enabled surveys available to the browser
-               foreach ( $configuredSurveys as $survey ) {
-                       if ( $survey['enabled'] === true ) {
-                               $survey['module'] = self::getSurveyModuleName( 
$survey );
-                               $enabledQuickSurveys[] = $survey;
-                       }
-               }
-               return $enabledQuickSurveys;
-       }
+               $surveys = array_map( '\\QuickSurveys\\SurveyFactory::factory', 
$configuredSurveys );
+               $enabledSurveys = array_filter( $surveys, function ( Survey 
$survey ) {
+                       return $survey->isEnabled();
+               } );
 
-       /**
-        * Returns the name of the specified survey's module
-        *
-        * @return string Survey's ResourceLoader module name
-        */
-       private static function getSurveyModuleName( $survey ) {
-               return 'ext.quicksurveys.survey.' . str_replace( ' ', '.', 
$survey['name'] );
+               return array_values( $enabledSurveys );
        }
 
        /**
@@ -132,4 +106,20 @@
 
                return true;
        }
+
+       /**
+        * UnitTestsList hook handler.
+        *
+        * Adds the path to the QuickSurveys PHPUnit tests to the set of enabled
+        * extension's test suites.
+        *
+        * @param array $paths The set of paths to other extension's PHPUnit 
test
+        *  suites
+        * @return bool Always true
+        */
+       public static function onUnitTestsList( array &$paths ) {
+               $paths[] = __DIR__ . '/../tests/phpunit';
+
+               return true;
+       }
 }
diff --git a/includes/Survey.php b/includes/Survey.php
new file mode 100644
index 0000000..354fca7
--- /dev/null
+++ b/includes/Survey.php
@@ -0,0 +1,93 @@
+<?php
+
+namespace QuickSurveys;
+
+abstract class Survey
+{
+       /**
+        * @var string The friendly name of the survey
+        */
+       private $name;
+
+       /**
+        * @var string The question that the survey is posing to the user
+        */
+       private $question;
+
+       /**
+        * @var string A user-friendly description of, or introduction to, the 
question
+        */
+       private $description;
+
+       /**
+        * @var bool Whether the survey is enabled
+        */
+       private $isEnabled;
+
+       /**
+        * @var float The percentage of users that will see the survey 
expressed as a fraction
+        */
+       private $coverage;
+
+       public function __construct(
+               $name,
+               $question,
+               $description,
+               $isEnabled,
+               $coverage
+       ) {
+               $this->name = $name;
+               $this->question = $question;
+               $this->description = $description;
+               $this->isEnabled = $isEnabled;
+               $this->coverage = $coverage;
+       }
+
+       // --------
+       /**
+        * Returns the name of the ResourceLoader module
+        *
+        * @return string
+        */
+       public function getResourceLoaderModuleName() {
+               return 'ext.quicksurveys.survey.' . str_replace( ' ', '.', 
$this->name );
+       }
+
+       /**
+        * Gets the list of i18n message keys that the survey uses
+        *
+        * @return [string]
+        */
+       public function getMessages() {
+               return array(
+                       $this->question,
+
+                       // FIXME: Should description be optional?
+                       $this->description,
+               );
+       }
+       // --------
+
+       /**
+        * Returns the JSON-encodable, minimal representation of the survey
+        *
+        * @return array
+        */
+       public function toArray() {
+               return array(
+                       'question' => $this->question,
+                       'description' => $this->description,
+                       'module' => $this->getResourceLoaderModuleName(),
+                       'coverage' => $this->coverage,
+               );
+       }
+
+       /**
+        * Gets whether the survey is enabled
+        *
+        * @return bool
+        */
+       public function isEnabled() {
+               return $this->isEnabled;
+       }
+}
diff --git a/includes/SurveyFactory.php b/includes/SurveyFactory.php
new file mode 100644
index 0000000..a7d0ac9
--- /dev/null
+++ b/includes/SurveyFactory.php
@@ -0,0 +1,105 @@
+<?php
+
+namespace QuickSurveys;
+
+use InvalidArgumentException;
+
+class SurveyFactory
+{
+       /**
+        * Creates an instance of either the InternalSurvey or ExternalSurvey 
class
+        * given a specification.
+        *
+        * An exception is thrown if any of the following conditions aren't met:
+        *
+        * <ul>
+        *   <li>A survey must have a question</li>
+        *   <li>A survey must have a description</li>
+        *   <li>A survey's type must be either "internal" or "external"</li>
+        *   <li>A survey must have a coverage</li>
+        *   <li>An internal survey must have a set of questions</li>
+        *   <li>An external survey must have a link</li>
+        *   <li>An external survey must have a privacy policy</li>
+        * </ul>
+        *
+        * @throws InvalidArgumentException If the configuration is invalid
+        * @return Survey
+        */
+       public static function factory( array $spec ) {
+               $name = $spec['name'];
+
+               if ( !isset( $spec['question'] ) ) {
+                       throw new InvalidArgumentException( "The \"{$name}\" 
survey doesn't have a question." );
+               }
+
+               if ( !isset( $spec['description'] ) ) {
+                       throw new InvalidArgumentException( "The \"{$name}\" 
survey doesn't have a description." );
+               }
+
+               if (
+                       !isset( $spec['type'] )
+                       || ( $spec['type'] !== 'internal' && $spec['type'] !== 
'external' )
+               ) {
+                       throw new InvalidArgumentException(
+                               "The \"{$name}\" survey isn't marked as 
internal or external."
+                       );
+               }
+
+               if ( !isset( $spec['coverage'] ) ) {
+                       throw new InvalidArgumentException( "The \"{$name}\" 
survey doesn't have a coverage." );
+               }
+
+               if ( !isset( $spec['enabled' ] ) ) {
+                       $spec['enabled'] = false;
+               }
+
+               if ( $spec['type'] === 'internal' ) {
+                       return self::factoryInternal( $spec );
+               }
+
+               return self::factoryExternal( $spec );
+       }
+
+       private static function factoryExternal( $spec ) {
+               $name = $spec['name'];
+
+               if ( !isset( $spec['link'] ) ) {
+                       throw new InvalidArgumentException( "The \"{$name}\" 
external survey doesn't have a link." );
+               }
+
+               if ( !isset( $spec['privacyPolicy'] ) ) {
+                       throw new InvalidArgumentException(
+                               "The \"{$name}\" external survey doesn't have a 
privacy policy."
+                       );
+               }
+
+               return new ExternalSurvey(
+                       $spec['name'],
+                       $spec['question'],
+                       $spec['description'],
+                       $spec['enabled'],
+                       $spec['coverage'],
+                       $spec['link'],
+                       $spec['privacyPolicy']
+               );
+       }
+
+       private static function factoryInternal( $spec ) {
+               $name = $spec['name'];
+
+               if ( !isset( $spec['answers'] ) ) {
+                       throw new InvalidArgumentException(
+                               "The \"{$name}\" internal survey doesn't have 
any answers."
+                       );
+               }
+
+               return new InternalSurvey(
+                       $spec['name'],
+                       $spec['question'],
+                       $spec['description'],
+                       $spec['enabled'],
+                       $spec['coverage'],
+                       $spec['answers']
+               );
+       }
+}
diff --git a/tests/browser/LocalSettings.php b/tests/browser/LocalSettings.php
index 98d3451..346a91e 100644
--- a/tests/browser/LocalSettings.php
+++ b/tests/browser/LocalSettings.php
@@ -11,7 +11,7 @@
                ),
                "schema" => "QuickSurveysResponses",
                "enabled" => true,
-               "coverage" => "100",
+               "coverage" => 1,
                "description" => "yo",
                "platform" => array(
                        "desktop" => array( "stable" ),
diff --git a/tests/phpunit/SurveyFactoryTest.php 
b/tests/phpunit/SurveyFactoryTest.php
new file mode 100644
index 0000000..9838076
--- /dev/null
+++ b/tests/phpunit/SurveyFactoryTest.php
@@ -0,0 +1,181 @@
+<?php
+
+namespace Tests\QuickSurveys;
+
+use PHPUnit_Framework_TestCase;
+use QuickSurveys\SurveyFactory;
+use QuickSurveys\InternalSurvey;
+use QuickSurveys\ExternalSurvey;
+
+class SurveyFactoryTest extends PHPUnit_Framework_TestCase
+{
+       /**
+        * @expectedException InvalidArgumentException
+        * @expectedExceptionMessage The "test" survey doesn't have a question.
+        */
+       public function testItShouldThrowWhenThereIsNoQuestion() {
+               SurveyFactory::factory( array(
+                       'name' => 'test',
+               ) );
+       }
+
+
+       /**
+        * @expectedException InvalidArgumentException
+        * @expectedExceptionMessage The "test" survey doesn't have a 
description.
+        */
+       public function testItShouldThrowWhenThereIsNoDescription() {
+               SurveyFactory::factory( array(
+                       'name' => 'test',
+                       'question' => 'Do you like writing unit tests?',
+               ) );
+       }
+
+       /**
+        * @expectedException InvalidArgumentException
+        * @expectedExceptionMessage The "test" survey isn't marked as internal 
or external.
+        */
+       public function testItShouldThrowWhenThereIsNoType() {
+               SurveyFactory::factory( array(
+                       'name' => 'test',
+                       'question' => 'Do you like writing unit tests?',
+                       'description' => 'A survey for (potential) developers 
on the QuickSurveys project.',
+               ) );
+       }
+
+       /**
+        * @expectedException InvalidArgumentException
+        * @expectedExceptionMessage The "test" external survey doesn't have a 
link.
+        */
+       public function testItShouldThrowWhenThereIsNoLink() {
+               SurveyFactory::factory( array(
+                       'name' => 'test',
+                       'type' => 'external',
+                       'question' => 'Do you like writing unit tests?',
+                       'description' => 'A survey for (potential) developers 
on the QuickSurveys project.',
+                       'coverage' => 1,
+               ) );
+       }
+
+       /**
+        * @expectedException InvalidArgumentException
+        * @expectedExceptionMessage The "test" external survey doesn't have a 
privacy policy.
+        */
+       public function testItShouldThrowWhenThereIsNoPrivacyPolicy() {
+               SurveyFactory::factory( array(
+                       'name' => 'test',
+                       'type' => 'external',
+                       'question' => 'Do you like writing unit tests?',
+                       'description' => 'A survey for (potential) developers 
on the QuickSurveys project.',
+                       'coverage' => 1,
+                       'link' => '//example.org/test-external-survey',
+               ) );
+       }
+
+       public function testItShouldFactoryAnExternalSurvey() {
+               $expected = new ExternalSurvey(
+                       'test',
+                       'Do you like writing unit tests?',
+                       'A survey for (potential) developers of the 
QuickSurveys extension.',
+                       true,
+                       1,
+                       '//example.org/test-external-survey',
+                       'ext-quicksurveys-test-external-survey-privacy-policy'
+               );
+
+               $actual = SurveyFactory::factory( array(
+                       'name' => 'test',
+                       'type' => 'external',
+                       'question' => 'Do you like writing unit tests?',
+                       'description' => 'A survey for (potential) developers 
of the QuickSurveys extension.',
+                       'enabled' => true,
+                       'coverage' => 1,
+                       'link' => '//example.org/test-external-survey',
+                       'privacyPolicy' => 
'ext-quicksurveys-test-external-survey-privacy-policy',
+               ) );
+
+               $this->assertEquals( $actual, $expected );
+       }
+
+       /**
+        * @expectedException InvalidArgumentException
+        * @expectedExceptionMessage The "test" internal survey doesn't have 
any answers.
+        */
+       public function testItShouldThrowWhenThereAreNoAnswers() {
+               SurveyFactory::factory( array(
+                       'name' => 'test',
+                       'type' => 'internal',
+                       'question' => 'Do you like writing unit tests?',
+                       'description' => 'A survey for (potential) developers 
on the QuickSurveys project.',
+                       'coverage' => 1,
+               ) );
+       }
+
+       public function testItShouldFactoryAnInternalSurvey() {
+               $expected = new InternalSurvey(
+                       'test',
+                       'Do you like writing unit tests?',
+                       'A survey for (potential) developers of the 
QuickSurveys extension.',
+                       true,
+                       1,
+                       array(
+                               'positive' => 
'ext-quicksurveys-test-internal-survey-positive',
+                       )
+               );
+
+               $actual = SurveyFactory::factory( array(
+                       'name' => 'test',
+                       'type' => 'internal',
+                       'question' => 'Do you like writing unit tests?',
+                       'description' => 'A survey for (potential) developers 
of the QuickSurveys extension.',
+                       'enabled' => true,
+                       'coverage' => 1,
+                       'answers' => array(
+                               'positive' => 
'ext-quicksurveys-test-internal-survey-positive',
+                       ),
+               ) );
+
+               $this->assertEquals( $actual, $expected );
+       }
+
+       /**
+        * @expectedException InvalidArgumentException
+        * @expectedExceptionMessage The "test" survey isn't marked as internal 
or external.
+        */
+       public function testItShouldThrowIfTheTypeIsNotRecognized() {
+               SurveyFactory::factory( array(
+                       'name' => 'test',
+                       'type' => 'ixternal',
+                       'question' => 'Do you like writing unit tests?',
+                       'description' => 'A survey for (potential) developers 
of the QuickSurveys extension.',
+               ) );
+       }
+
+       public function testItShouldMarkTheSurveyAsDisabledByDefault() {
+               $survey = SurveyFactory::factory( array(
+                       'name' => 'test',
+                       'type' => 'internal',
+                       'question' => 'Do you like writing unit tests?',
+                       'description' => 'A survey for (potential) developers 
of the QuickSurveys extension.',
+                       'coverage' => 1,
+                       'answers' => array(
+                               'positive' => 
'ext-quicksurveys-test-internal-survey-positive',
+                       ),
+               ) );
+
+               $this->assertFalse( $survey->isEnabled() );
+       }
+
+       /**
+        * @expectedException InvalidArgumentException
+        * @expectedExceptionMessage The "test" survey doesn't have a coverage.
+        */
+       public function testItShouldThrowWhenThereIsNoCoverage() {
+               SurveyFactory::factory( array(
+                       'name' => 'test',
+                       'type' => 'internal',
+                       'question' => 'Do you like writing unit tests?',
+                       'description' => 'A survey for (potential) developers 
of the QuickSurveys extension.',
+               ) );
+       }
+}

-- 
To view, visit https://gerrit.wikimedia.org/r/234267
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: I90b3ce5c7fb0ef06802cd9e0abe429e67bc742cc
Gerrit-PatchSet: 2
Gerrit-Project: mediawiki/extensions/QuickSurveys
Gerrit-Branch: dev
Gerrit-Owner: Phuedx <[email protected]>
Gerrit-Reviewer: Jdlrobson <[email protected]>
Gerrit-Reviewer: Phuedx <[email protected]>
Gerrit-Reviewer: jenkins-bot <>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to