def test_get_voice_for_field(qtbot): # pytest test_languagetools.py -k test_get_voice_for_field # valid voice # =========== config_gen = testing_utils.TestConfigGenerator() mock_language_tools = config_gen.build_languagetools_instance( 'batch_audio') dntf = config_gen.get_dntf_chinese() voice = mock_language_tools.get_voice_for_field(dntf) assert voice != None assert voice['voice_key'] == config_gen.chinese_voice_key # no language mapping # =================== config_gen = testing_utils.TestConfigGenerator() mock_language_tools = config_gen.build_languagetools_instance( 'no_language_mapping') dntf = config_gen.get_dntf_chinese() testcase_instance = unittest.TestCase() testcase_instance.assertRaises(errors.FieldLanguageMappingError, mock_language_tools.get_voice_for_field, dntf)
def test_add_translation_transliteration_no_language_mapping(qtbot): # pytest test_dialogs.py -rPP -k test_add_translation_transliteration_no_language_mapping config_gen = testing_utils.TestConfigGenerator() mock_language_tools = config_gen.build_languagetools_instance( 'no_language_mapping') deck_note_type = mock_language_tools.deck_utils.build_deck_note_type( config_gen.deck_id, config_gen.model_id) note_id_list = [42, 43] testcase_instance = unittest.TestCase() # translation testcase_instance.assertRaises( errors.LanguageMappingError, dialog_batchtransformation.BatchConversionDialog, mock_language_tools, deck_note_type, note_id_list, constants.TransformationType.Translation) # transliteration testcase_instance.assertRaises( errors.LanguageMappingError, dialog_batchtransformation.BatchConversionDialog, mock_language_tools, deck_note_type, note_id_list, constants.TransformationType.Transliteration)
def test_editor_audio(qtbot): # pytest test_editor.py -rPP -k test_editor_audio config_gen = testing_utils.TestConfigGenerator() mock_language_tools = config_gen.build_languagetools_instance( 'batch_audio') mock_language_tools.cloud_language_tools.transliteration_map = { '老人': 'laoren' } editor = config_gen.get_mock_editor_with_note(config_gen.note_id_1) editor_manager = editor_processing.EditorManager(mock_language_tools) field_index = 0 note_id = config_gen.note_id_1 field_value = '老人' # short version bridge_str = f'key:{field_index}:{note_id}:{field_value}' editor_manager.process_field_update(editor, bridge_str) # verify outputs # sound should have been played assert mock_language_tools.anki_utils.played_sound['text'] == '老人' assert len( mock_language_tools.anki_utils.editor_set_field_value_calls) == 1 assert mock_language_tools.anki_utils.editor_set_field_value_calls[0][ 'field_name'] == 'Sound' # sound assert '.mp3' in mock_language_tools.anki_utils.editor_set_field_value_calls[ 0]['text']
def test_process_choosetranslation(qtbot): # pytest test_editor.py -rPP -k test_process_choosetranslation config_gen = testing_utils.TestConfigGenerator() mock_language_tools = config_gen.build_languagetools_instance( 'batch_translation') mock_language_tools.cloud_language_tools.translate_all_result = { '老人家': { 'serviceA': 'first translation A', 'serviceB': 'second translation B' } } # when the choose translation dialog comes up, we should pick serviceB mock_language_tools.anki_utils.display_dialog_behavior = 'choose_serviceB' editor = config_gen.get_mock_editor_with_note(config_gen.note_id_1) editor_manager = editor_processing.EditorManager(mock_language_tools) editor_manager.run_choose_translation(editor, 'English') assert len( mock_language_tools.anki_utils.editor_set_field_value_calls) == 1 assert mock_language_tools.anki_utils.editor_set_field_value_calls[0][ 'field_name'] == 'English' assert mock_language_tools.anki_utils.editor_set_field_value_calls[0][ 'text'] == 'second translation B'
def test_voice_selection_no_voices(qtbot): # pytest test_dialogs.py -rPP -k test_voice_selection_no_voices # make sure the dialog comes up # ----------------------------- config_gen = testing_utils.TestConfigGenerator() mock_language_tools = config_gen.build_languagetools_instance( 'get_config_language_no_voices') voice_list = mock_language_tools.cloud_language_tools.get_tts_voice_list( 'yoyo') voice_selection_dialog = dialog_voiceselection.prepare_voice_selection_dialog( mock_language_tools, voice_list) languages_combobox = voice_selection_dialog.findChild( aqt.qt.QComboBox, 'languages_combobox') assert languages_combobox.count() == 3 assert languages_combobox.itemText(2) == 'Malagasy' qtbot.keyClicks(languages_combobox, 'Malagasy') # try to play audio play_sample_button = voice_selection_dialog.findChild( aqt.qt.QPushButton, f'play_sample_0') qtbot.mouseClick(play_sample_button, aqt.qt.Qt.LeftButton) # should have been a critical messages assert mock_language_tools.anki_utils.critical_message_received == 'No voice available for Malagasy'
def test_dialog_breakdown_chinese(qtbot): # pytest test_dialogs.py -rPP -k test_dialog_breakdown_chinese config_gen = testing_utils.TestConfigGenerator() mock_language_tools = config_gen.build_languagetools_instance('default') source_text = '老人家' # the response which should come mock_language_tools.cloud_language_tools.breakdown_map = { '老人家': [ { 'token': '老', 'lemma': '老', 'translation': 'old', 'transliteration': 'lao' }, { 'token': '人家', 'lemma': '人家', 'translation': 'people', 'transliteration': 'renjia' }, ] } deck_note_type = deck_utils.DeckNoteType(config_gen.deck_id, config_gen.deck_name, config_gen.model_id, config_gen.model_name) dialog = dialog_breakdown.prepare_dialog(mock_language_tools, source_text, 'zh_cn', None, deck_note_type) # wanted languages should be populated assert_combobox_items_equal(dialog.target_language_dropdown, ['English', 'Chinese']) assert dialog.target_language_dropdown.currentText() == 'Chinese' # there should be 1 translation option (Azure) assert_combobox_items_equal(dialog.translation_dropdown, ['Azure']) # there should be 2 transliteration options assert_combobox_items_equal(dialog.transliteration_dropdown, ['pinyin1', 'pinyin2']) # there should be 2 tokenization options assert_combobox_items_equal(dialog.tokenization_dropdown, [ 'Chinese (Simplified) (Characters) Spacy', 'Chinese (Simplified) (Jieba (words)) Spacy' ]) # run breakdown qtbot.mouseClick(dialog.load_button, aqt.qt.Qt.LeftButton) # check that result label has been populated result_text = dialog.breakdown_result.text() result_lines = result_text.split('<br/>') assert len(result_lines) == 2
def test_add_audio(qtbot): # pytest test_dialogs.py -rPP -k test_add_audio config_gen = testing_utils.TestConfigGenerator() # test 1 - everything setup but no prior setting # ---------------------------------------------- mock_language_tools = config_gen.build_languagetools_instance('default') deck_note_type = mock_language_tools.deck_utils.build_deck_note_type( config_gen.deck_id, config_gen.model_id) note_id_list = [42, 43] add_audio_dialog = dialogs.AddAudioDialog(mock_language_tools, deck_note_type, note_id_list) add_audio_dialog.setupUi() # do some checks on the from field combo box assert_combobox_items_equal(add_audio_dialog.from_field_combobox, ['Chinese', 'English']) assert_combobox_items_equal(add_audio_dialog.to_field_combobox, config_gen.all_fields) assert add_audio_dialog.voice_label.text( ) == '<b>' + config_gen.chinese_voice_description + '</b>' # test 2 - some defaults already exist # ------------------------------------ mock_language_tools = config_gen.build_languagetools_instance( 'batch_audio') add_audio_dialog = dialogs.AddAudioDialog(mock_language_tools, deck_note_type, note_id_list) add_audio_dialog.setupUi() assert_combobox_items_equal(add_audio_dialog.from_field_combobox, ['Chinese', 'English']) assert_combobox_items_equal(add_audio_dialog.to_field_combobox, config_gen.all_fields) # verify that selected fields match expectation assert add_audio_dialog.from_field_combobox.currentText( ) == config_gen.field_chinese assert add_audio_dialog.to_field_combobox.currentText( ) == config_gen.field_sound # test 3 - no language mapping done for any field # ----------------------------------------------- mock_language_tools = config_gen.build_languagetools_instance( 'no_language_mapping') # add_audio_dialog = dialogs.AddAudioDialog(mock_language_tools, deck_note_type, note_id_list) testcase_instance = unittest.TestCase() testcase_instance.assertRaises(errors.LanguageMappingError, dialogs.AddAudioDialog, mock_language_tools, deck_note_type, note_id_list)
def test_choose_translation(qtbot): # pytest test_dialogs.py -rPP -k test_choose_translation config_gen = testing_utils.TestConfigGenerator() mock_language_tools = config_gen.build_languagetools_instance('default') original_text = 'translate this' from_language = 'zh_cn' to_language = 'en' all_translations = { 'serviceA': 'first translation', 'serviceB': 'second translation' } dialog = dialog_choosetranslation.prepare_dialog(mock_language_tools, original_text, from_language, to_language, all_translations) assert dialog.original_text_label.text() == original_text assert dialog.from_language_label.text() == 'Chinese' assert dialog.to_language_label.text() == 'English' # first, apply button should be disabled assert dialog.apply_button.isEnabled() == False # check that services and translations are present # services service_1 = dialog.findChild(aqt.qt.QLabel, 'service_label_0') assert service_1.text() == '<b>serviceA</b>' service_2 = dialog.findChild(aqt.qt.QLabel, 'service_label_1') assert service_2.text() == '<b>serviceB</b>' # translations translation_1 = dialog.findChild(aqt.qt.QLabel, 'translation_label_0') assert translation_1.text() == 'first translation' translation_2 = dialog.findChild(aqt.qt.QLabel, 'translation_label_1') assert translation_2.text() == 'second translation' # pick second translation radio_button = dialog.findChild(aqt.qt.QRadioButton, 'radio_button_1') radio_button.click() # apply button should be enabled assert dialog.apply_button.isEnabled() == True qtbot.mouseClick(dialog.apply_button, aqt.qt.Qt.LeftButton) # verify translation retained assert dialog.selected_translation == 'second translation'
def test_get_transliteration(qtbot): # pytest test_languagetools.py -k test_get_transliteration config_gen = testing_utils.TestConfigGenerator() mock_language_tools = config_gen.build_languagetools_instance( 'text_replacement') source_text = 'unter etw' mock_language_tools.cloud_language_tools.transliteration_map = { 'unter etwas': 'ˈʊntɐ ˈɛtvas' } transliterated_text = mock_language_tools.get_transliteration( source_text, {'transliteration_key': 'de to en'}) assert transliterated_text == 'ˈʊntɐ ˈɛtvas'
def test_process_choosetranslation_empty(qtbot): # pytest test_editor.py -s -rPP -k test_process_choosetranslation_empty config_gen = testing_utils.TestConfigGenerator() mock_language_tools = config_gen.build_languagetools_instance( 'batch_translation') editor = config_gen.get_mock_editor_with_note(config_gen.note_id_3) editor_manager = editor_processing.EditorManager(mock_language_tools) editor_manager.run_choose_translation(editor, 'English') assert len( mock_language_tools.anki_utils.editor_set_field_value_calls) == 0 assert mock_language_tools.anki_utils.last_action == 'choosing translation' assert isinstance(mock_language_tools.anki_utils.last_exception, errors.LanguageToolsValidationFieldEmpty)
def test_get_tts_audio(qtbot): # pytest test_languagetools.py -k test_get_tts_audio config_gen = testing_utils.TestConfigGenerator() mock_language_tools = config_gen.build_languagetools_instance( 'text_replacement') filename = mock_language_tools.get_tts_audio('unter etw', 'Azure', 'de_de', {'name': 'voice1'}, {}) file_contents = open(filename, 'r').read() data = json.loads(file_contents) assert data['text'] == 'unter etwas' # after text replacement assert data['service'] == 'Azure' assert data['language_code'] == 'de_de' assert data['voice_key'] == {'name': 'voice1'} assert data['options'] == {}
def test_process_choosetranslation_request_error(qtbot): # pytest test_editor.py -s -rPP -k test_process_choosetranslation_request_error config_gen = testing_utils.TestConfigGenerator() mock_language_tools = config_gen.build_languagetools_instance( 'batch_translation') mock_language_tools.cloud_language_tools.translation_unhandled_exception_map = { '老人家': 'unhandled exception translate all' } editor = config_gen.get_mock_editor_with_note(config_gen.note_id_1) editor_manager = editor_processing.EditorManager(mock_language_tools) editor_manager.run_choose_translation(editor, 'English') assert len( mock_language_tools.anki_utils.editor_set_field_value_calls) == 0 assert mock_language_tools.anki_utils.last_action == 'choosing translation' assert isinstance(mock_language_tools.anki_utils.last_exception, Exception) assert str(mock_language_tools.anki_utils.last_exception ) == 'unhandled exception translate all'
def test_dialog_textprocessing_simple(qtbot): # pytest test_dialogs.py -rPP -k test_dialog_textprocessing_simple config_gen = testing_utils.TestConfigGenerator() mock_language_tools = config_gen.build_languagetools_instance('default') dialog = dialog_textprocessing.prepare_text_processing_dialog( mock_language_tools) # check table model # ================= # should have 0 rows assert dialog.textReplacementTableModel.rowCount(None) == 0 # check processing preview qtbot.keyClicks(dialog.sample_text_input, 'abdc1234 [9]+') assert dialog.sample_text_transformed_label.text( ) == '<b>abdc1234 [9]+</b>' # add a text transformation rule qtbot.mouseClick(dialog.add_replace_simple_button, aqt.qt.Qt.LeftButton) # enter pattern and replacement row = 0 index_pattern = dialog.textReplacementTableModel.createIndex( row, dialog_textprocessing.COL_INDEX_PATTERN) dialog.textReplacementTableModel.setData( index_pattern, ' [9]+', aqt.qt.Qt.EditRole) # should not get interpreted as regexp index_replacement = dialog.textReplacementTableModel.createIndex( row, dialog_textprocessing.COL_INDEX_REPLACEMENT) dialog.textReplacementTableModel.setData(index_replacement, 'rep', aqt.qt.Qt.EditRole) # dialog.exec_() # verify preview assert dialog.sample_text_transformed_label.text() == '<b>abdc1234rep</b>'
def test_batch_transformation_error_handling(qtbot): # pytest test_dialogs.py -rPP -k test_batch_transformation_error_handling config_gen = testing_utils.TestConfigGenerator() mock_language_tools = config_gen.build_languagetools_instance('default') deck_note_type = deck_utils.DeckNoteType(config_gen.deck_id, config_gen.deck_name, config_gen.model_id, config_gen.model_name) note_id_list = config_gen.get_note_id_list() transformation_type = constants.TransformationType.Translation dialog = dialog_batchtransformation.prepare_batch_transformation_dialogue( mock_language_tools, deck_note_type, note_id_list, transformation_type) # assertions # ========== assert_combobox_items_equal(dialog.from_combobox, ['Chinese', 'English']) assert_combobox_items_equal(dialog.to_combobox, ['Chinese', 'English']) assert_combobox_items_equal(dialog.service_combobox, ['Azure']) # set from to Chinese qtbot.keyClicks(dialog.from_combobox, 'Chinese') qtbot.keyClicks(dialog.to_combobox, 'English') # load translations button should be enabled assert dialog.load_translations_button.isEnabled() == True # apply to notes should be disabled assert dialog.applyButton.isEnabled() == False # cancel button should be enabled assert dialog.cancelButton.isEnabled() == True # check table model # ================= # headers assert dialog.noteTableModel.headerData( 0, aqt.qt.Qt.Orientation.Horizontal, aqt.qt.Qt.ItemDataRole.DisplayRole) == 'Chinese' assert dialog.noteTableModel.headerData( 1, aqt.qt.Qt.Orientation.Horizontal, aqt.qt.Qt.ItemDataRole.DisplayRole) == 'English' # data - input column = 0 index = dialog.noteTableModel.createIndex(0, column) assert dialog.noteTableModel.data( index, aqt.qt.Qt.ItemDataRole.DisplayRole) == '老人家' index = dialog.noteTableModel.createIndex(1, column) # second row assert dialog.noteTableModel.data( index, aqt.qt.Qt.ItemDataRole.DisplayRole) == '你好' # data - output column = 1 index = dialog.noteTableModel.createIndex(0, column) assert dialog.noteTableModel.data( index, aqt.qt.Qt.ItemDataRole.DisplayRole) == None index = dialog.noteTableModel.createIndex(1, column) # second row assert dialog.noteTableModel.data( index, aqt.qt.Qt.ItemDataRole.DisplayRole) == None # load translations # ================= mock_language_tools.cloud_language_tools.translation_error_map = { '老人家': 'translation error 42' } mock_language_tools.cloud_language_tools.translation_map = { '你好': 'translation 2' } qtbot.mouseClick(dialog.load_translations_button, aqt.qt.Qt.LeftButton) # ensure translations are displayed on the table # data - output column = 1 index = dialog.noteTableModel.createIndex(0, column) assert dialog.noteTableModel.data( index, aqt.qt.Qt.ItemDataRole.DisplayRole ) == None # should be empty, there was an error index = dialog.noteTableModel.createIndex(1, column) # second row assert dialog.noteTableModel.data( index, aqt.qt.Qt.ItemDataRole.DisplayRole) == 'translation 2' # dialog.exec_() # # apply button should be enabled now assert dialog.applyButton.isEnabled() == True # apply to notes # ============== qtbot.mouseClick(dialog.applyButton, aqt.qt.Qt.LeftButton) # verify error message assert 'Could not load translation: translation error 42 (1 times)' in mock_language_tools.anki_utils.critical_message_received # # verify effect on notes note_1 = config_gen.notes_by_id[config_gen.note_id_1] assert note_1.set_values == {} assert note_1.flush_called == False note_2 = config_gen.notes_by_id[config_gen.note_id_2] assert note_2.set_values == {'English': 'translation 2'} assert note_2.flush_called == True
def test_dialog_textprocessing(qtbot): # pytest test_dialogs.py -rPP -k test_dialog_textprocessing config_gen = testing_utils.TestConfigGenerator() mock_language_tools = config_gen.build_languagetools_instance('default') dialog = dialog_textprocessing.prepare_text_processing_dialog( mock_language_tools) # check table model # ================= # headers assert dialog.textReplacementTableModel.headerData( 0, aqt.qt.Qt.Orientation.Horizontal, aqt.qt.Qt.ItemDataRole.DisplayRole) == 'Type' assert dialog.textReplacementTableModel.headerData( 1, aqt.qt.Qt.Orientation.Horizontal, aqt.qt.Qt.ItemDataRole.DisplayRole) == 'Pattern' assert dialog.textReplacementTableModel.headerData( 2, aqt.qt.Qt.Orientation.Horizontal, aqt.qt.Qt.ItemDataRole.DisplayRole) == 'Replacement' assert dialog.textReplacementTableModel.headerData( 3, aqt.qt.Qt.Orientation.Horizontal, aqt.qt.Qt.ItemDataRole.DisplayRole) == 'Translation' assert dialog.textReplacementTableModel.headerData( 4, aqt.qt.Qt.Orientation.Horizontal, aqt.qt.Qt.ItemDataRole.DisplayRole) == 'Transliteration' assert dialog.textReplacementTableModel.headerData( 5, aqt.qt.Qt.Orientation.Horizontal, aqt.qt.Qt.ItemDataRole.DisplayRole) == 'Audio' # should have 0 rows assert dialog.textReplacementTableModel.rowCount(None) == 0 # check processing preview qtbot.keyClicks(dialog.sample_text_input, 'abdc1234') assert dialog.sample_text_transformed_label.text() == '<b>abdc1234</b>' # add a text transformation rule qtbot.mouseClick(dialog.add_replace_regex_button, aqt.qt.Qt.LeftButton) # enter pattern and replacement row = 0 index_pattern = dialog.textReplacementTableModel.createIndex( row, dialog_textprocessing.COL_INDEX_PATTERN) dialog.textReplacementTableModel.setData(index_pattern, '1234', aqt.qt.Qt.EditRole) index_replacement = dialog.textReplacementTableModel.createIndex( row, dialog_textprocessing.COL_INDEX_REPLACEMENT) dialog.textReplacementTableModel.setData(index_replacement, '5678', aqt.qt.Qt.EditRole) # verify preview assert dialog.sample_text_transformed_label.text() == '<b>abdc5678</b>' # add another transformation rule qtbot.mouseClick(dialog.add_replace_regex_button, aqt.qt.Qt.LeftButton) # enter pattern and replacement row = 1 index_pattern = dialog.textReplacementTableModel.createIndex( row, dialog_textprocessing.COL_INDEX_PATTERN) dialog.textReplacementTableModel.setData(index_pattern, ' / ', aqt.qt.Qt.EditRole) index_replacement = dialog.textReplacementTableModel.createIndex( row, dialog_textprocessing.COL_INDEX_REPLACEMENT) dialog.textReplacementTableModel.setData(index_replacement, ' ', aqt.qt.Qt.EditRole) # check processing preview dialog.sample_text_input.clear() qtbot.keyClicks(dialog.sample_text_input, 'word1 / word2') assert dialog.sample_text_transformed_label.text() == '<b>word1 word2</b>' # go back to previous preview string dialog.sample_text_input.clear() qtbot.keyClicks(dialog.sample_text_input, 'abdc1234') assert dialog.sample_text_transformed_label.text() == '<b>abdc5678</b>' # remove the first rule dialog.table_view.selectRow(0) index_first_row = dialog.textReplacementTableModel.createIndex( 0, dialog_textprocessing.COL_INDEX_PATTERN) dialog.table_view.selectionModel().select( index_first_row, aqt.qt.QItemSelectionModel.Select) qtbot.mouseClick(dialog.remove_replace_button, aqt.qt.Qt.LeftButton) # there should only be one row left assert dialog.textReplacementTableModel.rowCount(None) == 1 # check the rule at row 0 row = 0 index_pattern = dialog.textReplacementTableModel.createIndex( row, dialog_textprocessing.COL_INDEX_PATTERN) assert dialog.textReplacementTableModel.data( index_pattern, aqt.qt.Qt.ItemDataRole.DisplayRole) == '\" / \"' index_replacement = dialog.textReplacementTableModel.createIndex( row, dialog_textprocessing.COL_INDEX_REPLACEMENT) assert dialog.textReplacementTableModel.data( index_replacement, aqt.qt.Qt.ItemDataRole.DisplayRole) == '\" \"' # check the preview assert dialog.sample_text_transformed_label.text() == '<b>abdc1234</b>' # try a regexp rule qtbot.mouseClick(dialog.add_replace_regex_button, aqt.qt.Qt.LeftButton) row = 1 index_pattern = dialog.textReplacementTableModel.createIndex( row, dialog_textprocessing.COL_INDEX_PATTERN) dialog.textReplacementTableModel.setData(index_pattern, '[0-9]+', aqt.qt.Qt.EditRole) index_replacement = dialog.textReplacementTableModel.createIndex( row, dialog_textprocessing.COL_INDEX_REPLACEMENT) dialog.textReplacementTableModel.setData(index_replacement, 'number', aqt.qt.Qt.EditRole) # this transformation will only apply to transliteration index_translation = dialog.textReplacementTableModel.createIndex(row, 3) dialog.textReplacementTableModel.setData(index_translation, aqt.qt.Qt.Unchecked, aqt.qt.Qt.CheckStateRole) index_transliteration = dialog.textReplacementTableModel.createIndex( row, 4) dialog.textReplacementTableModel.setData(index_transliteration, aqt.qt.Qt.Checked, aqt.qt.Qt.CheckStateRole) index_audio = dialog.textReplacementTableModel.createIndex(row, 5) dialog.textReplacementTableModel.setData(index_audio, aqt.qt.Qt.Unchecked, aqt.qt.Qt.CheckStateRole) # check the preview in different transformation types dialog.sample_transformation_type_combo_box.setCurrentText( 'Transliteration') assert dialog.sample_text_transformed_label.text() == '<b>abdcnumber</b>' dialog.sample_transformation_type_combo_box.setCurrentText('Translation') assert dialog.sample_text_transformed_label.text() == '<b>abdc1234</b>' dialog.sample_transformation_type_combo_box.setCurrentText('Audio') assert dialog.sample_text_transformed_label.text() == '<b>abdc1234</b>' # now, click OK to the dialog qtbot.mouseClick(dialog.applyButton, aqt.qt.Qt.LeftButton) # ensure that config has been written actual_written_config = mock_language_tools.anki_utils.written_config actual_written_config_text_processing = actual_written_config[ constants.CONFIG_TEXT_PROCESSING] expected_written_config_text_processing = { 'replacements': [ { 'Audio': True, 'Transliteration': True, 'Translation': True, 'pattern': ' / ', 'replace': ' ', 'replace_type': 'regex' }, { 'Audio': False, 'Transliteration': True, 'Translation': False, 'pattern': '[0-9]+', 'replace': 'number', 'replace_type': 'regex' }, ] } assert actual_written_config_text_processing['replacements'][ 0] == expected_written_config_text_processing['replacements'][0] assert actual_written_config_text_processing['replacements'][ 1] == expected_written_config_text_processing['replacements'][1] # try to generate some translations source_text = 'word1 / word2' mock_language_tools.cloud_language_tools.translation_map = { 'word1 word2': 'word3 word4' } translated_text = mock_language_tools.get_translation( source_text, {'translation_key': 'de to en'}) assert translated_text == 'word3 word4' # try to generate transliteration source_text = 'word1 / word2' mock_language_tools.cloud_language_tools.transliteration_map = { 'wordnumber wordnumber': 'correct correct' } translated_text = mock_language_tools.get_transliteration( source_text, {'translation_key': 'de to en'}) assert translated_text == 'correct correct'
def test_api_key(qtbot): # pytest test_dialogs.py -rPP -k test_api_key config_gen = testing_utils.TestConfigGenerator() mock_language_tools = config_gen.build_languagetools_instance('default') # API key already populated # ========================= dialog = dialog_apikey.prepare_api_key_dialog(mock_language_tools) # api key will be verified immediately assert mock_language_tools.cloud_language_tools.verify_api_key_called == True assert mock_language_tools.cloud_language_tools.verify_api_key_input == 'yoyo' assert dialog.applyButton.isEnabled() == True qtbot.mouseClick(dialog.applyButton, aqt.qt.Qt.LeftButton) # verify that API key in config is correct assert mock_language_tools.anki_utils.written_config['api_key'] == 'yoyo' # API key empty # ============= mock_language_tools = config_gen.build_languagetools_instance('noapikey') assert mock_language_tools.api_key_checked == False dialog = dialog_apikey.prepare_api_key_dialog(mock_language_tools) assert mock_language_tools.cloud_language_tools.verify_api_key_called == False assert dialog.applyButton.isEnabled() == False # type in a fake API key mock_language_tools.cloud_language_tools.verify_api_key_is_valid = False qtbot.keyClicks(dialog.api_text_input, 'dummykey1') assert mock_language_tools.cloud_language_tools.verify_api_key_called == True assert mock_language_tools.cloud_language_tools.verify_api_key_input == 'dummykey1' assert dialog.applyButton.isEnabled() == False assert dialog.account_info_label.text() == '' # type in a valid API key mock_language_tools.cloud_language_tools.verify_api_key_is_valid = True dialog.api_text_input.clear() qtbot.keyClicks(dialog.api_text_input, 'validkey1') assert mock_language_tools.cloud_language_tools.verify_api_key_called == True assert mock_language_tools.cloud_language_tools.verify_api_key_input == 'validkey1' assert mock_language_tools.cloud_language_tools.account_info_called == True assert mock_language_tools.cloud_language_tools.account_info_api_key == 'validkey1' assert dialog.account_info_label.text( ) == """<b>type</b>: 250 chars<br/><b>email</b>: [email protected]""" assert dialog.applyButton.isEnabled() == True # click OK qtbot.mouseClick(dialog.applyButton, aqt.qt.Qt.LeftButton) # verify that API key in config is correct assert mock_language_tools.anki_utils.written_config[ 'api_key'] == 'validkey1' assert mock_language_tools.api_key_checked == True
def test_editor_translation(qtbot): # pytest test_editor.py -rPP -k test_editor_translation config_gen = testing_utils.TestConfigGenerator() mock_language_tools = config_gen.build_languagetools_instance( 'batch_translation') mock_language_tools.cloud_language_tools.translation_map = { '老人': 'old people (short)', '老人家': 'old people (long)', '电扇': 'electric fan' } editor = config_gen.get_mock_editor_with_note(config_gen.note_id_1) editor_manager = editor_processing.EditorManager(mock_language_tools) # regular example # --------------- field_index = 0 note_id = config_gen.note_id_1 field_value = '老人' # short version bridge_str = f'key:{field_index}:{note_id}:{field_value}' editor_manager.process_field_update(editor, bridge_str) # verify outputs assert len( mock_language_tools.anki_utils.editor_set_field_value_calls) == 1 assert mock_language_tools.anki_utils.editor_set_field_value_calls[0][ 'field_name'] == 'English' assert mock_language_tools.anki_utils.editor_set_field_value_calls[0][ 'text'] == 'old people (short)' # empty input # ----------- field_value = '' # empty bridge_str = f'key:{field_index}:{note_id}:{field_value}' editor_manager.process_field_update(editor, bridge_str) # verify outputs assert len( mock_language_tools.anki_utils.editor_set_field_value_calls) == 2 assert mock_language_tools.anki_utils.editor_set_field_value_calls[1][ 'field_name'] == 'English' assert mock_language_tools.anki_utils.editor_set_field_value_calls[1][ 'text'] == '' # empty input (html tag) # ---------------------- field_value = '<br/>' # empty bridge_str = f'key:{field_index}:{note_id}:{field_value}' editor_manager.process_field_update(editor, bridge_str) # verify outputs assert len( mock_language_tools.anki_utils.editor_set_field_value_calls) == 3 assert mock_language_tools.anki_utils.editor_set_field_value_calls[2][ 'field_name'] == 'English' assert mock_language_tools.anki_utils.editor_set_field_value_calls[2][ 'text'] == '' # disable live updates # -------------------- bridge_str = f'languagetools:liveupdates:false' editor_manager.process_command(editor, bridge_str) # now send another field update field_value = 'yoyoyyo' # short version bridge_str = f'key:{field_index}:{note_id}:{field_value}' editor_manager.process_field_update(editor, bridge_str) # nothing should have happened assert len( mock_language_tools.anki_utils.editor_set_field_value_calls) == 3 # re-enable live updates bridge_str = f'languagetools:liveupdates:true' editor_manager.process_command(editor, bridge_str) # send field update field_value = '老人' # short version bridge_str = f'key:{field_index}:{note_id}:{field_value}' editor_manager.process_field_update(editor, bridge_str) # verify outputs assert len( mock_language_tools.anki_utils.editor_set_field_value_calls) == 4 assert mock_language_tools.anki_utils.editor_set_field_value_calls[3][ 'field_name'] == 'English' assert mock_language_tools.anki_utils.editor_set_field_value_calls[3][ 'text'] == 'old people (short)' # force update # ------------ # disable updates again bridge_str = f'languagetools:liveupdates:false' editor_manager.process_command(editor, bridge_str) # force an update bridge_str = f'languagetools:{constants.COMMAND_FULLUPDATE}' editor_manager.process_command(editor, bridge_str) # verify outputs assert len( mock_language_tools.anki_utils.editor_set_field_value_calls) == 5 assert mock_language_tools.anki_utils.editor_set_field_value_calls[4][ 'field_name'] == 'English' assert mock_language_tools.anki_utils.editor_set_field_value_calls[4][ 'text'] == 'old people (long)' # change typing delay # ------------------- bridge_str = f'languagetools:typingdelay:1750' editor_manager.process_command(editor, bridge_str) assert mock_language_tools.anki_utils.written_config[ constants.CONFIG_LIVE_UPDATE_DELAY] == 1750 assert editor_manager.field_change_timer.delay_ms == 1750
def test_dialog_runrules(qtbot): # pytest test_dialogs.py -rPP -k test_dialog_runrules config_gen = testing_utils.TestConfigGenerator() mock_language_tools = config_gen.build_languagetools_instance( 'batch_audio_translation_transliteration') deck_note_type = deck_utils.DeckNoteType(config_gen.deck_id, config_gen.deck_name, config_gen.model_id, config_gen.model_name) note_id_list = config_gen.get_note_id_list() # setup maps for translation / transliteration mock_language_tools.cloud_language_tools.translation_map = { '老人家': 'translation 1', '你好': 'translation 2' } mock_language_tools.cloud_language_tools.transliteration_map = { '老人家': 'transliteration 1', '你好': 'transliteration 2' } dialog = dialog_notesettings.RunRulesDialog(mock_language_tools, deck_note_type, note_id_list) dialog.setupUi() # dialog.exec_() # click apply button qtbot.mouseClick(dialog.applyButton, aqt.qt.Qt.LeftButton) # ideally we would check things like buttons disabled, progress bar, etc # but for now, just check the effect on notes # verify effect on notes note_1 = config_gen.notes_by_id[config_gen.note_id_1] note_1_set_values = note_1.set_values assert 'sound:' in note_1_set_values['Sound'] del note_1_set_values['Sound'] # look at sound separately assert note_1_set_values == { 'English': 'translation 1', 'Pinyin': 'transliteration 1' } assert note_1.flush_called == True note_2 = config_gen.notes_by_id[config_gen.note_id_2] note_2_set_values = note_2.set_values assert 'sound:' in note_2_set_values['Sound'] del note_2_set_values['Sound'] assert note_2_set_values == { 'English': 'translation 2', 'Pinyin': 'transliteration 2' } assert note_2.flush_called == True note_3 = config_gen.notes_by_id[config_gen.note_id_3] assert note_3.set_values == {} # no values set assert note_3.flush_called == False # check action stats expected_action_stats = { 'adding translation to field English': { 'success': 2, 'error': { 'Field is empty': 1 } }, 'adding transliteration to field Pinyin': { 'success': 2, 'error': { 'Field is empty': 1 } }, 'adding audio to field Sound': { 'success': 2, 'error': { 'Field is empty': 1 } } } actual_action_stats = dialog.batch_error_manager.action_stats logging.info(actual_action_stats) assert expected_action_stats == actual_action_stats
def test_language_mapping(qtbot): # pytest test_dialogs.py -rPP -k test_language_mapping # make sure our deck appears # -------------------------- config_gen = testing_utils.TestConfigGenerator() mock_language_tools = config_gen.build_languagetools_instance( 'no_language_mapping') mapping_dialog = dialog_languagemapping.prepare_language_mapping_dialogue( mock_language_tools) # assert deck name, note type, and 3 fields deck_frame = mapping_dialog.findChild(aqt.qt.QFrame, f'frame_{config_gen.deck_name}') assert deck_frame != None deck_name_label = mapping_dialog.findChild( aqt.qt.QLabel, f'deck_name_{config_gen.deck_name}') assert deck_name_label.text() == config_gen.deck_name note_type_label = mapping_dialog.findChild( aqt.qt.QLabel, f'note_type_name_{config_gen.deck_name}_{config_gen.model_name}') assert note_type_label.text() == config_gen.model_name # look for labels on all 3 fields for field_name in config_gen.all_fields: field_label_obj_name = f'field_label_{config_gen.model_name} / {config_gen.deck_name} / {field_name}' field_label = mapping_dialog.findChild(aqt.qt.QLabel, field_label_obj_name) assert field_label.text() == field_name # none of the languages should be set for field_name in config_gen.all_fields: field_language_obj_name = f'field_language_{config_gen.model_name} / {config_gen.deck_name} / {field_name}' field_language = mapping_dialog.findChild(aqt.qt.QComboBox, field_language_obj_name) assert field_language != None # ensure the "not set" option is selected assert field_language.currentText() == 'Not Set' # now, set languages manually # --------------------------- field_language_obj_name = f'field_language_{config_gen.model_name} / {config_gen.deck_name} / {config_gen.field_chinese}' field_language_combobox = mapping_dialog.findChild( aqt.qt.QComboBox, field_language_obj_name) qtbot.keyClicks(field_language_combobox, 'Chinese') field_language_obj_name = f'field_language_{config_gen.model_name} / {config_gen.deck_name} / {config_gen.field_english}' field_language_combobox = mapping_dialog.findChild( aqt.qt.QComboBox, field_language_obj_name) qtbot.keyClicks(field_language_combobox, 'English') apply_button = mapping_dialog.findChild(aqt.qt.QPushButton, 'apply') qtbot.mouseClick(apply_button, aqt.qt.Qt.LeftButton) # ensure configuration has been modified model_name = config_gen.model_name deck_name = config_gen.deck_name assert mock_language_tools.anki_utils.written_config[ constants.CONFIG_DECK_LANGUAGES][model_name][deck_name][ config_gen.field_chinese] == 'zh_cn' assert mock_language_tools.anki_utils.written_config[ constants.CONFIG_DECK_LANGUAGES][model_name][deck_name][ config_gen.field_english] == 'en' # run automatic detection # ----------------------- mapping_dialog = dialog_languagemapping.prepare_language_mapping_dialogue( mock_language_tools) # apply button should be disabled apply_button = mapping_dialog.findChild(aqt.qt.QPushButton, 'apply') assert apply_button.isEnabled() == False autodetect_button = mapping_dialog.findChild(aqt.qt.QPushButton, 'run_autodetect') qtbot.mouseClick(autodetect_button, aqt.qt.Qt.LeftButton) # assert languages detected field_language_obj_name = f'field_language_{config_gen.model_name} / {config_gen.deck_name} / {config_gen.field_chinese}' field_language = mapping_dialog.findChild(aqt.qt.QComboBox, field_language_obj_name) assert field_language.currentText() == 'Chinese' field_language_obj_name = f'field_language_{config_gen.model_name} / {config_gen.deck_name} / {config_gen.field_english}' field_language = mapping_dialog.findChild(aqt.qt.QComboBox, field_language_obj_name) assert field_language.currentText() == 'English' # apply button should be enabled assert apply_button.isEnabled() == True # now , click the apply button qtbot.mouseClick(apply_button, aqt.qt.Qt.LeftButton) # ensure configuration has been modified model_name = config_gen.model_name deck_name = config_gen.deck_name assert mock_language_tools.anki_utils.written_config[ constants.CONFIG_DECK_LANGUAGES][model_name][deck_name][ config_gen.field_chinese] == 'zh_cn' assert mock_language_tools.anki_utils.written_config[ constants.CONFIG_DECK_LANGUAGES][model_name][deck_name][ config_gen.field_english] == 'en' # mapping_dialog.exec_() # show field samples # ================== # reset this mock_language_tools.anki_utils.written_config = None mapping_dialog = dialog_languagemapping.prepare_language_mapping_dialogue( mock_language_tools) field_samples_button_obj_name = f'field_samples_{config_gen.model_name} / {config_gen.deck_name} / {config_gen.field_english}' autodetect_button = mapping_dialog.findChild( aqt.qt.QPushButton, field_samples_button_obj_name) qtbot.mouseClick(autodetect_button, aqt.qt.Qt.LeftButton) assert 'old people' in mock_language_tools.anki_utils.info_message_received assert 'hello' in mock_language_tools.anki_utils.info_message_received field_samples_button_obj_name = f'field_samples_{config_gen.model_name} / {config_gen.deck_name} / {config_gen.field_sound}' autodetect_button = mapping_dialog.findChild( aqt.qt.QPushButton, field_samples_button_obj_name) qtbot.mouseClick(autodetect_button, aqt.qt.Qt.LeftButton) assert 'No usable field data found' in mock_language_tools.anki_utils.info_message_received # set one language manually field_language_obj_name = f'field_language_{config_gen.model_name} / {config_gen.deck_name} / {config_gen.field_chinese}' field_language_combobox = mapping_dialog.findChild( aqt.qt.QComboBox, field_language_obj_name) qtbot.keyClicks(field_language_combobox, 'Sound') # hit cancel cancel_button = mapping_dialog.findChild(aqt.qt.QPushButton, 'cancel') qtbot.mouseClick(cancel_button, aqt.qt.Qt.LeftButton) # there should not be any config change assert mock_language_tools.anki_utils.written_config == None
def test_voice_selection(qtbot): # pytest test_dialogs.py -rPP -k test_voice_selection # make sure the dialog comes up # ----------------------------- config_gen = testing_utils.TestConfigGenerator() mock_language_tools = config_gen.build_languagetools_instance('default') voice_list = mock_language_tools.cloud_language_tools.get_tts_voice_list( 'yoyo') voice_selection_dialog = dialog_voiceselection.prepare_voice_selection_dialog( mock_language_tools, voice_list) # there should be two languages. English And Chinese # for each language, there should be two voices # they should both have two samples each languages_combobox = voice_selection_dialog.findChild( aqt.qt.QComboBox, 'languages_combobox') assert languages_combobox.count() == 2 assert languages_combobox.itemText(0) == 'Chinese' assert languages_combobox.itemText(1) == 'English' voices_combobox = voice_selection_dialog.findChild(aqt.qt.QComboBox, 'voices_combobox') assert voices_combobox.count() == 2 assert 'Xiaoxiao' in voices_combobox.itemText(0) assert 'Yunyang' in voices_combobox.itemText(1) # check samples assert voice_selection_dialog.sample_labels[0].text() == '老人家' assert voice_selection_dialog.sample_labels[1].text() == '你好' # now, select English qtbot.keyClicks(languages_combobox, 'English') assert voices_combobox.count() == 2 assert 'Aria' in voices_combobox.itemText(0) assert 'Guy' in voices_combobox.itemText(1) # check samples assert voice_selection_dialog.sample_labels[0].text() == 'old people' assert voice_selection_dialog.sample_labels[1].text() == 'hello' # pick the Guy voice guy_voice = voices_combobox.itemText(1) qtbot.keyClicks(voices_combobox, guy_voice) # listen to samples play_sample_button = voice_selection_dialog.findChild( aqt.qt.QPushButton, f'play_sample_0') qtbot.mouseClick(play_sample_button, aqt.qt.Qt.LeftButton) # check that sample has been played assert mock_language_tools.anki_utils.played_sound['text'] == 'old people' assert 'Guy' in mock_language_tools.anki_utils.played_sound['voice_key'][ 'name'] apply_button = voice_selection_dialog.findChild(aqt.qt.QPushButton, 'apply') qtbot.mouseClick(apply_button, aqt.qt.Qt.LeftButton) assert 'en' in mock_language_tools.anki_utils.written_config[ constants.CONFIG_VOICE_SELECTION] assert 'Guy' in mock_language_tools.anki_utils.written_config[ constants.CONFIG_VOICE_SELECTION]['en']['voice_key']['name'] # use the written config as the new config mock_language_tools.config = mock_language_tools.anki_utils.written_config # open the dialog box again # ========================= voice_selection_dialog = dialog_voiceselection.prepare_voice_selection_dialog( mock_language_tools, voice_list) apply_button = voice_selection_dialog.findChild(aqt.qt.QPushButton, 'apply') assert apply_button.isEnabled() == False # should be disabled # switch language to english languages_combobox = voice_selection_dialog.findChild( aqt.qt.QComboBox, 'languages_combobox') qtbot.keyClicks(languages_combobox, 'English') # choose different voice voices_combobox = voice_selection_dialog.findChild(aqt.qt.QComboBox, 'voices_combobox') current_index = voices_combobox.currentIndex() assert voices_combobox.count() == 2 assert 'Guy' in voices_combobox.itemText( current_index) # check current english voice voice_wanted = voices_combobox.itemText(0) qtbot.keyClicks(voices_combobox, voice_wanted) assert apply_button.isEnabled() == True qtbot.mouseClick(apply_button, aqt.qt.Qt.LeftButton) assert 'Aria' in mock_language_tools.anki_utils.written_config[ constants.CONFIG_VOICE_SELECTION]['en']['voice_key']['name']
def test_generate_audio_for_field(qtbot): # regular case # ============ config_gen = testing_utils.TestConfigGenerator() mock_language_tools = config_gen.build_languagetools_instance('default') # common variables note_id = config_gen.note_id_1 from_field = config_gen.field_chinese to_field = config_gen.field_sound voice_list = mock_language_tools.get_tts_voice_list() chinese_voices = [x for x in voice_list if x['language_code'] == 'zh_cn'] voice = chinese_voices[0] result = mock_language_tools.generate_audio_for_field( note_id, from_field, to_field, voice) assert result == True # get the note note = config_gen.notes_by_id[config_gen.note_id_1] # make sure sound was added assert config_gen.field_sound in note.set_values assert 'sound:languagetools-' in note.set_values[config_gen.field_sound] assert note.flush_called == True assert mock_language_tools.anki_utils.added_media_file != None assert 'languagetools-' in mock_language_tools.anki_utils.added_media_file # empty field # =========== config_gen = EmptyFieldConfigGenerator() mock_language_tools = config_gen.build_languagetools_instance('default') result = mock_language_tools.generate_audio_for_field( note_id, from_field, to_field, voice) assert result == False # get the note note = config_gen.notes_by_id[config_gen.note_id_1] # make sure no sound was added assert config_gen.field_sound not in note.set_values assert note.flush_called == False assert mock_language_tools.anki_utils.added_media_file == None # empty field but with html junk # ============================== config_gen = DummyHtmlEmptyFieldConfigGenerator() mock_language_tools = config_gen.build_languagetools_instance('default') result = mock_language_tools.generate_audio_for_field( note_id, from_field, to_field, voice) assert result == False # get the note note = config_gen.notes_by_id[config_gen.note_id_1] # make sure no sound was added assert config_gen.field_sound not in note.set_values assert note.flush_called == False assert mock_language_tools.anki_utils.added_media_file == None
def test_error_manager(qtbot): # pytest test_errors.py -s -rPP -k test_error_manager config_gen = testing_utils.TestConfigGenerator() mock_language_tools = config_gen.build_languagetools_instance('default') error_manager = errors.ErrorManager(mock_language_tools.anki_utils) dnt = deck_utils.DeckNoteType(1, 'Deck', 1, 'NoteType') dntf = deck_utils.DeckNoteTypeField(dnt, 'field1') # single actions # ============== with error_manager.get_single_action_context('single action 1'): logging.info('single action 1') raise errors.LanguageToolsValidationFieldEmpty() assert isinstance(mock_language_tools.anki_utils.last_exception, errors.LanguageToolsValidationFieldEmpty) assert mock_language_tools.anki_utils.last_action == 'single action 1' mock_language_tools.anki_utils.reset_exceptions() with error_manager.get_single_action_context('single action 2'): logging.info('single action 2') logging.info('successful') assert mock_language_tools.anki_utils.last_exception == None mock_language_tools.anki_utils.reset_exceptions() # unhandled exception with error_manager.get_single_action_context('single action 3'): logging.info('single action 3') raise Exception('this is unhandled') assert isinstance(mock_language_tools.anki_utils.last_exception, Exception) assert mock_language_tools.anki_utils.last_action == 'single action 3' mock_language_tools.anki_utils.reset_exceptions() # batch actions # ============= batch_error_manager = error_manager.get_batch_error_manager('batch test 1') with batch_error_manager.get_batch_action_context('batch iteration 1'): logging.info('batch iteration 1') raise errors.LanguageToolsValidationFieldEmpty() with batch_error_manager.get_batch_action_context('batch iteration 2'): logging.info('batch iteration 2') logging.info('ok') with batch_error_manager.get_batch_action_context('batch iteration 3'): logging.info('batch iteration 3') raise errors.FieldLanguageMappingError(dntf) expected_action_stats = { 'batch iteration 1': { 'success': 0, 'error': { 'Field is empty': 1 } }, 'batch iteration 2': { 'success': 1, 'error': {} }, 'batch iteration 3': { 'success': 0, 'error': { 'No language set for NoteType / Deck / field1. Please setup Language Mappings, from the Anki main screen: <b>Tools -> Language Tools: Language Mapping</b>': 1 } } } actual_action_stats = batch_error_manager.action_stats assert expected_action_stats == actual_action_stats # batch_error_manager.display_stats() # batch actions with unhandled exceptions # ======================================= batch_error_manager = error_manager.get_batch_error_manager('batch test 2') with batch_error_manager.get_batch_action_context('batch iteration 1'): logging.info('batch iteration 1') logging.info('ok') with batch_error_manager.get_batch_action_context('batch iteration 2'): logging.info('batch iteration 2') raise Exception('this is unhandled') expected_action_stats = { 'batch iteration 1': { 'success': 1, 'error': {} }, 'batch iteration 2': { 'success': 0, 'error': { 'Unknown Error: this is unhandled': 1 } } }