예제 #1
0
    def testGooeyRequestsConfirmationWhenShowStopWarningModalTrue(self, mockModal):
        """
        When show_stop_warning=False, Gooey should immediately kill the
        running program without additional user confirmation.

        Otherwise, Gooey should show a confirmation modal and, dependending on the
        user's choice, either do nothing or kill the running program.
        """
        Case = namedtuple('Case', ['show_warning', 'shouldSeeConfirm', 'userChooses', 'shouldHaltProgram'])
        testcases = [
            Case(show_warning=True, shouldSeeConfirm=True, userChooses=True, shouldHaltProgram=True),
            Case(show_warning=True, shouldSeeConfirm=True, userChooses=False, shouldHaltProgram=False),
            Case(show_warning=False, shouldSeeConfirm=False, userChooses='N/A', shouldHaltProgram=True),
        ]

        for case in testcases:
            mockModal.reset_mock()
            parser = self.basicParser()
            with instrumentGooey(parser, show_stop_warning=case.show_warning) as (app, gapp):
                mockClientRunner = MagicMock()
                mockModal.return_value = case.userChooses
                gapp.clientRunner = mockClientRunner

                gapp.onStopExecution()

                if case.shouldSeeConfirm:
                    mockModal.assert_called()
                else:
                    mockModal.assert_not_called()

                if case.shouldHaltProgram:
                    mockClientRunner.stop.assert_called()
                else:
                    mockClientRunner.stop.assert_not_called()
예제 #2
0
    def testSliderDefault(self):
        cases = [
            [{}, 0],
            [{'default': 0}, 0],
            [{'default': 10}, 10],
            [{'default': 76}, 76],
            # note that WX caps the value
            # unless explicitly widened via gooey_options
            [{'default': 81234}, 100],
            # here we set the max to something higher than
            # the default and all works as expected.
            # this is a TODO for validation
            [{'default': 81234, 'gooey_options': {'max': 99999}}, 81234],

            # Initial Value cases
            [{}, 0],
            [{'gooey_options': {'initial_value': 0}}, 0],
            [{'gooey_options': {'initial_value': 10}}, 10],
            [{'gooey_options': {'initial_value': 76}}, 76],
            # note that WX caps the value
            # unless explicitly widened via gooey_options
            [{'gooey_options': {'initial_value': 81234}}, 100],
            # here we set the max to something higher than
            # the default and all works as expected.
            # this is a TODO for validation
            [{'gooey_options': {'initial_value': 81234, 'max': 99999}}, 81234],
        ]
        for inputs, expected in cases:
            with self.subTest(inputs):
                parser = self.makeParser(**inputs)
                with instrumentGooey(parser) as (app, frame, gapp):
                    slider = gapp.getActiveConfig().reifiedWidgets[0]
                    self.assertEqual(slider.getValue()['rawValue'], expected)
예제 #3
0
 def testFullscreen(self):
     parser = self.basicParser()
     for shouldShow in [True, False]:
         with self.subTest('Should set full screen: {}'.format(shouldShow)):
             with instrumentGooey(parser,
                                  fullscreen=shouldShow) as (app, gapp):
                 self.assertEqual(gapp.IsFullScreen(), shouldShow)
예제 #4
0
    def testInitialValue(self):
        widgets = ['ColourChooser',
                   'CommandField',
                   'DateChooser', 'DirChooser', 'FileChooser', 'FileSaver',
                   'FilterableDropdown',  'MultiDirChooser', 'MultiFileChooser',
                   'PasswordField',  'TextField', 'Textarea', 'TimeChooser']

        cases = [
            # initial_value supersedes, default
            Case(
                {'default': 'default', 'gooey_options': {'initial_value': 'some val'}},
                'some val'),
            Case(
                {'gooey_options': {'initial_value': 'some val'}},
                 'some val'),
            Case(
                {'default': 'default', 'gooey_options': {}},
                 'default'),
            Case({'default': 'default'},
                 'default')
        ]

        for widgetName in widgets:
            with self.subTest(widgetName):
                for case in cases:
                    parser = self.makeParser(widget=widgetName, **case.inputs)
                    with instrumentGooey(parser) as (app, gooeyApp):
                        widget = gooeyApp.configs[0].reifiedWidgets[0]
                        self.assertEqual(widget.getValue()['rawValue'], case.initialExpected)
예제 #5
0
    def test_optional_radiogroup_click_behavior(self):
        """
        Testing that select/deselect behaves as expected
        """
        testcases = [
            self.click_scenarios_optional_widget(),
            self.click_scenarios_required_widget(),
            self.click_scenarios_initial_selection()
        ]

        for testcase in testcases:
            with self.subTest(testcase['name']):
                # wire up the parse with our test case options
                parser = self.mutext_group(testcase['input'])

                with instrumentGooey(parser) as (app, frame, gapp):
                    radioGroup = gapp.getActiveConfig().reifiedWidgets[0]

                    for scenario in testcase['scenario']:
                        targetButton = scenario['clickButton']

                        event = wx.CommandEvent(wx.wxEVT_LEFT_DOWN, wx.Window.NewControlId())
                        event.SetEventObject(radioGroup.radioButtons[targetButton])

                        radioGroup.radioButtons[targetButton].ProcessEvent(event)

                        expectedEnabled, expectedDisabled = scenario['postState']

                        for index in expectedEnabled:
                            self.assertEqual(radioGroup.selected, radioGroup.radioButtons[index])
                            self.assertTrue(radioGroup.widgets[index].IsEnabled())

                        for index in expectedDisabled:
                            self.assertNotEqual(radioGroup.selected, radioGroup.radioButtons[index])
                            self.assertFalse(radioGroup.widgets[index].IsEnabled())
예제 #6
0
 def testDefaultAndInitialValue(self):
     cases = [
         # initial_value takes precedence when both are present
         Case(
             {
                 'default': 'default_val',
                 'gooey_options': {
                     'initial_value': 'some val'
                 }
             }, 'some val', None),
         # when no default is present
         # Case({'gooey_options': {'initial_value': 'some val'}},
         #  'some val',
         #  ''),
         # [{'default': 'default', 'gooey_options': {}},
         #  'default'],
         # [{'default': 'default'},
         #  'default'],
     ]
     for case in cases:
         parser = self.makeParser(**case.inputs)
         with instrumentGooey(parser) as (app, frame, gapp):
             widget = gapp.getActiveConfig().reifiedWidgets[0]
             self.assertEqual(widget.getValue()['rawValue'],
                              case.initialExpected)
             widget.setValue('')
             print(widget.getValue())
             self.assertEqual(widget.getValue()['cmd'],
                              case.expectedAfterClearing)
예제 #7
0
 def testInitialValue(self):
     cases = [
         # `initial` should supersede `default`
         {
             'inputs': {
                 'default': 'b',
                 'choices': ['a', 'b', 'c'],
                 'gooey_options': {
                     'initial_value': 'a'
                 }
             },
             'expect': ['a']
         },
         {
             'inputs': {
                 'choices': ['a', 'b', 'c'],
                 'gooey_options': {
                     'initial_value': 'a'
                 }
             },
             'expect': ['a']
         },
         {
             'inputs': {
                 'choices': ['a', 'b', 'c'],
                 'gooey_options': {
                     'initial_value': ['a', 'c']
                 }
             },
             'expect': ['a', 'c']
         },
         {
             'inputs': {
                 'choices': ['a', 'b', 'c'],
                 'default': 'b',
                 'gooey_options': {}
             },
             'expect': ['b']
         },
         {
             'inputs': {
                 'choices': ['a', 'b', 'c'],
                 'default': 'b'
             },
             'expect': ['b']
         },
         {
             'inputs': {
                 'choices': ['a', 'b', 'c']
             },
             'expect': []
         }
     ]
     for case in cases:
         with self.subTest(case):
             parser = self.makeParser(**case['inputs'])
             with instrumentGooey(parser) as (app, gooeyApp):
                 widget = gooeyApp.configs[0].reifiedWidgets[0]
                 self.assertEqual(widget.getValue()['rawValue'],
                                  case['expect'])
예제 #8
0
    def testInitialValue(self):
        cases = [
            # `initial` should supersede `default`
            {'inputs': {'default': False,
                        'widget': 'CheckBox',
                        'gooey_options': {'initial_value': True}},
             'expect': True},

            {'inputs': {'gooey_options': {'initial_value': True},
                        'widget': 'CheckBox'},
             'expect': True},

            {'inputs': {'gooey_options': {'initial_value': False},
                        'widget': 'CheckBox'},
             'expect': False},

            {'inputs': {'default': True,
                        'widget': 'CheckBox',
                        'gooey_options': {}},
             'expect': True},

            {'inputs': {'default': True,
                        'widget': 'CheckBox'},
             'expect': True},

            {'inputs': {'widget': 'CheckBox'},
             'expect': False}
        ]
        for case in cases:
            with self.subTest(case):
                parser = self.makeParser(**case['inputs'])
                with instrumentGooey(parser) as (app, gooeyApp):
                    widget = gooeyApp.configs[0].reifiedWidgets[0]
                    self.assertEqual(widget.getValue()['rawValue'], case['expect'])
예제 #9
0
 def testFontWeightsGetSet(self):
     ## Issue #625 font weight wasn't being correctly passed to the terminal
     for weight in [constants.FONTWEIGHT_LIGHT, constants.FONTWEIGHT_BOLD]:
         parser = self.basicParser()
         with instrumentGooey(parser, terminal_font_weight=weight) as (app, gapp):
             terminal = gapp.console.textbox
             self.assertEqual(terminal.GetFont().GetWeight(), weight)
예제 #10
0
 def testTerminalColorChanges(self):
     ## Issue #625 terminal panel color wasn't being set due to a typo
     parser = self.basicParser()
     expectedColors = [(255, 0, 0, 255), (255, 255, 255, 255), (100, 100, 100,100)]
     for expectedColor in expectedColors:
         with instrumentGooey(parser, terminal_panel_color=expectedColor) as (app, gapp):
             foundColor = gapp.console.GetBackgroundColour()
             self.assertEqual(tuple(foundColor), expectedColor)
예제 #11
0
 def testDefault(self):
     cases = [
         [{
             'widget': 'IntegerField'
         }, 0],
         [{
             'default': 0,
             'widget': 'IntegerField'
         }, 0],
         [{
             'default': 10,
             'widget': 'IntegerField'
         }, 10],
         [{
             'default': 76,
             'widget': 'IntegerField'
         }, 76],
         # note that WX caps the value
         # unless explicitly widened via gooey_options
         [{
             'default': 81234,
             'widget': 'IntegerField'
         }, 100],
         # here we set the max to something higher than
         # the default and all works as expected.
         # this is a TODO for validation
         [{
             'default': 81234,
             'widget': 'IntegerField',
             'gooey_options': {
                 'max': 99999
             }
         }, 81234],
         [{
             'widget': 'DecimalField'
         }, 0],
         [{
             'default': 0,
             'widget': 'DecimalField'
         }, 0],
         [{
             'default': 81234,
             'widget': 'DecimalField'
         }, 100],
         [{
             'default': 81234,
             'widget': 'DecimalField',
             'gooey_options': {
                 'max': 99999
             }
         }, 81234],
     ]
     for inputs, expected in cases:
         with self.subTest(inputs):
             parser = self.makeParser(**inputs)
             with instrumentGooey(parser) as (app, gooeyApp):
                 input = gooeyApp.configs[0].reifiedWidgets[0]
                 self.assertEqual(input.getValue()['rawValue'], expected)
예제 #12
0
 def testNoLossOfPrecision(self):
     parser = self.makeParser(widget='DecimalField',
                              default=12.23534,
                              gooey_options={'precision': 20})
     with instrumentGooey(parser) as (app, gooeyApp):
         field = gooeyApp.configs[0].reifiedWidgets[0]
         result = field.getValue()
         self.assertEqual(result['rawValue'], 12.23534)
         self.assertIsNotNone(result['cmd'])
예제 #13
0
 def testPlaceholder(self):
     cases = [[{}, ''], [{'placeholder': 'Hello'}, 'Hello']]
     for options, expected in cases:
         parser = self.makeParser(gooey_options=options)
         with instrumentGooey(parser) as (app, frame, gapp):
             # because of how poorly designed the Gooey widgets are
             # we have to reach down 3 levels in order to find the
             # actual WX object we need to test.
             widget = gapp.getActiveConfig().reifiedWidgets[0].widget
             self.assertEqual(widget.widget.GetHint(), expected)
예제 #14
0
 def test_header_string(self):
     """
     Verify that string in the buildspec get correctly
     placed into the UI.
     """
     parser = ArgumentParser(description='Foobar')
     with instrumentGooey(parser, program_name='BaZzEr') as (app, gooeyApp):
         self.assertEqual(gooeyApp.header._header.GetLabelText(), 'BaZzEr')
         self.assertEqual(gooeyApp.header._subheader.GetLabelText(),
                          'Foobar')
예제 #15
0
 def testZerosAreReturned(self):
     """
     Originally the formatter was dropping '0' due to
     it being interpreted as falsey
     """
     parser = self.makeParser()
     with instrumentGooey(parser) as (app, gooeyApp):
         field = gooeyApp.configs[0].reifiedWidgets[0]
         result = field.getValue()
         self.assertEqual(result['rawValue'], 0)
         self.assertIsNotNone(result['cmd'])
예제 #16
0
    def test_time_remaining_visibility(self):
        for testdata in self.testcases():
            with self.subTest(testdata):
                with instrumentGooey(self.make_parser(), timing_options=testdata) as (app, gooeyApp):

                    gooeyApp.showConsole()
                    footer = gooeyApp.footer

                    self.assertEqual(
                        footer.time_remaining_text.Shown,
                        testdata.get('show_time_remaining',False)
                    )
예제 #17
0
 def test_header_string(self):
     """
     Verify that string in the buildspec get correctly
     placed into the UI.
     """
     parser = ArgumentParser(description='Foobar')
     with instrumentGooey(parser,
                          program_name='BaZzEr') as (app, frame, gapp):
         self.assertEqual(
             frame.FindWindowByName("header_title").GetLabel(), 'BaZzEr')
         self.assertEqual(
             frame.FindWindowByName("header_subtitle").GetLabel(), 'Foobar')
예제 #18
0
    def test_initial_selection_options(self):
        """
        Ensure that the initial_selection GooeyOption behaves as expected.
        """
        # each pair in the below datastructure represents input/output
        # First position: kwargs which will be supplied to the parser
        # Second position: expected indices which buttons/widgets should be enabled/disabled
        testCases = [
            [{'required': True, 'gooey_options': {}},
             {'selected': None, 'enabled': [], 'disabled': [0, 1]}],

            # Issue #517 - initial section with required=True was not enabling
            # the inner widget
            [{'required': True, 'gooey_options': {"initial_selection": 0}},
             {'selected': 0, 'enabled': [0], 'disabled': [1]}],

            [{'required': True, 'gooey_options': {"initial_selection": 1}},
             {'selected': 1, 'enabled': [1], 'disabled': [0]}],

            [{'required': False, 'gooey_options': {}},
             {'selected': None, 'enabled': [], 'disabled': [0, 1]}],

            [{'required': False, 'gooey_options': {"initial_selection": 0}},
             {'selected': 0, 'enabled': [0], 'disabled': [1]}],

            [{'required': False, 'gooey_options': {"initial_selection": 1}},
             {'selected': 1, 'enabled': [1], 'disabled': [0]}],
        ]
        for options, expected in testCases:
            parser = self.mutext_group(options)
            with self.subTest(options):
                with instrumentGooey(parser) as (app, frame, gapp):
                    radioGroup = gapp.getActiveConfig().reifiedWidgets[0]

                    # verify that the checkboxes themselves are correct
                    if expected['selected'] is not None:
                        self.assertEqual(
                            radioGroup.selected,
                            radioGroup.radioButtons[expected['selected']])
                    else:
                        self.assertEqual(radioGroup.selected, None)

                    # verify the widgets contained in the radio group
                    # are in the correct state
                    for enabled in expected['enabled']:
                        # The widget contained within the group should be enabled
                        self.assertTrue(radioGroup.widgets[enabled].IsEnabled())

                    # make sure all widgets other than the selected
                    # are disabled
                    for enabled in expected['disabled']:
                        self.assertFalse(radioGroup.widgets[enabled].IsEnabled())
예제 #19
0
    def testOnCloseShutsDownActiveClients(self, mockModal):
        """
        Issue 592: Closing the UI should clean up any actively running programs
        """
        parser = self.basicParser()
        with instrumentGooey(parser) as (app, gapp):
            gapp.clientRunner = MagicMock()
            gapp.destroyGooey = MagicMock()
            # mocking that the user clicks "yes shut down" in the warning modal
            mockModal.return_value = True
            gapp.onClose()

            mockModal.assert_called()
            gapp.destroyGooey.assert_called()
예제 #20
0
    def test_time_remaining_visibility(self):
        for testdata in self.testcases():
            with self.subTest(testdata):
                with instrumentGooey(self.make_parser(),
                                     timing_options=testdata) as (app, frame,
                                                                  gapp):

                    gapp.set_state(s.consoleScreen(identity, gapp.state))
                    app: wx.App = app
                    wx.CallLater(1, app.ExitMainLoop)
                    app.MainLoop()

                    self.assertEqual(
                        frame.FindWindowByName('timing').Shown,
                        testdata.get('show_time_remaining', False))
예제 #21
0
    def test_time_remaining_visibility_on_complete(self):
        for testdata in self.testcases():
            with self.subTest(testdata):
                with instrumentGooey(self.make_parser(), timing_options=testdata) as (app, gooeyApp):

                    gooeyApp.showComplete()
                    footer = gooeyApp.footer


                    if not testdata.get('show_time_remaining') and testdata:
                        self.assertEqual(
                            footer.time_remaining_text.Shown,
                            testdata.get('hide_time_remaining_on_complete',True)
                        )
                    else:
                        return True
예제 #22
0
 def testInitialValue(self):
     cases = [
         # `initial` should supersede `default`
         {
             'inputs': {
                 'default': 1,
                 'gooey_options': {
                     'initial_value': 3
                 }
             },
             'expect': '3'
         },
         {
             'inputs': {
                 'gooey_options': {
                     'initial_value': 1
                 }
             },
             'expect': '1'
         },
         {
             'inputs': {
                 'default': 2,
                 'gooey_options': {}
             },
             'expect': '2'
         },
         {
             'inputs': {
                 'default': 1
             },
             'expect': '1'
         },
         {
             'inputs': {},
             'expect': None
         }
     ]
     for case in cases:
         with self.subTest(case):
             parser = self.makeParser(**case['inputs'])
             with instrumentGooey(parser) as (app, frame, gapp):
                 widget = gapp.getActiveConfig().reifiedWidgets[0]
                 self.assertEqual(widget.getValue()['rawValue'],
                                  case['expect'])
예제 #23
0
    def test_header_visibility(self):
        """
        Test that the title and subtitle components correctly show/hide
        based on config settings.

        Verifying Issue #497
        """
        for testdata in self.testcases():
            with self.subTest(testdata):
                with instrumentGooey(self.make_parser(),
                                     **testdata) as (app, gooeyApp):
                    header = gooeyApp.header

                    self.assertEqual(header._header.IsShown(),
                                     testdata.get('header_show_title', True))

                    self.assertEqual(
                        header._subheader.IsShown(),
                        testdata.get('header_show_subtitle', True))
예제 #24
0
    def test_time_remaining_visibility_on_complete(self):
        for testdata in self.testcases():
            with self.subTest(testdata):
                with instrumentGooey(self.make_parser(),
                                     timing_options=testdata) as (app, frame,
                                                                  gapp):

                    gapp.set_state(s.successScreen(identity, gapp.state))
                    app: wx.App = app
                    wx.CallLater(1, app.ExitMainLoop)
                    app.MainLoop()

                    if not testdata.get('show_time_remaining') and testdata:
                        self.assertEqual(
                            frame.FindWindowByName('timing').Shown,
                            testdata.get('hide_time_remaining_on_complete',
                                         True))
                    else:
                        return True
예제 #25
0
    def test_header_visibility(self):
        """
        Test that the title and subtitle components correctly show/hide
        based on config settings.

        Verifying Issue #497
        """
        for testdata in self.testcases():
            with self.subTest(testdata):
                with instrumentGooey(self.make_parser(),
                                     **testdata) as (app, frame, gapp):
                    frame: wx.Frame = frame

                    self.assertEqual(
                        frame.FindWindowByName("header_title").IsShown(),
                        testdata.get('header_show_title', True))

                    self.assertEqual(
                        frame.FindWindowByName("header_subtitle").IsShown(),
                        testdata.get('header_show_subtitle', True))
예제 #26
0
    def test_dropdown_behavior(self, mock):
        """
        Testing that:
            - default values are used as the initial selection (when present)
            - Initial selection defaults to placeholder when no defaults supplied
            - selection is preserved (when possible) across dynamic updates
        """
        testcases = [
            # tuples of [choices, default, initalSelection, dynamicUpdate, expectedFinalSelection]
            [['1', '2'], None, 'Select Option', ['1', '2', '3'],
             'Select Option'],
            [['1', '2'], '2', '2', ['1', '2', '3'], '2'],
            [['1', '2'], '1', '1', ['1', '2', '3'], '1'],
            # dynamic updates removed our selected value; defaults back to placeholder
            [['1', '2'], '2', '2', ['1', '3'], 'Select Option'],
            # TODO: this test case is currently passing wrong data for the dynamic
            # TODO: update due to a bug where Gooey doesn't apply the same ingestion
            # TODO: rules for data received dynamically as it does for parsers.
            # TODO: In short, Gooey should be able to handle a list of bools [True, False]
            # TODO: from dynamics just like it does in parser land. It doesn't currently
            # TODO: do this, so I'm manually casting it to strings for now.
            [[True, False], True, 'True', ['True', 'False'], 'True']
        ]

        for choices, default, initalSelection, dynamicUpdate, expectedFinalSelection in testcases:
            parser = self.makeParser(choices=choices, default=default)
            with instrumentGooey(parser) as (app, gooeyApp):
                dropdown = gooeyApp.configs[0].reifiedWidgets[0]
                # ensure that default values (when supplied) are selected in the UI
                self.assertEqual(dropdown.widget.GetValue(), initalSelection)
                # fire a dynamic update with the mock values
                mock.return_value = {'--dropdown': dynamicUpdate}
                gooeyApp.fetchExternalUpdates()
                # the values in the UI now reflect those returned from the update
                # note: we're appending the ['select option'] bit here as it gets automatically added
                # in the UI.
                expectedValues = ['Select Option'] + dynamicUpdate
                self.assertEqual(dropdown.widget.GetItems(), expectedValues)
                # and our selection is what we expect
                self.assertEqual(dropdown.widget.GetValue(),
                                 expectedFinalSelection)
예제 #27
0
 def testGooeyOptions(self):
     cases = [{
         'widget': 'DecimalField',
         'gooey_options': {
             'min': -100,
             'max': 1234,
             'increment': 1.240
         }
     }, {
         'widget': 'DecimalField',
         'gooey_options': {
             'min': 1234,
             'max': 3456,
             'increment': 2.2
         }
     }, {
         'widget': 'IntegerField',
         'gooey_options': {
             'min': -100,
             'max': 1234
         }
     }, {
         'widget': 'IntegerField',
         'gooey_options': {
             'min': 1234,
             'max': 3456
         }
     }]
     using = {
         'min': lambda widget: widget.GetMin(),
         'max': lambda widget: widget.GetMax(),
         'increment': lambda widget: widget.GetIncrement(),
     }
     for case in cases:
         with self.subTest(case):
             parser = self.makeParser(**case)
             with instrumentGooey(parser) as (app, gooeyApp):
                 wxWidget = gooeyApp.configs[0].reifiedWidgets[0].widget
                 for option, value in case['gooey_options'].items():
                     self.assertEqual(using[option](wxWidget), value)
예제 #28
0
    def testProgressBarHiddenWhenDisabled(self):
        options = [{
            'disable_progress_bar_animation': True
        }, {
            'disable_progress_bar_animation': False
        }, {}]
        for kwargs in options:
            parser = self.basicParser()
            with instrumentGooey(parser, **kwargs) as (app, frame, gapp):
                mockClientRunner = MagicMock()
                frame.clientRunner = mockClientRunner

                # transition's Gooey to the running state using the now mocked processor.
                # so that we can make assertions about the visibility of footer buttons
                gapp.onStart()

                # the progress bar flag is awkwardly inverted (is_disabled, rather than
                # is_enabled). Thus inverting the expectation here. When disabled is true,
                # shown should be False,
                expect_shown = not kwargs.get('disable_progress_bar_animation',
                                              False)
                self.assertEqual(gapp.state['progress']['show'], expect_shown)
예제 #29
0
    def test_validate_form(self):
        """
        Testing the major validation cases we support.
        """
        writer = MagicMock()
        exit = MagicMock()
        monkey_patch = control.validate_form(gooey_params(),
                                             write=writer,
                                             exit=exit)
        ArgumentParser.original_parse_args = ArgumentParser.parse_args
        ArgumentParser.parse_args = monkey_patch

        parser = GooeyParser()
        # examples:
        # ERROR: mismatched builtin type
        parser.add_argument('a',
                            type=int,
                            gooey_options={'initial_value': 'not-an-int'})
        # ERROR: mismatched custom type
        parser.add_argument('b',
                            type=custom_type,
                            gooey_options={'initial_value': 'not-a-float'})
        # ERROR: missing required positional arg
        parser.add_argument('c')
        # ERROR: missing required 'optional' arg
        parser.add_argument('--oc', required=True)
        # VALID: This is one of the bizarre cases which are possible
        # but don't make much sense. It should pass through as valid
        # because there's no way for us to send a 'not present optional value'
        parser.add_argument('--bo', action='store_true', required=True)
        # ERROR: a required mutex group, with no args supplied.
        # Should flag all as missing.
        group = parser.add_mutually_exclusive_group(required=True)
        group.add_argument('--gp1-a', type=str)
        group.add_argument('--gp1-b', type=str)

        # ERROR: required mutex group with a default option but nothing
        # selected will still fail
        group2 = parser.add_mutually_exclusive_group(required=True)
        group2.add_argument('--gp2-a', type=str)
        group2.add_argument('--gp2-b', type=str, default='Heeeeyyyyy')

        # VALID: now, same as above, but now the option is actually enabled via
        # the initial selection. No error.
        group3 = parser.add_mutually_exclusive_group(
            required=True, gooey_options={'initial_selection': 1})
        group3.add_argument('--gp3-a', type=str)
        group3.add_argument('--gp3-b', type=str, default='Heeeeyyyyy')
        # VALID: optional mutex.
        group4 = parser.add_mutually_exclusive_group()
        group4.add_argument('--gp4-a', type=str)
        group4.add_argument('--gp4-b', type=str)
        # VALID: arg present and type satisfied
        parser.add_argument('ga',
                            type=str,
                            gooey_options={'initial_value': 'whatever'})
        # VALID: arg present and custom type satisfied
        parser.add_argument('gb',
                            type=custom_type,
                            gooey_options={'initial_value': '1234'})
        # VALID: optional
        parser.add_argument('--gc')

        # now we're adding the same
        with instrumentGooey(parser, target='test') as (app, frame, gapp):
            # we start off with no errors
            self.assertFalse(s.has_errors(gapp.fullState()))

            # now we feed our form-validation
            cmd = s.buildFormValidationCmd(gapp.fullState())
            asdf = shlex.split(cmd)[1:]
            parser.parse_args(shlex.split(cmd)[1:])
            assert writer.called
            assert exit.called

        result = deserialize_inbound(writer.call_args[0][0].encode('utf-8'),
                                     'utf-8')
        # Host->Gooey communication is all done over the PublicGooeyState schema
        # as such, we coarsely validate it's shape here
        validate_public_state(result)

        # manually merging the two states back together
        nextState = s.mergeExternalState(gapp.fullState(), result)
        # and now we find that we have errors!
        self.assertTrue(s.has_errors(nextState))
        items = s.activeFormState(nextState)
        self.assertIn('invalid literal', get_by_id(items, 'a')['error'])
        self.assertIn('KABOOM!', get_by_id(items, 'b')['error'])
        self.assertIn('required', get_by_id(items, 'c')['error'])
        self.assertIn('required', get_by_id(items, 'oc')['error'])
        for item in get_by_id(items, 'group_gp1_a_gp1_b')['options']:
            self.assertIsNotNone(item['error'])
        for item in get_by_id(items, 'group_gp2_a_gp2_b')['options']:
            self.assertIsNotNone(item['error'])

        for item in get_by_id(items, 'group_gp3_a_gp3_b')['options']:
            self.assertIsNone(item['error'])
        # should be None, since this one was entirely optional
        for item in get_by_id(items, 'group_gp4_a_gp4_b')['options']:
            self.assertIsNone(item['error'])
        self.assertIsNone(get_by_id(items, 'bo')['error'])
        self.assertIsNone(get_by_id(items, 'ga')['error'])
        self.assertIsNone(get_by_id(items, 'gb')['error'])
        self.assertIsNone(get_by_id(items, 'gc')['error'])
예제 #30
0
    def test_subparsers(self):
        """
        Making sure that subparsers are handled correctly and
        all validations still work as expected.
        """
        writer = MagicMock()
        exit = MagicMock()
        monkey_patch = control.validate_form(gooey_params(),
                                             write=writer,
                                             exit=exit)
        ArgumentParser.original_parse_args = ArgumentParser.parse_args
        ArgumentParser.parse_args = monkey_patch

        def build_parser():
            # we build a new parser for each subtest
            # since we monkey patch the hell out of it
            # each time
            parser = GooeyParser()
            subs = parser.add_subparsers()
            foo = subs.add_parser('foo')
            foo.add_argument('a')
            foo.add_argument('b')
            foo.add_argument('p')

            bar = subs.add_parser('bar')
            bar.add_argument('a')
            bar.add_argument('b')
            bar.add_argument('z')
            return parser

        parser = build_parser()
        with instrumentGooey(parser, target='test') as (app, frame, gapp):
            with self.subTest('first subparser'):
                # we start off with no errors
                self.assertFalse(s.has_errors(gapp.fullState()))

                cmd = s.buildFormValidationCmd(gapp.fullState())
                parser.parse_args(shlex.split(cmd)[1:])
                assert writer.called
                assert exit.called

                result = deserialize_inbound(
                    writer.call_args[0][0].encode('utf-8'), 'utf-8')
                nextState = s.mergeExternalState(gapp.fullState(), result)
                # by default, the subparser defined first, 'foo', is selected.
                self.assertIn('foo', nextState['forms'])
                # and we should find its attributes
                expected = {'a', 'b', 'p'}
                actual = {x['id'] for x in nextState['forms']['foo']}
                self.assertEqual(expected, actual)

        parser = build_parser()
        with instrumentGooey(parser, target='test') as (app, frame, gapp):
            with self.subTest('Second subparser'):
                # mocking a 'selection change' event to select
                # the second subparser
                event = MagicMock()
                event.Selection = 1
                gapp.handleSelectAction(event)

                # Flushing our events by running the main loop
                wx.CallLater(1, app.ExitMainLoop)
                app.MainLoop()

                cmd = s.buildFormValidationCmd(gapp.fullState())
                parser.parse_args(shlex.split(cmd)[1:])
                assert writer.called
                assert exit.called

                result = deserialize_inbound(
                    writer.call_args[0][0].encode('utf-8'), 'utf-8')
                nextState = s.mergeExternalState(gapp.fullState(), result)
                # Now our second subparer, 'bar', should be present.
                self.assertIn('bar', nextState['forms'])
                # and we should find its attributes
                expected = {'a', 'b', 'z'}
                actual = {x['id'] for x in nextState['forms']['bar']}
                self.assertEqual(expected, actual)