diff --git a/.bettercodehub.yml b/.bettercodehub.yml new file mode 100644 index 00000000..61a33da0 --- /dev/null +++ b/.bettercodehub.yml @@ -0,0 +1,13 @@ +languages: +- python + +- name: python + production: + exclude: + - /test_suite/.* + + test: + include: + - /test_suite/.* + +component_depth: 2 diff --git a/.circleci/config.yml b/.circleci/config.yml index 96197dec..20ac952d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -26,11 +26,10 @@ jobs: command: | . venv/bin/activate mkdir test-reports - pytest -v --cov=/home/circleci/dionysus/ --junitxml=../test-results/junit.xml - coverage xml + pytest --junitxml=test-reports/junit.xml - store_test_results: - path: test-results + path: test-reports - store_artifacts: - path: test-reports \ No newline at end of file + path: test-reports diff --git a/CHANGELOG.md b/CHANGELOG.md index 533791a5..43e530f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added - Improved test coverage. +- Add load_chart_data, load_json_from_file +- Add .bettercodehub.yml - prevent failed PR checks because of test code. +### Changed +- Fix bug in save_as_dialogue that failed with TypeError when called without filetypes parameter or with default filetypes=None. +- Fix circleci not storing test metadata. +- Refactor save_as_dialogue to prevent TypeError. +- Refactor load_class_data using load_from_json_file. ## [0.3.0-alpha] - 2019-01-23 diff --git a/dionysus_app/UI_menus/UI_functions.py b/dionysus_app/UI_menus/UI_functions.py index d7904fc5..e63475aa 100644 --- a/dionysus_app/UI_menus/UI_functions.py +++ b/dionysus_app/UI_menus/UI_functions.py @@ -70,7 +70,7 @@ def scrub_candidate_filename(dirty_string: str): def save_as_dialogue(title_str=None, - default_file_type=None, + default_file_extension=None, filetypes=None, suggested_filename=None, start_dir=None @@ -81,6 +81,7 @@ def save_as_dialogue(title_str=None, filetype argument (if provided) eg '*.png'. title_str is string to be displayed in popup's title bar. + NB if none provided, or is None - title displayed is "Save as". default_filetype is a string to be added as an extension (eg '.png, but can be anything (eg 'dead_parrot', '_chart.png' in the event user does not @@ -102,8 +103,16 @@ def save_as_dialogue(title_str=None, Returns None instead of empty string if no file is selected. + + + NB When default_file_extension is given, but not in filetypes, if + the first filetype in filetypes is NOT ("all files", "*.*"), that + first filetype in filetypes will by appended rather than the default + extension. If ("all files", "*.*") is the first filetype, the + default_file_extension will be appended as expected. + :param title_str: str - :param default_file_type: list + :param default_file_extension: list :param suggested_filename: str :param filetypes: list :param start_dir: str @@ -112,17 +121,17 @@ def save_as_dialogue(title_str=None, root = tk.Tk() root.withdraw() - if not default_file_type: + if filetypes and not default_file_extension: # Make extension of first listed filetype default save extension. first_extension_without_wildcard = filetypes[0][1].strip('*') if first_extension_without_wildcard != '.': - default_file_type = first_extension_without_wildcard + default_file_extension = first_extension_without_wildcard default_filetypes = [("all files", "*.*")] if not filetypes: filetypes = default_filetypes filepath_str = filedialog.asksaveasfilename(title=title_str, - defaultextension=default_file_type, + defaultextension=default_file_extension, filetypes=filetypes, initialfile=suggested_filename, initialdir=start_dir, @@ -146,6 +155,9 @@ def select_file_dialogue(title_str=None, Returns None instead of empty string if no file is selected. + NB If no title is passed to filedialog.askopenfilename, the window + title will be "Open". + :param title_str: str :param filetypes: list :param start_dir: str diff --git a/dionysus_app/class_functions.py b/dionysus_app/class_functions.py index 5e292000..78c738a6 100644 --- a/dionysus_app/class_functions.py +++ b/dionysus_app/class_functions.py @@ -10,7 +10,10 @@ from dionysus_app.class_registry_functions import register_class from dionysus_app.data_folder import DataFolder, CLASSLIST_DATA_FILE_TYPE -from dionysus_app.file_functions import convert_to_json, load_from_json, copy_file +from dionysus_app.file_functions import (convert_to_json, + load_from_json_file, + copy_file, + ) from dionysus_app.UI_menus.class_functions_UI import (blank_class_dialogue, class_data_feedback, display_class_selection_menu, @@ -246,12 +249,21 @@ def load_class_data(class_name: str): class_data_filename = class_name + CLASSLIST_DATA_FILE_TYPE classlist_data_path = CLASSLIST_DATA_PATH.joinpath(class_name, class_data_filename) - with open(classlist_data_path, 'r') as class_datafile: - loaded_class_json = class_datafile.read() - class_data_dict = load_from_json(loaded_class_json) + class_data_dict = load_from_json_file(classlist_data_path) return class_data_dict +def load_chart_data(chart_data_path: str): + """ + Load class data from chart data ('.cdf') file. + + :param chart_data_path: Path or str + :return: dict + """ + chart_data_dict = load_from_json_file(chart_data_path) + return chart_data_dict + + def get_avatar_path(class_name, student_avatar): """ Take value from 'avatar' in list of student data, return path for student avatar or default avatar path if student diff --git a/dionysus_app/file_functions.py b/dionysus_app/file_functions.py index c7a2e942..d190bcd4 100644 --- a/dionysus_app/file_functions.py +++ b/dionysus_app/file_functions.py @@ -37,6 +37,19 @@ def load_from_json(data_to_convert: str): return converted_data +def load_from_json_file(json_file_path: str): + """ + Take a filepath and load json from that file. + + :param json_file_path: Path (or str) + :return: dict + """ + with open(json_file_path) as json_file: + json_data = json_file.read() + loaded_data = load_from_json(json_data) + return loaded_data + + def copy_file(origin_fullpath: str, destination_fullpath: str): """ Takes two filepaths, copying the origin file to the destination path and filename. diff --git a/test_suite/UI_menus_tests/test_UI_functions.py b/test_suite/UI_menus_tests/test_UI_functions.py index 650512be..b58b85b0 100644 --- a/test_suite/UI_menus_tests/test_UI_functions.py +++ b/test_suite/UI_menus_tests/test_UI_functions.py @@ -6,11 +6,17 @@ TestSelectFileDialogue, test_select_file_dialogue TestSelectFolderDialogue, test_select_folder_dialogue """ +from pathlib import Path from unittest import TestCase, mock from unittest.mock import patch -from dionysus_app.UI_menus.UI_functions import (clear_screen, +from dionysus_app.UI_menus.UI_functions import (clean_for_filename, + clear_screen, input_is_essentially_blank, + save_as_dialogue, + scrub_candidate_filename, + select_file_dialogue, + select_folder_dialogue, ) @@ -45,90 +51,481 @@ def test_clear_screen_default_argument(self): class TestInputIsEssentiallyBlank(TestCase): def setUp(self): # Test cases: (input_value, expected_return_value) - self.test_empty_string = ('', True) # ie no input - # Spaces - self.test_single_space = (' ', True) - self.test_2_spaces = (' ', True) - self.test_3_spaces = (' ', True) - self.test_5_spaces = (' ', True) - # Underscores - self.test_single_underscore = ('_', True) - self.test_2_underscores = ('__', True) - self.test_3_underscores = ('___', True) - self.test_5_underscores = ('_____', True) - - self.test_only_special_characters = ('''~`!@#$%^&*()-_+{}[]|\\:;"',.<>?/''', True) - self.test_singe_leading_space = (' test', False) - self.test_spaces_underscores_combo = (' _ _ _', True) - self.test_leading_spaces = (' test', False) - self.test_singe_trailing_space = ('test ', False) - self.test_trailing_spaces = ('test ', False) - self.test_leading_and_trailing_space = (' test ', False) - self.test_no_spaces = ('test', False) - self.test_sentence = ('not the Spanish inquisition', False) - self.test_sentence_leading_and_trailing_spaces = (' not the spanish inquisition ', False) - self.test_combination_underscore_spaces = ( - " because nobody_expects_the _spanish_ inquisition the 2nd time", False) - self.test_combination_underscore_spaces_special_characters = ( - " because nobody_expects_the !@#$%ing _spanish_ inquisition the 2nd ?~)*% time", False) - - self.test_cases = [self.test_empty_string, - self.test_single_space, - self.test_2_spaces, - self.test_3_spaces, - self.test_5_spaces, - self.test_single_underscore, - self.test_2_underscores, - self.test_3_underscores, - self.test_5_underscores, - self.test_only_special_characters, - self.test_singe_leading_space, - self.test_spaces_underscores_combo, - self.test_leading_spaces, - self.test_singe_trailing_space, - self.test_leading_and_trailing_space, - self.test_no_spaces, - self.test_sentence, - self.test_sentence_leading_and_trailing_spaces, - self.test_combination_underscore_spaces, - self.test_combination_underscore_spaces_special_characters, - ] + self.test_cases = { + 'test_empty_string': ('', True), # ie no input + # Spaces + 'test_single_space': (' ', True), + 'test_2_spaces': (' ', True), + 'test_3_spaces': (' ', True), + 'test_5_spaces': (' ', True), + # Underscores + 'test_single_underscore': ('_', True), + 'test_2_underscores': ('__', True), + 'test_3_underscores': ('___', True), + 'test_5_underscores': ('_____', True), + + 'test_only_special_characters': ('''~`!@#$%^&*()-_+{}[]|\\:;"',.<>?/''', True), + 'test_singe_leading_space': (' test', False), + 'test_spaces_underscores_combo': (' _ _ _', True), + 'test_leading_spaces': (' test', False), + 'test_singe_trailing_space': ('test ', False), + 'test_trailing_spaces': ('test ', False), + 'test_leading_and_trailing_space': (' test ', False), + 'test_no_spaces': ('test', False), + 'test_sentence': ('not the Spanish inquisition', False), + 'test_sentence_leading_and_trailing_spaces': (' not the spanish inquisition ', False), + 'test_combination_underscore_spaces': ( + " because nobody_expects_the _spanish_ inquisition the 2nd time", False), + 'test_combination_underscore_spaces_special_characters': ( + " because nobody_expects_the !@#$%ing _spanish_ inquisition the 2nd ?~)*% time", False), + } def test_input_is_essentially_blank(self): for test_case in self.test_cases: - with self.subTest(i=test_case): - test_input = test_case[0] - expected_output = test_case[1] + with self.subTest(i=self.test_cases[test_case]): + test_input = self.test_cases[test_case][0] + expected_output = self.test_cases[test_case][1] assert input_is_essentially_blank(test_input) == expected_output -""" +class TestCleanForFilename(TestCase): + def setUp(self): # Test cases: (input_value, expected_return_value) - self.test_empty_string = ('', # ie no input - # Spaces - self.test_single_space = (' ', - self.test_2_spaces = (' ', - self.test_3_spaces = (' ', - self.test_5_spaces = (' ', - # Underscores - self.test_single_underscore = ('_', - self.test_2_underscores = ('__', - self.test_3_underscores = ('___', - self.test_5_underscores = ('_____', - - self.test_only_special_characters = ('''~`!@#$%^&*()-_+{}[]|\\:;"',.<>?/''', - self.test_singe_leading_space = (' test', - self.test_spaces_underscores_combo = (' _ _ _', - self.test_leading_spaces = (' test', - self.test_singe_trailing_space = ('test ', - self.test_trailing_spaces = ('test ', - self.test_leading_and_trailing_space = (' test ', - self.test_no_spaces = ('test', - self.test_sentence = ('not the Spanish inquisition', - self.test_sentence_leading_and_trailing_spaces = (' not the spanish inquisition ', - self.test_combination_underscore_spaces = ( - " because nobody_expects_the _spanish_ inquisition the 2nd time", - self.test_combination_underscore_spaces_special_characters = ( - " because nobody_expects_the !@#$%ing _spanish_ inquisition the 2nd ?~)*% time", -""" + self.test_cases = { + 'test_empty_string': ('', ''), # ie no input + # Spaces + 'test_single_space': (' ', ''), # .rstrip() will remove trailing space. + 'test_2_spaces': (' ', ''), + 'test_3_spaces': (' ', ''), + 'test_5_spaces': (' ', ''), + # Underscores + 'test_single_underscore': ('_', '_'), + 'test_double_underscore': ('__', '__'), + 'test_triple_underscore': ('___', '___'), + 'test_spaces_underscores_combo': (' _ _ _', '______'), + 'test_spaces_underscores_combo_trailing_space': (' _ _ _ ', '______'), + 'test_leading_space': (' test', '_test'), + 'test_leading_spaces': (' test', '___test'), + 'test_singe_trailing_space': ('test ', 'test'), + 'test_trailing_spaces': ('test ', 'test'), + 'test_leading_and_trailing_space': (' test ', '_test'), + 'test_no_spaces': ('test', 'test'), + 'test_sentence': ('not the Spanish inquisition', 'not_the_Spanish_inquisition'), + 'test_sentence_leading_and_trailing_spaces': (' not the spanish inquisition ', + '_not_the_spanish_inquisition'), + 'test_combination_underscore_spaces': ( + ' because nobody_expects_the _spanish_ inquisition the 2nd time', + '_because_nobody_expects_the__spanish__inquisition_the_2nd_time'), + 'test_combination_underscore_spaces_special_characters': ( + ' because nobody_expects_the !@#$%ing _spanish_ inquisition the 2nd ?~)*% time', + '_because_nobody_expects_the_ing__spanish__inquisition_the_2nd__time') + } + + @patch('dionysus_app.UI_menus.UI_functions.scrub_candidate_filename') + def test_clean_for_filename_mocking_call(self, mocked_scrub_candidate_filename): + """ + Mock call to scrub_candidate_filename. + Essentially an input .replace(' ', '_') operation. + """ + for test_case in self.test_cases: + with self.subTest(i=self.test_cases[test_case]): + test_input = self.test_cases[test_case][0] + expected_output = test_input.replace(' ', '_') + + # Have scrub_candidate_filename return input_string. + mocked_scrub_candidate_filename.return_value = test_input + assert clean_for_filename(test_input) == expected_output + + mocked_scrub_candidate_filename.assert_called_once_with(test_input) + mocked_scrub_candidate_filename.reset_mock(return_value=True, side_effect=True) + + def test_clean_for_filename_unmocked(self): + for test_case in self.test_cases: + with self.subTest(i=self.test_cases[test_case]): + test_input = self.test_cases[test_case][0] + expected_output = self.test_cases[test_case][1] + + assert clean_for_filename(test_input) == expected_output + + +class TestScrubCandidateFilename(TestCase): + def setUp(self): + # Test cases: (input_value, expected_return_value) + self.test_cases = { + 'test_empty_string': ('', ''), # ie no input + # Spaces + 'test_single_space': (' ', ''), # .rstrip() will remove trailing space. + 'test_2_spaces': (' ', ''), + 'test_3_spaces': (' ', ''), + 'test_5_spaces': (' ', ''), + # Underscores + 'test_single_underscore': ('_', '_'), + 'test_double_underscore': ('__', '__'), + 'test_triple_underscore': ('___', '___'), + 'test_spaces_underscores_combo': (' _ _ _', ' _ _ _'), + 'test_spaces_underscores_combo_trailing_space': (' _ _ _ ', ' _ _ _'), + 'test_leading_space': (' test', ' test'), + 'test_leading_spaces': (' test', ' test'), + 'test_singe_trailing_space': ('test ', 'test'), + 'test_trailing_spaces': ('test ', 'test'), + 'test_leading_and_trailing_space': (' test ', ' test'), + 'test_no_spaces': ('test', 'test'), + 'test_sentence': ('not the Spanish inquisition', 'not the Spanish inquisition'), + 'test_sentence_leading_and_trailing_spaces': (' not the spanish inquisition ', + ' not the spanish inquisition'), + 'test_combination_underscore_spaces': ( + ' because nobody_expects_the _spanish_ inquisition the 2nd time', + ' because nobody_expects_the _spanish_ inquisition the 2nd time'), + 'test_combination_underscore_spaces_special_characters': ( + ' because nobody_expects_the !@#$%ing _spanish_ inquisition the 2nd ?~)*% time', + ' because nobody_expects_the ing _spanish_ inquisition the 2nd time') + } + + def test_scrub_candidate_filename(self): + for test_case in self.test_cases: + with self.subTest(i=self.test_cases[test_case]): + test_input = self.test_cases[test_case][0] + expected_output = self.test_cases[test_case][1] + + assert scrub_candidate_filename(test_input) == expected_output + + +@patch('dionysus_app.UI_menus.UI_functions.tk.Tk') +class TestSaveAsDialogue(TestCase): + def setUp(self): + self.default_filetypes = [("all files", "*.*")] + self.test_returned_filepath_str = "my save file" + + def test_save_as_dialogue_no_arguments(self, mock_tkinter): + with patch('dionysus_app.UI_menus.UI_functions.filedialog.asksaveasfilename') as save_as_filedialog: + save_as_filedialog.return_value = self.test_returned_filepath_str + assert save_as_filedialog.return_value == save_as_dialogue() + + mock_tkinter.assert_called() + save_as_filedialog.assert_called_once_with(title=None, + defaultextension=None, + filetypes=self.default_filetypes, + initialdir=None, + initialfile=None + ) + + def test_save_as_dialogue_all_None_arguments(self, mock_tkinter): + with patch('dionysus_app.UI_menus.UI_functions.filedialog.asksaveasfilename') as save_as_filedialog: + save_as_filedialog.return_value = self.test_returned_filepath_str + + assert save_as_dialogue(title_str=None, + default_file_extension=None, + filetypes=None, + suggested_filename=None, + start_dir=None + ) == save_as_filedialog.return_value + + mock_tkinter.assert_called() + save_as_filedialog.assert_called_once_with(title=None, + defaultextension=None, + filetypes=self.default_filetypes, + initialdir=None, + initialfile=None, + ) + + def test_save_as_dialogue_with_title_str(self, mock_tkinter): + with patch('dionysus_app.UI_menus.UI_functions.filedialog.asksaveasfilename') as save_as_filedialog: + title_string = "Save your super_file_as:" + + save_as_filedialog.return_value = self.test_returned_filepath_str + + assert save_as_dialogue(title_str=title_string) == save_as_filedialog.return_value + + mock_tkinter.assert_called() + save_as_filedialog.assert_called_once_with(title=title_string, + defaultextension=None, + filetypes=self.default_filetypes, + initialdir=None, + initialfile=None, + ) + + def test_save_as_dialogue_with_default_extension_some_ext(self, mock_tkinter): + with patch('dionysus_app.UI_menus.UI_functions.filedialog.asksaveasfilename') as save_as_filedialog: + test_default_extension = '.some_ext' + + save_as_filedialog.return_value = self.test_returned_filepath_str + + assert save_as_dialogue(default_file_extension=test_default_extension) == save_as_filedialog.return_value + + mock_tkinter.assert_called() + save_as_filedialog.assert_called_once_with(title=None, + defaultextension='.some_ext', + filetypes=self.default_filetypes, + initialdir=None, + initialfile=None + ) + + def test_save_as_dialogue_with_filetypes_all_files(self, mock_tkinter): + with patch('dionysus_app.UI_menus.UI_functions.filedialog.asksaveasfilename') as save_as_filedialog: + test_filetypes = [("all files", "*.*")] + + save_as_filedialog.return_value = self.test_returned_filepath_str + + assert save_as_dialogue(filetypes=test_filetypes) == save_as_filedialog.return_value + + mock_tkinter.assert_called() + save_as_filedialog.assert_called_once_with(title=None, + defaultextension=None, + filetypes=self.default_filetypes, + initialdir=None, + initialfile=None + ) + + def test_save_as_dialogue_with_filetypes_some_ext(self, mock_tkinter): + with patch('dionysus_app.UI_menus.UI_functions.filedialog.asksaveasfilename') as save_as_filedialog: + test_filetypes = [('some ext', '*.some_ext')] + test_default_extension = '.some_ext' + + save_as_filedialog.return_value = self.test_returned_filepath_str + + assert save_as_dialogue(default_file_extension=test_default_extension, + filetypes=test_filetypes + ) == self.test_returned_filepath_str + + mock_tkinter.assert_called() + save_as_filedialog.assert_called_once_with(title=None, + defaultextension=test_default_extension, + filetypes=test_filetypes, + initialdir=None, + initialfile=None, + ) + + def test_save_as_dialogue_with_filetypes_all_files_plus_some_ext(self, mock_tkinter): + with patch('dionysus_app.UI_menus.UI_functions.filedialog.asksaveasfilename') as save_as_filedialog: + test_filetypes = [('all files', '*.*'), ('some ext', '*.some_ext')] + + save_as_filedialog.return_value = self.test_returned_filepath_str + + assert save_as_dialogue(filetypes=test_filetypes) == self.test_returned_filepath_str + + mock_tkinter.assert_called() + save_as_filedialog.assert_called_once_with(title=None, + defaultextension=None, + filetypes=test_filetypes, + initialdir=None, + initialfile=None, + ) + + def test_save_as_dialogue_with_filetypes_some_ext_plus_all_files(self, mock_tkinter): + with patch('dionysus_app.UI_menus.UI_functions.filedialog.asksaveasfilename') as save_as_filedialog: + test_filetypes = [('some ext', '*.some_ext'), ('all files', '*.*')] + test_default_extension = '.some_ext' + + save_as_filedialog.return_value = self.test_returned_filepath_str + + assert save_as_dialogue(default_file_extension=test_default_extension, + filetypes=test_filetypes, + ) == save_as_filedialog.return_value + + mock_tkinter.assert_called() + save_as_filedialog.assert_called_once_with(title=None, + defaultextension=test_default_extension, + filetypes=test_filetypes, + initialdir=None, + initialfile=None, + ) + + def test_save_as_dialogue_with_filetypes_some_ext_no_default(self, mock_tkinter): + with patch('dionysus_app.UI_menus.UI_functions.filedialog.asksaveasfilename') as save_as_filedialog: + test_filetypes = [('some ext', '*.some_ext'), ('all files', '*.*')] + test_default_extension = '.some_ext' + + save_as_filedialog.return_value = self.test_returned_filepath_str + + assert save_as_dialogue(filetypes=test_filetypes, + ) == save_as_filedialog.return_value + + mock_tkinter.assert_called() + save_as_filedialog.assert_called_once_with(title=None, + defaultextension=test_default_extension, + filetypes=test_filetypes, + initialdir=None, + initialfile=None, + ) + + def test_save_as_dialogue_with_suggested_filename(self, mock_tkinter): + with patch('dionysus_app.UI_menus.UI_functions.filedialog.asksaveasfilename') as save_as_filedialog: + test_suggested_filename = 'you should call your save file THIS' + + save_as_filedialog.return_value = self.test_returned_filepath_str + + assert save_as_dialogue(suggested_filename=test_suggested_filename) == self.test_returned_filepath_str + + mock_tkinter.assert_called() + save_as_filedialog.assert_called_once_with(title=None, + defaultextension=None, + filetypes=self.default_filetypes, + initialdir=None, + initialfile=test_suggested_filename, + ) + + def test_save_as_dialogue_with_start_dir_str(self, mock_tkinter): + with patch('dionysus_app.UI_menus.UI_functions.filedialog.asksaveasfilename') as save_as_filedialog: + test_start_dir = 'where to start?' + + save_as_filedialog.return_value = self.test_returned_filepath_str + + assert save_as_dialogue(start_dir=test_start_dir) == self.test_returned_filepath_str + + mock_tkinter.assert_called() + save_as_filedialog.assert_called_once_with(title=None, + defaultextension=None, + filetypes=self.default_filetypes, + initialdir=test_start_dir, + initialfile=None, + ) + + def test_save_as_dialogue_with_start_dir_Path(self, mock_tkinter): + with patch('dionysus_app.UI_menus.UI_functions.filedialog.asksaveasfilename') as save_as_filedialog: + test_start_dir = 'where to start?' + test_start_dir_path = Path(test_start_dir) + + save_as_filedialog.return_value = self.test_returned_filepath_str + + assert save_as_dialogue(start_dir=test_start_dir_path) == self.test_returned_filepath_str + + mock_tkinter.assert_called() + save_as_filedialog.assert_called_once_with(title=None, + defaultextension=None, + filetypes=self.default_filetypes, + initialdir=test_start_dir_path, + initialfile=None, + ) + + def test_save_as_dialogue_no_input_returns_None(self, mock_tkinter): + with patch('dionysus_app.UI_menus.UI_functions.filedialog.asksaveasfilename') as save_as_filedialog: + save_as_filedialog.return_value = '' + + assert save_as_dialogue() is None + + mock_tkinter.assert_called() + save_as_filedialog.assert_called_once_with(title=None, + defaultextension=None, + filetypes=self.default_filetypes, + initialdir=None, + initialfile=None, + ) + + +@patch('dionysus_app.UI_menus.UI_functions.tk.Tk') +class TestSelectFileDialogue(TestCase): + def setUp(self): + self.test_default_filetypes = [('all files', '*.*')] + + self.test_title_str = 'First you must answer three questions' + self.test_filetypes = [('some ext', '*.some_ext'), ('all files', '*.*')] + self.test_start_dir = 'What\\is\\your\\quest' + + self.test_returned_filepath_str = 'strange women lying in ponds distributing swords' + + def test_select_file_dialogue_called_without_arguments(self, mock_tkinter): + # Tests filedialog called with default_filetypes. + with patch('dionysus_app.UI_menus.UI_functions.filedialog.askopenfilename') as select_filedialog: + select_filedialog.return_value = self.test_returned_filepath_str + + assert select_file_dialogue() == self.test_returned_filepath_str + + mock_tkinter.assert_called() + select_filedialog.assert_called_once_with(title=None, + filetype=self.test_default_filetypes, + initialdir=None) + + def test_select_file_dialogue_all_None_arguments(self, mock_tkinter): + with patch('dionysus_app.UI_menus.UI_functions.filedialog.askopenfilename') as select_filedialog: + select_filedialog.return_value = self.test_returned_filepath_str + + assert select_file_dialogue(title_str=None, + filetypes=None, + start_dir=None, + ) == self.test_returned_filepath_str + + mock_tkinter.assert_called() + select_filedialog.assert_called_once_with(title=None, + filetype=self.test_default_filetypes, + initialdir=None) + + def test_select_file_dialogue_called_with_all_arguments(self, mock_tkinter): + with patch('dionysus_app.UI_menus.UI_functions.filedialog.askopenfilename') as select_filedialog: + select_filedialog.return_value = self.test_returned_filepath_str + + assert select_file_dialogue(title_str=self.test_title_str, + filetypes=self.test_filetypes, + start_dir=self.test_start_dir, + ) == self.test_returned_filepath_str + + mock_tkinter.assert_called() + select_filedialog.assert_called_once_with(title=self.test_title_str, + filetype=self.test_filetypes, + initialdir=self.test_start_dir) + + def test_select_file_dialogue_no_input_returns_None(self, mock_tkinter): + with patch('dionysus_app.UI_menus.UI_functions.filedialog.askopenfilename') as select_filedialog: + select_filedialog.return_value = '' + + assert select_file_dialogue() is None + + mock_tkinter.assert_called() + select_filedialog.assert_called_once_with(title=None, + filetype=self.test_default_filetypes, + initialdir=None) + + +@patch('dionysus_app.UI_menus.UI_functions.tk.Tk') +class TestSelectFolderDialogue(TestCase): + def setUp(self): + self.test_default_start_dir = '..' + + self.test_title_str = 'First you must answer three questions' + self.test_start_dir = 'What\\is\\your\\quest' + + self.test_returned_folderpath_str = 'strange women lying in ponds distributing swords' + + def test_select_folder_dialogue_called_without_arguments(self, mock_tkinter): + with patch('dionysus_app.UI_menus.UI_functions.filedialog.askdirectory') as select_directorydialog: + select_directorydialog.return_value = self.test_returned_folderpath_str + + assert select_folder_dialogue() == self.test_returned_folderpath_str + + mock_tkinter.assert_called() + select_directorydialog.assert_called_once_with(title=None, + initialdir=self.test_default_start_dir) + + def test_select_folder_dialogue_all_None_arguments(self, mock_tkinter): + with patch('dionysus_app.UI_menus.UI_functions.filedialog.askdirectory') as select_directorydialog: + select_directorydialog.return_value = self.test_returned_folderpath_str + + assert select_folder_dialogue(title_str=None, + start_dir=None, + ) == self.test_returned_folderpath_str + + mock_tkinter.assert_called() + select_directorydialog.assert_called_once_with(title=None, + initialdir=None) + + def test_select_folder_dialogue_called_with_all_arguments(self, mock_tkinter): + with patch('dionysus_app.UI_menus.UI_functions.filedialog.askdirectory') as select_directorydialog: + select_directorydialog.return_value = self.test_returned_folderpath_str + + assert select_folder_dialogue(title_str=self.test_title_str, + start_dir=self.test_start_dir, + ) == self.test_returned_folderpath_str + + mock_tkinter.assert_called() + select_directorydialog.assert_called_once_with(title=self.test_title_str, + initialdir=self.test_start_dir) + + def test_select_folder_dialogue_no_input_returns_None(self, mock_tkinter): + with patch('dionysus_app.UI_menus.UI_functions.filedialog.askdirectory') as select_directorydialog: + select_directorydialog.return_value = '' + + assert select_folder_dialogue() is None + + mock_tkinter.assert_called() + select_directorydialog.assert_called_once_with(title=None, + initialdir=self.test_default_start_dir) diff --git a/test_suite/test_class_functions.py b/test_suite/test_class_functions.py index 5ca6ada8..67efcaa9 100644 --- a/test_suite/test_class_functions.py +++ b/test_suite/test_class_functions.py @@ -19,6 +19,7 @@ setup_class, setup_class_data_storage, write_classlist_to_file, + load_chart_data, ) from test_suite.testing_class_data import (testing_class_data_set as test_class_data_set, @@ -324,7 +325,7 @@ def test_load_class_data_from_disk(self): assert self.test_class_loaded_data == loaded_json_data def test_load_class_data_mocked_open(self): - with patch('dionysus_app.class_functions.open', mock_open(read_data=self.test_class_json_data)): + with patch('dionysus_app.file_functions.open', mock_open(read_data=self.test_class_json_data)): assert isinstance(self.test_class_loaded_data, dict) assert load_class_data(self.test_class_name) == self.test_class_loaded_data @@ -334,6 +335,18 @@ def tearDown(self): assert not os.path.exists(self.test_class_name) +class TestLoadChartData(TestCase): + def setUp(self): + self.test_chart_data_path = Path('my_test_path') + self.mock_load_from_json_file_return_data = {1: 'one', 2: 'two', 3: 'three'} + + def test_load_chart_data(self): + with patch('dionysus_app.class_functions.load_from_json_file') as mock_load_from_json_file: + mock_load_from_json_file.return_value = self.mock_load_from_json_file_return_data + + assert load_chart_data(self.test_chart_data_path) == self.mock_load_from_json_file_return_data + + class TestGetAvatarPath(TestCase): mock_DEFAULT_AVATAR_PATH = Path('mocked_default_avatar_path') mock_CLASSLIST_DATA_PATH = Path('mocked_classlist_data_path') diff --git a/test_suite/test_file_functions.py b/test_suite/test_file_functions.py index e0a7e050..529814f2 100644 --- a/test_suite/test_file_functions.py +++ b/test_suite/test_file_functions.py @@ -1,31 +1,50 @@ import os import shutil +from pathlib import Path from unittest import TestCase -from unittest.mock import patch +from unittest.mock import patch, mock_open -from dionysus_app.file_functions import convert_to_json, load_from_json +from dionysus_app.file_functions import (convert_to_json, + load_from_json, + load_from_json_file, + ) from dionysus_app.file_functions import copy_file, move_file from test_suite.testing_class_data import testing_class_data_set as test_json_class_data class TestConvertToJson(TestCase): - def test_convert_to_json(self): - data_to_convert = {1: 'a', 'b': 2, 3: 'c', 'd': 4} - json_converted_data = '{\n "1": "a",\n "b": 2,\n "3": "c",\n "d": 4\n}' + def setUp(self): + self.data_to_convert = {1: 'a', 'b': 2, 3: 'c', 'd': 4} + self.json_converted_data = '{\n "1": "a",\n "b": 2,\n "3": "c",\n "d": 4\n}' - assert convert_to_json(data_to_convert) == json_converted_data + def test_convert_to_json(self): + assert convert_to_json(self.data_to_convert) == self.json_converted_data class TestLoadFromJson(TestCase): - def test_load_from_json(self): - json_data_to_convert = '{\n "1": "a",\n "b": 2,\n "3": "c",\n "d": 4\n}' - converted_json_data = {"1": 'a', 'b': 2, "3": 'c', 'd': 4} + def setUp(self): + self.json_data_to_convert = '{\n "1": "a",\n "b": 2,\n "3": "c",\n "d": 4\n}' + self.converted_json_data = {"1": 'a', 'b': 2, "3": 'c', 'd': 4} + self.test_json_class_data = test_json_class_data - assert load_from_json(json_data_to_convert) == converted_json_data + def test_load_from_json(self): + assert load_from_json(self.json_data_to_convert) == self.converted_json_data def test_load_from_json_test_class_data(self): - assert test_json_class_data['loaded_dict'] == load_from_json(test_json_class_data['json_data_string']) + assert load_from_json(self.test_json_class_data['json_data_string']) == self.test_json_class_data['loaded_dict'] + + +class TestLoadFromJsonFile(TestCase): + def setUp(self): + self.test_file_json_data_to_convert = '{\n "1": "a",\n "b": 2,\n "3": "c",\n "d": 4\n}' + self.mock_file_path = Path('test_file_path') + self.converted_json_data = {"1": 'a', 'b': 2, "3": 'c', 'd': 4} + + def test_load_from_json_file(self): + with patch('dionysus_app.file_functions.open', mock_open(read_data=self.test_file_json_data_to_convert)): + + assert load_from_json_file(self.mock_file_path) == self.converted_json_data class TestCopyFile(TestCase):