Merge commit 'b922ad7a1b4bc8d968d0df2c7f307d4fec53435d' into glitch-soc/merge-upstream
Conflicts: - `package.json`: Upstream changed various script definitions in lines surrounding the one for `i18n:extract`, which had glitch-soc-specific changes. Updated the scripts as upstream did, while keeping our changes to `i18n:extract`.
This commit is contained in:
@ -19,7 +19,7 @@ describe Api::V1::Statuses::TranslationsController do
|
||||
|
||||
before do
|
||||
translation = TranslationService::Translation.new(text: 'Hello')
|
||||
service = instance_double(TranslationService::DeepL, translate: translation)
|
||||
service = instance_double(TranslationService::DeepL, translate: [translation])
|
||||
allow(TranslationService).to receive(:configured?).and_return(true)
|
||||
allow(TranslationService).to receive(:configured).and_return(service)
|
||||
Rails.cache.write('translation_service/languages', { 'es' => ['en'] })
|
||||
|
@ -209,6 +209,18 @@ describe ApplicationHelper do
|
||||
end
|
||||
end
|
||||
|
||||
context 'when S3 alias includes a path component' do
|
||||
around do |example|
|
||||
ClimateControl.modify S3_ALIAS_HOST: 's3.alias/path' do
|
||||
example.run
|
||||
end
|
||||
end
|
||||
|
||||
it 'returns a correct URL' do
|
||||
expect(helper.storage_host).to eq('https://s3.alias/path')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when S3 cloudfront is present' do
|
||||
around do |example|
|
||||
ClimateControl.modify S3_CLOUDFRONT_HOST: 's3.cloudfront' do
|
||||
@ -220,12 +232,6 @@ describe ApplicationHelper do
|
||||
expect(helper.storage_host).to eq('https://s3.cloudfront')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when neither env value is present' do
|
||||
it 'returns false' do
|
||||
expect(helper.storage_host).to eq('https:')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'storage_host?' do
|
||||
|
@ -22,7 +22,10 @@ RSpec.describe TranslationService::DeepL do
|
||||
.with(body: 'text=Hasta+la+vista&source_lang=ES&target_lang=en&tag_handling=html')
|
||||
.to_return(body: '{"translations":[{"detected_source_language":"ES","text":"See you soon"}]}')
|
||||
|
||||
translation = service.translate('Hasta la vista', 'es', 'en')
|
||||
translations = service.translate(['Hasta la vista'], 'es', 'en')
|
||||
expect(translations.size).to eq 1
|
||||
|
||||
translation = translations.first
|
||||
expect(translation.detected_source_language).to eq 'es'
|
||||
expect(translation.provider).to eq 'DeepL.com'
|
||||
expect(translation.text).to eq 'See you soon'
|
||||
@ -31,12 +34,27 @@ RSpec.describe TranslationService::DeepL do
|
||||
it 'returns translation with auto-detected source language' do
|
||||
stub_request(:post, 'https://api.deepl.com/v2/translate')
|
||||
.with(body: 'text=Guten+Tag&source_lang&target_lang=en&tag_handling=html')
|
||||
.to_return(body: '{"translations":[{"detected_source_language":"DE","text":"Good Morning"}]}')
|
||||
.to_return(body: '{"translations":[{"detected_source_language":"DE","text":"Good morning"}]}')
|
||||
|
||||
translation = service.translate('Guten Tag', nil, 'en')
|
||||
translations = service.translate(['Guten Tag'], nil, 'en')
|
||||
expect(translations.size).to eq 1
|
||||
|
||||
translation = translations.first
|
||||
expect(translation.detected_source_language).to eq 'de'
|
||||
expect(translation.provider).to eq 'DeepL.com'
|
||||
expect(translation.text).to eq 'Good Morning'
|
||||
expect(translation.text).to eq 'Good morning'
|
||||
end
|
||||
|
||||
it 'returns translation of multiple texts' do
|
||||
stub_request(:post, 'https://api.deepl.com/v2/translate')
|
||||
.with(body: 'text=Guten+Morgen&text=Gute+Nacht&source_lang=DE&target_lang=en&tag_handling=html')
|
||||
.to_return(body: '{"translations":[{"detected_source_language":"DE","text":"Good morning"},{"detected_source_language":"DE","text":"Good night"}]}')
|
||||
|
||||
translations = service.translate(['Guten Morgen', 'Gute Nacht'], 'de', 'en')
|
||||
expect(translations.size).to eq 2
|
||||
|
||||
expect(translations.first.text).to eq 'Good morning'
|
||||
expect(translations.last.text).to eq 'Good night'
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -31,24 +31,42 @@ RSpec.describe TranslationService::LibreTranslate do
|
||||
describe '#translate' do
|
||||
it 'returns translation with specified source language' do
|
||||
stub_request(:post, 'https://libretranslate.example.com/translate')
|
||||
.with(body: '{"q":"Hasta la vista","source":"es","target":"en","format":"html","api_key":"my-api-key"}')
|
||||
.to_return(body: '{"translatedText": "See you"}')
|
||||
.with(body: '{"q":["Hasta la vista"],"source":"es","target":"en","format":"html","api_key":"my-api-key"}')
|
||||
.to_return(body: '{"translatedText": ["See you"]}')
|
||||
|
||||
translation = service.translate('Hasta la vista', 'es', 'en')
|
||||
expect(translation.detected_source_language).to eq 'es'
|
||||
translations = service.translate(['Hasta la vista'], 'es', 'en')
|
||||
expect(translations.size).to eq 1
|
||||
|
||||
translation = translations.first
|
||||
expect(translation.detected_source_language).to be 'es'
|
||||
expect(translation.provider).to eq 'LibreTranslate'
|
||||
expect(translation.text).to eq 'See you'
|
||||
end
|
||||
|
||||
it 'returns translation with auto-detected source language' do
|
||||
stub_request(:post, 'https://libretranslate.example.com/translate')
|
||||
.with(body: '{"q":"Guten Morgen","source":"auto","target":"en","format":"html","api_key":"my-api-key"}')
|
||||
.to_return(body: '{"detectedLanguage":{"confidence":92,"language":"de"},"translatedText":"Good morning"}')
|
||||
.with(body: '{"q":["Guten Morgen"],"source":"auto","target":"en","format":"html","api_key":"my-api-key"}')
|
||||
.to_return(body: '{"detectedLanguage": [{"confidence": 92, "language": "de"}], "translatedText": ["Good morning"]}')
|
||||
|
||||
translation = service.translate('Guten Morgen', nil, 'en')
|
||||
expect(translation.detected_source_language).to be_nil
|
||||
translations = service.translate(['Guten Morgen'], nil, 'en')
|
||||
expect(translations.size).to eq 1
|
||||
|
||||
translation = translations.first
|
||||
expect(translation.detected_source_language).to eq 'de'
|
||||
expect(translation.provider).to eq 'LibreTranslate'
|
||||
expect(translation.text).to eq 'Good morning'
|
||||
end
|
||||
|
||||
it 'returns translation of multiple texts' do
|
||||
stub_request(:post, 'https://libretranslate.example.com/translate')
|
||||
.with(body: '{"q":["Guten Morgen","Gute Nacht"],"source":"de","target":"en","format":"html","api_key":"my-api-key"}')
|
||||
.to_return(body: '{"translatedText": ["Good morning", "Good night"]}')
|
||||
|
||||
translations = service.translate(['Guten Morgen', 'Gute Nacht'], 'de', 'en')
|
||||
expect(translations.size).to eq 2
|
||||
|
||||
expect(translations.first.text).to eq 'Good morning'
|
||||
expect(translations.last.text).to eq 'Good night'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
226
spec/services/translate_status_service_spec.rb
Normal file
226
spec/services/translate_status_service_spec.rb
Normal file
@ -0,0 +1,226 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe TranslateStatusService, type: :service do
|
||||
subject(:service) { described_class.new }
|
||||
|
||||
let(:status) { Fabricate(:status, text: text, spoiler_text: spoiler_text, language: 'en', preloadable_poll: poll, media_attachments: media_attachments) }
|
||||
let(:text) { 'Hello' }
|
||||
let(:spoiler_text) { '' }
|
||||
let(:poll) { nil }
|
||||
let(:media_attachments) { [] }
|
||||
|
||||
before do
|
||||
Fabricate(:custom_emoji, shortcode: 'highfive')
|
||||
end
|
||||
|
||||
describe '#call' do
|
||||
before do
|
||||
translation_service = TranslationService.new
|
||||
allow(translation_service).to receive(:languages).and_return({ 'en' => ['es'] })
|
||||
allow(translation_service).to receive(:translate) do |texts|
|
||||
texts.map do |text|
|
||||
TranslationService::Translation.new(
|
||||
text: text.gsub('Hello', 'Hola').gsub('higfive', 'cincoaltos'),
|
||||
detected_source_language: 'en',
|
||||
provider: 'Dummy'
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
allow(TranslationService).to receive(:configured?).and_return(true)
|
||||
allow(TranslationService).to receive(:configured).and_return(translation_service)
|
||||
end
|
||||
|
||||
it 'returns translated status content' do
|
||||
expect(service.call(status, 'es').content).to eq '<p>Hola</p>'
|
||||
end
|
||||
|
||||
it 'returns source language' do
|
||||
expect(service.call(status, 'es').detected_source_language).to eq 'en'
|
||||
end
|
||||
|
||||
it 'returns translation provider' do
|
||||
expect(service.call(status, 'es').provider).to eq 'Dummy'
|
||||
end
|
||||
|
||||
it 'returns original status' do
|
||||
expect(service.call(status, 'es').status).to eq status
|
||||
end
|
||||
|
||||
describe 'status has content with custom emoji' do
|
||||
let(:text) { 'Hello & :highfive:' }
|
||||
|
||||
it 'does not translate shortcode' do
|
||||
expect(service.call(status, 'es').content).to eq '<p>Hola & :highfive:</p>'
|
||||
end
|
||||
end
|
||||
|
||||
describe 'status has no spoiler_text' do
|
||||
it 'returns an empty string' do
|
||||
expect(service.call(status, 'es').spoiler_text).to eq ''
|
||||
end
|
||||
end
|
||||
|
||||
describe 'status has spoiler_text' do
|
||||
let(:spoiler_text) { 'Hello & Hello!' }
|
||||
|
||||
it 'translates the spoiler text' do
|
||||
expect(service.call(status, 'es').spoiler_text).to eq 'Hola & Hola!'
|
||||
end
|
||||
end
|
||||
|
||||
describe 'status has spoiler_text with custom emoji' do
|
||||
let(:spoiler_text) { 'Hello :highfive:' }
|
||||
|
||||
it 'does not translate shortcode' do
|
||||
expect(service.call(status, 'es').spoiler_text).to eq 'Hola :highfive:'
|
||||
end
|
||||
end
|
||||
|
||||
describe 'status has spoiler_text with unmatched custom emoji' do
|
||||
let(:spoiler_text) { 'Hello :Hello:' }
|
||||
|
||||
it 'translates the invalid shortcode' do
|
||||
expect(service.call(status, 'es').spoiler_text).to eq 'Hola :Hola:'
|
||||
end
|
||||
end
|
||||
|
||||
describe 'status has poll' do
|
||||
let(:poll) { Fabricate(:poll, options: ['Hello 1', 'Hello 2']) }
|
||||
|
||||
it 'translates the poll option title' do
|
||||
status_translation = service.call(status, 'es')
|
||||
expect(status_translation.poll_options.size).to eq 2
|
||||
expect(status_translation.poll_options.first.title).to eq 'Hola 1'
|
||||
end
|
||||
end
|
||||
|
||||
describe 'status has media attachment' do
|
||||
let(:media_attachments) { [Fabricate(:media_attachment, description: 'Hello & :highfive:')] }
|
||||
|
||||
it 'translates the media attachment description' do
|
||||
status_translation = service.call(status, 'es')
|
||||
|
||||
media_attachment = status_translation.media_attachments.first
|
||||
expect(media_attachment.id).to eq media_attachments.first.id
|
||||
expect(media_attachment.description).to eq 'Hola & :highfive:'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#source_texts' do
|
||||
before do
|
||||
service.instance_variable_set(:@status, status)
|
||||
end
|
||||
|
||||
describe 'status only has content' do
|
||||
it 'returns formatted content' do
|
||||
expect(service.send(:source_texts)).to eq({ content: '<p>Hello</p>' })
|
||||
end
|
||||
end
|
||||
|
||||
describe 'status content contains custom emoji' do
|
||||
let(:status) { Fabricate(:status, text: 'Hello :highfive:') }
|
||||
|
||||
it 'returns formatted content' do
|
||||
source_texts = service.send(:source_texts)
|
||||
expect(source_texts[:content]).to eq '<p>Hello <span translate="no">:highfive:</span></p>'
|
||||
end
|
||||
end
|
||||
|
||||
describe 'status content contains tags' do
|
||||
let(:status) { Fabricate(:status, text: 'Hello #hola') }
|
||||
|
||||
it 'returns formatted content' do
|
||||
source_texts = service.send(:source_texts)
|
||||
expect(source_texts[:content]).to include '<p>Hello <a'
|
||||
expect(source_texts[:content]).to include '/tags/hola'
|
||||
end
|
||||
end
|
||||
|
||||
describe 'status has spoiler text' do
|
||||
let(:status) { Fabricate(:status, spoiler_text: 'Hello :highfive:') }
|
||||
|
||||
it 'returns formatted spoiler text' do
|
||||
source_texts = service.send(:source_texts)
|
||||
expect(source_texts[:spoiler_text]).to eq 'Hello <span translate="no">:highfive:</span>'
|
||||
end
|
||||
end
|
||||
|
||||
describe 'status has poll' do
|
||||
let(:poll) { Fabricate(:poll, options: %w(Blue Green)) }
|
||||
|
||||
it 'returns formatted poll options' do
|
||||
source_texts = service.send(:source_texts)
|
||||
expect(source_texts.size).to eq 3
|
||||
expect(source_texts.values).to eq %w(<p>Hello</p> Blue Green)
|
||||
|
||||
expect(source_texts.keys.first).to eq :content
|
||||
|
||||
option1 = source_texts.keys.second
|
||||
expect(option1).to be_a Poll::Option
|
||||
expect(option1.id).to eq '0'
|
||||
expect(option1.title).to eq 'Blue'
|
||||
|
||||
option2 = source_texts.keys.third
|
||||
expect(option2).to be_a Poll::Option
|
||||
expect(option2.id).to eq '1'
|
||||
expect(option2.title).to eq 'Green'
|
||||
end
|
||||
end
|
||||
|
||||
describe 'status has poll with custom emoji' do
|
||||
let(:poll) { Fabricate(:poll, options: ['Blue', 'Green :highfive:']) }
|
||||
|
||||
it 'returns formatted poll options' do
|
||||
html = service.send(:source_texts).values.last
|
||||
expect(html).to eq 'Green <span translate="no">:highfive:</span>'
|
||||
end
|
||||
end
|
||||
|
||||
describe 'status has media attachments' do
|
||||
let(:text) { '' }
|
||||
let(:media_attachments) { [Fabricate(:media_attachment, description: 'Hello :highfive:')] }
|
||||
|
||||
it 'returns media attachments without custom emoji rendering' do
|
||||
source_texts = service.send(:source_texts)
|
||||
expect(source_texts.size).to eq 1
|
||||
|
||||
key, text = source_texts.first
|
||||
expect(key).to eq media_attachments.first
|
||||
expect(text).to eq 'Hello :highfive:'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#wrap_emoji_shortcodes' do
|
||||
before do
|
||||
service.instance_variable_set(:@status, status)
|
||||
end
|
||||
|
||||
describe 'string contains custom emoji' do
|
||||
let(:text) { ':highfive:' }
|
||||
|
||||
it 'renders the emoji' do
|
||||
html = service.send(:wrap_emoji_shortcodes, 'Hello :highfive:'.html_safe)
|
||||
expect(html).to eq 'Hello <span translate="no">:highfive:</span>'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#unwrap_emoji_shortcodes' do
|
||||
describe 'string contains custom emoji' do
|
||||
it 'inserts the shortcode' do
|
||||
fragment = service.send(:unwrap_emoji_shortcodes, '<p>Hello <span translate="no">:highfive:</span>!</p>')
|
||||
expect(fragment.to_html).to eq '<p>Hello :highfive:!</p>'
|
||||
end
|
||||
|
||||
it 'preserves other attributes than translate=no' do
|
||||
fragment = service.send(:unwrap_emoji_shortcodes, '<p>Hello <span translate="no" class="foo">:highfive:</span>!</p>')
|
||||
expect(fragment.to_html).to eq '<p>Hello <span class="foo">:highfive:</span>!</p>'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
Reference in New Issue
Block a user