Exemple #1
0
    def test_camelcase_to_snakecase(self):
        """Test camelcase_to_hyphenated method."""
        test_cases = [
            ('AbcDef', 'abc_def'),
            ('Abc', 'abc'),
            ('abc_def', 'abc_def'),
            ('Abc012Def345', 'abc012_def345'),
            ('abcDef', 'abc_def'),
            ('abc-def', 'abc-def'),
        ]

        for test_case in test_cases:
            self.assertEqual(utils.camelcase_to_snakecase(test_case[0]),
                             test_case[1])
Exemple #2
0
    def test_default_interactions_are_valid(self):
        """Test that the default interactions are valid."""

        all_interaction_ids = (
            interaction_registry.Registry.get_all_interaction_ids())

        for interaction_id in all_interaction_ids:
            # Check that the interaction id is valid.
            self.assertTrue(self._is_camel_cased(interaction_id))

            # Check that the interaction directory exists.
            interaction_dir = os.path.join(
                feconf.INTERACTIONS_DIR, interaction_id)
            self.assertTrue(os.path.isdir(interaction_dir))

            # The interaction directory should contain the following files:
            #  Required:
            #    * A python file called {InteractionName}.py.
            #    * An __init__.py file used to import the Python file.
            #    * A TypeScript file called {InteractionName}.ts.
            #    * A directory name 'directives' containing TS and HTML files
            #      for directives
            #    * A directory named 'static' containing at least a .png file.
            #  Optional:
            #    * A JS file called protractor.js.
            interaction_dir_contents = (
                self._listdir_omit_ignored(interaction_dir))

            interaction_dir_optional_dirs_and_files_count = 0

            try:
                self.assertTrue(os.path.isfile(os.path.join(
                    interaction_dir, 'protractor.js')))
                interaction_dir_optional_dirs_and_files_count += 1
            except Exception:
                pass

            try:
                self.assertTrue(os.path.isfile(os.path.join(
                    interaction_dir,
                    '%sPredictionService.ts' % interaction_id)))
                interaction_dir_optional_dirs_and_files_count += 1
            except Exception:
                pass

            try:
                self.assertTrue(os.path.isfile(os.path.join(
                    interaction_dir,
                    '%sPredictionServiceSpec.ts' % interaction_id)))
                interaction_dir_optional_dirs_and_files_count += 1
            except Exception:
                pass

            self.assertEqual(
                interaction_dir_optional_dirs_and_files_count + 5,
                len(interaction_dir_contents)
            )

            py_file = os.path.join(interaction_dir, '%s.py' % interaction_id)
            ts_file = os.path.join(
                interaction_dir, '%s.ts' % interaction_id)

            self.assertTrue(os.path.isfile(py_file))
            self.assertTrue(os.path.isfile(ts_file))

            # Check that __init__.py file exists.
            init_file = os.path.join(interaction_dir, '__init__.py')
            self.assertTrue(os.path.isfile(init_file))

            # Check that the directives subdirectory exists.
            directives_dir = os.path.join(
                interaction_dir, 'directives')
            self.assertTrue(os.path.isdir(directives_dir))

            # The directives directory should contain the following files:
            #  Required:
            #    * A TS file called
            #    OppiaInteractive{InteractionName}Directive.ts.
            #    * A TS file called OppiaResponse{InteractionName}Directive.ts.
            #    * A TS file called
            #    OppiaShortResponse{InteractionName}Directive.ts.
            #    * A TS file called {InteractionName}RulesService.ts.
            #    * A TS file called {InteractionName}ValidationService.ts.
            #    * A HTML file called
            #      {InteractionName}_interaction_directive.html.
            #    * A HTML file called
            #      {InteractionName}_response_directive.html.
            #    * A HTML file called
            #      {InteractionName}_short_response_directive.html.
            #  Optional:
            #    * A TS file called {InteractionName}ValidationServiceSpecs.ts.
            #    * A TS file called {InteractionName}RulesServiceSpecs.ts.

            snakecase_interaction_id = (
                utils.camelcase_to_snakecase(interaction_id))

            interaction_directive_ts_file = os.path.join(
                directives_dir, 'OppiaInteractive%sDirective.ts' % (
                    interaction_id))
            response_directive_ts_file = os.path.join(
                directives_dir, 'OppiaResponse%sDirective.ts' % interaction_id)
            short_response_directive_ts_file = os.path.join(
                directives_dir, 'OppiaShortResponse%sDirective.ts' % (
                    interaction_id))
            rules_service_ts_file = os.path.join(
                directives_dir, '%sRulesService.ts' % interaction_id)
            validation_service_ts_file = os.path.join(
                directives_dir, '%sValidationService.ts' % interaction_id)
            interaction_directive_html = os.path.join(
                directives_dir,
                '%s_interaction_directive.html' % snakecase_interaction_id)
            response_directive_html = os.path.join(
                directives_dir,
                '%s_response_directive.html' % snakecase_interaction_id)
            short_response_directive_html = os.path.join(
                directives_dir,
                '%s_short_response_directive.html' % snakecase_interaction_id)

            self.assertTrue(os.path.isfile(interaction_directive_ts_file))
            self.assertTrue(os.path.isfile(response_directive_ts_file))
            self.assertTrue(os.path.isfile(short_response_directive_ts_file))
            self.assertTrue(os.path.isfile(rules_service_ts_file))
            self.assertTrue(os.path.isfile(validation_service_ts_file))
            self.assertTrue(os.path.isfile(interaction_directive_html))
            self.assertTrue(os.path.isfile(response_directive_html))
            self.assertTrue(os.path.isfile(short_response_directive_html))

            # Check that the PNG thumbnail image has the correct dimensions.
            static_dir = os.path.join(interaction_dir, 'static')
            self.assertTrue(os.path.isdir(static_dir))
            png_file = os.path.join(
                interaction_dir, 'static', '%s.png' % interaction_id)

            self.assertTrue(os.path.isfile(png_file))
            with python_utils.open_file(png_file, 'rb', encoding=None) as f:
                img_data = f.read()
                width, height = struct.unpack('>LL', img_data[16:24])
                self.assertEqual(int(width), INTERACTION_THUMBNAIL_WIDTH_PX)
                self.assertEqual(int(height), INTERACTION_THUMBNAIL_HEIGHT_PX)

            interaction_directive_ts_file_content = utils.get_file_contents(
                interaction_directive_ts_file)
            response_directive_ts_file_content = utils.get_file_contents(
                response_directive_ts_file)
            short_response_directive_ts_file_content = utils.get_file_contents(
                short_response_directive_ts_file)
            ts_file_content = utils.get_file_contents(ts_file)
            rules_service_ts_file_content = utils.get_file_contents(
                rules_service_ts_file)
            validation_service_ts_file_content = utils.get_file_contents(
                validation_service_ts_file)

            self.assertIn(
                'oppiaInteractive%s' % interaction_id,
                interaction_directive_ts_file_content)
            self.assertIn(
                'oppiaResponse%s' % interaction_id,
                response_directive_ts_file_content)
            self.assertIn(
                'oppiaShortResponse%s' % interaction_id,
                short_response_directive_ts_file_content)
            self.assertIn(
                '%sRulesService' % (
                    interaction_id[0] + interaction_id[1:]),
                rules_service_ts_file_content)
            self.assertIn(
                '%sValidationService' % interaction_id,
                validation_service_ts_file_content)

            # Check that the html template includes js script for the
            # interaction.
            self.assertIn(
                'OppiaInteractive%sDirective.ts' % interaction_id,
                ts_file_content)
            self.assertIn(
                'OppiaResponse%sDirective.ts' % interaction_id,
                ts_file_content)
            self.assertIn(
                'OppiaShortResponse%sDirective.ts' % interaction_id,
                ts_file_content)
            self.assertIn(
                '%sRulesService.ts' % interaction_id,
                ts_file_content)
            self.assertIn(
                '%sValidationService.ts' % interaction_id,
                ts_file_content)

            self.assertNotIn('<script>', interaction_directive_ts_file_content)
            self.assertNotIn('</script>', interaction_directive_ts_file_content)
            self.assertNotIn('<script>', response_directive_ts_file_content)
            self.assertNotIn('</script>', response_directive_ts_file_content)
            self.assertNotIn(
                '<script>', short_response_directive_ts_file_content)
            self.assertNotIn(
                '</script>', short_response_directive_ts_file_content)
            self.assertNotIn('<script>', rules_service_ts_file_content)
            self.assertNotIn('</script>', rules_service_ts_file_content)
            self.assertNotIn('<script>', validation_service_ts_file_content)
            self.assertNotIn('</script>', validation_service_ts_file_content)

            interaction = interaction_registry.Registry.get_interaction_by_id(
                interaction_id)

            # Check that the specified interaction id is the same as the class
            # name.
            self.assertTrue(interaction_id, msg=interaction.__class__.__name__)

            # Check that the configuration file contains the correct
            # top-level keys, and that these keys have the correct types.
            for item, item_type in _INTERACTION_CONFIG_SCHEMA:
                self.assertTrue(isinstance(
                    getattr(interaction, item), item_type))
                if item_type == python_utils.BASESTRING:
                    self.assertTrue(getattr(interaction, item))

            self.assertIn(interaction.display_mode, base.ALLOWED_DISPLAY_MODES)

            if interaction.is_linear or interaction.is_terminal:
                self.assertIsNone(interaction.answer_type)
            else:
                # Check that the answer_type corresponds to a valid object
                # class.
                obj_services.Registry.get_object_class_by_type(
                    interaction.answer_type)

            self._validate_customization_arg_specs(
                interaction._customization_arg_specs)  # pylint: disable=protected-access

            self._validate_dependencies(interaction.dependency_ids)

            answer_visualization_specs = (
                interaction.answer_visualization_specs)
            self._validate_answer_visualization_specs(
                answer_visualization_specs)

            answer_visualizations = interaction.answer_visualizations
            for ind, visualization in enumerate(answer_visualizations):
                self.assertEqual(
                    visualization.id, answer_visualization_specs[ind]['id'])
                self.assertEqual(
                    visualization.calculation_id,
                    answer_visualization_specs[ind]['calculation_id'])
                self.assertEqual(
                    visualization.options,
                    answer_visualization_specs[ind]['options'])

                # Check that the derived visualization is valid.
                visualization.validate()

            # Check that supplemental interactions have instructions, and
            # inline ones do not.
            if interaction.display_mode == base.DISPLAY_MODE_INLINE:
                self.assertIsNone(interaction.instructions)
                self.assertIsNone(interaction.narrow_instructions)
            else:
                self.assertTrue(
                    isinstance(
                        interaction.instructions, python_utils.BASESTRING))
                self.assertIsNotNone(interaction.instructions)
                self.assertIsNotNone(interaction.narrow_instructions)

            # Check that terminal interactions are not linear.
            if interaction.is_terminal:
                self.assertFalse(interaction.is_linear)

            # Check that only linear interactions have a
            # default_outcome_heading property.
            if interaction.is_linear:
                self.assertTrue(
                    isinstance(
                        interaction.default_outcome_heading,
                        python_utils.BASESTRING)
                    and interaction.default_outcome_heading)
            else:
                self.assertIsNone(interaction.default_outcome_heading)

            # Check that interactions that can have solution cannot be linear.
            if interaction.can_have_solution:
                self.assertFalse(interaction.is_linear)

            default_object_values = obj_services.get_default_object_values()

            # Check that the rules for this interaction have object editor
            # templates and default values.
            for rule_name in list(interaction.rules_dict.keys()):
                param_list = interaction.get_rule_param_list(rule_name)

                for (_, param_obj_cls) in param_list:
                    # TODO(sll): Get rid of these special cases.
                    if param_obj_cls.__name__ in [
                            'NonnegativeInt', 'ListOfCodeEvaluation',
                            'ListOfCoordTwoDim', 'ListOfGraph',
                            'SetOfNormalizedString']:
                        continue

                    # Check that the rule has a default value.
                    self.assertIn(
                        param_obj_cls.__name__, default_object_values)