Repository: oppia/oppia

Issue:
Adding an exploration to a story, when its "correctness feedback" setting is not turned on, results in the exploration being uneditable.
**Describe the bug**
When an exploration is added as a chapter in a story in the Math classroom, and its "correctness feedback" setting is not turned on (or it fails validation in some other way), this results in the lesson being uneditable. The save changes button is green, but when it is clicked (and the subsequent modal is clicked through), there is a silent 400 error in the JS console and no warning is shown to the user.

**To Reproduce**
Steps to reproduce the behavior:
 1. Create a new exploration with the "correctness feedback" setting in the Settings tab turned off.
 2. Create a new topic and story for the Math classroom.
 3. Add the exploration to the newly-created story.
 4. Enter the exploration and make a few edits, then try to save.

**Observed behavior**
The save fails silently (after clicking through the 'save' modal), with no error displayed. There is a 400 error in the JavaScript console which is not surfaced visibly to the end user.

**Expected behavior**
There should be a warning explaining what the user needs to fix in order to save the exploration properly. 
OR (probably better): it should not be possible to add the exploration to the story without the exploration passing validation (which includes the correctness feedback being turned on).
Adding an exploration to a story, when its "correctness feedback" setting is not turned on, results in the exploration being uneditable.
**Describe the bug**
When an exploration is added as a chapter in a story in the Math classroom, and its "correctness feedback" setting is not turned on (or it fails validation in some other way), this results in the lesson being uneditable. The save changes button is green, but when it is clicked (and the subsequent modal is clicked through), there is a silent 400 error in the JS console and no warning is shown to the user.

**To Reproduce**
Steps to reproduce the behavior:
 1. Create a new exploration with the "correctness feedback" setting in the Settings tab turned off.
 2. Create a new topic and story for the Math classroom.
 3. Add the exploration to the newly-created story.
 4. Enter the exploration and make a few edits, then try to save.

**Observed behavior**
The save fails silently (after clicking through the 'save' modal), with no error displayed. There is a 400 error in the JavaScript console which is not surfaced visibly to the end user.

**Expected behavior**
There should be a warning explaining what the user needs to fix in order to save the exploration properly. 
OR (probably better): it should not be possible to add the exploration to the story without the exploration passing validation (which includes the correctness feedback being turned on).


Discussion:

Resolving patch:
diff --git a/core/domain/exp_services.py b/core/domain/exp_services.py
--- a/core/domain/exp_services.py
+++ b/core/domain/exp_services.py
@@ -1037,14 +1037,15 @@ def validate_exploration_for_story(exp, strict):
             android_validation_constants.SUPPORTED_LANGUAGES):
         error_string = (
             'Invalid language %s found for exploration '
-            'with ID %s.' % (exp.language_code, exp.id))
+            'with ID %s. This language is not supported for explorations '
+            'in a story on the mobile app.' % (exp.language_code, exp.id))
         if strict:
             raise utils.ValidationError(error_string)
         validation_error_messages.append(error_string)
 
     if exp.param_specs or exp.param_changes:
         error_string = (
-            'Expected no exploration to have parameter '
+            'Expected no exploration in a story to have parameter '
             'values in it. Invalid exploration: %s' % exp.id)
         if strict:
             raise utils.ValidationError(error_string)
@@ -1052,7 +1053,8 @@ def validate_exploration_for_story(exp, strict):
 
     if not exp.correctness_feedback_enabled:
         error_string = (
-            'Expected all explorations to have correctness feedback '
+            'Expected all explorations in a story to '
+            'have correctness feedback '
             'enabled. Invalid exploration: %s' % exp.id)
         if strict:
             raise utils.ValidationError(error_string)
@@ -1063,7 +1065,9 @@ def validate_exploration_for_story(exp, strict):
         if not state.interaction.is_supported_on_android_app():
             error_string = (
                 'Invalid interaction %s in exploration '
-                'with ID: %s.' % (state.interaction.id, exp.id))
+                'with ID: %s. This interaction is not supported for '
+                'explorations in a story on the '
+                'mobile app.' % (state.interaction.id, exp.id))
             if strict:
                 raise utils.ValidationError(error_string)
             validation_error_messages.append(error_string)
@@ -1071,8 +1075,8 @@ def validate_exploration_for_story(exp, strict):
         if not state.is_rte_content_supported_on_android():
             error_string = (
                 'RTE content in state %s of exploration '
-                'with ID %s is not supported on mobile.'
-                % (state_name, exp.id))
+                'with ID %s is not supported on mobile for explorations '
+                'in a story.' % (state_name, exp.id))
             if strict:
                 raise utils.ValidationError(error_string)
             validation_error_messages.append(error_string)
@@ -1083,9 +1087,10 @@ def validate_exploration_for_story(exp, strict):
                     'recommendedExplorationIds'].value)
             if len(recommended_exploration_ids) != 0:
                 error_string = (
-                    'Exploration with ID: %s contains exploration '
-                    'recommendations in its EndExploration interaction.'
-                    % (exp.id))
+                    'Explorations in a story are not expected to contain '
+                    'exploration recommendations. Exploration with ID: '
+                    '%s contains exploration recommendations in its '
+                    'EndExploration interaction.' % (exp.id))
                 if strict:
                     raise utils.ValidationError(error_string)
                 validation_error_messages.append(error_string)


Test patch:
diff --git a/core/domain/story_services_test.py b/core/domain/story_services_test.py
--- a/core/domain/story_services_test.py
+++ b/core/domain/story_services_test.py
@@ -1257,10 +1257,13 @@ def test_cannot_update_story_with_exps_with_other_languages(self):
             story_services.validate_explorations_for_story(['exp_id_1'], False))
         self.assertEqual(
             validation_error_messages, [
-                'Invalid language es found for exploration with ID exp_id_1.'])
+                'Invalid language es found for exploration with ID exp_id_1.'
+                ' This language is not supported for explorations in a story'
+                ' on the mobile app.'])
         with self.assertRaisesRegex(
             Exception, 'Invalid language es found for exploration with '
-            'ID exp_id_1'):
+            'ID exp_id_1. This language is not supported for explorations '
+            'in a story on the mobile app.'):
             story_services.update_story(
                 self.USER_ID, self.STORY_ID, change_list, 'Updated story node.')
 
@@ -1287,11 +1290,12 @@ def test_cannot_update_story_with_exps_without_correctness_feedback(self):
             story_services.validate_explorations_for_story(['exp_id_1'], False))
         self.assertEqual(
             validation_error_messages, [
-                'Expected all explorations to have correctness feedback '
-                'enabled. Invalid exploration: exp_id_1'])
+                'Expected all explorations in a story to '
+                'have correctness feedback enabled. Invalid '
+                'exploration: exp_id_1'])
         with self.assertRaisesRegex(
-            Exception, 'Expected all explorations to have correctness feedback '
-            'enabled. Invalid exploration: exp_id_1'):
+            Exception, 'Expected all explorations in a story to '
+            'have correctness feedback enabled. Invalid exploration: exp_id_1'):
             story_services.update_story(
                 self.USER_ID, self.STORY_ID, change_list, 'Updated story node.')
 
@@ -1319,10 +1323,12 @@ def test_cannot_update_story_with_exps_with_invalid_interactions(self):
         self.assertEqual(
             validation_error_messages, [
                 'Invalid interaction GraphInput in exploration with ID: '
-                'exp_id_1.'])
+                'exp_id_1. This interaction is not supported for explorations '
+                'in a story on the mobile app.'])
         with self.assertRaisesRegex(
             Exception, 'Invalid interaction GraphInput in exploration with '
-            'ID: exp_id_1'):
+            'ID: exp_id_1. This interaction is not supported for explorations '
+            'in a story on the mobile app.'):
             story_services.update_story(
                 self.USER_ID, self.STORY_ID, change_list, 'Updated story node.')
 
@@ -1363,11 +1369,15 @@ def test_cannot_update_story_with_exps_with_recommended_exps(self):
             story_services.validate_explorations_for_story(['exp_id_1'], False))
         self.assertEqual(
             validation_error_messages, [
-                'Exploration with ID: exp_id_1 contains exploration '
-                'recommendations in its EndExploration interaction.'])
+                'Explorations in a story are not expected to contain '
+                'exploration recommendations. Exploration with ID: exp_id_1 '
+                'contains exploration recommendations in its EndExploration '
+                'interaction.'])
         with self.assertRaisesRegex(
-            Exception, 'Exploration with ID: exp_id_1 contains exploration '
-            'recommendations in its EndExploration interaction.'):
+            Exception, 'Explorations in a story are not expected to contain '
+            'exploration recommendations. Exploration with ID: exp_id_1 '
+            'contains exploration recommendations in its EndExploration '
+            'interaction.'):
             story_services.update_story(
                 self.USER_ID, self.STORY_ID, change_list, 'Updated story node.')
 
@@ -1411,10 +1421,12 @@ def test_cannot_update_story_with_exps_with_invalid_rte_content(self):
         self.assertEqual(
             validation_error_messages, [
                 'RTE content in state Introduction of exploration with '
-                'ID exp_id_1 is not supported on mobile.'])
+                'ID exp_id_1 is not supported on mobile for explorations '
+                'in a story.'])
         with self.assertRaisesRegex(
             Exception, 'RTE content in state Introduction of exploration with '
-            'ID exp_id_1 is not supported on mobile.'):
+            'ID exp_id_1 is not supported on mobile for explorations '
+            'in a story.'):
             story_services.update_story(
                 self.USER_ID, self.STORY_ID, change_list, 'Updated story node.')
 
@@ -1451,11 +1463,11 @@ def test_cannot_update_story_with_exps_with_parameter_values(self):
             story_services.validate_explorations_for_story(['exp_id_1'], False))
         self.assertEqual(
             validation_error_messages, [
-                'Expected no exploration to have parameter values in'
-                ' it. Invalid exploration: exp_id_1'])
+                'Expected no exploration in a story to have parameter '
+                'values in it. Invalid exploration: exp_id_1'])
         with self.assertRaisesRegex(
-            Exception, 'Expected no exploration to have parameter values in'
-            ' it. Invalid exploration: exp_id_1'):
+            Exception, 'Expected no exploration in a story to have parameter '
+            'values in it. Invalid exploration: exp_id_1'):
             story_services.update_story(
                 self.USER_ID, self.STORY_ID, change_list, 'Updated story node.')
 
@@ -1492,8 +1504,8 @@ def test_cannot_update_story_with_exps_with_parameter_values(self):
         ]
 
         with self.assertRaisesRegex(
-            Exception, 'Expected no exploration to have parameter values in'
-            ' it. Invalid exploration: exp_id_2'):
+            Exception, 'Expected no exploration in a story to have parameter '
+            'values in it. Invalid exploration: exp_id_2'):
             story_services.update_story(
                 self.USER_ID, self.STORY_ID, change_list, 'Updated story node.')
 
diff --git a/core/tests/protractor_utils/workflow.js b/core/tests/protractor_utils/workflow.js
--- a/core/tests/protractor_utils/workflow.js
+++ b/core/tests/protractor_utils/workflow.js
@@ -174,6 +174,7 @@ var createAndPublishExploration = async function(
   if (language) {
     await explorationEditorSettingsTab.setLanguage(language);
   }
+  await explorationEditorSettingsTab.enableCorrectnessFeedback();
   await explorationEditorPage.saveChanges();
   await publishExploration();
 };