diff --git a/app/controllers/translations_controller.rb b/app/controllers/translations_controller.rb index ea71c8df..468480e8 100644 --- a/app/controllers/translations_controller.rb +++ b/app/controllers/translations_controller.rb @@ -106,21 +106,22 @@ def update # translators cannot modify approved copy return head(:forbidden) if @translation.approved? && current_user.role == 'translator' - TranslationUpdateMediator.new(@translation, current_user, params).update + mediator = TranslationUpdateMediator.new(@translation, current_user, params) + mediator.update! respond_with(@translation, location: project_key_translation_url(@project, @key, @translation)) do |format| format.json do - if @translation.valid? + if mediator.success? render json: decorate([@translation]).first else - render json: @translation.errors.full_messages, status: :unprocessable_entity + render json: mediator.errors, status: :unprocessable_entity end end format.html do - if @translation.errors.any? - redirect_to edit_project_key_translation_url(@project, @key, @translation), flash: { alert: @translation.errors.full_messages.unshift(t('controllers.translations.update.failure')) } - else + if mediator.success? redirect_to edit_project_key_translation_url(@project, @key, @translation), flash: { success: t('controllers.translations.update.success') } + else + redirect_to edit_project_key_translation_url(@project, @key, @translation), flash: { alert: mediator.errors.unshift(t('controllers.translations.update.failure')) } end end end diff --git a/app/mediators/basic_mediator.rb b/app/mediators/basic_mediator.rb new file mode 100644 index 00000000..2fd1f1b7 --- /dev/null +++ b/app/mediators/basic_mediator.rb @@ -0,0 +1,60 @@ +# Copyright 2014 Square Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Implements basic mediator functionality such as keeping track of errors and +# providing helper methods to interact with them. +# +# Fields +# ====== +# +# | | | +# |:---------|:---------------------------------------------------------------------------------| +# | `errors` | an array which should keep track of errors that happened in the inheriting class | + +class BasicMediator + + attr_reader :errors + + def initialize + @errors = [] + end + + # @return [true, false] true if no errors have been recorded, false otherwise + def success? + @errors.blank? + end + + # @return [true, false] true if any errors have been recorded, false otherwise + def failure? + !success? + end + + private + + # Add multiple errors at once + # + # @param [Array] msgs a list of error messages in String format + + def add_errors(msgs) + @errors.push(*msgs) + end + + # Add a single error + # + # @param [String] msg an error message in String format + + def add_error(msg) + @errors.push(msg) + end +end diff --git a/app/mediators/translation_update_mediator.rb b/app/mediators/translation_update_mediator.rb index de668dcf..bb5998f2 100644 --- a/app/mediators/translation_update_mediator.rb +++ b/app/mediators/translation_update_mediator.rb @@ -23,24 +23,38 @@ # | `user` | {User} who is making the change | # | `params` | params from controller | -class TranslationUpdateMediator +class TranslationUpdateMediator < BasicMediator # @param [Translation] primary_translation that will be updated # @param [User] user who is making the changes # @param [ActionController::Parameters] params that will be used to update the translation def initialize(primary_translation, user, params) + super() @primary_translation, @user, @params = primary_translation, user, params end # Updates this translation and its associated translations - def update - update_single_translation(@primary_translation, @params[:blank_string], @params.require(:translation).permit(:copy, :notes)) - # TODO (yunus): update user specified associated translations + def update! + copy_to_translations = translations_that_should_be_multi_updated + return if failure? + + Translation.transaction do + copy_to_translations.each do |translation| + update_single_translation!(translation) + end + update_single_translation!(@primary_translation) + end + + @primary_translation.key.reload.recalculate_ready! # Readiness hooks were skipped in the transaction above, now we gotta run them. + rescue ActiveRecord::RecordInvalid => err + add_errors(err.record.errors.full_messages.map { |msg| "(#{err.record.rfc5646_locale}): #{msg}" }) end - # Finds all translations that can be updated alongside the inputted {Translation} with the same copy. - # These translations make the keys of the returned hash. The values are the {LocaleAssociation LocaleAssociations} + # Finds all translations that is allowed to be updated alongside the inputted {Translation} with the same copy, + # based on {LocaleAssociation LocaleAssociations}. Doesn't include self, or base translation. + # + # These translations are the keys of the returned hash. The values are the {LocaleAssociation LocaleAssociations} # that tie the associated translations to the inputted translation. # # This should be used in 2 places: @@ -64,24 +78,45 @@ def self.multi_updateable_translations_to_locale_associations_hash(translation) private - # @return [Array] a list of translations that can be updated alongside the primary translation. - def multi_updateable_translations - self.class.multi_updateable_translations_to_locale_associations_hash(@primary_translation).keys + # Retrive all translations that should be updated alongside the primary translation, based on the `copyToLocales` + # field in user provided params. It maps the user provided `copyToLocales` to {Translation} objects. + # + # If one of the requested locales are not allowed to be updated, it adds an error and breaks out of the loop. + # + # The consumer of this function should check for errors before proceeding. + # + # @return [Array] array of translations that should be updated alongside the primary translation + + def translations_that_should_be_multi_updated + return [] unless @params[:copyToLocales] + translations_indexed_by_rfc5646_locale = self.class.multi_updateable_translations_to_locale_associations_hash(@primary_translation).keys.index_by(&:rfc5646_locale) + @params[:copyToLocales].map do |rfc5646_locale| + unless translations_indexed_by_rfc5646_locale.key?(rfc5646_locale) + add_error("Cannot update translation in locale #{rfc5646_locale}") + break + end + translations_indexed_by_rfc5646_locale[rfc5646_locale] + end + end + + # @return [Hash] a hash of rfc5646_locale to translations that is allowed to be updated alongside the primary translation. + def multi_updateable_translations_indexed_by_rfc5646_locale + @multi_updateable_translations_indexed_by_rfc5646_locale ||= + self.class.multi_updateable_translations_to_locale_associations_hash(@primary_translation).keys.index_by(&:rfc5646_locale) end # Updates a single translation. # # @param [Translation] translation that will be updated - # @param [true, false] is_blank_string true if the translation should be updated as an empty string, false otherwise - # @param [ActionController::Parameters] permitted_params that will be used to update the translation + # @raise [ActiveRecord::RecordInvalid] if translation is invalid - def update_single_translation(translation, is_blank_string, permitted_params) + def update_single_translation!(translation) translation.freeze_tracked_attributes translation.modifier = @user - translation.assign_attributes(permitted_params) + translation.assign_attributes(@params.require(:translation).permit(:copy, :notes)) # un-translate translation if empty but blank_string is not specified - if translation.copy.blank? && !is_blank_string + if translation.copy.blank? && !@params[:blank_string].parse_bool untranslate(translation) else translation.translator = @user if translation.copy != translation.copy_was @@ -91,7 +126,8 @@ def update_single_translation(translation, is_blank_string, permitted_params) translation.preserve_reviewed_status = true end end - translation.save + translation.skip_readiness_hooks = true + translation.save! end # Untranslates a translation, but doesn't call `save`. diff --git a/spec/controllers/translations_controller_spec.rb b/spec/controllers/translations_controller_spec.rb index 4c6e852e..cd273070 100644 --- a/spec/controllers/translations_controller_spec.rb +++ b/spec/controllers/translations_controller_spec.rb @@ -318,6 +318,47 @@ expect(translation.translation_changes.count).to eq(1) end end + + context "[multi-locale update]" do + before :each do + @user.update role: 'reviewer' + + @project = FactoryGirl.create(:project, targeted_rfc5646_locales: { 'fr' => true, 'fr-CA' =>true, 'fr-FR' => true } ) + @key = FactoryGirl.create(:key, ready: false, project:@project) + @fr_translation = FactoryGirl.create(:translation, key: @key, copy: nil, translator: nil, rfc5646_locale: 'fr') + @fr_CA_translation = FactoryGirl.create(:translation, key: @key, copy: nil, translator: nil, rfc5646_locale: 'fr-CA') + @fr_FR_translation = FactoryGirl.create(:translation, key: @key, copy: nil, translator: nil, rfc5646_locale: 'fr-FR') + @fr_to_fr_CA = FactoryGirl.create(:locale_association, source_rfc5646_locale: 'fr', target_rfc5646_locale: 'fr-CA') + @fr_to_fr_FR = FactoryGirl.create(:locale_association, source_rfc5646_locale: 'fr', target_rfc5646_locale: 'fr-FR') + expect(@key.reload).to_not be_ready + end + + it "updates the primary translation and 2 associated translations that user specifies" do + patch :update, project_id: @project.to_param, key_id: @key.to_param, id: @fr_translation.to_param, translation: { copy: "test" }, copyToLocales: %w(fr-CA fr-FR), format: 'json' + + expect(@fr_translation.reload.copy).to eql("test") + expect(@fr_translation.translator).to eql(@user) + expect(@fr_CA_translation.reload.copy).to eql("test") + expect(@fr_CA_translation.translator).to eql(@user) + expect(@fr_FR_translation.reload.copy).to eql("test") + expect(@fr_FR_translation.translator).to eql(@user) + expect(@key.reload.ready).to be_true + end + + it "doesn't update any of the requested translations because one of the translations are not linked to the primary one with a LocaleAssociation" do + @fr_to_fr_CA.delete + + patch :update, project_id: @project.to_param, key_id: @key.to_param, id: @fr_translation.to_param, translation: { copy: "test" }, copyToLocales: %w(fr-CA fr-FR), format: 'json' + + expect(@fr_translation.reload.copy).to be_nil + expect(@fr_translation.translator).to be_nil + expect(@fr_CA_translation.reload.copy).to be_nil + expect(@fr_CA_translation.translator).to be_nil + expect(@fr_FR_translation.reload.copy).to be_nil + expect(@fr_FR_translation.translator).to be_nil + expect(@key.reload.ready).to be_false + end + end end describe "#approve" do diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb index c2923b2b..0cc76765 100644 --- a/spec/controllers/users_controller_spec.rb +++ b/spec/controllers/users_controller_spec.rb @@ -95,14 +95,6 @@ {"name"=>"test2 user3", "email"=>"user3@example.com"}, {"name"=>"1test2 user4", "email"=>"user4@example.com"}]) end - - it "returns fuzzy matched users' email addresses and names" do - pending # TODO (yunus): implement fuzzy matching - FactoryGirl.create(:user, :activated, first_name: "tast", last_name: "user1", email: 'user1@example.com') # fuzzy match - FactoryGirl.create(:user, :activated, first_name: "attast", last_name: "user2", email: 'user2@example.com') # miss - subject - expect(JSON.parse(response.body)).to eql([{"name"=>"tast user1", "email"=>"user1@example.com"}]) - end end context "[matching by last name]" do @@ -118,14 +110,6 @@ {"name"=>"user3 test2", "email"=>"user3@example.com"}, {"name"=>"user4 1test2", "email"=>"user4@example.com"}]) end - - it "returns fuzzy matched users' email addresses and names" do - pending # TODO (yunus): implement fuzzy matching - FactoryGirl.create(:user, :activated, first_name: "user1", last_name: "tast", email: 'user1@example.com') # fuzzy match - FactoryGirl.create(:user, :activated, first_name: "user2", last_name: "attast", email: 'user2@example.com') # miss - subject - expect(JSON.parse(response.body)).to eql([{"name"=>"user1 tast", "email"=>"user1@example.com"}]) - end end diff --git a/spec/factories/users.rb b/spec/factories/users.rb index de12a037..af54946f 100644 --- a/spec/factories/users.rb +++ b/spec/factories/users.rb @@ -27,5 +27,17 @@ role { 'monitor' } confirmed_at { Time.now } end + + trait :translator do + role { 'translator' } + end + + trait :reviewer do + role { 'reviewer' } + end + + trait :admin do + role { 'admin' } + end end end diff --git a/spec/mediators/basic_mediator_spec.rb b/spec/mediators/basic_mediator_spec.rb new file mode 100644 index 00000000..5d84bc73 --- /dev/null +++ b/spec/mediators/basic_mediator_spec.rb @@ -0,0 +1,59 @@ +# Copyright 2014 Square Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'spec_helper' + +describe BasicMediator do + before :each do + @mediator = BasicMediator.new + end + + describe "#success?" do + it "returns true if there are no errors" do + expect(@mediator.success?).to eql(true) + end + + it "returns false if there are any errors" do + @mediator.send(:add_error, "Test") + expect(@mediator.success?).to eql(false) + end + end + + describe "#failure?" do + it "returns true if there are any errors" do + @mediator.send(:add_error, "Test") + expect(@mediator.failure?).to eql(true) + end + + it "returns false if there are no errors" do + expect(@mediator.failure?).to eql(false) + end + end + + describe "#add_error" do + it "adds an error to mediator" do + @mediator.send(:add_error, "Test1") + @mediator.send(:add_error, "Test2") + expect(@mediator.errors).to eql(["Test1", "Test2"]) + end + end + + describe "#add_errors" do + it "adds errors in bulk to mediator" do + @mediator.send(:add_errors, ["Test1", "Test2"]) + @mediator.send(:add_errors, ["Test3", "Test4"]) + expect(@mediator.errors).to eql(["Test1", "Test2", "Test3", "Test4"]) + end + end +end diff --git a/spec/mediators/translation_update_mediator_spec.rb b/spec/mediators/translation_update_mediator_spec.rb index c7df885c..37f6daca 100644 --- a/spec/mediators/translation_update_mediator_spec.rb +++ b/spec/mediators/translation_update_mediator_spec.rb @@ -17,6 +17,92 @@ require 'spec_helper' describe TranslationUpdateMediator do + let(:translator) { FactoryGirl.create(:user, :translator) } + let(:reviewer) { FactoryGirl.create(:user, :reviewer) } + + describe "#update!" do + before :each do + @params = ActionController::Parameters.new(translation: { copy: "test copy", notes: "test note" }) + + project = FactoryGirl.create(:project, targeted_rfc5646_locales: { 'fr' => true, 'fr-CA' =>true, 'fr-FR' => true } ) + @key = FactoryGirl.create(:key, ready: false, project: project) + @fr_translation = FactoryGirl.create(:translation, key: @key, copy: nil, translator: nil, rfc5646_locale: 'fr') + expect(@key.reload).to_not be_ready + end + + it "updates a single translation's 'copy' and 'notes' fields; doesn't approve translation if translator is not a reviewer; key doesn't become ready" do + TranslationUpdateMediator.new(@fr_translation, translator, @params).update! + expect(@fr_translation.reload.copy).to eql("test copy") + expect(@fr_translation.notes).to eql("test note") + expect(@fr_translation.translator).to eql(translator) + expect(@fr_translation.approved).to be_nil + expect(@fr_translation.reviewer).to be_nil + expect(@key.reload).to_not be_ready + end + + it "updates a single translation; approve translation if translator is a reviewer; key becomes ready" do + TranslationUpdateMediator.new(@fr_translation, reviewer, @params).update! + expect(@fr_translation.copy).to eql("test copy") + expect(@fr_translation.translator).to eql(reviewer) + expect(@fr_translation.approved).to be_true + expect(@fr_translation.reviewer).to eql(reviewer) + expect(@key.reload).to be_ready + end + + context "[update multiple]" do + before :each do + FactoryGirl.create(:locale_association, source_rfc5646_locale: 'fr', target_rfc5646_locale: 'fr-CA') + FactoryGirl.create(:locale_association, source_rfc5646_locale: 'fr', target_rfc5646_locale: 'fr-FR') + + @fr_CA_translation = FactoryGirl.create(:translation, key: @key, copy: nil, translator: nil, rfc5646_locale: 'fr-CA') + @fr_FR_translation = FactoryGirl.create(:translation, key: @key, copy: nil, translator: nil, rfc5646_locale: 'fr-FR') + end + + it "updates the primary translation and 2 associated translations that user specified; sets key readiness to true if all translations are approved" do + params = ActionController::Parameters.new( translation: { copy: "test" }, copyToLocales: %w(fr-CA fr-FR) ) + TranslationUpdateMediator.new(@fr_translation, reviewer, params).update! + expect(@fr_translation.reload.copy).to eql("test") + expect(@fr_translation.translator).to eql(reviewer) + expect(@fr_CA_translation.reload.copy).to eql("test") + expect(@fr_CA_translation.translator).to eql(reviewer) + expect(@fr_FR_translation.reload.copy).to eql("test") + expect(@fr_FR_translation.translator).to eql(reviewer) + expect(@key.reload.ready).to be_true + end + + it "recalculates readiness of the key only once even if it's updating multiple translations" do + params = ActionController::Parameters.new( translation: { copy: "test" }, copyToLocales: %w(fr-CA fr-FR) ) + expect_any_instance_of(Key).to receive(:recalculate_ready!).once.and_call_original + TranslationUpdateMediator.new(@fr_translation, reviewer, params).update! + expect(@key.reload.ready).to be_true + end + + it "doesn't update any of the requested translations because one of the translations don't exist" do + FactoryGirl.create(:locale_association, source_rfc5646_locale: 'fr', target_rfc5646_locale: 'fr-XX') + params = ActionController::Parameters.new( translation: { copy: "test" }, copyToLocales: %w(fr-CA fr-FR fr-XX) ) + mediator = TranslationUpdateMediator.new(@fr_translation, reviewer, params) + mediator.update! + expect(@fr_translation.reload.copy).to be_nil + expect(@fr_CA_translation.reload.copy).to be_nil + expect(@fr_FR_translation.reload.copy).to be_nil + expect(mediator.errors).to eql(["Cannot update translation in locale fr-XX"]) + end + + it "doesn't update any of the requested translations because there is a problem with one of them" do + @fr_translation.errors.add(:fake, 'error') + @fr_translation.stub!(:save!).and_raise(ActiveRecord::RecordInvalid, @fr_translation) # should cause save! to fail + + params = ActionController::Parameters.new( translation: { copy: "test" }, copyToLocales: %w(fr-CA fr-FR) ) + mediator = TranslationUpdateMediator.new(@fr_translation, reviewer, params) + mediator.update! + expect(@fr_translation.reload.copy).to be_nil + expect(@fr_CA_translation.reload.copy).to be_nil + expect(@fr_FR_translation.reload.copy).to be_nil + expect(mediator.errors).to eql(["(fr): Fake error"]) + end + end + end + describe "#multi_updateable_translations_to_locale_associations_hash" do it "returns a hash of translations that can be updated with the same copy as the primary translation will be updated with, and the LocaleAssociations that connect these translations with the primary translation" do @@ -46,4 +132,150 @@ expect(TranslationUpdateMediator.multi_updateable_translations_to_locale_associations_hash(translation1)).to eql(translation3 => la2) end end + + describe "#translations_that_should_be_multi_updated" do + before :each do + FactoryGirl.create(:locale_association, source_rfc5646_locale: 'fr', target_rfc5646_locale: 'fr-CA') + FactoryGirl.create(:locale_association, source_rfc5646_locale: 'fr', target_rfc5646_locale: 'fr-FR') + @key = FactoryGirl.create(:key) + @fr_translation = FactoryGirl.create(:translation, key: @key, copy: nil, translator: nil, rfc5646_locale: 'fr') + end + + it "returns the Translation objects corresponding to the user provided copyToLocales param; doesn't add an error if all are valid" do + fr_CA_translation = FactoryGirl.create(:translation, key: @key, copy: nil, translator: nil, rfc5646_locale: 'fr-CA') + fr_FR_translation = FactoryGirl.create(:translation, key: @key, copy: nil, translator: nil, rfc5646_locale: 'fr-FR') + mediator = TranslationUpdateMediator.new(@fr_translation, translator, ActionController::Parameters.new( copyToLocales: %w(fr-CA fr-FR) )) + expect(mediator.send(:translations_that_should_be_multi_updated)).to eql([fr_CA_translation, fr_FR_translation]) + expect(mediator.success?).to be_true + end + + it "adds an error to the mediator if one of the locales user wanted to copy to is not valid because there is no LocaleAssociation to that locales" do + fr_XX_translation = FactoryGirl.create(:translation, key: @key, copy: nil, translator: nil, rfc5646_locale: 'fr-XX') + mediator = TranslationUpdateMediator.new(@fr_translation, translator, ActionController::Parameters.new( copyToLocales: %w(fr-XX))) + mediator.send(:translations_that_should_be_multi_updated) + expect(mediator.success?).to be_false + expect(mediator.errors).to eql(["Cannot update translation in locale fr-XX"]) + end + + it "adds an error to the mediator if one of the locales user wanted to copy to is not valid because there is no Translation in one of those locales" do + fr_CA_translation = FactoryGirl.create(:translation, key: @key, copy: nil, translator: nil, rfc5646_locale: 'fr-CA') + mediator = TranslationUpdateMediator.new(@fr_translation, translator, ActionController::Parameters.new( copyToLocales: %w(fr-CA fr-FR))) + mediator.send(:translations_that_should_be_multi_updated) + expect(mediator.success?).to be_false + expect(mediator.errors).to eql(["Cannot update translation in locale fr-FR"]) + end + end + + describe "#update_single_translation!" do + let(:translation) { FactoryGirl.create(:translation, copy: nil, translator: nil) } + + it "updates a translation with the given copy and notes, sets its translator" do + params = ActionController::Parameters.new(translation: { copy: "test copy", notes: "test note" }) + mediator = TranslationUpdateMediator.new(translation, translator, params) + mediator.send(:update_single_translation!, translation) + expect(translation.copy).to eql("test copy") + expect(translation.notes).to eql("test note") + expect(translation.translator).to eql(translator) + end + + it "untranslates an approved translation if copy is blank and blank_string was not specified" do + translation.update copy: "test", approved: true, reviewer: reviewer, translator: translator + expect(translation.approved).to be_true + + params = ActionController::Parameters.new(translation: { copy: "" }) + mediator = TranslationUpdateMediator.new(translation, translator, params) + mediator.send(:update_single_translation!, translation) + expect(translation.copy).to be_nil + expect(translation.approved).to be_nil + expect(translation.translator).to be_nil + expect(translation.reviewer).to be_nil + end + + it "updates with empty string if copy is empty and blank_string is specified" do + params = ActionController::Parameters.new(translation: { copy: "" }, blank_string: "1" ) + mediator = TranslationUpdateMediator.new(translation, translator, params) + mediator.send(:update_single_translation!, translation) + expect(translation.copy).to eql("") + expect(translation.translated).to be_true + expect(translation.translator).to eql(translator) + end + + it "raises an ActiveRecord::RecordInvalid error if translation couldn't be saved" do + translation.source_rfc5646_locale = nil # should cause save! to fail + params = ActionController::Parameters.new(translation: { copy: "test" } ) + mediator = TranslationUpdateMediator.new(translation, translator, params) + expect { mediator.send(:update_single_translation!, translation) }.to raise_error(ActiveRecord::RecordInvalid) + end + + context "[role=translator]" do + it "updates translation but doesn't approve it" do + params = ActionController::Parameters.new(translation: { copy: "test" } ) + mediator = TranslationUpdateMediator.new(translation, translator, params) + mediator.send(:update_single_translation!, translation) + expect(translation.copy).to eql("test") + expect(translation.translated).to be_true + expect(translation.translator).to eql(translator) + expect(translation.approved).to be_nil + expect(translation.reviewer).to be_nil + end + + it "unapproves translation on update if copy changes" do + translation.update copy: "test", approved: true, reviewer: reviewer, translator: reviewer + expect(translation.approved).to be_true + + params = ActionController::Parameters.new(translation: { copy: "changed" }) + mediator = TranslationUpdateMediator.new(translation, translator, params) + mediator.send(:update_single_translation!, translation) + expect(translation.copy).to eql("changed") + expect(translation.approved).to be_nil + expect(translation.translator).to eql(translator) + expect(translation.reviewer).to be_nil + end + end + + context "[role=reviewer]" do + it "sets translator, reviewer and approves a translation" do + params = ActionController::Parameters.new(translation: { copy: "test" }) + mediator = TranslationUpdateMediator.new(translation, reviewer, params) + mediator.send(:update_single_translation!, translation) + expect(translation.approved).to be_true + expect(translation.translator).to eql(reviewer) + expect(translation.reviewer).to eql(reviewer) + end + + it "approves a translation without changing the translator if the copy didn't change" do + translation.update copy: "test", translator: translator + + params = ActionController::Parameters.new(translation: { copy: "test" }) + mediator = TranslationUpdateMediator.new(translation, reviewer, params) + mediator.send(:update_single_translation!, translation) + expect(translation.approved).to be_true + expect(translation.translator).to eql(translator) + expect(translation.reviewer).to eql(reviewer) + end + + it "saves notes field without approving an empty translation" do + params = ActionController::Parameters.new(translation: { notes: "test notes" }) + mediator = TranslationUpdateMediator.new(translation, reviewer, params) + mediator.send(:update_single_translation!, translation) + expect(translation.notes).to eql("test notes") + expect(translation.approved).to be_nil + expect(translation.translator).to be_nil + expect(translation.reviewer).to be_nil + end + end + end + + describe "#untranslate" do + it "clears copy, translator, approved and reviewer fields" do + translation = FactoryGirl.build(:translation, copy: "Test", approved: true, reviewer: reviewer, translator: reviewer) + mediator = TranslationUpdateMediator.new(translation, reviewer, ActionController::Parameters.new) + mediator.send(:untranslate, translation) + + expect(translation.copy).to be_nil + expect(translation.approved).to be_nil + expect(translation.translator).to be_nil + expect(translation.reviewer).to be_nil + end + end end