diff --git a/gooey/tests/integration/README.md b/gooey/tests/integration/README.md new file mode 100644 index 0000000..498f7bd --- /dev/null +++ b/gooey/tests/integration/README.md @@ -0,0 +1 @@ +These integration tests must be run one at a time. I can't figure out how to clear the wx context between runs and Unittest doesn't allow process isolation.. \ No newline at end of file diff --git a/gooey/tests/integration/__init__.py b/gooey/tests/integration/__init__.py new file mode 100644 index 0000000..ff52438 --- /dev/null +++ b/gooey/tests/integration/__init__.py @@ -0,0 +1,7 @@ +""" +Integration tests that exercise Gooey's various run modes + +WX Python needs to control the main thread. So, in order to simulate a user +running through the system, we have to execute the actual assertions in a +different thread +""" \ No newline at end of file diff --git a/gooey/tests/integration/integ_autostart.py b/gooey/tests/integration/integ_autostart.py new file mode 100644 index 0000000..5fbb339 --- /dev/null +++ b/gooey/tests/integration/integ_autostart.py @@ -0,0 +1,68 @@ +import time +import unittest + +from gooey.gui.lang.i18n import _ +from tests.integration.programs import auto_start as auto_start_module + + +class TestGooeyIntegration(unittest.TestCase): + + def test__gooeyAutoStart(self): + """Verifies that issue #201 doesn't regress and auto_start skips the config + screen and hops right into the client's program""" + from gooey.tests.integration import runner + runner.run_integration(auto_start_module, self.verifyAutoStart, auto_start=True) + + def verifyAutoStart(self, app, buildSpec): + """ + When the auto_start flag == True Gooey should skip the + configuration screen + """ + time.sleep(1) + try: + # Gooey should NOT be showing the name/description headers + # present on the config page + title = app.TopWindow.header._header.GetLabel() + subtitle = app.TopWindow.header._subheader.GetLabel() + self.assertNotEqual(title, buildSpec['program_name']) + self.assertNotEqual(subtitle, buildSpec['program_description']) + + # Gooey should be showing the console messages straight away + # without manually starting the program + title = app.TopWindow.header._header.GetLabel() + subtitle = app.TopWindow.header._subheader.GetLabel() + self.assertEqual(title,_("running_title")) + self.assertEqual(subtitle, _('running_msg')) + + # Wait for Gooey to swap the header to the final screen + while app.TopWindow.header._header.GetLabel() == _("running_title"): + time.sleep(.1) + + # verify that we've landed on the success screen + title = app.TopWindow.header._header.GetLabel() + subtitle = app.TopWindow.header._subheader.GetLabel() + self.assertEqual(title, _("finished_title")) + self.assertEqual(subtitle, _('finished_msg')) + + + # and that output was actually written to the console + self.assertIn("Success", app.TopWindow.console.textbox.GetValue()) + except: + app.TopWindow.Destroy() + raise + else: + import wx + wx.CallAfter(app.TopWindow.Destroy) + return None + + + + +if __name__ == '__main__': + unittest.main() + + + + + + diff --git a/gooey/tests/integration/integ_subparser_demo.py b/gooey/tests/integration/integ_subparser_demo.py new file mode 100644 index 0000000..b78bf02 --- /dev/null +++ b/gooey/tests/integration/integ_subparser_demo.py @@ -0,0 +1,62 @@ +import wx +import time +import unittest + +from gooey.gui.lang.i18n import _ +from tests.integration.programs import \ + all_widgets_subparser as all_widgets_subparser_module + + +class TestGooeyIntegration11(unittest.TestCase): + + def test_gooeySubparserMode(self): + """ Tests the happy path through the subparser run mode of Gooey """ + from gooey.tests.integration import runner + runner.run_integration(all_widgets_subparser_module, self.gooeySanityTest) + + def gooeySanityTest(self, app, buildSpec): + try: + # Check out header is present and showing data + title = app.TopWindow.header._header.GetLabel() + subtitle = app.TopWindow.header._subheader.GetLabel() + self.assertEqual(title, buildSpec['program_name']) + self.assertEqual(subtitle, buildSpec['program_description']) + + # switch to the run screen + app.TopWindow.onStart() + + # Should find the expected test in the header + title = app.TopWindow.header._header.GetLabel() + subtitle = app.TopWindow.header._subheader.GetLabel() + self.assertEqual(title,_("running_title")) + self.assertEqual(subtitle, _('running_msg')) + + # Wait for Gooey to swap the header to the final screen + while app.TopWindow.header._header.GetLabel() == _("running_title"): + time.sleep(.1) + + # verify that we've landed on the success screen + title = app.TopWindow.header._header.GetLabel() + subtitle = app.TopWindow.header._subheader.GetLabel() + self.assertEqual(title, _("finished_title")) + self.assertEqual(subtitle, _('finished_msg')) + + # and that output was actually written to the console + self.assertIn("Success", app.TopWindow.console.textbox.GetValue()) + time.sleep(1) + except: + wx.CallAfter(app.TopWindow.Destroy) + raise + else: + wx.CallAfter(app.TopWindow.Destroy) + return None + + +if __name__ == '__main__': + unittest.main() + + + + + + diff --git a/gooey/tests/integration/integ_validations.py b/gooey/tests/integration/integ_validations.py new file mode 100644 index 0000000..f20721c --- /dev/null +++ b/gooey/tests/integration/integ_validations.py @@ -0,0 +1,48 @@ +import time +import unittest + +from tests.integration.programs import validations as validations_module + + +class TestGooeyIntegration(unittest.TestCase): + """ + A few quick integration tests that exercise Gooey's various run modes + + WX Python needs to control the main thread. So, in order to simulate a user + running through the system, we have to execute the actual assertions in a + different thread + """ + + def test__gooeyValidation(self): + """Verifies that custom validation routines supplied via gooey_options prevents + the user from advancing past the configuration page when they fail""" + from gooey.tests.integration import runner + runner.run_integration(validations_module, self.verifyValidators) + + + def verifyValidators(self, app, buildSpec): + time.sleep(1) + try: + app.TopWindow.onStart() + title = app.TopWindow.header._header.GetLabel() + subtitle = app.TopWindow.header._subheader.GetLabel() + self.assertNotEqual(title, buildSpec['program_name']) + self.assertNotEqual(subtitle, buildSpec['program_description']) + except: + app.TopWindow.Destroy() + raise + else: + import wx + wx.CallAfter(app.TopWindow.Destroy) + return None + + + +if __name__ == '__main__': + unittest.main() + + + + + + diff --git a/gooey/tests/integration/integ_widget_demo.py b/gooey/tests/integration/integ_widget_demo.py new file mode 100644 index 0000000..7033213 --- /dev/null +++ b/gooey/tests/integration/integ_widget_demo.py @@ -0,0 +1,64 @@ +import time +import unittest + +import wx +from gooey.gui.lang.i18n import _ +from tests.integration.programs import all_widgets as all_widgets_module + + +class TestGooeyIntegration99(unittest.TestCase): + + + def test_gooeyNormalRun(self): + """ Tests the happy path through the default run mode of Gooey """ + from gooey.tests.integration import runner + runner.run_integration(all_widgets_module, self.gooeySanityTest) + + + def gooeySanityTest(self, app, buildSpec): + time.sleep(1) + try: + # Check out header is present and showing data + title = app.TopWindow.header._header.GetLabel() + subtitle = app.TopWindow.header._subheader.GetLabel() + self.assertEqual(title, buildSpec['program_name']) + self.assertEqual(subtitle, buildSpec['program_description']) + + # switch to the run screen + app.TopWindow.onStart() + + # Should find the expected test in the header + title = app.TopWindow.header._header.GetLabel() + subtitle = app.TopWindow.header._subheader.GetLabel() + self.assertEqual(title,_("running_title")) + self.assertEqual(subtitle, _('running_msg')) + + # Wait for Gooey to swap the header to the final screen + while app.TopWindow.header._header.GetLabel() == _("running_title"): + time.sleep(.1) + + # verify that we've landed on the success screen + title = app.TopWindow.header._header.GetLabel() + subtitle = app.TopWindow.header._subheader.GetLabel() + self.assertEqual(title, _("finished_title")) + self.assertEqual(subtitle, _('finished_msg')) + + # and that output was actually written to the console + self.assertIn("Success", app.TopWindow.console.textbox.GetValue()) + time.sleep(1) + except: + app.TopWindow.Destroy() + raise + else: + wx.CallAfter(app.TopWindow.Destroy) + return None + + +if __name__ == '__main__': + unittest.main() + + + + + + diff --git a/gooey/tests/integration/programs/__init__.py b/gooey/tests/integration/programs/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/gooey/tests/integration/programs/all_widgets.py b/gooey/tests/integration/programs/all_widgets.py new file mode 100644 index 0000000..ded56e7 --- /dev/null +++ b/gooey/tests/integration/programs/all_widgets.py @@ -0,0 +1,94 @@ +from gooey import Gooey +from gooey import GooeyParser + + +@Gooey( + sidebar_title="Your Custom Title", + show_sidebar=True, + dump_build_config=True, + show_success_modal=False, + force_stop_is_error=False, + language='chinese' +) +def main(): + dest_vars = [ + 'textfield', + 'textarea', + 'password', + 'commandfield', + 'dropdown', + 'listboxie', + 'counter', + 'overwrite', + 'mutextwo', + 'filechooser', + 'filesaver', + 'dirchooser', + 'datechooser' + + ] + + parser = get_parser() + args = parser.parse_args() + import time + for i in dest_vars: + assert getattr(args, i) is not None + print("Success") + + + +def get_parser(): + desc = "Example application to show Gooey's various widgets" + parser = GooeyParser(description=desc, add_help=False) + + parser.add_argument('--textfield', default=2, widget="TextField") + parser.add_argument('--textarea', default="oneline twoline", widget='Textarea') + parser.add_argument('--password', default="hunter42", widget='PasswordField') + parser.add_argument('--commandfield', default="cmdr", widget='CommandField') + parser.add_argument('--dropdown', choices=["one", "two"], default="two", widget='Dropdown') + parser.add_argument('--listboxie', + nargs='+', + default=['Option three', 'Option four'], + choices=['Option one', 'Option two', 'Option three', + 'Option four'], + widget='Listbox', + gooey_options={ + 'height': 300, + 'validate': '', + 'heading_color': '', + 'text_color': '', + 'hide_heading': True, + 'hide_text': True, + }) + parser.add_argument('-c', '--counter', default=3, action='count', widget='Counter') + parser.add_argument("-o", "--overwrite", action="store_true", default=True, widget='CheckBox') + parser.add_argument("-bo", "--blockcheckbox", action="store_true", default=True, widget='BlockCheckbox') + + ### Mutex Group ### + verbosity = parser.add_mutually_exclusive_group( + required=True, + gooey_options={ + 'initial_selection': 1 + } + ) + verbosity.add_argument( + '--mutexone', + default=True, + action='store_true', + help="Show more details") + + verbosity.add_argument( + '--mutextwo', + default='mut-2', + widget='TextField') + + parser.add_argument("--filechooser", default="fc-value", widget='FileChooser') + parser.add_argument("--filesaver", default="fs-value", widget='FileSaver') + parser.add_argument("--dirchooser", default="dc-value", widget='DirChooser') + parser.add_argument("--datechooser", default="2015-01-01", widget='DateChooser') + parser.add_argument("--multidirchooser", default="2015-01-01", widget='MultiDirChooser') + + return parser + +if __name__ == '__main__': + main() diff --git a/gooey/tests/integration/programs/all_widgets_subparser.py b/gooey/tests/integration/programs/all_widgets_subparser.py new file mode 100644 index 0000000..24f4e34 --- /dev/null +++ b/gooey/tests/integration/programs/all_widgets_subparser.py @@ -0,0 +1,147 @@ +""" +Example program to demonstrate Gooey's presentation of subparsers +""" + +from gooey import Gooey, GooeyParser + +@Gooey( + optional_cols=2, + program_name="Subparser Demo", + dump_build_config=True, + show_success_modal=False) +def main(): + dest_vars = [ + 'textfield', + 'textarea', + 'password', + 'commandfield', + 'dropdown', + 'listboxie', + 'counter', + 'overwrite', + 'mutextwo', + 'filechooser', + 'filesaver', + 'dirchooser', + 'datechooser' + ] + parser = get_parser() + args = parser.parse_args() + import time + time.sleep(.6) + for i in dest_vars: + assert getattr(args, i) is not None + print("Success") + + +def get_parser(): + parser = GooeyParser() + subs = parser.add_subparsers(help='commands', dest='command') + + parser_one = subs.add_parser('parser1', prog="Parser 1") + parser_one.add_argument('--textfield', default=2, widget="TextField") + parser_one.add_argument('--textarea', default="oneline twoline", + widget='Textarea') + parser_one.add_argument('--password', default="hunter42", + widget='PasswordField') + parser_one.add_argument('--commandfield', default="cmdr", + widget='CommandField') + parser_one.add_argument('--dropdown', + choices=["one", "two"], default="two", + widget='Dropdown') + parser_one.add_argument('--listboxie', + nargs='+', + default=['Option three', 'Option four'], + choices=['Option one', 'Option two', 'Option three', + 'Option four'], + widget='Listbox', + gooey_options={ + 'height': 300, + 'validate': '', + 'heading_color': '', + 'text_color': '', + 'hide_heading': True, + 'hide_text': True, + } + ) + parser_one.add_argument('-c', '--counter', default=3, action='count', + widget='Counter') + # + parser_one.add_argument("-o", "--overwrite", action="store_true", + default=True, + widget='CheckBox') + + ### Mutex Group ### + verbosity = parser_one.add_mutually_exclusive_group( + required=True, + gooey_options={ + 'initial_selection': 1 + } + ) + verbosity.add_argument( + '--mutexone', + default=True, + action='store_true', + help="Show more details") + + verbosity.add_argument( + '--mutextwo', + default='mut-2', + widget='TextField') + + parser_one.add_argument("--filechooser", default="fc-value", widget='FileChooser') + parser_one.add_argument("--filesaver", default="fs-value", widget='FileSaver') + parser_one.add_argument("--dirchooser", default="dc-value", widget='DirChooser') + parser_one.add_argument("--datechooser", default="2015-01-01", widget='DateChooser') + + parser_two = subs.add_parser('parser2', prog="parser 2") + parser_two.add_argument('--textfield', default=2, widget="TextField") + parser_two.add_argument('--textarea', default="oneline twoline", widget='Textarea') + parser_two.add_argument('--password', default="hunter42", widget='PasswordField') + parser_two.add_argument('--commandfield', default="cmdr", widget='CommandField') + parser_two.add_argument('--dropdown', choices=["one", "two"], default="two", widget='Dropdown') + parser_two.add_argument('--listboxie', + nargs='+', + default=['Option three', 'Option four'], + choices=['Option one', 'Option two', 'Option three', + 'Option four'], + widget='Listbox', + gooey_options={ + 'height': 300, + 'validate': '', + 'heading_color': '', + 'text_color': '', + 'hide_heading': True, + 'hide_text': True, + } + ) + parser_two.add_argument('-c', '--counter', default=3, action='count', widget='Counter') + parser_two.add_argument("-o", "--overwrite", action="store_true", default=True, widget='CheckBox') + + ### Mutex Group ### + verbosity = parser_two.add_mutually_exclusive_group( + required=True, + gooey_options={ + 'initial_selection': 1 + } + ) + verbosity.add_argument( + '--mutexone', + default=True, + action='store_true', + help="Show more details") + + verbosity.add_argument( + '--mutextwo', + default='mut-2', + widget='TextField') + + parser_two.add_argument("--filechooser", default="fc-value", widget='FileChooser') + parser_two.add_argument("--filesaver", default="fs-value", widget='FileSaver') + parser_two.add_argument("--dirchooser", default="dc-value", widget='DirChooser') + parser_two.add_argument("--datechooser", default="2015-01-01", widget='DateChooser') + + return parser + +if __name__ == '__main__': + main() diff --git a/gooey/tests/integration/programs/auto_start.py b/gooey/tests/integration/programs/auto_start.py new file mode 100644 index 0000000..d1cd18a --- /dev/null +++ b/gooey/tests/integration/programs/auto_start.py @@ -0,0 +1,27 @@ +import sys + +from gooey import Gooey +from gooey import GooeyParser +from argparse import ArgumentParser + +@Gooey( + progress_regex=r"^progress: (-?\d+)%$", + disable_progress_bar_animation=True, + dump_build_config=True, + show_success_modal=False, + auto_start=True +) +def main(): + parser = get_parser() + _ = parser.parse_args(sys.argv[1:]) + import time + time.sleep(2) + print('Success') + + + +def get_parser(): + return GooeyParser(prog="example_progress_bar_1") + +if __name__ == '__main__': + main() diff --git a/gooey/tests/integration/programs/gooey_config.json b/gooey/tests/integration/programs/gooey_config.json new file mode 100644 index 0000000..7ba0504 --- /dev/null +++ b/gooey/tests/integration/programs/gooey_config.json @@ -0,0 +1,645 @@ +{ + "language": "chinese", + "target": "\"C:\\Users\\Chris\\Dropbox\\pretty_gui\\Gooey\\venv3\\Scripts\\python.exe\" -u \"C:/Users/Chris/Dropbox/pretty_gui/Gooey/gooey/tests/integration/programs/all_widgets.py\"", + "program_name": "all_widgets", + "program_description": "Example application to show Gooey's various widgets", + "sidebar_title": "Your Custom Title", + "default_size": [ + 610, + 530 + ], + "auto_start": false, + "show_advanced": true, + "run_validators": true, + "encoding": "utf-8", + "show_stop_warning": true, + "show_success_modal": false, + "force_stop_is_error": false, + "poll_external_updates": false, + "return_to_config": false, + "show_restart_button": true, + "requires_shell": true, + "menu": [], + "clear_before_run": false, + "use_legacy_titles": true, + "num_required_cols": 2, + "num_optional_cols": 2, + "manual_start": false, + "monospace_display": false, + "image_dir": "::gooey/default", + "language_dir": "C:\\Users\\Chris\\Dropbox\\pretty_gui\\Gooey\\gooey\\languages", + "progress_regex": null, + "progress_expr": null, + "hide_progress_msg": false, + "disable_progress_bar_animation": false, + "disable_stop_button": false, + "navigation": "SIDEBAR", + "show_sidebar": true, + "tabbed_groups": false, + "group_by_type": true, + "body_bg_color": "#f0f0f0", + "header_bg_color": "#ffffff", + "header_height": 80, + "header_show_title": true, + "header_show_subtitle": true, + "header_image_center": false, + "footer_bg_color": "#f0f0f0", + "sidebar_bg_color": "#f2f2f2", + "terminal_panel_color": "#F0F0F0", + "terminal_font_color": "#000000", + "terminal_font_family": null, + "terminal_font_weight": null, + "terminal_font_size": null, + "richtext_controls": false, + "error_color": "#ea7878", + "layout": "standard", + "widgets": { + "all_widgets.py": { + "command": "::gooey/default", + "name": "all_widgets.py", + "help": null, + "description": "", + "contents": [ + { + "name": "Optional Arguments", + "items": [ + { + "id": "--textfield", + "type": "TextField", + "cli_type": "optional", + "required": false, + "data": { + "display_name": "textfield", + "help": null, + "required": false, + "nargs": "", + "commands": [ + "--textfield" + ], + "choices": [], + "default": 2, + "dest": "textfield" + }, + "options": { + "error_color": "#ea7878", + "label_color": "#000000", + "help_color": "#363636", + "full_width": false, + "validator": { + "test": "True", + "message": "" + }, + "external_validator": { + "cmd": "" + } + } + }, + { + "id": "--textarea", + "type": "Textarea", + "cli_type": "optional", + "required": false, + "data": { + "display_name": "textarea", + "help": null, + "required": false, + "nargs": "", + "commands": [ + "--textarea" + ], + "choices": [], + "default": "oneline twoline", + "dest": "textarea" + }, + "options": { + "error_color": "#ea7878", + "label_color": "#000000", + "help_color": "#363636", + "full_width": false, + "validator": { + "test": "True", + "message": "" + }, + "external_validator": { + "cmd": "" + } + } + }, + { + "id": "--password", + "type": "PasswordField", + "cli_type": "optional", + "required": false, + "data": { + "display_name": "password", + "help": null, + "required": false, + "nargs": "", + "commands": [ + "--password" + ], + "choices": [], + "default": "hunter42", + "dest": "password" + }, + "options": { + "error_color": "#ea7878", + "label_color": "#000000", + "help_color": "#363636", + "full_width": false, + "validator": { + "test": "True", + "message": "" + }, + "external_validator": { + "cmd": "" + } + } + }, + { + "id": "--commandfield", + "type": "CommandField", + "cli_type": "optional", + "required": false, + "data": { + "display_name": "commandfield", + "help": null, + "required": false, + "nargs": "", + "commands": [ + "--commandfield" + ], + "choices": [], + "default": "cmdr", + "dest": "commandfield" + }, + "options": { + "error_color": "#ea7878", + "label_color": "#000000", + "help_color": "#363636", + "full_width": false, + "validator": { + "test": "True", + "message": "" + }, + "external_validator": { + "cmd": "" + } + } + }, + { + "id": "--dropdown", + "type": "Dropdown", + "cli_type": "optional", + "required": false, + "data": { + "display_name": "dropdown", + "help": null, + "required": false, + "nargs": "", + "commands": [ + "--dropdown" + ], + "choices": [ + "one", + "two" + ], + "default": "two", + "dest": "dropdown" + }, + "options": { + "error_color": "#ea7878", + "label_color": "#000000", + "help_color": "#363636", + "full_width": false, + "validator": { + "test": "True", + "message": "" + }, + "external_validator": { + "cmd": "" + } + } + }, + { + "id": "--listboxie", + "type": "Listbox", + "cli_type": "optional", + "required": false, + "data": { + "display_name": "listboxie", + "help": null, + "required": false, + "nargs": "+", + "commands": [ + "--listboxie" + ], + "choices": [ + "Option one", + "Option two", + "Option three", + "Option four" + ], + "default": [ + "Option three", + "Option four" + ], + "dest": "listboxie" + }, + "options": { + "error_color": "#ea7878", + "label_color": "#000000", + "help_color": "#363636", + "full_width": false, + "validator": { + "test": "True", + "message": "" + }, + "external_validator": { + "cmd": "" + }, + "height": 300, + "validate": "", + "heading_color": "", + "text_color": "", + "hide_heading": true, + "hide_text": true + } + }, + { + "id": "-c", + "type": "Counter", + "cli_type": "optional", + "required": false, + "data": { + "display_name": "counter", + "help": null, + "required": false, + "nargs": "", + "commands": [ + "-c", + "--counter" + ], + "choices": [ + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + "10" + ], + "default": "3", + "dest": "counter" + }, + "options": { + "error_color": "#ea7878", + "label_color": "#000000", + "help_color": "#363636", + "full_width": false, + "validator": { + "test": "True", + "message": "" + }, + "external_validator": { + "cmd": "" + } + } + }, + { + "id": "-o", + "type": "CheckBox", + "cli_type": "optional", + "required": false, + "data": { + "display_name": "overwrite", + "help": null, + "required": false, + "nargs": "", + "commands": [ + "-o", + "--overwrite" + ], + "choices": [], + "default": true, + "dest": "overwrite" + }, + "options": { + "error_color": "#ea7878", + "label_color": "#000000", + "help_color": "#363636", + "full_width": false, + "validator": { + "test": "True", + "message": "" + }, + "external_validator": { + "cmd": "" + } + } + }, + { + "id": "-bo", + "type": "BlockCheckbox", + "cli_type": "optional", + "required": false, + "data": { + "display_name": "blockcheckbox", + "help": null, + "required": false, + "nargs": "", + "commands": [ + "-bo", + "--blockcheckbox" + ], + "choices": [], + "default": true, + "dest": "blockcheckbox" + }, + "options": { + "error_color": "#ea7878", + "label_color": "#000000", + "help_color": "#363636", + "full_width": false, + "validator": { + "test": "True", + "message": "" + }, + "external_validator": { + "cmd": "" + } + } + }, + { + "id": "94c2fd11-1925-4cc8-8634-44ef0ce07986", + "type": "RadioGroup", + "cli_type": "optional", + "group_name": "Choose Option", + "required": true, + "options": { + "error_color": "#ea7878", + "label_color": "#000000", + "help_color": "#363636", + "full_width": false, + "validator": { + "type": "local", + "test": "lambda x: True", + "message": "" + }, + "external_validator": { + "cmd": "" + }, + "initial_selection": 1 + }, + "data": { + "commands": [ + [ + "--mutexone" + ], + [ + "--mutextwo" + ] + ], + "widgets": [ + { + "id": "--mutexone", + "type": "CheckBox", + "cli_type": "optional", + "required": false, + "data": { + "display_name": "mutexone", + "help": "Show more details", + "required": false, + "nargs": "", + "commands": [ + "--mutexone" + ], + "choices": [], + "default": true, + "dest": "mutexone" + }, + "options": { + "error_color": "#ea7878", + "label_color": "#000000", + "help_color": "#363636", + "full_width": false, + "validator": { + "test": "True", + "message": "" + }, + "external_validator": { + "cmd": "" + } + } + }, + { + "id": "--mutextwo", + "type": "TextField", + "cli_type": "optional", + "required": false, + "data": { + "display_name": "mutextwo", + "help": null, + "required": false, + "nargs": "", + "commands": [ + "--mutextwo" + ], + "choices": [], + "default": "mut-2", + "dest": "mutextwo" + }, + "options": { + "error_color": "#ea7878", + "label_color": "#000000", + "help_color": "#363636", + "full_width": false, + "validator": { + "test": "True", + "message": "" + }, + "external_validator": { + "cmd": "" + } + } + } + ] + } + }, + { + "id": "--filechooser", + "type": "FileChooser", + "cli_type": "optional", + "required": false, + "data": { + "display_name": "filechooser", + "help": null, + "required": false, + "nargs": "", + "commands": [ + "--filechooser" + ], + "choices": [], + "default": "fc-value", + "dest": "filechooser" + }, + "options": { + "error_color": "#ea7878", + "label_color": "#000000", + "help_color": "#363636", + "full_width": false, + "validator": { + "test": "True", + "message": "" + }, + "external_validator": { + "cmd": "" + } + } + }, + { + "id": "--filesaver", + "type": "FileSaver", + "cli_type": "optional", + "required": false, + "data": { + "display_name": "filesaver", + "help": null, + "required": false, + "nargs": "", + "commands": [ + "--filesaver" + ], + "choices": [], + "default": "fs-value", + "dest": "filesaver" + }, + "options": { + "error_color": "#ea7878", + "label_color": "#000000", + "help_color": "#363636", + "full_width": false, + "validator": { + "test": "True", + "message": "" + }, + "external_validator": { + "cmd": "" + } + } + }, + { + "id": "--dirchooser", + "type": "DirChooser", + "cli_type": "optional", + "required": false, + "data": { + "display_name": "dirchooser", + "help": null, + "required": false, + "nargs": "", + "commands": [ + "--dirchooser" + ], + "choices": [], + "default": "dc-value", + "dest": "dirchooser" + }, + "options": { + "error_color": "#ea7878", + "label_color": "#000000", + "help_color": "#363636", + "full_width": false, + "validator": { + "test": "True", + "message": "" + }, + "external_validator": { + "cmd": "" + } + } + }, + { + "id": "--datechooser", + "type": "DateChooser", + "cli_type": "optional", + "required": false, + "data": { + "display_name": "datechooser", + "help": null, + "required": false, + "nargs": "", + "commands": [ + "--datechooser" + ], + "choices": [], + "default": "2015-01-01", + "dest": "datechooser" + }, + "options": { + "error_color": "#ea7878", + "label_color": "#000000", + "help_color": "#363636", + "full_width": false, + "validator": { + "test": "True", + "message": "" + }, + "external_validator": { + "cmd": "" + } + } + }, + { + "id": "--multidirchooser", + "type": "MultiDirChooser", + "cli_type": "optional", + "required": false, + "data": { + "display_name": "multidirchooser", + "help": null, + "required": false, + "nargs": "", + "commands": [ + "--multidirchooser" + ], + "choices": [], + "default": "2015-01-01", + "dest": "multidirchooser" + }, + "options": { + "error_color": "#ea7878", + "label_color": "#000000", + "help_color": "#363636", + "full_width": false, + "validator": { + "test": "True", + "message": "" + }, + "external_validator": { + "cmd": "" + } + } + } + ], + "groups": [], + "description": null, + "options": { + "label_color": "#000000", + "description_color": "#363636", + "legacy": { + "required_cols": 2, + "optional_cols": 2 + }, + "columns": 2, + "padding": 10, + "show_border": false + } + } + ] + } + } +} \ No newline at end of file diff --git a/gooey/tests/integration/programs/validations.py b/gooey/tests/integration/programs/validations.py new file mode 100644 index 0000000..5c1799f --- /dev/null +++ b/gooey/tests/integration/programs/validations.py @@ -0,0 +1,33 @@ +import time + +from gooey import Gooey +from gooey import GooeyParser + + +@Gooey( + sidebar_title="Your Custom Title", + show_sidebar=True, + show_success_modal=False, + force_stop_is_error=False, +) +def main(): + parser = get_parser() + args = parser.parse_args() + time.sleep(2) + print("Success") + + +def get_parser(): + """ + A simple parser with a single required argument and no default thus + ensuring that clicking the start button in the UI will throw + a validation error. + """ + desc = "Example application to show Gooey's various widgets" + parser = GooeyParser(description=desc, add_help=False) + parser.add_argument('--textfield', widget="TextField", required=True) + return parser + + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/gooey/tests/integration/runner.py b/gooey/tests/integration/runner.py new file mode 100644 index 0000000..63d0325 --- /dev/null +++ b/gooey/tests/integration/runner.py @@ -0,0 +1,46 @@ +import os +import time +from concurrent import futures + +from gooey.gui.util.freeze import getResourcePath +from gooey.python_bindings import config_generator +from gooey.util.functional import merge + + +def run_integration(module, assertionFunction, **kwargs): + """ + Integration test harness. + + WXPython is *super* finicky when it comes to integration tests. It needs + the main Python thread for its app loop, which means we have to integration + test on a separate thread. The causes further strangeness in how Unittest + and WXPython interact. In short, each test must be in its own module and + thus import its own wx instance, and be run in its own "space." + + So long as the above is satisfied, then integration tests can run reliably. + + """ + from gooey.gui import application + options = merge({ + 'image_dir': '::gooey/default', + 'language_dir': getResourcePath('languages'), + 'show_success_modal': False + }, kwargs) + module_path = os.path.abspath(module.__file__) + parser = module.get_parser() + build_spec = config_generator.create_from_parser(parser, module_path, **options) + + time.sleep(2) + app = application.build_app(build_spec=build_spec) + executor = futures.ThreadPoolExecutor(max_workers=1) + # executor runs in parallel and will submit a wx.Destroy request + # when done making its assertions + testResult = executor.submit(assertionFunction, app, build_spec) + # main loop blocks the main thread + app.MainLoop() + # .result() blocks as well while we wait for the thread to finish + # any waiting it may be doing. + testResult.result() + del app + + diff --git a/gooey/tests/test_integration.py b/gooey/tests/test_integration.py deleted file mode 100644 index b9e9923..0000000 --- a/gooey/tests/test_integration.py +++ /dev/null @@ -1,174 +0,0 @@ -import json -import sys -import time -import unittest -from concurrent import futures -from os import path - -from gooey.gui import application -from gooey.gui.lang.i18n import _ -from gooey.gui.util.freeze import getResourcePath -from gooey.gui.util.quoting import quote - - -class TestGooeyIntegration(unittest.TestCase): - """ - A few quick integration tests that exercise Gooey's various run modes - - WX Python needs to control the main thread. So, in order to simulate a user - running through the system, we have to execute the actual assertions in a - different thread - """ - LOCAL_DIR = path.dirname(__file__) - - def performTest(self, configPath, assertionFunction): - """ - Primary test harness. - - Instantiates the WX App, and spawns the threads - required to make assertions against it - """ - with open(configPath, 'r') as f: - build_spec = json.loads(f.read()) - # swaps the absolute path stored by Gooey at write time - # for a relative one based on our current test location - target_pyfile = path.split(build_spec['target'].replace('"', ''))[-1] - file_path = path.join(path.dirname(__file__), target_pyfile) - run_cmd = '{} -u {}'.format(quote(sys.executable), quote(file_path)) - build_spec['language_dir'] = getResourcePath('languages') - build_spec['target'] = run_cmd - - app = application.build_app(build_spec=build_spec) - executor = futures.ThreadPoolExecutor(max_workers=1) - testResult = executor.submit(assertionFunction, app, build_spec) - app.MainLoop() - testResult.result() - # some extra padding time between starting/stopping the wx App - app.Destroy() - time.sleep(1) - - - def test_gooeyNormalRun(self): - """ Tests the happy path through the default run mode of Gooey """ - self.performTest(path.join(self.LOCAL_DIR, 'gooey_config__normal.json'), self.gooeySanityTest) - - def test_gooeySubparserMode(self): - """ Tests the happy path through the subparser run mode of Gooey """ - self.performTest(path.join(self.LOCAL_DIR, 'gooey_config__subparser.json'), self.gooeySanityTest) - - def test__gooeyAutoStart(self): - """Verifies that issue #201 doesn't regress and auto_start skips the config - screen and hops right into the client's program""" - self.performTest(path.join(self.LOCAL_DIR, 'gooey_config__autostart.json'), self.verifyAutoStart) - - def test__gooeyValidation(self): - """Verifies that custom validation routines supplied via gooey_options prevents - the user from advancing past the configuration page when they fail""" - self.performTest(path.join(self.LOCAL_DIR, 'gooey_config__autostart.json'), self.verifyValidators) - - - def verifyValidators(self, app, buildSpec): - time.sleep(1) - try: - app.TopWindow.onStart() - # we should still be on the configuration page due to a validation fail - title = app.TopWindow.header._header.GetLabel() - subtitle = app.TopWindow.header._subheader.GetLabel() - self.assertNotEqual(title, buildSpec['program_name']) - self.assertNotEqual(subtitle, buildSpec['program_description']) - except: - app.TopWindow.Destroy() - raise - else: - app.TopWindow.Destroy() - return None - - def verifyAutoStart(self, app, buildSpec): - """ - When the auto_start flag == True Gooey should skip the - configuration screen - """ - time.sleep(1) - try: - # Gooey should NOT be showing the name/description headers - # present on the config page - title = app.TopWindow.header._header.GetLabel() - subtitle = app.TopWindow.header._subheader.GetLabel() - self.assertNotEqual(title, buildSpec['program_name']) - self.assertNotEqual(subtitle, buildSpec['program_description']) - - # Gooey should be showing the console messages straight away - # without manually starting the program - title = app.TopWindow.header._header.GetLabel() - subtitle = app.TopWindow.header._subheader.GetLabel() - self.assertEqual(title,_("running_title")) - self.assertEqual(subtitle, _('running_msg')) - - # Wait for Gooey to swap the header to the final screen - while app.TopWindow.header._header.GetLabel() == _("running_title"): - time.sleep(.1) - - # verify that we've landed on the success screen - title = app.TopWindow.header._header.GetLabel() - subtitle = app.TopWindow.header._subheader.GetLabel() - self.assertEqual(title, _("finished_title")) - self.assertEqual(subtitle, _('finished_msg')) - - - # and that output was actually written to the console - self.assertIn("Success", app.TopWindow.console.textbox.GetValue()) - except: - app.TopWindow.Destroy() - raise - else: - app.TopWindow.Destroy() - return None - - - def gooeySanityTest(self, app, buildSpec): - time.sleep(1) - try: - # Check out header is present and showing data - title = app.TopWindow.header._header.GetLabel() - subtitle = app.TopWindow.header._subheader.GetLabel() - self.assertEqual(title, buildSpec['program_name']) - self.assertEqual(subtitle, buildSpec['program_description']) - - # switch to the run screen - app.TopWindow.onStart() - - # Should find the expected test in the header - title = app.TopWindow.header._header.GetLabel() - subtitle = app.TopWindow.header._subheader.GetLabel() - self.assertEqual(title,_("running_title")) - self.assertEqual(subtitle, _('running_msg')) - - # Wait for Gooey to swap the header to the final screen - while app.TopWindow.header._header.GetLabel() == _("running_title"): - time.sleep(.1) - - # verify that we've landed on the success screen - title = app.TopWindow.header._header.GetLabel() - subtitle = app.TopWindow.header._subheader.GetLabel() - self.assertEqual(title, _("finished_title")) - self.assertEqual(subtitle, _('finished_msg')) - - - # and that output was actually written to the console - self.assertIn("Success", app.TopWindow.console.textbox.GetValue()) - except: - app.TopWindow.Destroy() - raise - else: - app.TopWindow.Destroy() - return None - - -if __name__ == '__main__': - unittest.main() - - - - - -