def _make_app_with_shadow_forms(self):
     factory = AppFactory('upstream', "Upstream App", include_xmlns=True)
     module1, form1 = factory.new_advanced_module('M1', 'casetype')
     factory.new_form(module1)
     module2, form2 = factory.new_advanced_module('M2', 'casetype')
     factory.new_form(module2)
     self._make_shadow_form(factory, 'M3', form1)
     self._make_shadow_form(factory, 'M4', form2)
     factory.app.save()
     self.addCleanup(factory.app.delete)
     # the test form names are unique
     form_names = [f.name['en'] for f in factory.app.get_forms()]
     self.assertEqual(len(form_names), len(set(form_names)))
     return factory.app
    def test_scheduler_module(self):
        factory = AppFactory()
        m1, m1f1 = factory.new_basic_module('open_case', 'house')
        factory.form_opens_case(m1f1)
        m2, m2f1 = factory.new_advanced_module('scheduler_module', 'house')
        m2f2 = factory.new_form(m2)
        factory.form_requires_case(m2f1, case_type='house', update={
            'foo': '/data/question1',
            'bar': '/data/question2',
        })
        factory.form_requires_case(m2f2, case_type='house', update={
            'bleep': '/data/question1',
            'bloop': '/data/question2',
        })

        self._add_scheduler_to_module(m2)
        self._add_scheduler_to_form(m2f1, m2, 'form1')
        self._add_scheduler_to_form(m2f2, m2, 'form2')

        self.assertCaseProperties(factory.app, 'house', [
            'foo',
            'bar',
            'bleep',
            'bloop',
            # Scheduler properties:
            'last_visit_date_form1',
            'last_visit_number_form1',
            'last_visit_date_form2',
            'last_visit_number_form2',
            'current_schedule_phase',
        ])
    def test_scheduler_module(self):
        factory = AppFactory()
        m1, m1f1 = factory.new_basic_module('open_case', 'house')
        factory.form_opens_case(m1f1)
        m2, m2f1 = factory.new_advanced_module('scheduler_module', 'house')
        m2f2 = factory.new_form(m2)
        factory.form_requires_case(m2f1, case_type='house', update={
            'foo': '/data/question1',
            'bar': '/data/question2',
        })
        factory.form_requires_case(m2f2, case_type='house', update={
            'bleep': '/data/question1',
            'bloop': '/data/question2',
        })

        self._add_scheduler_to_module(m2)
        self._add_scheduler_to_form(m2f1, m2, 'form1')
        self._add_scheduler_to_form(m2f2, m2, 'form2')

        self.assertCaseProperties(factory.app, 'house', [
            'foo',
            'bar',
            'bleep',
            'bloop',
            # Scheduler properties:
            'last_visit_date_form1',
            'last_visit_number_form1',
            'last_visit_date_form2',
            'last_visit_number_form2',
            'current_schedule_phase',
        ])
    def test_form_links_submodule(self):
        """
        Test that when linking between two forms in a submodule we match up the session variables between the source
        and target form correctly
        :return:
        """
        factory = AppFactory(build_version='2.9.0/latest')
        m0, m0f0 = factory.new_basic_module('child visit', 'child')
        factory.form_requires_case(m0f0)
        factory.form_opens_case(m0f0, 'visit', is_subcase=True)

        m1, m1f0 = factory.new_advanced_module('visit histroy', 'visit', parent_module=m0)
        factory.form_requires_case(m1f0, 'child')
        factory.form_requires_case(m1f0, 'visit', parent_case_type='child')

        m1f1 = factory.new_form(m1)
        factory.form_requires_case(m1f1, 'child')
        factory.form_requires_case(m1f1, 'visit', parent_case_type='child')

        m1f0.post_form_workflow = WORKFLOW_FORM
        m1f0.form_links = [
            FormLink(xpath="true()", form_id=m1f1.unique_id),
        ]

        self.assertXmlPartialEqual(self.get_xml('form_link_submodule'), factory.app.create_suite(), "./entry")
Exemple #5
0
    def test_case_list_form_advanced_module_different_case_config(self, *args):
        case_tile_error = {
            'type': "all forms in case list module must load the same cases",
            'module': {
                'id': 1,
                'name': {
                    'en': 'update module'
                }
            },
            'form': {
                'id': 1,
                'name': {
                    'en': 'update form 1'
                }
            },
        }

        factory = AppFactory(build_version='2.11.0')
        m0, m0f0 = factory.new_basic_module('register', 'person')
        factory.form_opens_case(m0f0)

        m1, m1f0 = factory.new_advanced_module('update',
                                               'person',
                                               case_list_form=m0f0)
        factory.form_requires_case(m1f0, case_type='house')
        factory.form_requires_case(m1f0, parent_case_type='house')

        m1f1 = factory.new_form(m1)
        factory.form_requires_case(
            m1f1)  # only loads a person case and not a house case

        errors = factory.app.validate_app()
        self._clean_unique_id(errors)
        self.assertIn(case_tile_error, errors)
Exemple #6
0
    def test_form_links_submodule(self):
        # Test that when linking between two forms in a submodule we match up the
        # session variables between the source and target form correctly
        factory = AppFactory(build_version='2.9.0/latest')
        m0, m0f0 = factory.new_basic_module('child visit', 'child')
        factory.form_requires_case(m0f0)
        factory.form_opens_case(m0f0, 'visit', is_subcase=True)

        m1, m1f0 = factory.new_advanced_module('visit histroy',
                                               'visit',
                                               parent_module=m0)
        factory.form_requires_case(m1f0, 'child')
        factory.form_requires_case(m1f0, 'visit', parent_case_type='child')

        m1f1 = factory.new_form(m1)
        factory.form_requires_case(m1f1, 'child')
        factory.form_requires_case(m1f1, 'visit', parent_case_type='child')

        m1f0.post_form_workflow = WORKFLOW_FORM
        m1f0.form_links = [
            FormLink(xpath="true()", form_id=m1f1.unique_id),
        ]

        self.assertXmlPartialEqual(self.get_xml('form_link_submodule'),
                                   factory.app.create_suite(), "./entry")
Exemple #7
0
    def setUp(self):
        factory = AppFactory(DOMAIN, "App with DR and child modules", build_version='2.53.0')
        m0, f0 = factory.new_basic_module("case list", "case")
        factory.form_requires_case(f0)

        m1, f1 = factory.new_basic_module("child case list", "case", parent_module=m0)
        m1.parent_select = ParentSelect(active=True, relationship="other", module_id=m0.get_or_create_unique_id())
        f2 = factory.new_form(m1)

        factory.form_requires_case(f1)
        factory.form_requires_case(f2)

        m1.search_config = CaseSearch(
            properties=[CaseSearchProperty(name='name', label={'en': 'Name'})],
            data_registry="myregistry",
            data_registry_workflow=REGISTRY_WORKFLOW_LOAD_CASE,
        )

        # link from f1 to f2 (both in the child module)
        f1.post_form_workflow = WORKFLOW_FORM
        f1.form_links = [FormLink(form_id=f2.get_unique_id())]

        factory.app._id = "123"
        # wrap to have assign_references called
        self.app = Application.wrap(factory.app.to_json())
    def setUpClass(cls):
        super(UCRAggregationTest, cls).setUpClass()
        # cleanup any previous data
        cls._cleanup_data()

        # setup app
        factory = AppFactory(domain=cls.domain)
        # parent case module, incl opening child cases of main type
        m_parent, f_parent = factory.new_basic_module('Parent Module', cls.parent_case_type)
        factory.form_opens_case(f_parent, case_type=cls.parent_case_type)
        factory.form_opens_case(f_parent, case_type=cls.case_type, is_subcase=True)

        # main module
        m0, f0 = factory.new_basic_module('A Module', cls.case_type)
        f1 = factory.new_form(m0)
        f1.source = cls._get_xform()
        factory.form_requires_case(f1, case_type=cls.case_type, update={
            cp[0]: '/data/{}'.format(cp[0]) for cp in cls.case_properties
        })
        cls.followup_form = f1

        cls.app = factory.app
        cls.app.save()

        # create form and case ucrs
        cls.form_data_source = get_form_data_source(cls.app, cls.followup_form)
        cls.case_data_source = get_case_data_source(cls.app, cls.case_type)
        cls.parent_case_data_source = get_case_data_source(cls.app, cls.parent_case_type)

        # create some data - first just create the case
        cls.parent_case_id = cls._create_parent_case(cls.parent_name)
        cls.case_id = cls._create_case(cls.parent_case_id)
        for fu_date in cls.fu_visit_dates:
            cls._submit_followup_form(cls.case_id, received_on=fu_date)

        # the closed case causes there to be some data with an end_column
        cls.closed_case_id = cls._create_closed_case()

        # populate the UCRs with the data we just created
        cls.form_adapter = get_indicator_adapter(cls.form_data_source)
        cls.case_adapter = get_indicator_adapter(cls.case_data_source)
        cls.parent_case_adapter = get_indicator_adapter(cls.parent_case_data_source)

        cls.form_adapter.rebuild_table()
        cls.case_adapter.rebuild_table()
        cls.parent_case_adapter.rebuild_table()

        _iteratively_build_table(cls.form_data_source)
        _iteratively_build_table(cls.case_data_source)
        _iteratively_build_table(cls.parent_case_data_source)

        # setup AggregateTableDefinition
        cls.monthly_aggregate_table_definition = cls._get_monthly_aggregate_table_definition()
        cls.weekly_aggregate_table_definition = cls._get_weekly_aggregate_table_definition()
        cls.basic_aggregate_table_definition = cls._get_basic_aggregate_table_definition()

        # and adapter
        cls.monthly_adapter = get_indicator_adapter(cls.monthly_aggregate_table_definition)
Exemple #9
0
    def setUpClass(cls):
        super(UCRAggregationTest, cls).setUpClass()
        # cleanup any previous data
        cls._cleanup_data()

        # setup app
        factory = AppFactory(domain=cls.domain)
        # parent case module, incl opening child cases of main type
        m_parent, f_parent = factory.new_basic_module('Parent Module', cls.parent_case_type)
        factory.form_opens_case(f_parent, case_type=cls.parent_case_type)
        factory.form_opens_case(f_parent, case_type=cls.case_type, is_subcase=True)

        # main module
        m0, f0 = factory.new_basic_module('A Module', cls.case_type)
        f1 = factory.new_form(m0)
        f1.source = cls._get_xform()
        factory.form_requires_case(f1, case_type=cls.case_type, update={
            cp[0]: '/data/{}'.format(cp[0]) for cp in cls.case_properties
        })
        cls.followup_form = f1

        cls.app = factory.app
        cls.app.save()

        # create form and case ucrs
        cls.form_data_source = get_form_data_source(cls.app, cls.followup_form)
        cls.case_data_source = get_case_data_source(cls.app, cls.case_type)
        cls.parent_case_data_source = get_case_data_source(cls.app, cls.parent_case_type)

        # create some data - first just create the case
        cls.parent_case_id = cls._create_parent_case(cls.parent_name)
        cls.case_id = cls._create_case(cls.parent_case_id)
        for fu_date in cls.fu_visit_dates:
            cls._submit_followup_form(cls.case_id, received_on=fu_date)

        # the closed case causes there to be some data with an end_column
        cls.closed_case_id = cls._create_closed_case()

        # populate the UCRs with the data we just created
        cls.form_adapter = get_indicator_adapter(cls.form_data_source)
        cls.case_adapter = get_indicator_adapter(cls.case_data_source)
        cls.parent_case_adapter = get_indicator_adapter(cls.parent_case_data_source)

        cls.form_adapter.rebuild_table()
        cls.case_adapter.rebuild_table()
        cls.parent_case_adapter.rebuild_table()

        _iteratively_build_table(cls.form_data_source)
        _iteratively_build_table(cls.case_data_source)
        _iteratively_build_table(cls.parent_case_data_source)

        # setup AggregateTableDefinition
        cls.monthly_aggregate_table_definition = cls._get_monthly_aggregate_table_definition()
        cls.weekly_aggregate_table_definition = cls._get_weekly_aggregate_table_definition()
        cls.basic_aggregate_table_definition = cls._get_basic_aggregate_table_definition()

        # and adapter
        cls.monthly_adapter = get_indicator_adapter(cls.monthly_aggregate_table_definition)
 def test_basic_apps(self):
     for class_ in (Module, AdvancedModule):
         factory = AppFactory()
         m1, m1f1 = factory.new_module(class_, 'open_case', 'house')
         factory.form_opens_case(m1f1)
         m1f2 = factory.new_form(m1)
         factory.form_requires_case(m1f2, case_type='house', update={
             'foo': '/data/question1',
             'bar': '/data/question2',
         })
         self.assertCaseProperties(factory.app, 'house', ['foo', 'bar'])
 def test_basic_apps(self):
     for class_ in (Module, AdvancedModule):
         factory = AppFactory()
         m1, m1f1 = factory.new_module(class_, 'open_case', 'house')
         factory.form_opens_case(m1f1)
         m1f2 = factory.new_form(m1)
         factory.form_requires_case(m1f2, case_type='house', update={
             'foo': '/data/question1',
             'bar': '/data/question2',
         })
         self.assertCaseProperties(factory.app, 'house', ['foo', 'bar'])
    def test_target_module_different_datums(self):
        """
        * Registration
          * Register patient form
            * open case (patient), update user_case
        * Visits (case type = patient, case list form = 'Register patient')
          * Visit form
            * update case, open child case (visit), load from user_case
          * Record notes
            * update case, open child case (visit)
          * Update patient
            * update case
        """

        factory = AppFactory(build_version='2.9')
        registration_module, registration_form = factory.new_basic_module('registration', 'patient')

        factory.form_opens_case(registration_form)
        factory.form_uses_usercase(registration_form, preload={'username': '******'})

        visit_module, visit_form = factory.new_basic_module(
            'visits',
            'patient',
            case_list_form=registration_form
        )

        factory.form_requires_case(visit_form)
        factory.form_opens_case(visit_form, 'visit', is_subcase=True)
        factory.form_uses_usercase(visit_form, preload={'username': '******'})

        notes_form = factory.new_form(visit_module)
        factory.form_requires_case(notes_form)
        factory.form_opens_case(notes_form, 'visit', is_subcase=True)

        update_patient_form = factory.new_form(visit_module)
        factory.form_requires_case(update_patient_form)

        self.assertXmlPartialEqual(
            self.get_xml('target_module_different_datums'),
            factory.app.create_suite(),
            './entry')
    def test_target_module_different_datums(self):
        """
        * Registration
          * Register patient form
            * open case (patient), update user_case
        * Visits (case type = patient, case list form = 'Register patient')
          * Visit form
            * update case, open child case (visit), load from user_case
          * Record notes
            * update case, open child case (visit)
          * Update patient
            * update case
        """

        factory = AppFactory(build_version='2.9')
        registration_module, registration_form = factory.new_basic_module(
            'registration', 'patient')

        factory.form_opens_case(registration_form)
        factory.form_uses_usercase(registration_form,
                                   preload={'username': '******'})

        visit_module, visit_form = factory.new_basic_module(
            'visits', 'patient', case_list_form=registration_form)

        factory.form_requires_case(visit_form)
        factory.form_opens_case(visit_form, 'visit', is_subcase=True)
        factory.form_uses_usercase(visit_form,
                                   preload={'username': '******'})

        notes_form = factory.new_form(visit_module)
        factory.form_requires_case(notes_form)
        factory.form_opens_case(notes_form, 'visit', is_subcase=True)

        update_patient_form = factory.new_form(visit_module)
        factory.form_requires_case(update_patient_form)

        self.assertXmlPartialEqual(
            self.get_xml('target_module_different_datums'),
            factory.app.create_suite(), './entry')
Exemple #14
0
    def test_form_links_other_child_module(self):
        # This test demonstrates current behavior that I believe to be flawed

        factory = AppFactory(build_version='2.9.0')
        m0, m0f0 = factory.new_basic_module('parent', 'mother')
        factory.form_opens_case(m0f0)
        m1, m1f0 = factory.new_basic_module('child', 'baby', parent_module=m0)
        factory.form_requires_case(m1f0)

        m0f1 = factory.new_form(m0)
        m0f1.post_form_workflow = WORKFLOW_FORM
        m0f1.form_links = [FormLink(xpath='true()', form_id=m1f0.unique_id)]

        m2, m2f0 = factory.new_basic_module('other_module', 'baby')
        m2f0.post_form_workflow = WORKFLOW_FORM
        m2f0.form_links = [FormLink(xpath='true()', form_id=m1f0.unique_id)]
        suite_xml = factory.app.create_suite()

        # m0f1 links to m1f0, a form in its child module. Here's the stack for that:
        expected = """
        <partial>
            <stack>
                <create if="true()">
                    <command value="'m0'"/>
                    <command value="'m1'"/>
                    <datum id="case_id_new_mother_0" value="uuid()"/>
                    <datum id="case_id" value="instance('commcaresession')/session/data/case_id"/>
                    <command value="'m1-f0'"/>
                </create>
            </stack>
        </partial>
        """
        self.assertXmlPartialEqual(expected, suite_xml, "./entry[2]/stack")

        # m2f0 links to m1f0, a form in a separate child module, here's the stack there
        expected = """
        <partial>
            <stack>
                <create if="true()">
                    <command value="'m0'"/>
                    <command value="'m1'"/>
                    <datum id="case_id_new_mother_0" value="uuid()"/>
                    <datum id="case_id" value="instance('commcaresession')/session/data/case_id"/>
                    <command value="'m1-f0'"/>
                </create>
            </stack>
        </partial>
        """
        self.assertXmlPartialEqual(expected, suite_xml, "./entry[4]/stack")
    def test_with_case_management_multiple_links(self):
        factory = AppFactory(build_version='2.9.0/latest')
        m0, m0f0 = factory.new_basic_module('m0', 'frog')
        factory.form_opens_case(m0f0)
        m1, m1f0 = factory.new_basic_module('m1', 'frog')
        factory.form_requires_case(m1f0)

        m1f1 = factory.new_form(m1)
        factory.form_opens_case(m1f1)

        m0f0.post_form_workflow = WORKFLOW_FORM
        m0f0.form_links = [
            FormLink(xpath="a = 1", form_id=m1f0.unique_id),
            FormLink(xpath="a = 2", form_id=m1f1.unique_id)
        ]

        self.assertXmlPartialEqual(self.get_xml('form_link_multiple'), factory.app.create_suite(), "./entry[1]")
Exemple #16
0
    def test_with_case_management_multiple_links(self):
        factory = AppFactory(build_version='2.9.0/latest')
        m0, m0f0 = factory.new_basic_module('m0', 'frog')
        factory.form_opens_case(m0f0)
        m1, m1f0 = factory.new_basic_module('m1', 'frog')
        factory.form_requires_case(m1f0)

        m1f1 = factory.new_form(m1)
        factory.form_opens_case(m1f1)

        m0f0.post_form_workflow = WORKFLOW_FORM
        m0f0.form_links = [
            FormLink(xpath="a = 1", form_id=m1f0.unique_id),
            FormLink(xpath="a = 2", form_id=m1f1.unique_id)
        ]

        self.assertXmlPartialEqual(self.get_xml('form_link_multiple'), factory.app.create_suite(), "./entry[1]")
Exemple #17
0
    def _build_workflow_app(self, mode):
        factory = AppFactory(build_version='2.9.0')
        m0, m0f0 = factory.new_basic_module('m0', '')
        factory.new_form(m0)

        m1, m1f0 = factory.new_basic_module('m1', 'patient')
        m1f1 = factory.new_form(m1)
        factory.form_opens_case(m1f0)
        factory.form_requires_case(m1f1)

        m2, m2f0 = factory.new_basic_module('m2', 'patient')
        m2f1 = factory.new_form(m2)
        factory.form_requires_case(m2f0)
        factory.form_requires_case(m2f1)

        m3, m3f0 = factory.new_basic_module('m3', 'child')
        m3f1 = factory.new_form(m3)
        factory.form_requires_case(m3f0, parent_case_type='patient')
        factory.form_requires_case(m3f1)

        m4, m4f0 = factory.new_advanced_module('m4', 'patient')
        factory.form_requires_case(m4f0, case_type='patient')
        factory.form_requires_case(m4f0, case_type='patient')

        m4f1 = factory.new_form(m4)
        factory.form_requires_case(m4f1, case_type='patient')
        factory.form_requires_case(m4f1, case_type='patient')
        factory.form_requires_case(m4f1, case_type='patient')

        m4f2 = factory.new_form(m4)
        factory.form_requires_case(m4f2, case_type='patient')
        factory.form_requires_case(m4f2, case_type='patient')
        factory.advanced_form_autoloads(m4f2, AUTO_SELECT_RAW, 'case_id')

        m5, m5f0 = factory.new_basic_module('m5', 'patient', parent_module=m1)
        factory.form_requires_case(m5f0)

        m6 = factory.new_shadow_module('shadow_module', m1, with_form=False)
        factory.new_shadow_module('shadow_child',
                                  m5,
                                  with_form=False,
                                  parent_module=m6)

        for module in factory.app.get_modules():
            for form in module.get_forms():
                form.post_form_workflow = mode

        return factory.app
    def test_case_list_form_advanced_module_different_case_config(self):
        case_tile_error = {
            'type': "all forms in case list module must load the same cases",
            'module': {'id': 1, 'name': {u'en': u'update module'}},
            'form': {'id': 1, 'name': {u'en': u'update form 1'}},
        }

        factory = AppFactory(build_version='2.11')
        m0, m0f0 = factory.new_basic_module('register', 'person')
        factory.form_opens_case(m0f0)

        m1, m1f0 = factory.new_advanced_module('update', 'person', case_list_form=m0f0)
        factory.form_requires_case(m1f0, case_type='house')
        factory.form_requires_case(m1f0, parent_case_type='house')

        m1f1 = factory.new_form(m1)
        factory.form_requires_case(m1f1)  # only loads a person case and not a house case

        errors = factory.app.validate_app()
        self.assertIn(case_tile_error, errors)
    def _build_workflow_app(self, mode):
        factory = AppFactory(build_version="2.9.0/latest")
        m0, m0f0 = factory.new_basic_module("m0", "")
        factory.new_form(m0)

        m1, m1f0 = factory.new_basic_module("m1", "patient")
        m1f1 = factory.new_form(m1)
        factory.form_opens_case(m1f0)
        factory.form_updates_case(m1f1)

        m2, m2f0 = factory.new_basic_module("m2", "patient")
        m2f1 = factory.new_form(m2)
        factory.form_updates_case(m2f0)
        factory.form_updates_case(m2f1)

        m3, m3f0 = factory.new_basic_module("m3", "child")
        m3f1 = factory.new_form(m3)
        factory.form_updates_case(m3f0, parent_case_type="patient")
        factory.form_updates_case(m3f1)

        m4, m4f0 = factory.new_advanced_module("m4", "patient")
        factory.form_updates_case(m4f0, case_type="patient")
        factory.form_updates_case(m4f0, case_type="patient")

        m4f1 = factory.new_form(m4)
        factory.form_updates_case(m4f1, case_type="patient")
        factory.form_updates_case(m4f1, case_type="patient")
        factory.form_updates_case(m4f1, case_type="patient")

        m4f2 = factory.new_form(m4)
        factory.form_updates_case(m4f2, case_type="patient")
        factory.form_updates_case(m4f2, case_type="patient")
        factory.advanced_form_autoloads(m4f2, AUTO_SELECT_RAW, "case_id")
        for module in factory.app.get_modules():
            for form in module.get_forms():
                form.post_form_workflow = mode

        return factory.app
    def _build_workflow_app(self, mode):
        factory = AppFactory(build_version='2.9.0/latest')
        m0, m0f0 = factory.new_basic_module('m0', '')
        factory.new_form(m0)

        m1, m1f0 = factory.new_basic_module('m1', 'patient')
        m1f1 = factory.new_form(m1)
        factory.form_opens_case(m1f0)
        factory.form_requires_case(m1f1)

        m2, m2f0 = factory.new_basic_module('m2', 'patient')
        m2f1 = factory.new_form(m2)
        factory.form_requires_case(m2f0)
        factory.form_requires_case(m2f1)

        m3, m3f0 = factory.new_basic_module('m3', 'child')
        m3f1 = factory.new_form(m3)
        factory.form_requires_case(m3f0, parent_case_type='patient')
        factory.form_requires_case(m3f1)

        m4, m4f0 = factory.new_advanced_module('m4', 'patient')
        factory.form_requires_case(m4f0, case_type='patient')
        factory.form_requires_case(m4f0, case_type='patient')

        m4f1 = factory.new_form(m4)
        factory.form_requires_case(m4f1, case_type='patient')
        factory.form_requires_case(m4f1, case_type='patient')
        factory.form_requires_case(m4f1, case_type='patient')

        m4f2 = factory.new_form(m4)
        factory.form_requires_case(m4f2, case_type='patient')
        factory.form_requires_case(m4f2, case_type='patient')
        factory.advanced_form_autoloads(m4f2, AUTO_SELECT_RAW, 'case_id')
        for module in factory.app.get_modules():
            for form in module.get_forms():
                form.post_form_workflow = mode

        return factory.app
Exemple #21
0
class HandleShadowChildModulesTest(TestCase):
    """Test how shadow child modules are created and deleted
    """
    def setUp(self):
        super().setUp()
        self.domain = "test-domain"

        self.factory = AppFactory(domain=self.domain)

        # m0
        self.basic_module, self.form0 = self.factory.new_basic_module(
            "basic_module", "parrot")
        form1 = self.factory.new_form(self.basic_module)

        # m1
        self.child_module, form2 = self.factory.new_basic_module(
            "child_module", "parrot", parent_module=self.basic_module)
        self.child_module.put_in_root = True
        self.form3 = self.factory.new_form(self.child_module)

        # m2
        self.shadow_module = self.factory.new_shadow_module("shadow_module",
                                                            self.basic_module,
                                                            with_form=False)
        self.shadow_module.excluded_form_ids = [
            self.form0.unique_id, self.form3.unique_id
        ]
        self.factory.app.save()

        self.app = self.factory.app

    def tearDown(self):
        delete_all_apps()
        super().tearDown()

    def test_creates_new_modules(self):

        handle_shadow_child_modules(
            self.app,
            self.app.get_module_by_unique_id(self.shadow_module.unique_id))

        app = Application.get(self.app.get_id)

        # A new shadow module should be created whose module source is the child
        self.assertEqual(len(app.modules), 4)
        self.assertEqual(app.modules[3].module_type, "shadow")
        self.assertEqual(app.modules[3].source_module_id,
                         self.child_module.unique_id)

        # excluded form ids should move from the parent to the child module,
        # retaining the pertinent parent ids
        self.assertItemsEqual(app.modules[3].excluded_form_ids,
                              [self.form3.unique_id])
        self.assertItemsEqual(
            app.get_module_by_unique_id(
                self.shadow_module.unique_id).excluded_form_ids,
            [self.form0.unique_id],
        )

        # Calling the command again should not make new modules
        shadow_child_module_id = app.modules[3].unique_id
        handle_shadow_child_modules(
            app, app.get_module_by_unique_id(self.shadow_module.unique_id))
        app = Application.get(app.get_id)
        self.assertEqual(len(app.modules), 4)
        self.assertEqual(app.modules[3].unique_id, shadow_child_module_id)

    def test_deletes_module_child_removed(self):
        # Create new module
        handle_shadow_child_modules(
            self.app,
            self.app.get_module_by_unique_id(self.shadow_module.unique_id))

        # Change child module's parent
        app = Application.get(self.app.get_id)
        app.modules[1].root_module_id = None
        app.save()

        # The new shadow module should be deleted, since it is no longer needed
        handle_shadow_child_modules(
            app, app.get_module_by_unique_id(self.shadow_module.unique_id))
        self.assertEqual(len(app.modules), 3)

    def test_deletes_module_source_changed(self):
        # Create new module
        handle_shadow_child_modules(
            self.app,
            self.app.get_module_by_unique_id(self.shadow_module.unique_id))

        # Change parent shadow module's source
        app = Application.get(self.app.get_id)
        new_module = Module.new_module("name", "en")
        app.add_module(new_module)
        app.get_module_by_unique_id(self.shadow_module.unique_id
                                    ).source_module_id = new_module.unique_id
        app.save()
        self.assertEqual(len(app.modules), 5)

        # Child shadow module should be removed
        handle_shadow_child_modules(
            app, app.get_module_by_unique_id(self.shadow_module.unique_id))
        self.assertEqual(len(app.modules), 4)

    def test_shadow_source_is_child(self):
        # If the source is a child, the parent of the shadow should be the same as the source
        shadow_child = self.factory.new_shadow_module("shadow_child_module",
                                                      self.child_module,
                                                      with_form=False)
        self.factory.app.save()

        handle_shadow_child_modules(
            self.app, self.app.get_module_by_unique_id(shadow_child.unique_id))

        app = Application.get(self.app.get_id)
        shadow_child = app.get_module_by_unique_id(shadow_child.unique_id)
        self.assertEqual(shadow_child.root_module_id,
                         self.child_module.root_module_id)

        # change the source
        shadow_child.source_module_id = self.basic_module.unique_id
        app.save()

        handle_shadow_child_modules(
            app, app.get_module_by_unique_id(shadow_child.unique_id))

        app = Application.get(self.app.get_id)
        shadow_child = app.get_module_by_unique_id(shadow_child.unique_id)
        self.assertIsNone(shadow_child.root_module_id)

    def test_all_shadow_module_properties_set(self):
        ignored_properties = [
            "module_type",
            "source_module_id",
            "doc_type",
            "_root_module_id",
            "forms",
            "excluded_form_ids",
            "shadow_module_version",
        ]
        required_properties = {
            prop
            for prop in vars(ShadowModule) if isinstance(
                vars(ShadowModule)[prop], jsonobject.base_properties.
                JsonProperty) and prop not in ignored_properties
        }
        copied_properties = {
            prop
            for prop, _ in SHADOW_MODULE_PROPERTIES_TO_COPY
        }

        self.assertItemsEqual(
            required_properties - copied_properties,
            [],
            (f"Please handle the new property you just added '{required_properties - copied_properties}' "
             "to ShadowModule in app_manager.views.utils.handle_child_shadow_modules, or add it "
             "to the ignored_propertieslist in this test."),
        )

    def test_all_base_module_properties_set(self):
        ignored_properties = [
            "doc_type", "root_module_id", "name", "case_type", "unique_id"
        ]
        required_properties = {
            prop
            for prop in vars(ModuleBase) if isinstance(
                vars(ModuleBase)[prop], jsonobject.base_properties.JsonProperty
            ) and prop not in ignored_properties
        }
        required_properties |= {
            prop
            for prop in vars(NavMenuItemMediaMixin) if isinstance(
                vars(NavMenuItemMediaMixin)[prop], jsonobject.base_properties.
                JsonProperty) and prop not in ignored_properties
        }

        copied_properties = {
            prop
            for prop, _ in MODULE_BASE_PROPERTIES_TO_COPY
        }

        self.assertItemsEqual(
            required_properties - copied_properties,
            [],
            (f"Please handle the new property you just added '{required_properties - copied_properties}' "
             "to ShadowModule in app_manager.views.utils.handle_child_shadow_modules, or add it "
             "to the ignored_propertieslist in this test."),
        )
    def test(self, mock):
        add_build(version='2.7.0', build_number=20655)
        domain = 'form-versioning-test'

        # set up inital app
        factory = AppFactory(domain, 'Foo')
        m0, f0 = factory.new_basic_module("bar", "bar")
        f0.source = get_simple_form(xmlns='xmlns-0.0')
        f1 = factory.new_form(m0)
        f1.source = get_simple_form(xmlns='xmlns-1')
        app = factory.app
        app.build_spec = BuildSpec.from_string('2.7.0/latest')
        app.save()

        # make a build
        build1 = app.make_build()
        build1.save()

        # modify first form
        app.get_module(0).get_form(0).source = get_simple_form(
            xmlns='xmlns-0.1')
        app.save()

        # make second build
        build2 = app.make_build()
        build2.save()

        # modify first form
        app.get_module(0).get_form(0).source = get_simple_form(
            xmlns='xmlns-0.2')
        app.save()
        app.save()
        app.save()

        # make third build
        build3 = app.make_build()
        build3.save()

        self.assertEqual(self.get_form_versions(build1), [1, 1])
        self.assertEqual(self.get_form_versions(build2), [2, 1])
        self.assertEqual(self.get_form_versions(build3), [5, 1])

        # revert to build2
        app = app.make_reversion_to_copy(build2)
        app.save()

        # make reverted build
        build4 = app.make_build()
        build4.save()

        self.assertEqual(self.get_form_versions(build4), [6, 1])

        # copy app
        xxx_app = import_app(app.export_json(dump_json=False), domain)

        # make build of copy
        xxx_build1 = xxx_app.make_build()
        xxx_build1.save()

        # modify first form of copy app
        xxx_app.get_module(0).get_form(0).source = get_simple_form(
            xmlns='xmlns-0.xxx.0')
        xxx_app.save()

        # make second build of copy
        xxx_build2 = xxx_app.make_build()
        xxx_build2.save()

        self.assertEqual(self.get_form_versions(xxx_build1), [1, 1])
        self.assertEqual(self.get_form_versions(xxx_build2), [2, 1])
class SuiteResourceOverridesTest(TestCase, TestXmlMixin):
    file_path = ('data', 'suite')

    def setUp(self):
        super().setUp()
        self.factory = AppFactory(build_version='2.40.0')
        module, form = self.factory.new_basic_module('module0', 'noun')
        self.factory.new_form(module)
        self.factory.new_form(module)
        self.factory.app.save()

    def test_overrides(self):
        forms = list(self.factory.app.get_module(0).get_forms())
        add_xform_resource_overrides(self.factory.app.domain,
                                     self.factory.app.origin_id, {
                                         forms[0].unique_id: '123',
                                         forms[1].unique_id: '456',
                                     })
        expected = """
            <partial>
              <xform>
                <resource descriptor="Form: (Module module0 module) - module0 form 0" id="{}" version="1">
                  <location authority="local">./modules-0/forms-0.xml</location>
                  <location authority="remote">./modules-0/forms-0.xml</location>
                </resource>
              </xform>
              <xform>
                <resource descriptor="Form: (Module module0 module) - module0 form 1" id="{}" version="1">
                  <location authority="local">./modules-0/forms-1.xml</location>
                  <location authority="remote">./modules-0/forms-1.xml</location>
                </resource>
              </xform>
              <xform>
                <resource descriptor="Form: (Module module0 module) - module0 form 2" id="{}" version="1">
                  <location authority="local">./modules-0/forms-2.xml</location>
                  <location authority="remote">./modules-0/forms-2.xml</location>
                </resource>
              </xform>
            </partial>
        """.format('123', '456', forms[2].unique_id)
        self.assertXmlPartialEqual(expected, self.factory.app.create_suite(),
                                   "./xform")

    def test_duplicate_overrides_raises(self):
        forms = list(self.factory.app.get_module(0).get_forms())
        add_xform_resource_overrides(
            self.factory.app.domain, self.factory.app.origin_id, {
                forms[0].unique_id: '123',
                forms[1].unique_id: '456',
                forms[2].unique_id: '456',
            })
        with self.assertRaises(ResourceOverrideError):
            self.factory.app.create_suite()

    def test_copy_xform_resource_overrides(self):
        forms = list(self.factory.app.get_module(0).get_forms())
        add_xform_resource_overrides(self.factory.app.domain,
                                     self.factory.app.origin_id, {
                                         forms[0].unique_id: '123',
                                         forms[1].unique_id: '456',
                                     })

        copy_xform_resource_overrides(self.factory.app.domain,
                                      self.factory.app.origin_id, {
                                          forms[0].unique_id: '321',
                                          '123': '987',
                                      })

        overrides = get_xform_resource_overrides(self.factory.app.domain,
                                                 self.factory.app.origin_id)
        self.assertEqual({o.pre_id: o.post_id
                          for o in overrides.values()}, {
                              forms[0].unique_id: '123',
                              forms[1].unique_id: '456',
                              '321': '123',
                          })
class ShadowModuleFormSelectionSuiteTest(SimpleTestCase, TestXmlMixin):
    def setUp(self):
        self.factory = AppFactory()
        self.basic_module, self.form0 = self.factory.new_basic_module(
            'basic_module', 'parrot')
        self.form0.xmlns = 'http://openrosa.org/formdesigner/firstform'
        self.form1 = self.factory.new_form(self.basic_module)
        self.form1.xmlns = 'http://openrosa.org/formdesigner/secondform'
        self.shadow_module = self.factory.new_shadow_module('shadow_module',
                                                            self.basic_module,
                                                            with_form=False)
        self.child_module, self.form2 = self.factory.new_basic_module(
            'child_module', 'parrot', parent_module=self.basic_module)

    def test_all_forms_selected(self):
        expected = """
        <partial>
          <menu id="m1">
            <text>
              <locale id="modules.m1"/>
            </text>
            <command id="m1-f0"/>
            <command id="m1-f1"/>
          </menu>
        </partial>
        """
        self.assertXmlPartialEqual(expected, self.factory.app.create_suite(),
                                   "./menu[@id='m1']")

    def test_some_forms_selected(self):
        self.shadow_module.excluded_form_ids = [self.form0.unique_id]
        expected = """
        <partial>
          <menu id="m1">
            <text>
              <locale id="modules.m1"/>
            </text>
            <command id="m1-f1"/>
          </menu>
        </partial>
        """
        self.assertXmlPartialEqual(expected, self.factory.app.create_suite(),
                                   "./menu[@id='m1']")

    def test_no_forms_selected(self):
        self.shadow_module.excluded_form_ids = [
            self.form0.unique_id, self.form1.unique_id
        ]
        self.assertXmlDoesNotHaveXpath(self.factory.app.create_suite(),
                                       "./menu[@id='m1']")

    def test_no_child_forms_selected(self):
        self.shadow_module.excluded_form_ids = [self.form2.unique_id]
        self.assertXmlPartialEqual(
            '''
                <partial>
                  <menu id="m0">
                    <text>
                      <locale id="modules.m0"/>
                    </text>
                    <command id="m0-f0"/>
                    <command id="m0-f1"/>
                  </menu>
                  <menu id="m1">
                    <text>
                      <locale id="modules.m1"/>
                    </text>
                    <command id="m1-f0"/>
                    <command id="m1-f1"/>
                  </menu>
                  <menu id="m2" root="m0">
                    <text>
                      <locale id="modules.m2"/>
                    </text>
                    <command id="m2-f0"/>
                  </menu>
                </partial>
            ''', self.factory.app.create_suite(), "./menu")

        self.basic_module.put_in_root = True
        self.shadow_module.put_in_root = False
        self.child_module.put_in_root = False
        self.assertXmlPartialEqual(
            '''
                <partial>
                  <menu id="root">
                    <text>
                      <locale id="modules.m0"/>
                    </text>
                    <command id="m0-f0"/>
                    <command id="m0-f1"/>
                  </menu>
                  <menu id="m1">
                    <text>
                      <locale id="modules.m1"/>
                    </text>
                    <command id="m1-f0"/>
                    <command id="m1-f1"/>
                  </menu>
                  <menu id="m2" root="root">
                    <text>
                      <locale id="modules.m2"/>
                    </text>
                    <command id="m2-f0"/>
                  </menu>
                </partial>
            ''', self.factory.app.create_suite(), "./menu")

        self.basic_module.put_in_root = False
        self.shadow_module.put_in_root = True
        self.child_module.put_in_root = False
        self.assertXmlPartialEqual(
            '''
                <partial>
                  <menu id="m0">
                    <text>
                      <locale id="modules.m0"/>
                    </text>
                    <command id="m0-f0"/>
                    <command id="m0-f1"/>
                  </menu>
                  <menu id="root">
                    <text>
                      <locale id="modules.m1"/>
                    </text>
                    <command id="m1-f0"/>
                    <command id="m1-f1"/>
                  </menu>
                  <menu id="m2" root="m0">
                    <text>
                      <locale id="modules.m2"/>
                    </text>
                    <command id="m2-f0"/>
                  </menu>
                </partial>
            ''', self.factory.app.create_suite(), "./menu")

        self.basic_module.put_in_root = False
        self.shadow_module.put_in_root = False
        self.child_module.put_in_root = True
        self.assertXmlPartialEqual(
            '''
                <partial>
                  <menu id="m0">
                    <text>
                      <locale id="modules.m0"/>
                    </text>
                    <command id="m0-f0"/>
                    <command id="m0-f1"/>
                  </menu>
                  <menu id="m1">
                    <text>
                      <locale id="modules.m1"/>
                    </text>
                    <command id="m1-f0"/>
                    <command id="m1-f1"/>
                  </menu>
                  <menu id="m0">
                    <text>
                      <locale id="modules.m0"/>
                    </text>
                    <command id="m2-f0"/>
                  </menu>
                </partial>
            ''', self.factory.app.create_suite(), "./menu")

        self.basic_module.put_in_root = False
        self.shadow_module.put_in_root = True
        self.child_module.put_in_root = True
        self.assertXmlPartialEqual(
            '''
                <partial>
                  <menu id="m0">
                    <text>
                      <locale id="modules.m0"/>
                    </text>
                    <command id="m0-f0"/>
                    <command id="m0-f1"/>
                  </menu>
                  <menu id="root">
                    <text>
                      <locale id="modules.m1"/>
                    </text>
                    <command id="m1-f0"/>
                    <command id="m1-f1"/>
                  </menu>
                  <menu id="m0">
                    <text>
                      <locale id="modules.m0"/>
                    </text>
                    <command id="m2-f0"/>
                  </menu>
                </partial>
            ''', self.factory.app.create_suite(), "./menu")

        self.basic_module.put_in_root = True
        self.shadow_module.put_in_root = False
        self.child_module.put_in_root = True
        self.assertXmlPartialEqual(
            '''
                <partial>
                  <menu id="root">
                    <text>
                      <locale id="modules.m0"/>
                    </text>
                    <command id="m0-f0"/>
                    <command id="m0-f1"/>
                  </menu>
                  <menu id="m1">
                    <text>
                      <locale id="modules.m1"/>
                    </text>
                    <command id="m1-f0"/>
                    <command id="m1-f1"/>
                  </menu>
                  <menu id="root">
                    <text>
                      <locale id="modules.m2"/>
                    </text>
                    <command id="m2-f0"/>
                  </menu>
                </partial>
            ''', self.factory.app.create_suite(), "./menu")

        self.basic_module.put_in_root = True
        self.shadow_module.put_in_root = True
        self.child_module.put_in_root = False
        self.assertXmlPartialEqual(
            '''
                <partial>
                  <menu id="root">
                    <text>
                      <locale id="modules.m0"/>
                    </text>
                    <command id="m0-f0"/>
                    <command id="m0-f1"/>
                  </menu>
                  <menu id="root">
                    <text>
                      <locale id="modules.m1"/>
                    </text>
                    <command id="m1-f0"/>
                    <command id="m1-f1"/>
                  </menu>
                  <menu id="m2" root="root">
                    <text>
                      <locale id="modules.m2"/>
                    </text>
                    <command id="m2-f0"/>
                  </menu>
                </partial>
            ''', self.factory.app.create_suite(), "./menu")

        self.basic_module.put_in_root = True
        self.shadow_module.put_in_root = True
        self.child_module.put_in_root = True
        self.assertXmlPartialEqual(
            '''
                <partial>
                  <menu id="root">
                    <text>
                      <locale id="modules.m0"/>
                    </text>
                    <command id="m0-f0"/>
                    <command id="m0-f1"/>
                  </menu>
                  <menu id="root">
                    <text>
                      <locale id="modules.m1"/>
                    </text>
                    <command id="m1-f0"/>
                    <command id="m1-f1"/>
                  </menu>
                  <menu id="root">
                    <text>
                      <locale id="modules.m2"/>
                    </text>
                    <command id="m2-f0"/>
                  </menu>
                </partial>
            ''', self.factory.app.create_suite(), "./menu")

    def test_form_added(self):
        self.shadow_module.excluded_form_ids = [self.form0.unique_id]
        self.factory.new_form(self.basic_module)
        expected = """
        <partial>
          <menu id="m1">
            <text>
              <locale id="modules.m1"/>
            </text>
            <command id="m1-f1"/>
            <command id="m1-f2"/>
          </menu>
        </partial>
        """
        self.assertXmlPartialEqual(expected, self.factory.app.create_suite(),
                                   "./menu[@id='m1']")

    def test_form_removed(self):
        self.basic_module.forms.remove(self.form1)
        expected = """
        <partial>
          <menu id="m1">
            <text>
              <locale id="modules.m1"/>
            </text>
            <command id="m1-f0"/>
          </menu>
        </partial>
        """
        self.assertXmlPartialEqual(expected, self.factory.app.create_suite(),
                                   "./menu[@id='m1']")

    def test_forms_reordered(self):
        expected_before = """
        <partial>
          <form>http://openrosa.org/formdesigner/firstform</form>
          <form>http://openrosa.org/formdesigner/secondform</form>

          <form>http://openrosa.org/formdesigner/firstform</form>
          <form>http://openrosa.org/formdesigner/secondform</form>
        </partial>
        """
        self.assertXmlPartialEqual(expected_before,
                                   self.factory.app.create_suite(),
                                   "./entry/form")
        # Swap forms around
        self.basic_module.forms = [self.form1, self.form0]
        expected_after = """
        <partial>
          <form>http://openrosa.org/formdesigner/secondform</form>
          <form>http://openrosa.org/formdesigner/firstform</form>

          <form>http://openrosa.org/formdesigner/secondform</form>
          <form>http://openrosa.org/formdesigner/firstform</form>
        </partial>
        """
        self.assertXmlPartialEqual(expected_after,
                                   self.factory.app.create_suite(),
                                   "./entry/form")

    def _create_parent_selection_app(self):
        '''
           setUp creates an app with three modules: basic, shadow, and child, all using parrot cases.
           This app adds an additional module that uses child cases (baby_parrot) and uses it as the
           source for the shadow module. Tests verify that when the shadow module uses parent child
           selection, it fetches both parrot and baby_parrot datums, using the correct details.
        '''
        self.factory.form_requires_case(self.form0)
        self.child_case_module, self.form3 = self.factory.new_basic_module(
            'child_case_module', 'baby_parrot')
        self.factory.form_requires_case(self.form3, parent_case_type='parrot')
        self.shadow_module.source_module_id = self.child_case_module.unique_id
        self.shadow_module.parent_select.active = True

    def test_parent_selection_first(self):
        self._create_parent_selection_app()
        self.shadow_module.parent_select.module_id = self.basic_module.unique_id

        # Test the entry for the shadow module's single form
        expected_entry = """
            <partial>
              <entry>
                <command id="m1-f0">
                  <text>
                    <locale id="forms.m3f0"/>
                  </text>
                </command>
                <instance id="casedb" src="jr://instance/casedb"/>
                <instance id="commcaresession" src="jr://instance/session"/>
                <session>
                  <datum id="parent_id" nodeset="instance('casedb')/casedb/case[@case_type='parrot'][@status='open']"
                         value="./@case_id" detail-select="m0_case_short"/>
                  <datum id="case_id" nodeset="instance('casedb')/casedb/case[@case_type='baby_parrot'][@status='open'][index/parent=instance('commcaresession')/session/data/parent_id]"
                         value="./@case_id" detail-select="m1_case_short" detail-confirm="m1_case_long"/>
                </session>
              </entry>
            </partial>
        """
        self.assertXmlPartialEqual(expected_entry,
                                   self.factory.app.create_suite(),
                                   './entry[3]')

    def test_parent_selection_different_module_than_source(self):
        self._create_parent_selection_app()
        self.additional_basic_module, dummy = self.factory.new_basic_module(
            'additional_basic_module', 'parrot')
        self.child_case_module.parent_select.active = True
        self.child_case_module.parent_select.module_id = self.basic_module.unique_id
        self.shadow_module.parent_select.module_id = self.additional_basic_module.unique_id

        # Test the entry for the shadow module's single form
        expected_entry = """
            <partial>
              <entry>
                <command id="m1-f0">
                  <text>
                    <locale id="forms.m3f0"/>
                  </text>
                </command>
                <instance id="casedb" src="jr://instance/casedb"/>
                <instance id="commcaresession" src="jr://instance/session"/>
                <session>
                  <datum id="parent_id" nodeset="instance('casedb')/casedb/case[@case_type='parrot'][@status='open']"
                         value="./@case_id" detail-select="m4_case_short"/>
                  <datum id="case_id" nodeset="instance('casedb')/casedb/case[@case_type='baby_parrot'][@status='open'][index/parent=instance('commcaresession')/session/data/parent_id]"
                         value="./@case_id" detail-select="m1_case_short" detail-confirm="m1_case_long"/>
                </session>
              </entry>
            </partial>
        """
        self.assertXmlPartialEqual(expected_entry,
                                   self.factory.app.create_suite(),
                                   './entry[3]')
class ShadowModuleFormSelectionSuiteTest(SimpleTestCase, TestXmlMixin):

    def setUp(self):
        self.factory = AppFactory()
        self.basic_module, self.form0 = self.factory.new_basic_module('basic_module', 'parrot')
        self.form0.xmlns = 'http://openrosa.org/formdesigner/firstform'
        self.form1 = self.factory.new_form(self.basic_module)
        self.form1.xmlns = 'http://openrosa.org/formdesigner/secondform'
        self.shadow_module = self.factory.new_shadow_module('shadow_module', self.basic_module, with_form=False)
        self.child_module, self.form2 = self.factory.new_basic_module('child_module', 'parrot',
                                                                      parent_module=self.basic_module)

    def test_all_forms_selected(self):
        expected = """
        <partial>
          <menu id="m1">
            <text>
              <locale id="modules.m1"/>
            </text>
            <command id="m1-f0"/>
            <command id="m1-f1"/>
          </menu>
        </partial>
        """
        self.assertXmlPartialEqual(
            expected,
            self.factory.app.create_suite(),
            "./menu[@id='m1']"
        )

    def test_some_forms_selected(self):
        self.shadow_module.excluded_form_ids = [self.form0.unique_id]
        expected = """
        <partial>
          <menu id="m1">
            <text>
              <locale id="modules.m1"/>
            </text>
            <command id="m1-f1"/>
          </menu>
        </partial>
        """
        self.assertXmlPartialEqual(
            expected,
            self.factory.app.create_suite(),
            "./menu[@id='m1']"
        )

    def test_no_forms_selected(self):
        self.shadow_module.excluded_form_ids = [self.form0.unique_id, self.form1.unique_id]
        self.assertXmlDoesNotHaveXpath(self.factory.app.create_suite(), "./menu[@id='m1']")

    def test_no_child_forms_selected(self):
        self.shadow_module.excluded_form_ids = [self.form2.unique_id]
        self.assertXmlPartialEqual(
            '''
                <partial>
                  <menu id="m0">
                    <text>
                      <locale id="modules.m0"/>
                    </text>
                    <command id="m0-f0"/>
                    <command id="m0-f1"/>
                  </menu>
                  <menu id="m1">
                    <text>
                      <locale id="modules.m1"/>
                    </text>
                    <command id="m1-f0"/>
                    <command id="m1-f1"/>
                  </menu>
                  <menu id="m2" root="m0">
                    <text>
                      <locale id="modules.m2"/>
                    </text>
                    <command id="m2-f0"/>
                  </menu>
                </partial>
            ''',
            self.factory.app.create_suite(),
            "./menu"
        )

        self.basic_module.put_in_root = True
        self.shadow_module.put_in_root = False
        self.child_module.put_in_root = False
        self.assertXmlPartialEqual(
            '''
                <partial>
                  <menu id="root">
                    <text>
                      <locale id="modules.m0"/>
                    </text>
                    <command id="m0-f0"/>
                    <command id="m0-f1"/>
                  </menu>
                  <menu id="m1">
                    <text>
                      <locale id="modules.m1"/>
                    </text>
                    <command id="m1-f0"/>
                    <command id="m1-f1"/>
                  </menu>
                  <menu id="m2" root="root">
                    <text>
                      <locale id="modules.m2"/>
                    </text>
                    <command id="m2-f0"/>
                  </menu>
                </partial>
            ''',
            self.factory.app.create_suite(),
            "./menu"
        )

        self.basic_module.put_in_root = False
        self.shadow_module.put_in_root = True
        self.child_module.put_in_root = False
        self.assertXmlPartialEqual(
            '''
                <partial>
                  <menu id="m0">
                    <text>
                      <locale id="modules.m0"/>
                    </text>
                    <command id="m0-f0"/>
                    <command id="m0-f1"/>
                  </menu>
                  <menu id="root">
                    <text>
                      <locale id="modules.m1"/>
                    </text>
                    <command id="m1-f0"/>
                    <command id="m1-f1"/>
                  </menu>
                  <menu id="m2" root="m0">
                    <text>
                      <locale id="modules.m2"/>
                    </text>
                    <command id="m2-f0"/>
                  </menu>
                </partial>
            ''',
            self.factory.app.create_suite(),
            "./menu"
        )

        self.basic_module.put_in_root = False
        self.shadow_module.put_in_root = False
        self.child_module.put_in_root = True
        self.assertXmlPartialEqual(
            '''
                <partial>
                  <menu id="m0">
                    <text>
                      <locale id="modules.m0"/>
                    </text>
                    <command id="m0-f0"/>
                    <command id="m0-f1"/>
                  </menu>
                  <menu id="m1">
                    <text>
                      <locale id="modules.m1"/>
                    </text>
                    <command id="m1-f0"/>
                    <command id="m1-f1"/>
                  </menu>
                  <menu id="m0">
                    <text>
                      <locale id="modules.m0"/>
                    </text>
                    <command id="m2-f0"/>
                  </menu>
                </partial>
            ''',
            self.factory.app.create_suite(),
            "./menu"
        )

        self.basic_module.put_in_root = False
        self.shadow_module.put_in_root = True
        self.child_module.put_in_root = True
        self.assertXmlPartialEqual(
            '''
                <partial>
                  <menu id="m0">
                    <text>
                      <locale id="modules.m0"/>
                    </text>
                    <command id="m0-f0"/>
                    <command id="m0-f1"/>
                  </menu>
                  <menu id="root">
                    <text>
                      <locale id="modules.m1"/>
                    </text>
                    <command id="m1-f0"/>
                    <command id="m1-f1"/>
                  </menu>
                  <menu id="m0">
                    <text>
                      <locale id="modules.m0"/>
                    </text>
                    <command id="m2-f0"/>
                  </menu>
                </partial>
            ''',
            self.factory.app.create_suite(),
            "./menu"
        )

        self.basic_module.put_in_root = True
        self.shadow_module.put_in_root = False
        self.child_module.put_in_root = True
        self.assertXmlPartialEqual(
            '''
                <partial>
                  <menu id="root">
                    <text>
                      <locale id="modules.m0"/>
                    </text>
                    <command id="m0-f0"/>
                    <command id="m0-f1"/>
                  </menu>
                  <menu id="m1">
                    <text>
                      <locale id="modules.m1"/>
                    </text>
                    <command id="m1-f0"/>
                    <command id="m1-f1"/>
                  </menu>
                  <menu id="root">
                    <text>
                      <locale id="modules.m2"/>
                    </text>
                    <command id="m2-f0"/>
                  </menu>
                </partial>
            ''',
            self.factory.app.create_suite(),
            "./menu"
        )

        self.basic_module.put_in_root = True
        self.shadow_module.put_in_root = True
        self.child_module.put_in_root = False
        self.assertXmlPartialEqual(
            '''
                <partial>
                  <menu id="root">
                    <text>
                      <locale id="modules.m0"/>
                    </text>
                    <command id="m0-f0"/>
                    <command id="m0-f1"/>
                  </menu>
                  <menu id="root">
                    <text>
                      <locale id="modules.m1"/>
                    </text>
                    <command id="m1-f0"/>
                    <command id="m1-f1"/>
                  </menu>
                  <menu id="m2" root="root">
                    <text>
                      <locale id="modules.m2"/>
                    </text>
                    <command id="m2-f0"/>
                  </menu>
                </partial>
            ''',
            self.factory.app.create_suite(),
            "./menu"
        )

        self.basic_module.put_in_root = True
        self.shadow_module.put_in_root = True
        self.child_module.put_in_root = True
        self.assertXmlPartialEqual(
            '''
                <partial>
                  <menu id="root">
                    <text>
                      <locale id="modules.m0"/>
                    </text>
                    <command id="m0-f0"/>
                    <command id="m0-f1"/>
                  </menu>
                  <menu id="root">
                    <text>
                      <locale id="modules.m1"/>
                    </text>
                    <command id="m1-f0"/>
                    <command id="m1-f1"/>
                  </menu>
                  <menu id="root">
                    <text>
                      <locale id="modules.m2"/>
                    </text>
                    <command id="m2-f0"/>
                  </menu>
                </partial>
            ''',
            self.factory.app.create_suite(),
            "./menu"
        )


    def test_form_added(self):
        self.shadow_module.excluded_form_ids = [self.form0.unique_id]
        self.factory.new_form(self.basic_module)
        expected = """
        <partial>
          <menu id="m1">
            <text>
              <locale id="modules.m1"/>
            </text>
            <command id="m1-f1"/>
            <command id="m1-f2"/>
          </menu>
        </partial>
        """
        self.assertXmlPartialEqual(
            expected,
            self.factory.app.create_suite(),
            "./menu[@id='m1']"
        )

    def test_form_removed(self):
        self.basic_module.forms.remove(self.form1)
        expected = """
        <partial>
          <menu id="m1">
            <text>
              <locale id="modules.m1"/>
            </text>
            <command id="m1-f0"/>
          </menu>
        </partial>
        """
        self.assertXmlPartialEqual(
            expected,
            self.factory.app.create_suite(),
            "./menu[@id='m1']"
        )

    def test_forms_reordered(self):
        expected_before = """
        <partial>
          <form>http://openrosa.org/formdesigner/firstform</form>
          <form>http://openrosa.org/formdesigner/secondform</form>

          <form>http://openrosa.org/formdesigner/firstform</form>
          <form>http://openrosa.org/formdesigner/secondform</form>
        </partial>
        """
        self.assertXmlPartialEqual(
            expected_before,
            self.factory.app.create_suite(),
            "./entry/form"
        )
        # Swap forms around
        self.basic_module.forms = [self.form1, self.form0]
        expected_after = """
        <partial>
          <form>http://openrosa.org/formdesigner/secondform</form>
          <form>http://openrosa.org/formdesigner/firstform</form>

          <form>http://openrosa.org/formdesigner/secondform</form>
          <form>http://openrosa.org/formdesigner/firstform</form>
        </partial>
        """
        self.assertXmlPartialEqual(
            expected_after,
            self.factory.app.create_suite(),
            "./entry/form"
        )

    def _create_parent_selection_app(self):
        '''
           setUp creates an app with three modules: basic, shadow, and child, all using parrot cases.
           This app adds an additional module that uses child cases (baby_parrot) and uses it as the
           source for the shadow module. Tests verify that when the shadow module uses parent child
           selection, it fetches both parrot and baby_parrot datums, using the correct details.
        '''
        self.factory.form_requires_case(self.form0)
        self.child_case_module, self.form3 = self.factory.new_basic_module('child_case_module', 'baby_parrot')
        self.factory.form_requires_case(self.form3, parent_case_type='parrot')
        self.shadow_module.source_module_id = self.child_case_module.unique_id
        self.shadow_module.parent_select.active = True

    def test_parent_selection_first(self):
        self._create_parent_selection_app()
        self.shadow_module.parent_select.module_id = self.basic_module.unique_id

        # Test the entry for the shadow module's single form
        expected_entry = """
            <partial>
              <entry>
                <command id="m1-f0">
                  <text>
                    <locale id="forms.m3f0"/>
                  </text>
                </command>
                <instance id="casedb" src="jr://instance/casedb"/>
                <instance id="commcaresession" src="jr://instance/session"/>
                <session>
                  <datum id="parent_id" nodeset="instance('casedb')/casedb/case[@case_type='parrot'][@status='open']"
                         value="./@case_id" detail-select="m0_case_short"/>
                  <datum id="case_id" nodeset="instance('casedb')/casedb/case[@case_type='baby_parrot'][@status='open'][index/parent=instance('commcaresession')/session/data/parent_id]"
                         value="./@case_id" detail-select="m1_case_short" detail-confirm="m1_case_long"/>
                </session>
              </entry>
            </partial>
        """
        self.assertXmlPartialEqual(
            expected_entry,
            self.factory.app.create_suite(),
            './entry[3]'
        )

    def test_parent_selection_different_module_than_source(self):
        self._create_parent_selection_app()
        self.additional_basic_module, dummy = self.factory.new_basic_module('additional_basic_module', 'parrot')
        self.child_case_module.parent_select.active = True
        self.child_case_module.parent_select.module_id = self.basic_module.unique_id
        self.shadow_module.parent_select.module_id = self.additional_basic_module.unique_id

        # Test the entry for the shadow module's single form
        expected_entry = """
            <partial>
              <entry>
                <command id="m1-f0">
                  <text>
                    <locale id="forms.m3f0"/>
                  </text>
                </command>
                <instance id="casedb" src="jr://instance/casedb"/>
                <instance id="commcaresession" src="jr://instance/session"/>
                <session>
                  <datum id="parent_id" nodeset="instance('casedb')/casedb/case[@case_type='parrot'][@status='open']"
                         value="./@case_id" detail-select="m4_case_short"/>
                  <datum id="case_id" nodeset="instance('casedb')/casedb/case[@case_type='baby_parrot'][@status='open'][index/parent=instance('commcaresession')/session/data/parent_id]"
                         value="./@case_id" detail-select="m1_case_short" detail-confirm="m1_case_long"/>
                </session>
              </entry>
            </partial>
        """
        self.assertXmlPartialEqual(
            expected_entry,
            self.factory.app.create_suite(),
            './entry[3]'
        )
class ShadowModuleFormSelectionSuiteTest(SimpleTestCase, TestXmlMixin):

    def setUp(self):
        self.factory = AppFactory()
        self.basic_module, self.form0 = self.factory.new_basic_module('basic_module', 'parrot')
        self.form0.xmlns = 'http://openrosa.org/formdesigner/firstform'
        self.form1 = self.factory.new_form(self.basic_module)
        self.form1.xmlns = 'http://openrosa.org/formdesigner/secondform'
        self.shadow_module = self.factory.new_shadow_module('shadow_module', self.basic_module, with_form=False)
        self.child_module, self.form2 = self.factory.new_basic_module('child_module', 'parrot',
                                                                      parent_module=self.basic_module)

    def test_all_forms_selected(self):
        expected = """
        <partial>
          <menu id="m1">
            <text>
              <locale id="modules.m1"/>
            </text>
            <command id="m1-f0"/>
            <command id="m1-f1"/>
          </menu>
        </partial>
        """
        self.assertXmlPartialEqual(
            expected,
            self.factory.app.create_suite(),
            "./menu[@id='m1']"
        )

    def test_some_forms_selected(self):
        self.shadow_module.excluded_form_ids = [self.form0.unique_id]
        expected = """
        <partial>
          <menu id="m1">
            <text>
              <locale id="modules.m1"/>
            </text>
            <command id="m1-f1"/>
          </menu>
        </partial>
        """
        self.assertXmlPartialEqual(
            expected,
            self.factory.app.create_suite(),
            "./menu[@id='m1']"
        )

    def test_no_forms_selected(self):
        self.shadow_module.excluded_form_ids = [self.form0.unique_id, self.form1.unique_id]
        self.assertXmlDoesNotHaveXpath(self.factory.app.create_suite(), "./menu[@id='m1']")

    def test_no_child_forms_selected(self):
        self.shadow_module.excluded_form_ids = [self.form2.unique_id]
        self.assertXmlPartialEqual(
            '''
                <partial>
                  <menu id="m0">
                    <text>
                      <locale id="modules.m0"/>
                    </text>
                    <command id="m0-f0"/>
                    <command id="m0-f1"/>
                  </menu>
                  <menu id="m1">
                    <text>
                      <locale id="modules.m1"/>
                    </text>
                    <command id="m1-f0"/>
                    <command id="m1-f1"/>
                  </menu>
                  <menu id="m2" root="m0">
                    <text>
                      <locale id="modules.m2"/>
                    </text>
                    <command id="m2-f0"/>
                  </menu>
                </partial>
            ''',
            self.factory.app.create_suite(),
            "./menu"
        )

        self.basic_module.put_in_root = True
        self.shadow_module.put_in_root = False
        self.child_module.put_in_root = False
        self.assertXmlPartialEqual(
            '''
                <partial>
                  <menu id="root">
                    <text>
                      <locale id="modules.m0"/>
                    </text>
                    <command id="m0-f0"/>
                    <command id="m0-f1"/>
                  </menu>
                  <menu id="m1">
                    <text>
                      <locale id="modules.m1"/>
                    </text>
                    <command id="m1-f0"/>
                    <command id="m1-f1"/>
                  </menu>
                  <menu id="m2" root="root">
                    <text>
                      <locale id="modules.m2"/>
                    </text>
                    <command id="m2-f0"/>
                  </menu>
                </partial>
            ''',
            self.factory.app.create_suite(),
            "./menu"
        )

        self.basic_module.put_in_root = False
        self.shadow_module.put_in_root = True
        self.child_module.put_in_root = False
        self.assertXmlPartialEqual(
            '''
                <partial>
                  <menu id="m0">
                    <text>
                      <locale id="modules.m0"/>
                    </text>
                    <command id="m0-f0"/>
                    <command id="m0-f1"/>
                  </menu>
                  <menu id="root">
                    <text>
                      <locale id="modules.m1"/>
                    </text>
                    <command id="m1-f0"/>
                    <command id="m1-f1"/>
                  </menu>
                  <menu id="m2" root="m0">
                    <text>
                      <locale id="modules.m2"/>
                    </text>
                    <command id="m2-f0"/>
                  </menu>
                </partial>
            ''',
            self.factory.app.create_suite(),
            "./menu"
        )

        self.basic_module.put_in_root = False
        self.shadow_module.put_in_root = False
        self.child_module.put_in_root = True
        self.assertXmlPartialEqual(
            '''
                <partial>
                  <menu id="m0">
                    <text>
                      <locale id="modules.m0"/>
                    </text>
                    <command id="m0-f0"/>
                    <command id="m0-f1"/>
                  </menu>
                  <menu id="m1">
                    <text>
                      <locale id="modules.m1"/>
                    </text>
                    <command id="m1-f0"/>
                    <command id="m1-f1"/>
                  </menu>
                  <menu id="m0">
                    <text>
                      <locale id="modules.m2"/>
                    </text>
                    <command id="m2-f0"/>
                  </menu>
                </partial>
            ''',
            self.factory.app.create_suite(),
            "./menu"
        )

        self.basic_module.put_in_root = False
        self.shadow_module.put_in_root = True
        self.child_module.put_in_root = True
        self.assertXmlPartialEqual(
            '''
                <partial>
                  <menu id="m0">
                    <text>
                      <locale id="modules.m0"/>
                    </text>
                    <command id="m0-f0"/>
                    <command id="m0-f1"/>
                  </menu>
                  <menu id="root">
                    <text>
                      <locale id="modules.m1"/>
                    </text>
                    <command id="m1-f0"/>
                    <command id="m1-f1"/>
                  </menu>
                  <menu id="m0">
                    <text>
                      <locale id="modules.m2"/>
                    </text>
                    <command id="m2-f0"/>
                  </menu>
                </partial>
            ''',
            self.factory.app.create_suite(),
            "./menu"
        )

        self.basic_module.put_in_root = True
        self.shadow_module.put_in_root = False
        self.child_module.put_in_root = True
        self.assertXmlPartialEqual(
            '''
                <partial>
                  <menu id="root">
                    <text>
                      <locale id="modules.m0"/>
                    </text>
                    <command id="m0-f0"/>
                    <command id="m0-f1"/>
                  </menu>
                  <menu id="m1">
                    <text>
                      <locale id="modules.m1"/>
                    </text>
                    <command id="m1-f0"/>
                    <command id="m1-f1"/>
                  </menu>
                  <menu id="root">
                    <text>
                      <locale id="modules.m2"/>
                    </text>
                    <command id="m2-f0"/>
                  </menu>
                </partial>
            ''',
            self.factory.app.create_suite(),
            "./menu"
        )

        self.basic_module.put_in_root = True
        self.shadow_module.put_in_root = True
        self.child_module.put_in_root = False
        self.assertXmlPartialEqual(
            '''
                <partial>
                  <menu id="root">
                    <text>
                      <locale id="modules.m0"/>
                    </text>
                    <command id="m0-f0"/>
                    <command id="m0-f1"/>
                  </menu>
                  <menu id="root">
                    <text>
                      <locale id="modules.m1"/>
                    </text>
                    <command id="m1-f0"/>
                    <command id="m1-f1"/>
                  </menu>
                  <menu id="m2" root="root">
                    <text>
                      <locale id="modules.m2"/>
                    </text>
                    <command id="m2-f0"/>
                  </menu>
                </partial>
            ''',
            self.factory.app.create_suite(),
            "./menu"
        )

        self.basic_module.put_in_root = True
        self.shadow_module.put_in_root = True
        self.child_module.put_in_root = True
        self.assertXmlPartialEqual(
            '''
                <partial>
                  <menu id="root">
                    <text>
                      <locale id="modules.m0"/>
                    </text>
                    <command id="m0-f0"/>
                    <command id="m0-f1"/>
                  </menu>
                  <menu id="root">
                    <text>
                      <locale id="modules.m1"/>
                    </text>
                    <command id="m1-f0"/>
                    <command id="m1-f1"/>
                  </menu>
                  <menu id="root">
                    <text>
                      <locale id="modules.m2"/>
                    </text>
                    <command id="m2-f0"/>
                  </menu>
                </partial>
            ''',
            self.factory.app.create_suite(),
            "./menu"
        )


    def test_form_added(self):
        self.shadow_module.excluded_form_ids = [self.form0.unique_id]
        self.factory.new_form(self.basic_module)
        expected = """
        <partial>
          <menu id="m1">
            <text>
              <locale id="modules.m1"/>
            </text>
            <command id="m1-f1"/>
            <command id="m1-f2"/>
          </menu>
        </partial>
        """
        self.assertXmlPartialEqual(
            expected,
            self.factory.app.create_suite(),
            "./menu[@id='m1']"
        )

    def test_form_removed(self):
        self.basic_module.forms.remove(self.form1)
        expected = """
        <partial>
          <menu id="m1">
            <text>
              <locale id="modules.m1"/>
            </text>
            <command id="m1-f0"/>
          </menu>
        </partial>
        """
        self.assertXmlPartialEqual(
            expected,
            self.factory.app.create_suite(),
            "./menu[@id='m1']"
        )

    def test_forms_reordered(self):
        expected_before = """
        <partial>
          <form>http://openrosa.org/formdesigner/firstform</form>
          <form>http://openrosa.org/formdesigner/secondform</form>

          <form>http://openrosa.org/formdesigner/firstform</form>
          <form>http://openrosa.org/formdesigner/secondform</form>
        </partial>
        """
        self.assertXmlPartialEqual(
            expected_before,
            self.factory.app.create_suite(),
            "./entry/form"
        )
        # Swap forms around
        self.basic_module.forms = [self.form1, self.form0]
        expected_after = """
        <partial>
          <form>http://openrosa.org/formdesigner/secondform</form>
          <form>http://openrosa.org/formdesigner/firstform</form>

          <form>http://openrosa.org/formdesigner/secondform</form>
          <form>http://openrosa.org/formdesigner/firstform</form>
        </partial>
        """
        self.assertXmlPartialEqual(
            expected_after,
            self.factory.app.create_suite(),
            "./entry/form"
        )