Merge pull request #3296 from consul/make_budgets_translatable
[Backport] Make budgets translatable
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
class Admin::BudgetGroupsController < Admin::BaseController
|
||||
include Translatable
|
||||
include FeatureFlags
|
||||
feature_flag :budgets
|
||||
|
||||
@@ -57,7 +58,8 @@ class Admin::BudgetGroupsController < Admin::BaseController
|
||||
end
|
||||
|
||||
def budget_group_params
|
||||
params.require(:budget_group).permit(:name, :max_votable_headings)
|
||||
valid_attributes = [:max_votable_headings]
|
||||
params.require(:budget_group).permit(*valid_attributes, translation_params(Budget::Group))
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
class Admin::BudgetHeadingsController < Admin::BaseController
|
||||
include Translatable
|
||||
include FeatureFlags
|
||||
feature_flag :budgets
|
||||
|
||||
@@ -62,8 +63,8 @@ class Admin::BudgetHeadingsController < Admin::BaseController
|
||||
end
|
||||
|
||||
def budget_heading_params
|
||||
params.require(:budget_heading).permit(:name, :price, :population, :allow_custom_content,
|
||||
:latitude, :longitude)
|
||||
valid_attributes = [:price, :population, :allow_custom_content, :latitude, :longitude]
|
||||
params.require(:budget_heading).permit(*valid_attributes, translation_params(Budget::Heading))
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
class Admin::BudgetPhasesController < Admin::BaseController
|
||||
include Translatable
|
||||
|
||||
before_action :load_phase, only: [:edit, :update]
|
||||
|
||||
@@ -21,8 +22,8 @@ class Admin::BudgetPhasesController < Admin::BaseController
|
||||
end
|
||||
|
||||
def budget_phase_params
|
||||
valid_attributes = [:starts_at, :ends_at, :summary, :description, :enabled]
|
||||
params.require(:budget_phase).permit(*valid_attributes)
|
||||
valid_attributes = [:starts_at, :ends_at, :enabled]
|
||||
params.require(:budget_phase).permit(*valid_attributes, translation_params(Budget::Phase))
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
class Admin::BudgetsController < Admin::BaseController
|
||||
include Translatable
|
||||
include FeatureFlags
|
||||
feature_flag :budgets
|
||||
|
||||
@@ -56,8 +57,8 @@ class Admin::BudgetsController < Admin::BaseController
|
||||
|
||||
def budget_params
|
||||
descriptions = Budget::Phase::PHASE_KINDS.map{|p| "description_#{p}"}.map(&:to_sym)
|
||||
valid_attributes = [:name, :phase, :currency_symbol] + descriptions
|
||||
params.require(:budget).permit(*valid_attributes)
|
||||
valid_attributes = [:phase, :currency_symbol] + descriptions
|
||||
params.require(:budget).permit(*valid_attributes, translation_params(Budget))
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -75,8 +75,8 @@ class Valuation::BudgetInvestmentsController < Valuation::BaseController
|
||||
def heading_filters
|
||||
investments = @budget.investments.by_valuator(current_user.valuator.try(:id))
|
||||
.visible_to_valuators.distinct
|
||||
|
||||
investment_headings = Budget::Heading.where(id: investments.pluck(:heading_id).uniq)
|
||||
investment_headings = Budget::Heading.joins(:translations)
|
||||
.where(id: investments.pluck(:heading_id).uniq)
|
||||
.order(name: :asc)
|
||||
|
||||
all_headings_filter = [
|
||||
|
||||
@@ -30,7 +30,7 @@ module AdminHelper
|
||||
end
|
||||
|
||||
def menu_budgets?
|
||||
%w[budgets budget_groups budget_headings budget_investments].include?(controller_name)
|
||||
controller_name.starts_with?("budget")
|
||||
end
|
||||
|
||||
def menu_budget?
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
module BudgetHeadingsHelper
|
||||
|
||||
def budget_heading_select_options(budget)
|
||||
budget.headings.order_by_group_name.map do |heading|
|
||||
budget.headings.sort_by_name.map do |heading|
|
||||
[heading.name_scoped_by_group, heading.id]
|
||||
end
|
||||
end
|
||||
|
||||
@@ -3,9 +3,14 @@ class Budget < ActiveRecord::Base
|
||||
include Measurable
|
||||
include Sluggable
|
||||
|
||||
translates :name, touch: true
|
||||
include Globalizable
|
||||
|
||||
CURRENCY_SYMBOLS = %w(€ $ £ ¥).freeze
|
||||
|
||||
validates :name, presence: true, uniqueness: true
|
||||
before_validation :assign_model_to_translations
|
||||
|
||||
validates_translation :name, presence: true
|
||||
validates :phase, inclusion: { in: Budget::Phase::PHASE_KINDS }
|
||||
validates :currency_symbol, presence: true
|
||||
validates :slug, presence: true, format: /\A[a-z0-9\-_]+\z/
|
||||
|
||||
@@ -2,14 +2,21 @@ class Budget
|
||||
class Group < ActiveRecord::Base
|
||||
include Sluggable
|
||||
|
||||
translates :name, touch: true
|
||||
include Globalizable
|
||||
|
||||
belongs_to :budget
|
||||
|
||||
has_many :headings, dependent: :destroy
|
||||
|
||||
before_validation :assign_model_to_translations
|
||||
|
||||
validates_translation :name, presence: true
|
||||
validates :budget_id, presence: true
|
||||
validates :name, presence: true, uniqueness: { scope: :budget }
|
||||
validates :slug, presence: true, format: /\A[a-z0-9\-_]+\z/
|
||||
|
||||
scope :sort_by_name, -> { includes(:translations).order(:name) }
|
||||
|
||||
def single_heading_group?
|
||||
headings.count == 1
|
||||
end
|
||||
|
||||
13
app/models/budget/group/translation.rb
Normal file
13
app/models/budget/group/translation.rb
Normal file
@@ -0,0 +1,13 @@
|
||||
class Budget::Group::Translation < Globalize::ActiveRecord::Translation
|
||||
delegate :budget, to: :globalized_model
|
||||
|
||||
validate :name_uniqueness_by_budget
|
||||
|
||||
def name_uniqueness_by_budget
|
||||
if budget.groups.joins(:translations)
|
||||
.where(name: name)
|
||||
.where.not("budget_group_translations.budget_group_id": budget_group_id).any?
|
||||
errors.add(:name, I18n.t("errors.messages.taken"))
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -4,13 +4,18 @@ class Budget
|
||||
|
||||
include Sluggable
|
||||
|
||||
translates :name, touch: true
|
||||
include Globalizable
|
||||
|
||||
belongs_to :group
|
||||
|
||||
has_many :investments
|
||||
has_many :content_blocks
|
||||
|
||||
before_validation :assign_model_to_translations
|
||||
|
||||
validates_translation :name, presence: true
|
||||
validates :group_id, presence: true
|
||||
validates :name, presence: true, uniqueness: { if: :name_exists_in_budget_headings }
|
||||
validates :price, presence: true
|
||||
validates :slug, presence: true, format: /\A[a-z0-9\-_]+\z/
|
||||
validates :population, numericality: { greater_than: 0 }, allow_nil: true
|
||||
@@ -21,17 +26,19 @@ class Budget
|
||||
|
||||
delegate :budget, :budget_id, to: :group, allow_nil: true
|
||||
|
||||
scope :order_by_group_name, -> { includes(:group).order('budget_groups.name', 'budget_headings.name') }
|
||||
scope :allow_custom_content, -> { where(allow_custom_content: true).order(:name) }
|
||||
scope :i18n, -> { includes(:translations) }
|
||||
scope :allow_custom_content, -> { i18n.where(allow_custom_content: true).order(:name) }
|
||||
|
||||
def self.sort_by_name
|
||||
all.sort do |heading, other_heading|
|
||||
[other_heading.group.name, heading.name] <=> [heading.group.name, other_heading.name]
|
||||
end
|
||||
end
|
||||
|
||||
def name_scoped_by_group
|
||||
group.single_heading_group? ? name : "#{group.name}: #{name}"
|
||||
end
|
||||
|
||||
def name_exists_in_budget_headings
|
||||
group.budget.headings.where(name: name).where.not(id: id).any?
|
||||
end
|
||||
|
||||
def can_be_deleted?
|
||||
investments.empty?
|
||||
end
|
||||
|
||||
14
app/models/budget/heading/translation.rb
Normal file
14
app/models/budget/heading/translation.rb
Normal file
@@ -0,0 +1,14 @@
|
||||
class Budget::Heading::Translation < Globalize::ActiveRecord::Translation
|
||||
delegate :budget, to: :globalized_model
|
||||
|
||||
validate :name_uniqueness_by_budget
|
||||
|
||||
def name_uniqueness_by_budget
|
||||
if budget.headings
|
||||
.joins(:translations)
|
||||
.where(name: name)
|
||||
.where.not("budget_heading_translations.budget_heading_id": budget_heading_id).any?
|
||||
errors.add(:name, I18n.t("errors.messages.taken"))
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -6,20 +6,22 @@ class Budget
|
||||
SUMMARY_MAX_LENGTH = 1000
|
||||
DESCRIPTION_MAX_LENGTH = 2000
|
||||
|
||||
translates :summary, touch: true
|
||||
translates :description, touch: true
|
||||
include Globalizable
|
||||
|
||||
belongs_to :budget
|
||||
belongs_to :next_phase, class_name: 'Budget::Phase', foreign_key: :next_phase_id
|
||||
has_one :prev_phase, class_name: 'Budget::Phase', foreign_key: :next_phase_id
|
||||
|
||||
validates_translation :summary, length: { maximum: SUMMARY_MAX_LENGTH }
|
||||
validates_translation :description, length: { maximum: DESCRIPTION_MAX_LENGTH }
|
||||
validates :budget, presence: true
|
||||
validates :kind, presence: true, uniqueness: { scope: :budget }, inclusion: { in: PHASE_KINDS }
|
||||
validates :summary, length: { maximum: SUMMARY_MAX_LENGTH }
|
||||
validates :description, length: { maximum: DESCRIPTION_MAX_LENGTH }
|
||||
validate :invalid_dates_range?
|
||||
validate :prev_phase_dates_valid?
|
||||
validate :next_phase_dates_valid?
|
||||
|
||||
before_validation :sanitize_description
|
||||
|
||||
after_save :adjust_date_ranges
|
||||
after_save :touch_budget
|
||||
|
||||
@@ -87,8 +89,5 @@ class Budget
|
||||
end
|
||||
end
|
||||
|
||||
def sanitize_description
|
||||
self.description = WYSIWYGSanitizer.new.sanitize(description)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
9
app/models/budget/phase/translation.rb
Normal file
9
app/models/budget/phase/translation.rb
Normal file
@@ -0,0 +1,9 @@
|
||||
class Budget::Phase::Translation < Globalize::ActiveRecord::Translation
|
||||
before_validation :sanitize_description
|
||||
|
||||
private
|
||||
|
||||
def sanitize_description
|
||||
self.description = WYSIWYGSanitizer.new.sanitize(description)
|
||||
end
|
||||
end
|
||||
11
app/models/budget/translation.rb
Normal file
11
app/models/budget/translation.rb
Normal file
@@ -0,0 +1,11 @@
|
||||
class Budget::Translation < Globalize::ActiveRecord::Translation
|
||||
validate :name_uniqueness_by_budget
|
||||
|
||||
def name_uniqueness_by_budget
|
||||
if Budget.joins(:translations)
|
||||
.where(name: name)
|
||||
.where.not("budget_translations.budget_id": budget_id).any?
|
||||
errors.add(:name, I18n.t("errors.messages.taken"))
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -8,6 +8,10 @@ module Globalizable
|
||||
def locales_not_marked_for_destruction
|
||||
translations.reject(&:_destroy).map(&:locale)
|
||||
end
|
||||
|
||||
def assign_model_to_translations
|
||||
translations.each { |translation| translation.globalized_model = self }
|
||||
end
|
||||
end
|
||||
|
||||
class_methods do
|
||||
|
||||
@@ -14,7 +14,7 @@ class Milestone < ActiveRecord::Base
|
||||
validates :milestoneable, presence: true
|
||||
validates :publication_date, presence: true
|
||||
|
||||
before_validation :assign_milestone_to_translations
|
||||
before_validation :assign_model_to_translations
|
||||
validates_translation :description, presence: true, unless: -> { status_id.present? }
|
||||
|
||||
scope :order_by_publication_date, -> { order(publication_date: :asc, created_at: :asc) }
|
||||
@@ -25,9 +25,4 @@ class Milestone < ActiveRecord::Base
|
||||
80
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def assign_milestone_to_translations
|
||||
translations.each { |translation| translation.globalized_model = self }
|
||||
end
|
||||
end
|
||||
|
||||
@@ -17,12 +17,7 @@ class ProgressBar < ActiveRecord::Base
|
||||
}
|
||||
validates :percentage, presence: true, inclusion: RANGE, numericality: { only_integer: true }
|
||||
|
||||
before_validation :assign_progress_bar_to_translations
|
||||
before_validation :assign_model_to_translations
|
||||
validates_translation :title, presence: true, unless: :primary?
|
||||
|
||||
private
|
||||
|
||||
def assign_progress_bar_to_translations
|
||||
translations.each { |translation| translation.globalized_model = self }
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
<div class="small-12 medium-6">
|
||||
<%= form_for [:admin, @budget, @group], url: path do |f| %>
|
||||
<%= render "admin/shared/globalize_locales", resource: @group %>
|
||||
|
||||
<%= f.text_field :name,
|
||||
label: t("admin.budget_groups.form.name"),
|
||||
maxlength: 50,
|
||||
placeholder: t("admin.budget_groups.form.name") %>
|
||||
<div class="small-12 medium-6">
|
||||
<%= translatable_form_for [:admin, @budget, @group], url: path do |f| %>
|
||||
|
||||
<%= render 'shared/errors', resource: @group %>
|
||||
|
||||
<%= f.translatable_fields do |translations_form| %>
|
||||
<%= translations_form.text_field :name,
|
||||
label: t("admin.budget_groups.form.name"),
|
||||
maxlength: 50,
|
||||
placeholder: t("admin.budget_groups.form.name") %>
|
||||
<% end %>
|
||||
|
||||
<% if @group.persisted? %>
|
||||
<%= f.select :max_votable_headings,
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
<%= render "admin/shared/globalize_locales", resource: @heading %>
|
||||
|
||||
<div class="small-12 medium-6">
|
||||
|
||||
<%= form_for [:admin, @budget, @group, @heading], url: path do |f| %>
|
||||
<%= translatable_form_for [:admin, @budget, @group, @heading], url: path do |f| %>
|
||||
|
||||
<%= f.text_field :name,
|
||||
label: t("admin.budget_headings.form.name"),
|
||||
maxlength: 50,
|
||||
placeholder: t("admin.budget_headings.form.name") %>
|
||||
<%= render 'shared/errors', resource: @heading %>
|
||||
|
||||
<%= f.translatable_fields do |translations_form| %>
|
||||
<%= translations_form.text_field :name,
|
||||
label: t("admin.budget_headings.form.name"),
|
||||
maxlength: 50,
|
||||
placeholder: t("admin.budget_headings.form.name") %>
|
||||
<% end %>
|
||||
|
||||
<%= f.text_field :price,
|
||||
label: t("admin.budget_headings.form.amount"),
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
<%= form_for [:admin, @phase.budget, @phase] do |f| %>
|
||||
<%= render "admin/shared/globalize_locales", resource: @phase %>
|
||||
|
||||
<%= translatable_form_for [:admin, @phase.budget, @phase] do |f| %>
|
||||
|
||||
<%= render 'shared/errors', resource: @phase %>
|
||||
|
||||
<div class="small-12 medium-6 column">
|
||||
<%= f.label :starts_at, t("admin.budget_phases.edit.start_date") %>
|
||||
@@ -18,32 +22,34 @@
|
||||
</div>
|
||||
|
||||
<div class="small-12 column">
|
||||
<%= f.label :description, t("admin.budget_phases.edit.description") %>
|
||||
<%= f.translatable_fields do |translations_form| %>
|
||||
|
||||
<span class="help-text" id="phase-description-help-text">
|
||||
<%= t("admin.budget_phases.edit.description_help_text") %>
|
||||
</span>
|
||||
<%= f.label :description, t("admin.budget_phases.edit.description") %>
|
||||
|
||||
<%= f.cktext_area :description,
|
||||
maxlength: Budget::Phase::DESCRIPTION_MAX_LENGTH,
|
||||
ckeditor: { language: I18n.locale },
|
||||
label: false %>
|
||||
<span class="help-text" id="phase-description-help-text">
|
||||
<%= t("admin.budget_phases.edit.description_help_text") %>
|
||||
</span>
|
||||
|
||||
<div class="ckeditor">
|
||||
<%= translations_form.cktext_area :description,
|
||||
maxlength: Budget::Phase::DESCRIPTION_MAX_LENGTH,
|
||||
label: false %>
|
||||
</div>
|
||||
|
||||
<%= f.label :summary, t("admin.budget_phases.edit.summary") %>
|
||||
|
||||
<span class="help-text" id="phase-summary-help-text">
|
||||
<%= t("admin.budget_phases.edit.summary_help_text") %>
|
||||
</span>
|
||||
|
||||
<div class="ckeditor">
|
||||
<%= translations_form.cktext_area :summary,
|
||||
maxlength: Budget::Phase::SUMMARY_MAX_LENGTH,
|
||||
label: false%>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<div class="small-12 column margin-top">
|
||||
<%= f.label :summary, t("admin.budget_phases.edit.summary") %>
|
||||
|
||||
<span class="help-text" id="phase-summary-help-text">
|
||||
<%= t("admin.budget_phases.edit.summary_help_text") %>
|
||||
</span>
|
||||
|
||||
<%= f.cktext_area :summary,
|
||||
maxlength: Budget::Phase::SUMMARY_MAX_LENGTH,
|
||||
ckeditor: { language: I18n.locale },
|
||||
label: false %>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="small-12 column margin-top">
|
||||
<%= f.check_box :enabled, label: t("admin.budget_phases.edit.enabled") %>
|
||||
|
||||
|
||||
@@ -1,7 +1,16 @@
|
||||
<%= form_for [:admin, @budget] do |f| %>
|
||||
<%= render "admin/shared/globalize_locales", resource: @budget %>
|
||||
|
||||
<%= translatable_form_for [:admin, @budget] do |f| %>
|
||||
|
||||
<%= render 'shared/errors', resource: @budget %>
|
||||
|
||||
<div class="small-12 medium-9 column">
|
||||
<%= f.text_field :name, maxlength: Budget.title_max_length %>
|
||||
<%= f.translatable_fields do |translations_form| %>
|
||||
<%= translations_form.text_field :name,
|
||||
label: t("activerecord.attributes.budget.name"),
|
||||
maxlength: Budget.title_max_length,
|
||||
placeholder: t("activerecord.attributes.budget.name") %>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<div class="margin-top">
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
</div>
|
||||
|
||||
<div class="row ballot">
|
||||
<% ballot_groups = @ballot.groups.order(name: :asc) %>
|
||||
<% ballot_groups = @ballot.groups.sort_by_name %>
|
||||
<% ballot_groups.each do |group| %>
|
||||
<div id="<%= dom_id(group) %>" class="small-12 medium-6 column end">
|
||||
<div class="margin-top ballot-content">
|
||||
@@ -52,7 +52,7 @@
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<% no_balloted_groups = @budget.groups.order(name: :asc) - ballot_groups %>
|
||||
<% no_balloted_groups = @budget.groups.sort_by_name - ballot_groups %>
|
||||
<% no_balloted_groups.each do |group| %>
|
||||
<div id="<%= dom_id(group) %>" class="small-12 medium-6 column end">
|
||||
<div class="margin-top ballot-content">
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
<div class="row margin">
|
||||
<div id="headings" class="small-12 medium-7 column select-district">
|
||||
<div class="row">
|
||||
<% @group.headings.order_by_group_name.each_slice(7) do |slice| %>
|
||||
<% @group.headings.sort_by_name.each_slice(7) do |slice| %>
|
||||
<div class="small-6 medium-4 column end">
|
||||
<% slice.each do |heading| %>
|
||||
<span id="<%= dom_id(heading) %>"
|
||||
|
||||
@@ -70,7 +70,7 @@
|
||||
<% current_budget.groups.each do |group| %>
|
||||
<h2 id="<%= group.name.parameterize %>"><%= group.name %></h2>
|
||||
<ul class="no-bullet" data-equalizer data-equalizer-on="medium">
|
||||
<% group.headings.order_by_group_name.each do |heading| %>
|
||||
<% group.headings.sort_by_name.each do |heading| %>
|
||||
<li class="heading small-12 medium-4 large-2" data-equalizer-watch>
|
||||
<% unless current_budget.informing? || current_budget.finished? %>
|
||||
<%= link_to budget_investments_path(current_budget.id,
|
||||
|
||||
@@ -5,8 +5,10 @@
|
||||
module ActionDispatch::Routing::UrlFor
|
||||
def resource_hierarchy_for(resource)
|
||||
case resource.class.name
|
||||
when "Budget::Investment"
|
||||
when "Budget::Investment", "Budget::Phase", "Budget::Group"
|
||||
[resource.budget, resource]
|
||||
when "Budget::Heading"
|
||||
[resource.group.budget, resource.group, resource]
|
||||
when "Milestone"
|
||||
[*resource_hierarchy_for(resource.milestoneable), resource]
|
||||
when "ProgressBar"
|
||||
|
||||
@@ -181,6 +181,7 @@ en:
|
||||
proposal_notification: "Notification"
|
||||
spending_proposal: Spending proposal
|
||||
budget/investment: Investment
|
||||
budget/group: Budget Group
|
||||
budget/heading: Budget Heading
|
||||
poll/shift: Shift
|
||||
poll/question/answer: Answer
|
||||
|
||||
@@ -181,6 +181,7 @@ es:
|
||||
proposal_notification: "la notificación"
|
||||
spending_proposal: la propuesta de gasto
|
||||
budget/investment: el proyecto de gasto
|
||||
budget/group: el grupo de partidas presupuestarias
|
||||
budget/heading: la partida presupuestaria
|
||||
poll/shift: el turno
|
||||
poll/question/answer: la respuesta
|
||||
|
||||
@@ -26,45 +26,93 @@ end
|
||||
|
||||
section "Creating Budgets" do
|
||||
Budget.create(
|
||||
name: "#{I18n.t('seeds.budgets.budget')} #{Date.current.year - 1}",
|
||||
currency_symbol: I18n.t('seeds.budgets.currency'),
|
||||
phase: 'finished'
|
||||
name_en: "#{I18n.t("seeds.budgets.budget", locale: :en)} #{Date.current.year - 1}",
|
||||
name_es: "#{I18n.t("seeds.budgets.budget", locale: :es)} #{Date.current.year - 1}",
|
||||
currency_symbol: I18n.t("seeds.budgets.currency"),
|
||||
phase: "finished"
|
||||
)
|
||||
|
||||
Budget.create(
|
||||
name: "#{I18n.t('seeds.budgets.budget')} #{Date.current.year}",
|
||||
currency_symbol: I18n.t('seeds.budgets.currency'),
|
||||
phase: 'accepting'
|
||||
name_en: "#{I18n.t("seeds.budgets.budget", locale: :en)} #{Date.current.year}",
|
||||
name_es: "#{I18n.t("seeds.budgets.budget", locale: :es)} #{Date.current.year}",
|
||||
currency_symbol: I18n.t("seeds.budgets.currency"),
|
||||
phase: "accepting"
|
||||
)
|
||||
|
||||
Budget.find_each do |budget|
|
||||
budget.phases.each do |phase|
|
||||
random_locales.map do |locale|
|
||||
Globalize.with_locale(locale) do
|
||||
phase.description = "Description for locale #{locale}"
|
||||
phase.summary = "Summary for locale #{locale}"
|
||||
phase.save!
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Budget.all.each do |budget|
|
||||
city_group = budget.groups.create!(name: I18n.t('seeds.budgets.groups.all_city'))
|
||||
city_group.headings.create!(name: I18n.t('seeds.budgets.groups.all_city'),
|
||||
price: 1000000,
|
||||
population: 1000000,
|
||||
latitude: '40.416775',
|
||||
longitude: '-3.703790')
|
||||
city_group_params = {
|
||||
name_en: I18n.t("seeds.budgets.groups.all_city", locale: :en),
|
||||
name_es: I18n.t("seeds.budgets.groups.all_city", locale: :es)
|
||||
}
|
||||
city_group = budget.groups.create!(city_group_params)
|
||||
|
||||
districts_group = budget.groups.create!(name: I18n.t('seeds.budgets.groups.districts'))
|
||||
districts_group.headings.create!(name: I18n.t('seeds.geozones.north_district'),
|
||||
price: rand(5..10) * 100000,
|
||||
population: 350000,
|
||||
latitude: '40.416775',
|
||||
longitude: '-3.703790')
|
||||
districts_group.headings.create!(name: I18n.t('seeds.geozones.west_district'),
|
||||
price: rand(5..10) * 100000,
|
||||
population: 300000,
|
||||
latitude: '40.416775',
|
||||
longitude: '-3.703790')
|
||||
districts_group.headings.create!(name: I18n.t('seeds.geozones.east_district'),
|
||||
price: rand(5..10) * 100000,
|
||||
population: 200000,
|
||||
latitude: '40.416775',
|
||||
longitude: '-3.703790')
|
||||
districts_group.headings.create!(name: I18n.t('seeds.geozones.central_district'),
|
||||
price: rand(5..10) * 100000,
|
||||
population: 150000,
|
||||
latitude: '40.416775',
|
||||
longitude: '-3.703790')
|
||||
city_heading_params = {
|
||||
name_en: I18n.t("seeds.budgets.groups.all_city", locale: :en),
|
||||
name_es: I18n.t("seeds.budgets.groups.all_city", locale: :es),
|
||||
price: 1000000,
|
||||
population: 1000000,
|
||||
latitude: "40.416775",
|
||||
longitude: "-3.703790"
|
||||
}
|
||||
city_group.headings.create!(city_heading_params)
|
||||
|
||||
districts_group_params = {
|
||||
name_en: I18n.t("seeds.budgets.groups.districts", locale: :en),
|
||||
name_es: I18n.t("seeds.budgets.groups.districts", locale: :es)
|
||||
}
|
||||
districts_group = budget.groups.create!(districts_group_params)
|
||||
|
||||
north_heading_params = {
|
||||
name_en: I18n.t("seeds.geozones.north_district", locale: :en),
|
||||
name_es: I18n.t("seeds.geozones.north_district", locale: :es),
|
||||
price: rand(5..10) * 100000,
|
||||
population: 350000,
|
||||
latitude: "40.416775",
|
||||
longitude: "-3.703790"
|
||||
}
|
||||
districts_group.headings.create!(north_heading_params)
|
||||
|
||||
west_heading_params = {
|
||||
name_en: I18n.t("seeds.geozones.west_district", locale: :en),
|
||||
name_es: I18n.t("seeds.geozones.west_district", locale: :es),
|
||||
price: rand(5..10) * 100000,
|
||||
population: 300000,
|
||||
latitude: "40.416775",
|
||||
longitude: "-3.703790"
|
||||
}
|
||||
districts_group.headings.create!(west_heading_params)
|
||||
|
||||
east_heading_params = {
|
||||
name_en: I18n.t("seeds.geozones.east_district", locale: :en),
|
||||
name_es: I18n.t("seeds.geozones.east_district", locale: :es),
|
||||
price: rand(5..10) * 100000,
|
||||
population: 200000,
|
||||
latitude: "40.416775",
|
||||
longitude: "-3.703790"
|
||||
}
|
||||
districts_group.headings.create!(east_heading_params)
|
||||
|
||||
central_heading_params = {
|
||||
name_en: I18n.t("seeds.geozones.central_district", locale: :en),
|
||||
name_es: I18n.t("seeds.geozones.central_district", locale: :es),
|
||||
price: rand(5..10) * 100000,
|
||||
population: 150000,
|
||||
latitude: "40.416775",
|
||||
longitude: "-3.703790"
|
||||
}
|
||||
districts_group.headings.create!(central_heading_params)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
16
db/migrate/20190118135741_add_budget_translations.rb
Normal file
16
db/migrate/20190118135741_add_budget_translations.rb
Normal file
@@ -0,0 +1,16 @@
|
||||
class AddBudgetTranslations < ActiveRecord::Migration
|
||||
|
||||
def self.up
|
||||
Budget.create_translation_table!(
|
||||
{
|
||||
name: :string
|
||||
},
|
||||
{ migrate_data: true }
|
||||
)
|
||||
end
|
||||
|
||||
def self.down
|
||||
Budget.drop_translation_table!
|
||||
end
|
||||
|
||||
end
|
||||
17
db/migrate/20190119160418_add_budget_phase_translations.rb
Normal file
17
db/migrate/20190119160418_add_budget_phase_translations.rb
Normal file
@@ -0,0 +1,17 @@
|
||||
class AddBudgetPhaseTranslations < ActiveRecord::Migration
|
||||
|
||||
def self.up
|
||||
Budget::Phase.create_translation_table!(
|
||||
{
|
||||
description: :text,
|
||||
summary: :text
|
||||
},
|
||||
{ migrate_data: true }
|
||||
)
|
||||
end
|
||||
|
||||
def self.down
|
||||
Budget::Phase.drop_translation_table!
|
||||
end
|
||||
|
||||
end
|
||||
16
db/migrate/20190120155819_add_budget_group_translations.rb
Normal file
16
db/migrate/20190120155819_add_budget_group_translations.rb
Normal file
@@ -0,0 +1,16 @@
|
||||
class AddBudgetGroupTranslations < ActiveRecord::Migration
|
||||
|
||||
def self.up
|
||||
Budget::Group.create_translation_table!(
|
||||
{
|
||||
name: :string
|
||||
},
|
||||
{ migrate_data: true }
|
||||
)
|
||||
end
|
||||
|
||||
def self.down
|
||||
Budget::Group.drop_translation_table!
|
||||
end
|
||||
|
||||
end
|
||||
16
db/migrate/20190121171237_add_budget_heading_translations.rb
Normal file
16
db/migrate/20190121171237_add_budget_heading_translations.rb
Normal file
@@ -0,0 +1,16 @@
|
||||
class AddBudgetHeadingTranslations < ActiveRecord::Migration
|
||||
|
||||
def self.up
|
||||
Budget::Heading.create_translation_table!(
|
||||
{
|
||||
name: :string
|
||||
},
|
||||
{ migrate_data: true }
|
||||
)
|
||||
end
|
||||
|
||||
def self.down
|
||||
Budget::Heading.drop_translation_table!
|
||||
end
|
||||
|
||||
end
|
||||
45
db/schema.rb
45
db/schema.rb
@@ -138,6 +138,17 @@ ActiveRecord::Schema.define(version: 20190205131722) do
|
||||
|
||||
add_index "budget_content_blocks", ["heading_id"], name: "index_budget_content_blocks_on_heading_id", using: :btree
|
||||
|
||||
create_table "budget_group_translations", force: :cascade do |t|
|
||||
t.integer "budget_group_id", null: false
|
||||
t.string "locale", null: false
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.string "name"
|
||||
end
|
||||
|
||||
add_index "budget_group_translations", ["budget_group_id"], name: "index_budget_group_translations_on_budget_group_id", using: :btree
|
||||
add_index "budget_group_translations", ["locale"], name: "index_budget_group_translations_on_locale", using: :btree
|
||||
|
||||
create_table "budget_groups", force: :cascade do |t|
|
||||
t.integer "budget_id"
|
||||
t.string "name", limit: 50
|
||||
@@ -147,6 +158,17 @@ ActiveRecord::Schema.define(version: 20190205131722) do
|
||||
|
||||
add_index "budget_groups", ["budget_id"], name: "index_budget_groups_on_budget_id", using: :btree
|
||||
|
||||
create_table "budget_heading_translations", force: :cascade do |t|
|
||||
t.integer "budget_heading_id", null: false
|
||||
t.string "locale", null: false
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.string "name"
|
||||
end
|
||||
|
||||
add_index "budget_heading_translations", ["budget_heading_id"], name: "index_budget_heading_translations_on_budget_heading_id", using: :btree
|
||||
add_index "budget_heading_translations", ["locale"], name: "index_budget_heading_translations_on_locale", using: :btree
|
||||
|
||||
create_table "budget_headings", force: :cascade do |t|
|
||||
t.integer "group_id"
|
||||
t.string "name", limit: 50
|
||||
@@ -242,6 +264,18 @@ ActiveRecord::Schema.define(version: 20190205131722) do
|
||||
add_index "budget_investments", ["heading_id"], name: "index_budget_investments_on_heading_id", using: :btree
|
||||
add_index "budget_investments", ["tsv"], name: "index_budget_investments_on_tsv", using: :gin
|
||||
|
||||
create_table "budget_phase_translations", force: :cascade do |t|
|
||||
t.integer "budget_phase_id", null: false
|
||||
t.string "locale", null: false
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.text "description"
|
||||
t.text "summary"
|
||||
end
|
||||
|
||||
add_index "budget_phase_translations", ["budget_phase_id"], name: "index_budget_phase_translations_on_budget_phase_id", using: :btree
|
||||
add_index "budget_phase_translations", ["locale"], name: "index_budget_phase_translations_on_locale", using: :btree
|
||||
|
||||
create_table "budget_phases", force: :cascade do |t|
|
||||
t.integer "budget_id"
|
||||
t.integer "next_phase_id"
|
||||
@@ -266,6 +300,17 @@ ActiveRecord::Schema.define(version: 20190205131722) do
|
||||
t.datetime "updated_at", null: false
|
||||
end
|
||||
|
||||
create_table "budget_translations", force: :cascade do |t|
|
||||
t.integer "budget_id", null: false
|
||||
t.string "locale", null: false
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.string "name"
|
||||
end
|
||||
|
||||
add_index "budget_translations", ["budget_id"], name: "index_budget_translations_on_budget_id", using: :btree
|
||||
add_index "budget_translations", ["locale"], name: "index_budget_translations_on_locale", using: :btree
|
||||
|
||||
create_table "budget_valuator_assignments", force: :cascade do |t|
|
||||
t.integer "valuator_id"
|
||||
t.integer "investment_id"
|
||||
|
||||
@@ -9,6 +9,11 @@ feature "Admin budget groups" do
|
||||
login_as(admin.user)
|
||||
end
|
||||
|
||||
it_behaves_like "translatable",
|
||||
"budget_group",
|
||||
"edit_admin_budget_group_path",
|
||||
%w[name]
|
||||
|
||||
context "Feature flag" do
|
||||
|
||||
background do
|
||||
@@ -140,6 +145,30 @@ feature "Admin budget groups" do
|
||||
expect(page).to have_field "Maximum number of headings in which a user can vote", with: "2"
|
||||
end
|
||||
|
||||
scenario "Changing name for current locale will update the slug if budget is in draft phase", :js do
|
||||
group = create(:budget_group, budget: budget)
|
||||
old_slug = group.slug
|
||||
|
||||
visit edit_admin_budget_group_path(budget, group)
|
||||
|
||||
select "Español", from: "translation_locale"
|
||||
fill_in "Group name", with: "Spanish name"
|
||||
click_button "Save group"
|
||||
|
||||
expect(page).to have_content "Group updated successfully"
|
||||
expect(group.reload.slug).to eq old_slug
|
||||
|
||||
visit edit_admin_budget_group_path(budget, group)
|
||||
|
||||
click_link "English"
|
||||
fill_in "Group name", with: "New English Name"
|
||||
click_button "Save group"
|
||||
|
||||
expect(page).to have_content "Group updated successfully"
|
||||
expect(group.reload.slug).not_to eq old_slug
|
||||
expect(group.slug).to eq "new-english-name"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
context "Update" do
|
||||
@@ -173,7 +202,7 @@ feature "Admin budget groups" do
|
||||
|
||||
expect(page).not_to have_content "Group updated successfully"
|
||||
expect(page).to have_css("label.error", text: "Group name")
|
||||
expect(page).to have_content "has already been taken"
|
||||
expect(page).to have_css("small.error", text: "has already been taken")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -10,6 +10,11 @@ feature "Admin budget headings" do
|
||||
login_as(admin.user)
|
||||
end
|
||||
|
||||
it_behaves_like "translatable",
|
||||
"budget_heading",
|
||||
"edit_admin_budget_group_heading_path",
|
||||
%w[name]
|
||||
|
||||
context "Feature flag" do
|
||||
|
||||
background do
|
||||
@@ -151,6 +156,30 @@ feature "Admin budget headings" do
|
||||
expect(find_field("Allow content block")).not_to be_checked
|
||||
end
|
||||
|
||||
scenario "Changing name for current locale will update the slug if budget is in draft phase", :js do
|
||||
heading = create(:budget_heading, group: group)
|
||||
old_slug = heading.slug
|
||||
|
||||
visit edit_admin_budget_group_heading_path(budget, group, heading)
|
||||
|
||||
select "Español", from: "translation_locale"
|
||||
fill_in "Heading name", with: "Spanish name"
|
||||
click_button "Save heading"
|
||||
|
||||
expect(page).to have_content "Heading updated successfully"
|
||||
expect(heading.reload.slug).to eq old_slug
|
||||
|
||||
visit edit_admin_budget_group_heading_path(budget, group, heading)
|
||||
|
||||
click_link "English"
|
||||
fill_in "Heading name", with: "New English Name"
|
||||
click_button "Save heading"
|
||||
|
||||
expect(page).to have_content "Heading updated successfully"
|
||||
expect(heading.reload.slug).not_to eq old_slug
|
||||
expect(heading.slug).to eq "new-english-name"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
context "Update" do
|
||||
@@ -203,7 +232,7 @@ feature "Admin budget headings" do
|
||||
|
||||
expect(page).not_to have_content "Heading updated successfully"
|
||||
expect(page).to have_css("label.error", text: "Heading name")
|
||||
expect(page).to have_content "has already been taken"
|
||||
expect(page).to have_css("small.error", text: "has already been taken")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -10,13 +10,19 @@ feature 'Admin budget phases' do
|
||||
login_as(admin.user)
|
||||
end
|
||||
|
||||
scenario 'Update phase' do
|
||||
it_behaves_like "translatable",
|
||||
"budget_phase",
|
||||
"edit_admin_budget_budget_phase_path",
|
||||
[],
|
||||
{ "description" => :ckeditor, "summary" => :ckeditor }
|
||||
|
||||
scenario "Update phase", :js do
|
||||
visit edit_admin_budget_budget_phase_path(budget, budget.current_phase)
|
||||
|
||||
fill_in 'start_date', with: Date.current + 1.days
|
||||
fill_in 'end_date', with: Date.current + 12.days
|
||||
fill_in 'budget_phase_summary', with: 'This is the summary of the phase.'
|
||||
fill_in 'budget_phase_description', with: 'This is the description of the phase.'
|
||||
fill_in_translatable_ckeditor "summary", :en, with: "New summary of the phase."
|
||||
fill_in_translatable_ckeditor "description", :en, with: "New description of the phase."
|
||||
uncheck 'budget_phase_enabled'
|
||||
click_button 'Save changes'
|
||||
|
||||
@@ -25,8 +31,8 @@ feature 'Admin budget phases' do
|
||||
|
||||
expect(budget.current_phase.starts_at.to_date).to eq((Date.current + 1.days).to_date)
|
||||
expect(budget.current_phase.ends_at.to_date).to eq((Date.current + 12.days).to_date)
|
||||
expect(budget.current_phase.summary).to eq('This is the summary of the phase.')
|
||||
expect(budget.current_phase.description).to eq('This is the description of the phase.')
|
||||
expect(budget.current_phase.summary).to include("New summary of the phase.")
|
||||
expect(budget.current_phase.description).to include("New description of the phase.")
|
||||
expect(budget.current_phase.enabled).to be(false)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -7,6 +7,11 @@ feature 'Admin budgets' do
|
||||
login_as(admin.user)
|
||||
end
|
||||
|
||||
it_behaves_like "translatable",
|
||||
"budget",
|
||||
"edit_admin_budget_path",
|
||||
%w[name]
|
||||
|
||||
context 'Feature flag' do
|
||||
|
||||
background do
|
||||
@@ -95,7 +100,7 @@ feature 'Admin budgets' do
|
||||
visit admin_budgets_path
|
||||
click_link 'Create new budget'
|
||||
|
||||
fill_in 'budget_name', with: 'M30 - Summer campaign'
|
||||
fill_in "Name", with: "M30 - Summer campaign"
|
||||
select 'Accepting projects', from: 'budget[phase]'
|
||||
|
||||
click_button 'Create Budget'
|
||||
@@ -112,6 +117,18 @@ feature 'Admin budgets' do
|
||||
expect(page).to have_css("label.error", text: "Name")
|
||||
end
|
||||
|
||||
scenario "Name should be unique" do
|
||||
create(:budget, name: "Existing Name")
|
||||
|
||||
visit new_admin_budget_path
|
||||
fill_in "Name", with: "Existing Name"
|
||||
click_button "Create Budget"
|
||||
|
||||
expect(page).not_to have_content "New participatory budget created successfully!"
|
||||
expect(page).to have_css("label.error", text: "Name")
|
||||
expect(page).to have_css("small.error", text: "has already been taken")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
context 'Destroy' do
|
||||
@@ -174,6 +191,31 @@ feature 'Admin budgets' do
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
scenario "Changing name for current locale will update the slug if budget is in draft phase", :js do
|
||||
budget.update(phase: "drafting")
|
||||
old_slug = budget.slug
|
||||
|
||||
visit edit_admin_budget_path(budget)
|
||||
|
||||
select "Español", from: "translation_locale"
|
||||
fill_in "Name", with: "Spanish name"
|
||||
click_button "Update Budget"
|
||||
|
||||
expect(page).to have_content "Participatory budget updated successfully"
|
||||
expect(budget.reload.slug).to eq old_slug
|
||||
|
||||
visit edit_admin_budget_path(budget)
|
||||
|
||||
click_link "English"
|
||||
fill_in "Name", with: "New English Name"
|
||||
click_button "Update Budget"
|
||||
|
||||
expect(page).to have_content "Participatory budget updated successfully"
|
||||
expect(budget.reload.slug).not_to eq old_slug
|
||||
expect(budget.slug).to eq "new-english-name"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
context 'Update' do
|
||||
@@ -186,7 +228,7 @@ feature 'Admin budgets' do
|
||||
visit admin_budgets_path
|
||||
click_link 'Edit budget'
|
||||
|
||||
fill_in 'budget_name', with: 'More trees on the streets'
|
||||
fill_in "Name", with: "More trees on the streets"
|
||||
click_button 'Update Budget'
|
||||
|
||||
expect(page).to have_content('More trees on the streets')
|
||||
|
||||
@@ -60,6 +60,35 @@ feature 'Budgets' do
|
||||
end
|
||||
end
|
||||
|
||||
scenario "Show headings ordered by name" do
|
||||
group = create(:budget_group, budget: budget)
|
||||
last_heading = create(:budget_heading, group: group, name: "BBB")
|
||||
first_heading = create(:budget_heading, group: group, name: "AAA")
|
||||
|
||||
visit budgets_path
|
||||
|
||||
expect(first_heading.name).to appear_before(last_heading.name)
|
||||
end
|
||||
|
||||
scenario "Show groups and headings for missing translations" do
|
||||
group1 = create(:budget_group, budget: budget)
|
||||
group2 = create(:budget_group, budget: budget)
|
||||
|
||||
heading1 = create(:budget_heading, group: group1)
|
||||
heading2 = create(:budget_heading, group: group2)
|
||||
|
||||
visit budgets_path locale: :es
|
||||
|
||||
within("#budget_info") do
|
||||
expect(page).to have_content group1.name
|
||||
expect(page).to have_content group2.name
|
||||
expect(page).to have_content heading1.name
|
||||
expect(page).to have_content budget.formatted_heading_price(heading1)
|
||||
expect(page).to have_content heading2.name
|
||||
expect(page).to have_content budget.formatted_heading_price(heading2)
|
||||
end
|
||||
end
|
||||
|
||||
scenario "Show informing index without links" do
|
||||
budget.update_attributes(phase: "informing")
|
||||
group = create(:budget_group, budget: budget)
|
||||
|
||||
19
spec/features/budgets/groups_spec.rb
Normal file
19
spec/features/budgets/groups_spec.rb
Normal file
@@ -0,0 +1,19 @@
|
||||
require "rails_helper"
|
||||
|
||||
feature "Budget Groups" do
|
||||
|
||||
let(:budget) { create(:budget) }
|
||||
let(:group) { create(:budget_group, budget: budget) }
|
||||
|
||||
context "Show" do
|
||||
scenario "Headings are sorted by name" do
|
||||
last_heading = create(:budget_heading, group: group, name: "BBB")
|
||||
first_heading = create(:budget_heading, group: group, name: "AAA")
|
||||
|
||||
visit budget_group_path(budget, group)
|
||||
|
||||
expect(first_heading.name).to appear_before(last_heading.name)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@@ -948,9 +948,9 @@ feature 'Budget Investments' do
|
||||
|
||||
select_options = find('#budget_investment_heading_id').all('option').collect(&:text)
|
||||
expect(select_options.first).to eq('')
|
||||
expect(select_options.second).to eq('Health: More health professionals')
|
||||
expect(select_options.third).to eq('Health: More hospitals')
|
||||
expect(select_options.fourth).to eq('Toda la ciudad')
|
||||
expect(select_options.second).to eq("Toda la ciudad")
|
||||
expect(select_options.third).to eq("Health: More health professionals")
|
||||
expect(select_options.fourth).to eq("Health: More hospitals")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -1,23 +1,35 @@
|
||||
require 'rails_helper'
|
||||
require "rails_helper"
|
||||
|
||||
describe Budget::Group do
|
||||
|
||||
let(:budget) { create(:budget) }
|
||||
|
||||
it_behaves_like "sluggable", updatable_slug_trait: :drafting_budget
|
||||
|
||||
describe "name" do
|
||||
before do
|
||||
create(:budget_group, budget: budget, name: 'object name')
|
||||
describe "Validations" do
|
||||
|
||||
let(:budget) { create(:budget) }
|
||||
let(:group) { create(:budget_group, budget: budget) }
|
||||
|
||||
describe "name" do
|
||||
before do
|
||||
group.update(name: "object name")
|
||||
end
|
||||
|
||||
it "can be repeatead in other budget's groups" do
|
||||
expect(build(:budget_group, budget: create(:budget), name: "object name")).to be_valid
|
||||
end
|
||||
|
||||
it "may be repeated for the same group and a different locale" do
|
||||
group.update(name_fr: "object name")
|
||||
|
||||
expect(group.translations.last).to be_valid
|
||||
end
|
||||
|
||||
it "must not be repeated for a different group in any locale" do
|
||||
group.update(name_en: "English", name_es: "Español")
|
||||
|
||||
expect(build(:budget_group, budget: budget, name_en: "English")).not_to be_valid
|
||||
expect(build(:budget_group, budget: budget, name_en: "Español")).not_to be_valid
|
||||
end
|
||||
end
|
||||
|
||||
it "can be repeatead in other budget's groups" do
|
||||
expect(build(:budget_group, budget: create(:budget), name: 'object name')).to be_valid
|
||||
end
|
||||
|
||||
it "must be unique among all budget's groups" do
|
||||
expect(build(:budget_group, budget: budget, name: 'object name')).not_to be_valid
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -14,20 +14,41 @@ describe Budget::Heading do
|
||||
end
|
||||
|
||||
describe "name" do
|
||||
|
||||
let(:heading) { create(:budget_heading, group: group) }
|
||||
|
||||
before do
|
||||
create(:budget_heading, group: group, name: 'object name')
|
||||
heading.update(name_en: "object name")
|
||||
end
|
||||
|
||||
it "can be repeatead in other budget's groups" do
|
||||
expect(build(:budget_heading, group: create(:budget_group), name: 'object name')).to be_valid
|
||||
it "can be repeatead in other budgets" do
|
||||
new_budget = create(:budget)
|
||||
new_group = create(:budget_group, budget: new_budget)
|
||||
|
||||
expect(build(:budget_heading, group: new_group, name_en: "object name")).to be_valid
|
||||
end
|
||||
|
||||
it "must be unique among all budget's groups" do
|
||||
expect(build(:budget_heading, group: create(:budget_group, budget: budget), name: 'object name')).not_to be_valid
|
||||
new_group = create(:budget_group, budget: budget)
|
||||
|
||||
expect(build(:budget_heading, group: new_group, name_en: "object name")).not_to be_valid
|
||||
end
|
||||
|
||||
it "must be unique among all it's group" do
|
||||
expect(build(:budget_heading, group: group, name: 'object name')).not_to be_valid
|
||||
expect(build(:budget_heading, group: group, name_en: "object name")).not_to be_valid
|
||||
end
|
||||
|
||||
it "can be repeated for the same heading and a different locale" do
|
||||
heading.update(name_fr: "object name")
|
||||
|
||||
expect(heading.translations.last).to be_valid
|
||||
end
|
||||
|
||||
it "must not be repeated for a different heading in any locale" do
|
||||
heading.update(name_en: "English", name_es: "Español")
|
||||
|
||||
expect(build(:budget_heading, group: group, name_en: "English")).not_to be_valid
|
||||
expect(build(:budget_heading, group: group, name_en: "Español")).not_to be_valid
|
||||
end
|
||||
end
|
||||
|
||||
@@ -259,4 +280,45 @@ describe Budget::Heading do
|
||||
end
|
||||
end
|
||||
|
||||
describe ".sort_by_name" do
|
||||
|
||||
it "returns headings sorted by DESC group name first and then ASC heading name" do
|
||||
last_group = create(:budget_group, name: "Group A")
|
||||
first_group = create(:budget_group, name: "Group B")
|
||||
|
||||
heading4 = create(:budget_heading, group: last_group, name: "Name B")
|
||||
heading3 = create(:budget_heading, group: last_group, name: "Name A")
|
||||
heading2 = create(:budget_heading, group: first_group, name: "Name D")
|
||||
heading1 = create(:budget_heading, group: first_group, name: "Name C")
|
||||
|
||||
sorted_headings = [heading1, heading2, heading3, heading4]
|
||||
expect(Budget::Heading.sort_by_name).to eq sorted_headings
|
||||
end
|
||||
|
||||
it "only sort headings using the group name (DESC) in the current locale" do
|
||||
last_group = create(:budget_group, name_en: "CCC", name_es: "BBB")
|
||||
first_group = create(:budget_group, name_en: "DDD", name_es: "AAA")
|
||||
|
||||
last_heading = create(:budget_heading, group: last_group, name: "Name")
|
||||
first_heading = create(:budget_heading, group: first_group, name: "Name")
|
||||
|
||||
expect(Budget::Heading.sort_by_name.size).to be 2
|
||||
expect(Budget::Heading.sort_by_name.first).to eq first_heading
|
||||
expect(Budget::Heading.sort_by_name.last).to eq last_heading
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe "scope allow_custom_content" do
|
||||
it "returns headings with allow_custom_content order by name" do
|
||||
excluded_heading = create(:budget_heading, name: "Name A")
|
||||
last_heading = create(:budget_heading, allow_custom_content: true, name: "Name C")
|
||||
first_heading = create(:budget_heading, allow_custom_content: true, name: "Name B")
|
||||
|
||||
expect(Budget::Heading.allow_custom_content.count).to be 2
|
||||
expect(Budget::Heading.allow_custom_content.first).to eq first_heading
|
||||
expect(Budget::Heading.allow_custom_content.last).to eq last_heading
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -8,11 +8,20 @@ describe Budget do
|
||||
|
||||
describe "name" do
|
||||
before do
|
||||
create(:budget, name: 'object name')
|
||||
budget.update(name_en: "object name")
|
||||
end
|
||||
|
||||
it "is validated for uniqueness" do
|
||||
expect(build(:budget, name: 'object name')).not_to be_valid
|
||||
it "must not be repeated for a different budget and same locale" do
|
||||
expect(build(:budget, name_en: "object name")).not_to be_valid
|
||||
end
|
||||
|
||||
it "must not be repeated for a different budget and a different locale" do
|
||||
expect(build(:budget, name_fr: "object name")).not_to be_valid
|
||||
end
|
||||
|
||||
it "may be repeated for the same budget and a different locale" do
|
||||
budget.update(name_fr: "object name")
|
||||
expect(budget.translations.last).to be_valid
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ shared_examples "translatable" do |factory_name, path_name, input_fields, textar
|
||||
|
||||
let(:input_fields) { input_fields } # So it's accessible by methods
|
||||
let(:textarea_fields) { textarea_fields } # So it's accessible by methods
|
||||
let(:path_name) { path_name } # So it's accessible by methods
|
||||
|
||||
let(:fields) { input_fields + textarea_fields.keys }
|
||||
|
||||
@@ -31,7 +32,16 @@ shared_examples "translatable" do |factory_name, path_name, input_fields, textar
|
||||
fields - optional_fields
|
||||
end
|
||||
|
||||
let(:translatable) { create(factory_name, attributes) }
|
||||
let(:translatable) do
|
||||
if factory_name == "budget_phase"
|
||||
budget = create(:budget)
|
||||
budget.phases.first.update attributes
|
||||
budget.phases.first
|
||||
else
|
||||
create(factory_name, attributes)
|
||||
end
|
||||
end
|
||||
|
||||
let(:path) { send(path_name, *resource_hierarchy_for(translatable)) }
|
||||
before { login_as(create(:administrator).user) }
|
||||
|
||||
@@ -134,8 +144,8 @@ shared_examples "translatable" do |factory_name, path_name, input_fields, textar
|
||||
click_button update_button_text
|
||||
|
||||
expect(page).not_to have_css "#error_explanation"
|
||||
expect(page).not_to have_link "English"
|
||||
|
||||
path = updated_path_for(translatable)
|
||||
visit path
|
||||
|
||||
expect(page).not_to have_link "English"
|
||||
@@ -257,6 +267,10 @@ shared_examples "translatable" do |factory_name, path_name, input_fields, textar
|
||||
end
|
||||
end
|
||||
|
||||
def updated_path_for(resource)
|
||||
send(path_name, *resource_hierarchy_for(resource.reload))
|
||||
end
|
||||
|
||||
def text_for(field, locale)
|
||||
I18n.with_locale(locale) do
|
||||
"#{translatable_class.human_attribute_name(field)} #{language_texts[locale]}"
|
||||
@@ -306,8 +320,8 @@ def expect_page_to_have_translatable_field(field, locale, with:)
|
||||
expect(page).to have_field field_for(field, locale), with: with
|
||||
click_link class: "fullscreen-toggle"
|
||||
elsif textarea_type == :ckeditor
|
||||
within("div.js-globalize-attribute[data-locale='#{locale}'] .ckeditor ") do
|
||||
within_frame(0) { expect(page).to have_content with }
|
||||
within("div.js-globalize-attribute[data-locale='#{locale}'] .ckeditor [id$='#{field}']") do
|
||||
within_frame(textarea_fields.keys.index(field)) { expect(page).to have_content with }
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -323,12 +337,18 @@ def update_button_text
|
||||
"Update notification"
|
||||
when "Poll"
|
||||
"Update poll"
|
||||
when "Budget"
|
||||
"Update Budget"
|
||||
when "Poll::Question", "Poll::Question::Answer"
|
||||
"Save"
|
||||
when "SiteCustomization::Page"
|
||||
"Update Custom page"
|
||||
when "Widget::Card"
|
||||
"Save card"
|
||||
when "Budget::Group"
|
||||
"Save group"
|
||||
when "Budget::Heading"
|
||||
"Save heading"
|
||||
else
|
||||
"Save changes"
|
||||
end
|
||||
|
||||
@@ -44,6 +44,12 @@ module Verifications
|
||||
end
|
||||
end
|
||||
|
||||
def fill_in_translatable_ckeditor(field, locale, params = {})
|
||||
selector = ".translatable-fields[data-locale='#{locale}'] textarea[id$='_#{field}']"
|
||||
locator = find(selector, visible: false)[:id]
|
||||
fill_in_ckeditor(locator, params)
|
||||
end
|
||||
|
||||
# @param [String] locator label text for the textarea or textarea id
|
||||
def fill_in_ckeditor(locator, params = {})
|
||||
# Find out ckeditor id at runtime using its label
|
||||
|
||||
Reference in New Issue
Block a user