diff options
Diffstat (limited to 'spec/lib/gitlab/i18n/po_linter_spec.rb')
-rw-r--r-- | spec/lib/gitlab/i18n/po_linter_spec.rb | 163 |
1 files changed, 126 insertions, 37 deletions
diff --git a/spec/lib/gitlab/i18n/po_linter_spec.rb b/spec/lib/gitlab/i18n/po_linter_spec.rb index 3a962ba7f22..3dbc23d2aaf 100644 --- a/spec/lib/gitlab/i18n/po_linter_spec.rb +++ b/spec/lib/gitlab/i18n/po_linter_spec.rb @@ -1,10 +1,31 @@ require 'spec_helper' require 'simple_po_parser' +# Disabling this cop to allow for multi-language examples in comments +# rubocop:disable Style/AsciiComments describe Gitlab::I18n::PoLinter do let(:linter) { described_class.new(po_path) } let(:po_path) { 'spec/fixtures/valid.po' } + def fake_translation(msgid:, translation:, plural_id: nil, plurals: []) + data = { msgid: msgid, msgid_plural: plural_id } + + if plural_id + [translation, *plurals].each_with_index do |plural, index| + allow(FastGettext::Translation).to receive(:n_).with(msgid, plural_id, index).and_return(plural) + data.merge!("msgstr[#{index}]" => plural) + end + else + allow(FastGettext::Translation).to receive(:_).with(msgid).and_return(translation) + data[:msgstr] = translation + end + + Gitlab::I18n::TranslationEntry.new( + data, + plurals.size + 1 + ) + end + describe '#errors' do it 'only calls validation once' do expect(linter).to receive(:validate_po).once.and_call_original @@ -155,9 +176,8 @@ describe Gitlab::I18n::PoLinter do describe '#validate_entries' do it 'keeps track of errors for entries' do - fake_invalid_entry = Gitlab::I18n::TranslationEntry.new( - { msgid: "Hello %{world}", msgstr: "Bonjour %{monde}" }, 2 - ) + fake_invalid_entry = fake_translation(msgid: "Hello %{world}", + translation: "Bonjour %{monde}") allow(linter).to receive(:translation_entries) { [fake_invalid_entry] } expect(linter).to receive(:validate_entry) @@ -177,6 +197,7 @@ describe Gitlab::I18n::PoLinter do expect(linter).to receive(:validate_newlines).with([], fake_entry) expect(linter).to receive(:validate_number_of_plurals).with([], fake_entry) expect(linter).to receive(:validate_unescaped_chars).with([], fake_entry) + expect(linter).to receive(:validate_translation).with([], fake_entry) linter.validate_entry(fake_entry) end @@ -185,7 +206,7 @@ describe Gitlab::I18n::PoLinter do describe '#validate_number_of_plurals' do it 'validates when there are an incorrect number of translations' do fake_metadata = double - allow(fake_metadata).to receive(:expected_plurals).and_return(2) + allow(fake_metadata).to receive(:expected_forms).and_return(2) allow(linter).to receive(:metadata_entry).and_return(fake_metadata) fake_entry = Gitlab::I18n::TranslationEntry.new( @@ -201,13 +222,16 @@ describe Gitlab::I18n::PoLinter do end describe '#validate_variables' do - it 'validates both signular and plural in a pluralized string when the entry has a singular' do - pluralized_entry = Gitlab::I18n::TranslationEntry.new( - { msgid: 'Hello %{world}', - msgid_plural: 'Hello all %{world}', - 'msgstr[0]' => 'Bonjour %{world}', - 'msgstr[1]' => 'Bonjour tous %{world}' }, - 2 + before do + allow(linter).to receive(:validate_variables_in_message).and_call_original + end + + it 'validates both singular and plural in a pluralized string when the entry has a singular' do + pluralized_entry = fake_translation( + msgid: 'Hello %{world}', + translation: 'Bonjour %{world}', + plural_id: 'Hello all %{world}', + plurals: ['Bonjour tous %{world}'] ) expect(linter).to receive(:validate_variables_in_message) @@ -221,11 +245,10 @@ describe Gitlab::I18n::PoLinter do end it 'only validates plural when there is no separate singular' do - pluralized_entry = Gitlab::I18n::TranslationEntry.new( - { msgid: 'Hello %{world}', - msgid_plural: 'Hello all %{world}', - 'msgstr[0]' => 'Bonjour %{world}' }, - 1 + pluralized_entry = fake_translation( + msgid: 'Hello %{world}', + translation: 'Bonjour %{world}', + plural_id: 'Hello all %{world}' ) expect(linter).to receive(:validate_variables_in_message) @@ -235,37 +258,65 @@ describe Gitlab::I18n::PoLinter do end it 'validates the message variables' do - entry = Gitlab::I18n::TranslationEntry.new( - { msgid: 'Hello', msgstr: 'Bonjour' }, - 2 - ) + entry = fake_translation(msgid: 'Hello', translation: 'Bonjour') expect(linter).to receive(:validate_variables_in_message) .with([], 'Hello', 'Bonjour') linter.validate_variables([], entry) end + + it 'validates variable usage in message ids' do + entry = fake_translation( + msgid: 'Hello %{world}', + translation: 'Bonjour %{world}', + plural_id: 'Hello all %{world}', + plurals: ['Bonjour tous %{world}'] + ) + + expect(linter).to receive(:validate_variables_in_message) + .with([], 'Hello %{world}', 'Hello %{world}') + .and_call_original + expect(linter).to receive(:validate_variables_in_message) + .with([], 'Hello all %{world}', 'Hello all %{world}') + .and_call_original + + linter.validate_variables([], entry) + end end describe '#validate_variables_in_message' do it 'detects when a variables are used incorrectly' do errors = [] - expected_errors = ['<hello %{world} %d> is missing: [%{hello}]', - '<hello %{world} %d> is using unknown variables: [%{world}]', - 'is combining multiple unnamed variables'] + expected_errors = ['<%d hello %{world} %s> is missing: [%{hello}]', + '<%d hello %{world} %s> is using unknown variables: [%{world}]', + 'is combining multiple unnamed variables', + 'is combining named variables with unnamed variables'] - linter.validate_variables_in_message(errors, '%{hello} world %d', 'hello %{world} %d') + linter.validate_variables_in_message(errors, '%d %{hello} world %s', '%d hello %{world} %s') expect(errors).to include(*expected_errors) end + + it 'does not allow combining 1 `%d` unnamed variable with named variables' do + errors = [] + + linter.validate_variables_in_message(errors, + '%{type} detected %d vulnerability', + '%{type} detecteerde %d kwetsbaarheid') + + expect(errors).not_to be_empty + end end describe '#validate_translation' do + let(:entry) { fake_translation(msgid: 'Hello %{world}', translation: 'Bonjour %{world}') } + it 'succeeds with valid variables' do errors = [] - linter.validate_translation(errors, 'Hello %{world}', ['%{world}']) + linter.validate_translation(errors, entry) expect(errors).to be_empty end @@ -275,43 +326,80 @@ describe Gitlab::I18n::PoLinter do expect(FastGettext::Translation).to receive(:_) { raise 'broken' } - linter.validate_translation(errors, 'Hello', []) + linter.validate_translation(errors, entry) - expect(errors).to include('Failure translating to en with []: broken') + expect(errors).to include('Failure translating to en: broken') end it 'adds an error message when translating fails when translating with context' do + entry = fake_translation(msgid: 'Tests|Hello', translation: 'broken') errors = [] expect(FastGettext::Translation).to receive(:s_) { raise 'broken' } - linter.validate_translation(errors, 'Tests|Hello', []) + linter.validate_translation(errors, entry) - expect(errors).to include('Failure translating to en with []: broken') + expect(errors).to include('Failure translating to en: broken') end it "adds an error when trying to translate with incorrect variables when using unnamed variables" do + entry = fake_translation(msgid: 'Hello %s', translation: 'Hello %d') errors = [] - linter.validate_translation(errors, 'Hello %d', ['%s']) + linter.validate_translation(errors, entry) - expect(errors.first).to start_with("Failure translating to en with") + expect(errors.first).to start_with("Failure translating to en") end it "adds an error when trying to translate with named variables when unnamed variables are expected" do + entry = fake_translation(msgid: 'Hello %s', translation: 'Hello %{thing}') errors = [] - linter.validate_translation(errors, 'Hello %d', ['%{world}']) + linter.validate_translation(errors, entry) - expect(errors.first).to start_with("Failure translating to en with") + expect(errors.first).to start_with("Failure translating to en") end - it 'adds an error when translated with incorrect variables using named variables' do - errors = [] + it 'tests translation for all given forms' do + # Fake a language that has 3 forms to translate + fake_metadata = double + allow(fake_metadata).to receive(:forms_to_test).and_return(3) + allow(linter).to receive(:metadata_entry).and_return(fake_metadata) + entry = fake_translation( + msgid: '%d exception', + translation: '%d uitzondering', + plural_id: '%d exceptions', + plurals: ['%d uitzonderingen', '%d uitzonderingetjes'] + ) + + # Make each count use a different index + allow(linter).to receive(:index_for_pluralization).with(0).and_return(0) + allow(linter).to receive(:index_for_pluralization).with(1).and_return(1) + allow(linter).to receive(:index_for_pluralization).with(2).and_return(2) + + expect(FastGettext::Translation).to receive(:n_).with('%d exception', '%d exceptions', 0).and_call_original + expect(FastGettext::Translation).to receive(:n_).with('%d exception', '%d exceptions', 1).and_call_original + expect(FastGettext::Translation).to receive(:n_).with('%d exception', '%d exceptions', 2).and_call_original + + linter.validate_translation([], entry) + end + end + + describe '#numbers_covering_all_plurals' do + it 'can correctly find all required numbers to translate to Polish' do + # Polish used as an example with 3 different forms: + # 0, all plurals except the ones ending in 2,3,4: Kotów + # 1: Kot + # 2-3-4: Koty + # So translating with [0, 1, 2] will give us all different posibilities + fake_metadata = double + allow(fake_metadata).to receive(:forms_to_test).and_return(4) + allow(linter).to receive(:metadata_entry).and_return(fake_metadata) + allow(linter).to receive(:locale).and_return('pl_PL') - linter.validate_translation(errors, 'Hello %{thing}', ['%d']) + numbers = linter.numbers_covering_all_plurals - expect(errors.first).to start_with("Failure translating to en with") + expect(numbers).to contain_exactly(0, 1, 2) end end @@ -336,3 +424,4 @@ describe Gitlab::I18n::PoLinter do end end end +# rubocop:enable Style/AsciiComments |