Repository: oppia/oppia

Issue:
Question creation doesn't work
1. Go to the skill editor
2. Click create question on the main skill editor page
3.select difficulty
4. click done
5. see error

I've fixed it


Discussion:

Resolving patch:
diff --git a/scripts/linters/js_ts_linter.py b/scripts/linters/js_ts_linter.py
--- a/scripts/linters/js_ts_linter.py
+++ b/scripts/linters/js_ts_linter.py
@@ -20,6 +20,7 @@
 from __future__ import unicode_literals  # pylint: disable=import-only-modules
 
 import collections
+import json
 import os
 import re
 import shutil
@@ -45,6 +46,12 @@
 
 COMPILED_TYPESCRIPT_TMP_PATH = 'tmpcompiledjs/'
 
+TS_IGNORE_EXCEPTIONS_FILEPATH = os.path.join(
+    CURR_DIR, 'scripts', 'linters', 'ts_ignore_exceptions.json')
+
+TS_IGNORE_EXCEPTIONS = json.load(python_utils.open_file(
+    TS_IGNORE_EXCEPTIONS_FILEPATH, 'r'))
+
 
 def _get_expression_from_node_if_one_exists(
         parsed_node, components_to_check):
@@ -284,6 +291,166 @@ def _check_http_requests(self):
 
         return summary_messages
 
+    def _check_ts_ignore(self):
+        """Checks if ts ignore is used."""
+        summary_messages = []
+        if self.verbose_mode_enabled:
+            python_utils.PRINT('Starting ts ignore check')
+            python_utils.PRINT('----------------------------------------')
+
+        ts_ignore_pattern = r'@ts-ignore'
+        comment_pattern = r'^ *// '
+        comment_with_ts_error_pattern = r'^ *// This throws'
+
+        with linter_utils.redirect_stdout(sys.stdout):
+            failed = False
+
+            for file_path in self.all_filepaths:
+                file_content = self.file_cache.read(file_path)
+                previous_line_has_ts_ignore = False
+                previous_line_has_comment = False
+                previous_line_has_comment_with_ts_error = False
+
+                for line_number, line in enumerate(file_content.split('\n')):
+                    if previous_line_has_ts_ignore:
+                        if file_path in TS_IGNORE_EXCEPTIONS:
+                            line_contents = TS_IGNORE_EXCEPTIONS[file_path]
+                            this_line_is_exception = False
+
+                            for line_content in line_contents:
+                                if line.find(line_content) != -1:
+                                    this_line_is_exception = True
+                                    break
+
+                            if this_line_is_exception:
+                                previous_line_has_ts_ignore = False
+                                continue
+
+                        failed = True
+                        previous_line_has_ts_ignore = False
+                        summary_message = (
+                            '%s --> @ts-ignore found at line %s. '
+                            'Please add this exception in %s.' % (
+                                file_path, line_number,
+                                TS_IGNORE_EXCEPTIONS_FILEPATH))
+                        python_utils.PRINT(summary_message)
+                        summary_messages.append(summary_message)
+                        python_utils.PRINT('')
+
+                    previous_line_has_ts_ignore = bool(
+                        re.findall(ts_ignore_pattern, line))
+
+                    if (
+                            previous_line_has_ts_ignore and
+                            not previous_line_has_comment_with_ts_error):
+                        failed = True
+                        summary_message = (
+                            '%s --> Please add a comment above the @ts-ignore '
+                            'explaining the @ts-ignore at line %s. The format '
+                            'of comment should be -> This throws "...". This '
+                            'needs to be suppressed because ...' % (
+                                file_path, line_number + 1))
+                        python_utils.PRINT(summary_message)
+                        summary_messages.append(summary_message)
+                        python_utils.PRINT('')
+
+                    previous_line_has_comment = bool(
+                        re.findall(comment_pattern, line))
+
+                    previous_line_has_comment_with_ts_error = (
+                        bool(
+                            re.findall(
+                                comment_with_ts_error_pattern, line))
+                        or (
+                            previous_line_has_comment_with_ts_error and
+                            previous_line_has_comment))
+
+            if failed:
+                summary_message = (
+                    '%s TS ignore check failed' % (
+                        linter_utils.FAILED_MESSAGE_PREFIX))
+            else:
+                summary_message = (
+                    '%s TS ignore check passed' % (
+                        linter_utils.SUCCESS_MESSAGE_PREFIX))
+
+            python_utils.PRINT(summary_message)
+            summary_messages.append(summary_message)
+            python_utils.PRINT('')
+
+        return summary_messages
+
+    def _check_ts_expect_error(self):
+        """Checks if ts expect error is used in non spec file."""
+        summary_messages = []
+        if self.verbose_mode_enabled:
+            python_utils.PRINT('Starting ts expect error check')
+            python_utils.PRINT('----------------------------------------')
+
+        ts_expect_error_pattern = r'@ts-expect-error'
+        comment_pattern = r'^ *// '
+        comment_with_ts_error_pattern = r'^ *// This throws'
+
+        with linter_utils.redirect_stdout(sys.stdout):
+            failed = False
+            previous_line_has_comment = False
+            previous_line_has_comment_with_ts_error = False
+
+            for file_path in self.all_filepaths:
+                file_content = self.file_cache.read(file_path)
+                for line_number, line in enumerate(file_content.split('\n')):
+                    if re.findall(ts_expect_error_pattern, line):
+                        if not (
+                                file_path.endswith('.spec.ts') or
+                                file_path.endswith('Spec.ts')):
+                            failed = True
+                            summary_message = (
+                                '%s --> @ts-expect-error found at line %s. '
+                                'It can be used only in spec files.' % (
+                                    file_path, line_number + 1))
+                            python_utils.PRINT(summary_message)
+                            summary_messages.append(summary_message)
+                            python_utils.PRINT('')
+
+                        if not previous_line_has_comment_with_ts_error:
+                            failed = True
+                            summary_message = (
+                                '%s --> Please add a comment above the '
+                                '@ts-expect-error explaining the '
+                                '@ts-expect-error at line %s. The format '
+                                'of comment should be -> This throws "...". '
+                                'This needs to be suppressed because ...' % (
+                                    file_path, line_number + 1))
+                            python_utils.PRINT(summary_message)
+                            summary_messages.append(summary_message)
+                            python_utils.PRINT('')
+
+                    previous_line_has_comment = bool(
+                        re.findall(comment_pattern, line))
+
+                    previous_line_has_comment_with_ts_error = (
+                        bool(
+                            re.findall(
+                                comment_with_ts_error_pattern, line))
+                        or (
+                            previous_line_has_comment_with_ts_error and
+                            previous_line_has_comment))
+
+            if failed:
+                summary_message = (
+                    '%s TS expect error check failed' % (
+                        linter_utils.FAILED_MESSAGE_PREFIX))
+            else:
+                summary_message = (
+                    '%s TS expect error check passed' % (
+                        linter_utils.SUCCESS_MESSAGE_PREFIX))
+
+            python_utils.PRINT(summary_message)
+            summary_messages.append(summary_message)
+            python_utils.PRINT('')
+
+        return summary_messages
+
     def _check_extra_js_files(self):
         """Checks if the changes made include extra js files in core
         or extensions folder which are not specified in
@@ -893,7 +1060,8 @@ def _check_comments(self):
         # Example: // eslint-disable max-len
         # This comment will be excluded from this check.
         allowed_start_phrases = [
-            '@ts-ignore', '--params', 'eslint-disable', 'eslint-enable']
+            '@ts-expect-error', '@ts-ignore', '--params', 'eslint-disable',
+            'eslint-enable']
 
         # We allow comments to not have a terminating punctuation if any of the
         # below phrases appears in the last word of a comment.
@@ -994,6 +1162,8 @@ def perform_all_lint_checks(self):
         self.parsed_expressions_in_files = (
             self._get_expressions_from_parsed_script())
 
+        ts_ignore_messages = self._check_ts_ignore()
+        ts_expect_error_messages = self._check_ts_expect_error()
         extra_js_files_messages = self._check_extra_js_files()
         http_requests_messages = self._check_http_requests()
         js_and_ts_component_messages = (
@@ -1009,11 +1179,11 @@ def perform_all_lint_checks(self):
         shutil.rmtree(COMPILED_TYPESCRIPT_TMP_PATH, ignore_errors=True)
 
         all_messages = (
-            extra_js_files_messages +
-            http_requests_messages + js_and_ts_component_messages +
-            directive_scope_messages + sorted_dependencies_messages +
-            controller_dependency_messages + constant_declaration_messages +
-            comments_style_messages)
+            ts_ignore_messages + ts_expect_error_messages +
+            extra_js_files_messages + http_requests_messages +
+            js_and_ts_component_messages + directive_scope_messages +
+            sorted_dependencies_messages + controller_dependency_messages +
+            constant_declaration_messages + comments_style_messages)
         return all_messages
 
 


Test patch:
diff --git a/scripts/linters/js_ts_linter_test.py b/scripts/linters/js_ts_linter_test.py
--- a/scripts/linters/js_ts_linter_test.py
+++ b/scripts/linters/js_ts_linter_test.py
@@ -77,6 +77,14 @@
     LINTER_TESTS_DIR, 'invalid_comments.ts')
 INVALID_DIRECTIVE_WITH_NO_RETURN_BLOCK = os.path.join(
     LINTER_TESTS_DIR, 'invalid_directive_without_return.ts')
+INVALID_TS_IGNORE_FILEPATH = os.path.join(
+    LINTER_TESTS_DIR, 'invalid_ts_ignore.ts')
+VALID_TS_IGNORE_FILEPATH = os.path.join(
+    LINTER_TESTS_DIR, 'valid_ts_ignore.ts')
+INVALID_TS_EXPECT_ERROR_FILEPATH = os.path.join(
+    LINTER_TESTS_DIR, 'invalid_ts_expect_error.ts')
+VALID_TS_EXPECT_ERROR_FILEPATH = os.path.join(
+    LINTER_TESTS_DIR, 'valid_ts_expect_error.spec.ts')
 
 
 class JsTsLintTests(test_utils.LinterTestBase):
@@ -543,6 +551,144 @@ def mock_compile_all_ts_files():
             'backend api services.'], self.linter_stdout)
         self.assert_failed_messages_count(self.linter_stdout, 1)
 
+    def test_ts_ignore_found_error(self):
+        def mock_compile_all_ts_files():
+            cmd = (
+                './node_modules/typescript/bin/tsc -outDir %s -allowJS %s '
+                '-lib %s -noImplicitUseStrict %s -skipLibCheck '
+                '%s -target %s -typeRoots %s %s typings/*') % (
+                    js_ts_linter.COMPILED_TYPESCRIPT_TMP_PATH +
+                    'scripts/linters/test_files/', 'true', 'es2017,dom', 'true',
+                    'true', 'es5', './node_modules/@types',
+                    INVALID_TS_IGNORE_FILEPATH)
+            subprocess.call(cmd, shell=True, stdout=subprocess.PIPE)
+
+        ts_ignore_exceptions_swap = self.swap(
+            js_ts_linter, 'TS_IGNORE_EXCEPTIONS', {})
+        compile_all_ts_files_swap = self.swap(
+            js_ts_linter, 'compile_all_ts_files', mock_compile_all_ts_files)
+
+        with self.print_swap, compile_all_ts_files_swap:
+            with ts_ignore_exceptions_swap:
+                js_ts_linter.JsTsLintChecksManager(
+                    [], [INVALID_TS_IGNORE_FILEPATH], FILE_CACHE,
+                    True).perform_all_lint_checks()
+        shutil.rmtree(
+            js_ts_linter.COMPILED_TYPESCRIPT_TMP_PATH, ignore_errors=True)
+        self.assert_same_list_elements(
+            ['@ts-ignore found at line 25.'],
+            self.linter_stdout)
+        self.assert_same_list_elements(
+            ['@ts-ignore found at line 31.'],
+            self.linter_stdout)
+        self.assert_same_list_elements([
+            'Please add a comment above the @ts-ignore '
+            'explaining the @ts-ignore at line 25. The format '
+            'of comment should be -> This throws "...". '
+            'This needs to be suppressed because ...'], self.linter_stdout)
+        self.assert_same_list_elements([
+            'Please add a comment above the @ts-ignore '
+            'explaining the @ts-ignore at line 31. The format '
+            'of comment should be -> This throws "...". '
+            'This needs to be suppressed because ...'], self.linter_stdout)
+        self.assert_failed_messages_count(self.linter_stdout, 1)
+
+    def test_ts_ignore_found_success(self):
+        def mock_compile_all_ts_files():
+            cmd = (
+                './node_modules/typescript/bin/tsc -outDir %s -allowJS %s '
+                '-lib %s -noImplicitUseStrict %s -skipLibCheck '
+                '%s -target %s -typeRoots %s %s typings/*') % (
+                    js_ts_linter.COMPILED_TYPESCRIPT_TMP_PATH +
+                    'scripts/linters/test_files/', 'true', 'es2017,dom', 'true',
+                    'true', 'es5', './node_modules/@types',
+                    VALID_TS_IGNORE_FILEPATH)
+            subprocess.call(cmd, shell=True, stdout=subprocess.PIPE)
+
+        ts_ignore_exceptions_swap = self.swap(
+            js_ts_linter, 'TS_IGNORE_EXCEPTIONS', {
+                VALID_TS_IGNORE_FILEPATH: ['let b: number = c;']
+            })
+        compile_all_ts_files_swap = self.swap(
+            js_ts_linter, 'compile_all_ts_files', mock_compile_all_ts_files)
+
+        with self.print_swap, compile_all_ts_files_swap:
+            with ts_ignore_exceptions_swap:
+                js_ts_linter.JsTsLintChecksManager(
+                    [], [VALID_TS_IGNORE_FILEPATH], FILE_CACHE,
+                    True).perform_all_lint_checks()
+        shutil.rmtree(
+            js_ts_linter.COMPILED_TYPESCRIPT_TMP_PATH, ignore_errors=True)
+        self.assert_same_list_elements([
+            'SUCCESS  TS ignore check passed'], self.linter_stdout)
+        self.assert_failed_messages_count(self.linter_stdout, 0)
+
+    def test_ts_expect_error_error(self):
+        def mock_compile_all_ts_files():
+            cmd = (
+                './node_modules/typescript/bin/tsc -outDir %s -allowJS %s '
+                '-lib %s -noImplicitUseStrict %s -skipLibCheck '
+                '%s -target %s -typeRoots %s %s typings/*') % (
+                    js_ts_linter.COMPILED_TYPESCRIPT_TMP_PATH +
+                    'scripts/linters/test_files/', 'true', 'es2017,dom', 'true',
+                    'true', 'es5', './node_modules/@types',
+                    INVALID_TS_EXPECT_ERROR_FILEPATH)
+            subprocess.call(cmd, shell=True, stdout=subprocess.PIPE)
+
+        compile_all_ts_files_swap = self.swap(
+            js_ts_linter, 'compile_all_ts_files', mock_compile_all_ts_files)
+
+        with self.print_swap, compile_all_ts_files_swap:
+            js_ts_linter.JsTsLintChecksManager(
+                [], [INVALID_TS_EXPECT_ERROR_FILEPATH], FILE_CACHE,
+                True).perform_all_lint_checks()
+        shutil.rmtree(
+            js_ts_linter.COMPILED_TYPESCRIPT_TMP_PATH, ignore_errors=True)
+        self.assert_same_list_elements(
+            ['@ts-expect-error found at line 24.'],
+            self.linter_stdout)
+        self.assert_same_list_elements(
+            ['@ts-expect-error found at line 30.'],
+            self.linter_stdout)
+        self.assert_same_list_elements([
+            'Please add a comment above the '
+            '@ts-expect-error explaining the '
+            '@ts-expect-error at line 24. The format '
+            'of comment should be -> This throws "...". '
+            'This needs to be suppressed because ...'], self.linter_stdout)
+        self.assert_same_list_elements([
+            'Please add a comment above the '
+            '@ts-expect-error explaining the '
+            '@ts-expect-error at line 30. The format '
+            'of comment should be -> This throws "...". '
+            'This needs to be suppressed because ...'], self.linter_stdout)
+        self.assert_failed_messages_count(self.linter_stdout, 1)
+
+    def test_ts_expect_error_success(self):
+        def mock_compile_all_ts_files():
+            cmd = (
+                './node_modules/typescript/bin/tsc -outDir %s -allowJS %s '
+                '-lib %s -noImplicitUseStrict %s -skipLibCheck '
+                '%s -target %s -typeRoots %s %s typings/*') % (
+                    js_ts_linter.COMPILED_TYPESCRIPT_TMP_PATH +
+                    'scripts/linters/test_files/', 'true', 'es2017,dom', 'true',
+                    'true', 'es5', './node_modules/@types',
+                    VALID_TS_EXPECT_ERROR_FILEPATH)
+            subprocess.call(cmd, shell=True, stdout=subprocess.PIPE)
+
+        compile_all_ts_files_swap = self.swap(
+            js_ts_linter, 'compile_all_ts_files', mock_compile_all_ts_files)
+
+        with self.print_swap, compile_all_ts_files_swap:
+            js_ts_linter.JsTsLintChecksManager(
+                [], [VALID_TS_EXPECT_ERROR_FILEPATH], FILE_CACHE,
+                True).perform_all_lint_checks()
+        shutil.rmtree(
+            js_ts_linter.COMPILED_TYPESCRIPT_TMP_PATH, ignore_errors=True)
+        self.assert_same_list_elements([
+            'SUCCESS  TS expect error check passed'], self.linter_stdout)
+        self.assert_failed_messages_count(self.linter_stdout, 0)
+
     def test_missing_punctuation_at_end_of_comment(self):
         def mock_compile_all_ts_files():
             cmd = (
diff --git a/scripts/linters/test_files/invalid_ts_expect_error.ts b/scripts/linters/test_files/invalid_ts_expect_error.ts
new file mode 100644
--- /dev/null
+++ b/scripts/linters/test_files/invalid_ts_expect_error.ts
@@ -0,0 +1,31 @@
+// Copyright 2020 The Oppia Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/**
+ * @fileoverview Valid syntax .ts file, used by scripts/linters/
+ * js_ts_linter_test.py. Ts expect error is used but it is not a spec file.
+ */
+
+let y: string;
+
+// 1. No comment.
+
+// @ts-expect-error
+let b: number = y;
+
+// 2. Invalid Comment Format.
+
+// We need expect error to test lint checks.
+// @ts-expect-error
+b = y;
diff --git a/scripts/linters/test_files/invalid_ts_ignore.ts b/scripts/linters/test_files/invalid_ts_ignore.ts
new file mode 100644
--- /dev/null
+++ b/scripts/linters/test_files/invalid_ts_ignore.ts
@@ -0,0 +1,32 @@
+// Copyright 2020 The Oppia Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/**
+ * @fileoverview Invalid syntax .ts file, used by scripts/linters/
+ * js_ts_linter_test.py. Ts ignore is used but no comment explains
+ * the ts-ignore.
+ */
+
+let a: string;
+
+// 1. No comment to explain the @ts-ignore.
+
+// @ts-ignore
+let b: number = a;
+
+// 2. Comment with invalid format.
+
+// We need ts-ignore here to test lint checks.
+// @ts-ignore
+b = a;
diff --git a/scripts/linters/test_files/valid_ts_expect_error.spec.ts b/scripts/linters/test_files/valid_ts_expect_error.spec.ts
new file mode 100644
--- /dev/null
+++ b/scripts/linters/test_files/valid_ts_expect_error.spec.ts
@@ -0,0 +1,25 @@
+// Copyright 2020 The Oppia Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/**
+ * @fileoverview Valid syntax .ts file, used by scripts/linters/
+ * js_ts_linter_test.py. Ts expect error is used and it is a spec file.
+ */
+
+let x: string;
+
+// This throws "Type 'string' is not assignable to type 'number'."
+// We need this to test the lint checks.
+// @ts-expect-error
+let b: number = x;
diff --git a/scripts/linters/test_files/valid_ts_ignore.ts b/scripts/linters/test_files/valid_ts_ignore.ts
new file mode 100644
--- /dev/null
+++ b/scripts/linters/test_files/valid_ts_ignore.ts
@@ -0,0 +1,26 @@
+// Copyright 2020 The Oppia Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS-IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/**
+ * @fileoverview Valid syntax .ts file, used by scripts/linters/
+ * js_ts_linter_test.py. Ts ignore is used and a comment explains
+ * the ts-ignore.
+ */
+
+let c: string;
+
+// This throws "Type string can't be assigned to number". We need to
+// suppress this to check the lint tests.
+// @ts-ignore
+let b: number = c;