diff --git a/Gemfile b/Gemfile index b3da39ab3..211236afe 100644 --- a/Gemfile +++ b/Gemfile @@ -227,10 +227,6 @@ group :development, :test do # Slack helper for testing gem 'slack-ruby-client', require: false - - # Can be used to detect for example the current - # operating system in tests, to handle things differently. - gem 'os' end # Want to extend Zammad with additional gems? diff --git a/Gemfile.lock b/Gemfile.lock index 07cd7097f..9726c691d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -392,7 +392,6 @@ GEM omniauth (~> 1.5) omniauth-oauth2 (>= 1.4.0) openssl (2.2.0) - os (1.1.1) overcommit (0.58.0) childprocess (>= 0.6.3, < 5) iniparse (~> 1.4) @@ -711,7 +710,6 @@ DEPENDENCIES omniauth-twitter omniauth-weibo-oauth2 openssl - os overcommit pg (= 0.21.0) pry-rails diff --git a/script/build/test_slice_tests.sh b/script/build/test_slice_tests.sh index 7ba0280de..2dd3a432f 100755 --- a/script/build/test_slice_tests.sh +++ b/script/build/test_slice_tests.sh @@ -67,7 +67,6 @@ if [ "$LEVEL" == '1' ]; then # test/browser/swich_to_user_test.rb # test/browser/taskbar_session_test.rb # test/browser/taskbar_task_test.rb - # test/browser/translation_test.rb rm test/browser/user_access_permissions_test.rb rm test/browser/user_switch_cache_test.rb @@ -133,7 +132,6 @@ elif [ "$LEVEL" == '2' ]; then rm test/browser/switch_to_user_test.rb rm test/browser/taskbar_session_test.rb rm test/browser/taskbar_task_test.rb - rm test/browser/translation_test.rb # test/browser/user_access_permissions_test.rb # test/browser/user_switch_cache_test.rb @@ -199,7 +197,6 @@ elif [ "$LEVEL" == '3' ]; then rm test/browser/switch_to_user_test.rb rm test/browser/taskbar_session_test.rb rm test/browser/taskbar_task_test.rb - rm test/browser/translation_test.rb rm test/browser/user_access_permissions_test.rb rm test/browser/user_switch_cache_test.rb @@ -265,7 +262,6 @@ elif [ "$LEVEL" == '4' ]; then rm test/browser/switch_to_user_test.rb rm test/browser/taskbar_session_test.rb rm test/browser/taskbar_task_test.rb - rm test/browser/translation_test.rb rm test/browser/user_access_permissions_test.rb rm test/browser/user_switch_cache_test.rb @@ -330,7 +326,6 @@ elif [ "$LEVEL" == '5' ]; then rm test/browser/switch_to_user_test.rb rm test/browser/taskbar_session_test.rb rm test/browser/taskbar_task_test.rb - rm test/browser/translation_test.rb rm test/browser/user_access_permissions_test.rb rm test/browser/user_switch_cache_test.rb @@ -398,7 +393,6 @@ elif [ "$LEVEL" == '6' ]; then rm test/browser/switch_to_user_test.rb rm test/browser/taskbar_session_test.rb rm test/browser/taskbar_task_test.rb - rm test/browser/translation_test.rb rm test/browser/user_access_permissions_test.rb rm test/browser/user_switch_cache_test.rb diff --git a/spec/system/examples/text_modules_examples.rb b/spec/system/examples/text_modules_examples.rb index e87501820..e6ee7f755 100644 --- a/spec/system/examples/text_modules_examples.rb +++ b/spec/system/examples/text_modules_examples.rb @@ -28,7 +28,7 @@ RSpec.shared_examples 'text modules' do |path:| # The click is needed to get the focus back to the field for chrome. find(:richtext).click - if OS.mac? + if Gem::Platform.local.os.eql? 'darwin' find(:richtext).send_keys(%i[command backspace]) else find(:richtext).send_keys(%i[control backspace]) diff --git a/spec/system/system/translations_spec.rb b/spec/system/system/translations_spec.rb index a37059a8c..73bf9f07b 100644 --- a/spec/system/system/translations_spec.rb +++ b/spec/system/system/translations_spec.rb @@ -17,4 +17,128 @@ RSpec.describe 'System > Translations', type: :system do modal_disappear # make sure test is not terminated while modal is visible end + + # see https://github.com/zammad/zammad/issues/2056 + # + # The purpose of this test is to verify that the Translation admin panel automatically re-renders under certain edge cases: + # + # Clicking into the Translation panel from another admin panel ALWAYS causes a rerender, + # but clicking into it from, e.g., a Ticket or the Dashboard does not. + # + # We want to ensure that in the latter case, the Translation panel rerenders automatically if there are new phrases to translate. + context "when missing translations are found in 'developer_mode'", authenticated_as: :admin_de do + let(:admin_de) { create(:admin, preferences: { locale: 'de-de' }) } + + before do + # developer_mode is required to track missing translations in the GUI. + Setting.set('developer_mode', true) + end + + it 're-renders the Translation admin panel correctly' do + + # The only way to test the edge case describe above + # (i.e., visiting the Translation panel directly from a Ticket or the Dashboard) + # is to first click into the admin settings and visit the Translation panel, + # then leave, then come back. + visit('/#system/translation') + + expect(page).to have_text('Inline Übersetzung') + + visit('/#dashboard') + + new_ui_phrase = 'Charlie bit me!' + page.evaluate_script("App.i18n.translateContent('#{new_ui_phrase}')") + + visit('/#system/translation') + expect(page).to have_text(new_ui_phrase) + end + end + + context 'when using the source locale', authenticated_as: :admin do + let(:admin) { create(:admin, preferences: { locale: 'en-us' }) } + + it 'offers no translations to change' do + visit '/#system/translation' + expect(page).to have_text('English is the source language, so we have nothing to translate') + end + end + + context 'when using a translation locale', authenticated_as: :admin_de do + let(:admin_de) { create(:admin, preferences: { locale: 'de-de' }) } + + it 'allows translations to be changed locally' do + + visit '/#system/translation' + field = find('.content.active input.js-Item[data-source="Translations"]') + field.fill_in(with: 'Übersetzung2') + field.native.send_keys :tab + + # Cause nav to re-render + visit '/#dashboard' + visit '/#system/translation' + + within :active_content do + + expect(find('.sidebar a[href="#system/translation"]').text).to eq('Übersetzung2') + find('input.js-Item[data-source="Translations"]').ancestor('tr').find('.js-Reset').click + # Let find() wait for the field to be updated... + expect(find('input.js-Item[data-source="Translations"][value="Übersetzung"]').value).to eq('Übersetzung') + expect(find('.sidebar a[href="#system/translation"]').text).to eq('Übersetzung2') + end + + # Cause nav to re-render + visit '/#dashboard' + visit '/#system/translation' + expect(find('.sidebar a[href="#system/translation"]').text).to eq('Übersetzung') + end + end + + context 'when using inline translation', authenticated_as: :admin do + shared_examples 'check inline translations' do |overviews_translated| + it 'allows to use inline translations' do + visit '/#system/translation' + + def toggle_inline_translations + if Gem::Platform.local.os.eql? 'darwin' + page.send_keys [:control, :alt, 't'] + else + page.send_keys [:control, :shift, 't'] + end + end + + toggle_inline_translations + + span = find '.sidebar span.translation[title="Overviews"]' + # Move cursor to the end of the string. + if Gem::Platform.local.os.eql? 'darwin' + span.send_keys %i[command right], '_modified', :tab + else + span.send_keys %i[control right], '_modified', :tab + end + + # Leave the span to be able to turn off inline translations again + visit '/#dashboard' + toggle_inline_translations + + visit '/#system/translation' + expect(page).to have_no_css('.sidebar span.translation[title="Overviews"]') + expect(find('a[href="#manage/overviews"]')).to have_text("#{overviews_translated}_modified") + expect(find('.content.active input.js-Item[data-source="Overviews"]').value).to eq("#{overviews_translated}_modified") + end + end + + context 'for source locale' do + let(:admin) { create(:admin, preferences: { locale: 'en-us' }) } + # This may seem unexpected: en-us currently offers inline translation changing, + # even though the System > Translations screen says "nothing to translate". + + include_examples 'check inline translations', 'Overviews' + end + + context 'for translated locale' do + let(:admin) { create(:admin, preferences: { locale: 'de-de' }) } + + include_examples 'check inline translations', 'Übersichten' + end + end end diff --git a/test/browser/translation_test.rb b/test/browser/translation_test.rb deleted file mode 100644 index f46e6de2e..000000000 --- a/test/browser/translation_test.rb +++ /dev/null @@ -1,374 +0,0 @@ -# Copyright (C) 2012-2021 Zammad Foundation, http://zammad-foundation.org/ - -require 'browser_test_helper' - -class TranslationTest < TestCase - - def test_preferences - @browser = browser_instance - login( - username: 'admin@example.com', - password: 'test', - url: browser_url, - ) - tasks_close_all - - click(css: 'a[href="#current_user"]') - click(css: 'a[href="#profile"]') - click(css: 'a[href="#profile/language"]') - select( - css: '.js-language [name="locale"]', - value: 'English (United States)', - ) - click(css: '.content.active button[type="submit"]') - sleep 2 - watch_for( - css: 'body', - value: 'Language', - ) - - click(css: 'a[href="#manage"]') - click(css: 'a[href="#system/translation"]') - - watch_for( - css: '.content.active', - value: 'English is the source language, so we have nothing to translate', - ) - - click(css: 'a[href="#current_user"]') - click(css: 'a[href="#profile"]') - click(css: 'a[href="#profile/language"]') - select( - css: '.js-language [name="locale"]', - value: 'Deutsch', - ) - click(css: '.content.active button[type="submit"]') - watch_for( - css: 'body', - value: 'Sprache', - ) - - click(css: 'a[href="#manage"]') - click(css: 'a[href="#system/translation"]') - - notify_close(optional: true) # to be not in click area - set( - css: '.content.active input.js-Item[data-source="Translations"]', - value: 'Übersetzung2', - ) - sleep 5 # wait until nofify is gone - click(css: '#global-search') - sleep 4 # wait till rerender - - click(css: 'a[href="#dashboard"]') - sleep 2 # wait till nav is rendered - - click(css: 'a[href="#manage"]') - click(css: 'a[href="#system/translation"]') - - match( - css: '.content.active .sidebar', - value: 'Übersetzung2', - ) - match( - css: '.content.active input.js-Item[data-source="Translations"]', - value: 'Übersetzung2', - ) - - execute( - js: "$('.js-Item[data-source=Translations]').parents('tr').find('.js-Reset:visible').click()", - ) - sleep 5 - - match( - css: '.content.active .sidebar', - value: 'Übersetzung2', - ) - match_not( - css: '.content.active input.js-Item[data-source="Translations"]', - value: 'Übersetzung2', - ) - - click(css: 'a[href="#dashboard"]') - sleep 4 # wait till rerender - - click(css: 'a[href="#manage"]') - click(css: 'a[href="#system/translation"]') - sleep 2 - - match_not( - css: '.content.active .sidebar', - value: 'Übersetzung2', - ) - match_not( - css: '.content.active input.js-Item[data-source="Translations"]', - value: 'Übersetzung2', - ) - match_not( - css: '.content.active .sidebar', - value: 'Übersetzung2', - ) - - @browser.action.key_down(:control) - .key_down(:shift) - .send_keys('t') - .key_up(:shift) - .key_up(:control) - .perform - - watch_for( - css: 'span.translation[title="Overviews"]', - value: 'Übersichten', - ) - set( - css: 'span.translation[title="Overviews"]', - value: 'Übersichten123', - ) - sleep 1 - click(css: 'a[href="#dashboard"]') - sleep 5 - - @browser.action.key_down(:control) - .key_down(:shift) - .send_keys('t') - .key_up(:shift) - .key_up(:control) - .perform - - sleep 5 - exists_not( - css: 'span.translation[title="Overviews"]', - ) - match( - css: '.js-menu', - value: 'Übersichten123', - ) - - reload - exists_not( - css: 'span.translation[title="Overviews"]', - ) - match( - css: '.js-menu', - value: 'Übersichten123', - ) - - click(css: 'a[href="#manage"]') - click(css: 'a[href="#system/translation"]') - sleep 4 - - match( - css: '.content.active input.js-Item[data-source="Overviews"]', - value: 'Übersichten123', - ) - - execute( - js: "$('.js-Item[data-source=Overviews]').parents('tr').find('.js-Reset:visible').click()", - ) - sleep 5 - - click(css: 'a[href="#dashboard"]') - sleep 5 - - match_not( - css: '.js-menu', - value: 'Übersichten123', - ) - match( - css: '.js-menu', - value: 'Übersichten', - ) - - click(css: 'a[href="#current_user"]') - click(css: 'a[href="#profile"]') - click(css: 'a[href="#profile/language"]') - select( - css: '.js-language [name="locale"]', - value: 'English (United States)', - ) - click(css: '.content.active button[type="submit"]') - sleep 2 - watch_for( - css: 'body', - value: 'Language', - ) - sleep 5 - - @browser.action.key_down(:control) - .key_down(:shift) - .send_keys('t') - .key_up(:shift) - .key_up(:control) - .perform - - watch_for( - css: 'span.translation[title="Overviews"]', - value: 'Overviews', - ) - set( - css: 'span.translation[title="Overviews"]', - value: 'Overviews123', - ) - sleep 1 - click(css: 'a[href="#dashboard"]') - sleep 5 - - @browser.action.key_down(:control) - .key_down(:shift) - .send_keys('t') - .key_up(:shift) - .key_up(:control) - .perform - - sleep 5 - exists_not( - css: 'span.translation[title="Overviews"]', - ) - match( - css: '.js-menu', - value: 'Overviews123', - ) - - click(css: 'a[href="#manage"]') - click(css: 'a[href="#system/translation"]') - sleep 4 - - match( - css: '.content.active input.js-Item[data-source="Overviews"]', - value: 'Overviews123', - ) - match_not( - css: '.content.active', - value: 'English is the source language, so we have nothing to translate', - ) - - execute( - js: "$('.js-Item[data-source=Overviews]').parents('tr').find('.js-Reset:visible').click()", - ) - - watch_for( - css: '.content.active', - value: 'English is the source language, so we have nothing to translate', - ) - - end - - def test_admin_sync - @browser = browser_instance - login( - username: 'admin@example.com', - password: 'test', - url: browser_url, - ) - tasks_close_all - - click(css: 'a[href="#current_user"]') - click(css: 'a[href="#profile"]') - click(css: 'a[href="#profile/language"]') - select( - css: '.js-language [name="locale"]', - value: 'Deutsch', - ) - click(css: '.content.active button[type="submit"]') - watch_for( - css: 'body', - value: 'Sprache', - ) - - click(css: 'a[href="#manage"]') - click(css: 'a[href="#system/translation"]') - - watch_for( - css: '.content.active', - value: 'Inline Übersetzung', - ) - - click(css: '.content.active .js-syncChanges') - - modal_ready - watch_for( - css: '.content.active .modal', - value: 'Letzte Übersetzung laden', - ) - modal_disappear( - timeout: 6.minutes, - ) - - click(css: 'a[href="#current_user"]') - click(css: 'a[href="#profile"]') - click(css: 'a[href="#profile/language"]') - select( - css: '.js-language [name="locale"]', - value: 'English (United States)', - ) - click(css: '.content.active button[type="submit"]') - watch_for( - css: 'body', - value: 'Language', - ) - - end - - # see https://github.com/zammad/zammad/issues/2056 - # - # The purpose of this test is to verify that - # the Translation admin panel automatically re-renders - # under certain edge cases: - # - # Clicking into the Translation panel from another admin panel ALWAYS causes a rerender, - # but clicking into it from, e.g., a Ticket or the Dashboard does not. - # - # We want to ensure that in the latter case, - # the Translation panel rerenders automatically if there are new phrases to translate. - def test_rerender_when_new_phrases_detected - @browser = browser_instance - login( - username: 'admin@example.com', - password: 'test', - url: browser_url, - ) - tasks_close_all - - click(css: 'a[href="#current_user"]') - click(css: 'a[href="#profile"]') - click(css: 'a[href="#profile/language"]') - select( - css: '.js-language [name="locale"]', - value: 'Deutsch', - ) - click(css: '.content.active button[type="submit"]') - watch_for( - css: 'body', - value: 'Sprache', - ) - - # The only way to test the edge case describe above - # (i.e., visiting the Translation panel directly from a Ticket or the Dashboard) - # is to first click into the admin settings and visit the Translation panel, - # then leave, then come back. - # - # (/#manage remembers the most-recent admin panel.) - click(css: 'a[href="#manage"]') - click(css: 'a[href="#system/translation"]') - - watch_for( - css: '.content.active', - value: 'Inline Übersetzung', - ) - - click(css: 'a[href="#dashboard"]') - - new_ui_phrase = 'Charlie bit me!' - @browser.execute_script("App.i18n.translateContent('#{new_ui_phrase}')") - - click(css: 'a[href="#manage"]') - - watch_for( - css: %(td[title="#{new_ui_phrase}"]), - value: new_ui_phrase, - timeout: 3 - ) - - end - -end