diff --git a/decidim-templates/config/locales/en.yml b/decidim-templates/config/locales/en.yml
index 853a17d69c6be..d8c72ba254e8b 100644
--- a/decidim-templates/config/locales/en.yml
+++ b/decidim-templates/config/locales/en.yml
@@ -27,6 +27,7 @@ en:
destroy:
success: Template deleted successfully.
empty: There are no templates yet.
+ missing_resource: "(missing resource)"
update:
error: There was a problem updating this template.
success: Template updated successfully.
@@ -57,6 +58,26 @@ en:
title: New block user message template
template_chooser:
select_template: Select a template answer
+ proposal_answer_templates:
+ edit:
+ title: Edit proposal answer template
+ form:
+ component_constraint_help: Note that only participatory spaces having components of the type "proposals" will be listed.
+ hint1_html: "%{organization} will be replaced by the organization's name"
+ hint2_html: "%{name} will be replaced by the author's name"
+ hint3_html: "%{admin} will be replaced by the admin's name (the one answering the proposal)"
+ hint_html: "Hint: You can use these variables anywhere on the answer template that will be replaced when using the template"
+ save: Save
+ index:
+ component_constraint: Add constraint
+ confirm_delete: Are you sure you want to delete this template?
+ global_scope: Global (available everywhere)
+ internal_state: Internal state
+ title: Proposal answers
+ new:
+ title: New proposal answer template
+ template_chooser:
+ select_template: Select a template answer
questionnaire_templates:
choose:
create_from_template: Create from template
@@ -88,4 +109,5 @@ en:
update: "%{user_name} updated the %{resource_name} questionnaire template"
template_types:
block_user: Block user messages
+ proposal_answer_templates: Proposal answers
questionnaires: Questionnaires
diff --git a/decidim-templates/lib/decidim/templates/admin_engine.rb b/decidim-templates/lib/decidim/templates/admin_engine.rb
index 5c24a2452b02e..84e1cab6bb7f8 100644
--- a/decidim-templates/lib/decidim/templates/admin_engine.rb
+++ b/decidim-templates/lib/decidim/templates/admin_engine.rb
@@ -10,6 +10,15 @@ class AdminEngine < ::Rails::Engine
paths["lib/tasks"] = nil
routes do
+ resources :proposal_answer_templates do
+ member do
+ post :copy
+ end
+ collection do
+ get :fetch
+ end
+ end
+
## Routes for Questionnaire Templates
resources :questionnaire_templates do
member do
@@ -48,7 +57,8 @@ class AdminEngine < ::Rails::Engine
active: (
is_active_link?(decidim_admin_templates.questionnaire_templates_path) ||
is_active_link?(decidim_admin_templates.root_path)
- ) && !is_active_link?(decidim_admin_templates.block_user_templates_path)
+ ) && !is_active_link?(decidim_admin_templates.block_user_templates_path) &&
+ !is_active_link?(decidim_admin_templates.proposal_answer_templates_path)
menu.add_item :user_reports,
I18n.t("template_types.block_user", scope: "decidim.templates"),
@@ -56,6 +66,13 @@ class AdminEngine < ::Rails::Engine
icon_name: "user-forbid-line",
if: allowed_to?(:index, :templates),
active: is_active_link?(decidim_admin_templates.block_user_templates_path)
+
+ menu.add_item :proposal_answers,
+ I18n.t("template_types.proposal_answer_templates", scope: "decidim.templates"),
+ decidim_admin_templates.proposal_answer_templates_path,
+ icon_name: "file-copy-line",
+ if: allowed_to?(:index, :templates),
+ active: is_active_link?(decidim_admin_templates.proposal_answer_templates_path)
end
end
diff --git a/decidim-templates/lib/decidim/templates/test/factories.rb b/decidim-templates/lib/decidim/templates/test/factories.rb
index a5fcf410d25c0..941e65686a48e 100644
--- a/decidim-templates/lib/decidim/templates/test/factories.rb
+++ b/decidim-templates/lib/decidim/templates/test/factories.rb
@@ -16,6 +16,12 @@
target { :user_block }
end
+ trait :proposal_answer do
+ templatable { organization }
+ target { :proposal_answer }
+ field_values { { internal_state: :accepted } }
+ end
+
## Questionnaire templates
factory :questionnaire_template do
target { "questionnaire" }
diff --git a/decidim-templates/spec/commands/decidim/templates/admin/copy_proposal_answer_template_spec.rb b/decidim-templates/spec/commands/decidim/templates/admin/copy_proposal_answer_template_spec.rb
new file mode 100644
index 0000000000000..a57f011caa0c0
--- /dev/null
+++ b/decidim-templates/spec/commands/decidim/templates/admin/copy_proposal_answer_template_spec.rb
@@ -0,0 +1,52 @@
+# frozen_string_literal: true
+
+require "spec_helper"
+
+module Decidim
+ module Templates
+ module Admin
+ describe CopyProposalAnswerTemplate do
+ let(:template) { create(:template, :proposal_answer) }
+
+ let(:user) { create(:user, :admin, organization: template.organization) }
+
+ describe "when the template is invalid" do
+ before do
+ template.update(name: nil)
+ end
+
+ it "broadcasts invalid" do
+ expect { described_class.call(template, user) }.to broadcast(:invalid)
+ end
+ end
+
+ describe "when the template is valid" do
+ let(:destination_template) do
+ events = described_class.call(template, user)
+ expect(events).to have_key(:ok)
+ events[:ok]
+ end
+
+ it "applies template attributes to the questionnaire" do
+ expect(destination_template.name).to eq(template.name)
+ expect(destination_template.description).to eq(template.description)
+ expect(destination_template.field_values).to eq(template.field_values)
+ expect(destination_template.templatable).to eq(template.templatable)
+ expect(destination_template.target).to eq(template.target)
+ end
+
+ it "traces the action", versioning: true do
+ expect(Decidim.traceability)
+ .to receive(:perform_action!)
+ .with("duplicate", Decidim::Templates::Template, user)
+ .and_call_original
+
+ expect { described_class.call(template, user) }.to change(Decidim::ActionLog, :count)
+ action_log = Decidim::ActionLog.last
+ expect(action_log.version).to be_present
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/decidim-templates/spec/commands/decidim/templates/admin/create_proposal_answer_template_spec.rb b/decidim-templates/spec/commands/decidim/templates/admin/create_proposal_answer_template_spec.rb
new file mode 100644
index 0000000000000..ddee346788de4
--- /dev/null
+++ b/decidim-templates/spec/commands/decidim/templates/admin/create_proposal_answer_template_spec.rb
@@ -0,0 +1,130 @@
+# frozen_string_literal: true
+
+require "spec_helper"
+
+module Decidim
+ module Templates
+ module Admin
+ describe CreateProposalAnswerTemplate do
+ subject { described_class.new(form) }
+
+ let(:organization) { create(:organization) }
+ let(:user) { create(:user, :admin, :confirmed, organization:) }
+ let(:component_constraint) { 0 }
+ let(:name) { { "en" => "name" } }
+ let(:description) { { "en" => "description" } }
+ let(:internal_state) { "accepted" }
+
+ let(:form) do
+ instance_double(
+ ProposalAnswerTemplateForm,
+ invalid?: invalid,
+ valid?: !invalid,
+ current_user: user,
+ current_organization: organization,
+ name:,
+ description:,
+ internal_state:,
+ component_constraint:
+ )
+ end
+
+ let(:invalid) { false }
+
+ context "when the form is not valid" do
+ let(:invalid) { true }
+
+ it "is not valid" do
+ expect { subject.call }.to broadcast(:invalid)
+ end
+ end
+
+ context "when the form is valid" do
+ it "broadcasts ok" do
+ expect { subject.call }.to broadcast(:ok)
+ end
+
+ it "creates a new template for the organization" do
+ expect { subject.call }.to change(Decidim::Templates::Template, :count).by(1)
+ end
+
+ it "traces the action", versioning: true do
+ expect(Decidim.traceability)
+ .to receive(:perform_action!)
+ .with(:create, Decidim::Templates::Template, user, {})
+ .and_call_original
+
+ expect { subject.call }.to change(Decidim::ActionLog, :count)
+ action_log = Decidim::ActionLog.last
+ expect(action_log.version).to be_present
+ end
+
+ it "creates the second resource" do
+ expect(Decidim::Templates::Template.where(target: :proposal_answer).count).to eq(0)
+ expect { subject.call }.to broadcast(:ok)
+ expect(Decidim::Templates::Template.where(target: :proposal_answer).count).to eq(1)
+ end
+
+ context "when changing the internal state" do
+ context "with rejected" do
+ let(:internal_state) { "rejected" }
+
+ it "saves the internal state" do
+ subject.call
+ expect(Decidim::Templates::Template.where(target: :proposal_answer).last.field_values["internal_state"]).to eq(internal_state)
+ end
+ end
+
+ context "with accepted" do
+ let(:internal_state) { "accepted" }
+
+ it "saves the internal state" do
+ subject.call
+ expect(Decidim::Templates::Template.where(target: :proposal_answer).last.field_values["internal_state"]).to eq(internal_state)
+ end
+ end
+
+ context "with evaluating" do
+ let(:internal_state) { "evaluating" }
+
+ it "saves the internal state" do
+ subject.call
+ expect(Decidim::Templates::Template.where(target: :proposal_answer).last.field_values["internal_state"]).to eq(internal_state)
+ end
+ end
+
+ context "with not_answered" do
+ let(:internal_state) { "not_answered" }
+
+ it "saves the internal state" do
+ subject.call
+ expect(Decidim::Templates::Template.where(target: :proposal_answer).last.field_values["internal_state"]).to eq(internal_state)
+ end
+ end
+ end
+
+ context "when the form has a component constraint" do
+ context "and templatable is Organization" do
+ it "creates the second resource" do
+ expect(Decidim::Templates::Template.where(target: :proposal_answer).count).to eq(0)
+ expect { subject.call }.to broadcast(:ok)
+ expect(Decidim::Templates::Template.where(target: :proposal_answer).last.templatable).to eq(organization)
+ end
+ end
+
+ context "and templatable is Proposal Component" do
+ let(:component) { create(:proposal_component, organization:) }
+ let(:component_constraint) { component.id }
+
+ it "creates the second resource" do
+ expect(Decidim::Templates::Template.where(target: :proposal_answer).count).to eq(0)
+ expect { subject.call }.to broadcast(:ok)
+ expect(Decidim::Templates::Template.where(target: :proposal_answer).last.templatable).to eq(component)
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/decidim-templates/spec/commands/decidim/templates/admin/update_proposal_answer_template_spec.rb b/decidim-templates/spec/commands/decidim/templates/admin/update_proposal_answer_template_spec.rb
new file mode 100644
index 0000000000000..2cb1c03342036
--- /dev/null
+++ b/decidim-templates/spec/commands/decidim/templates/admin/update_proposal_answer_template_spec.rb
@@ -0,0 +1,126 @@
+# frozen_string_literal: true
+
+require "spec_helper"
+
+module Decidim
+ module Templates
+ module Admin
+ describe UpdateProposalAnswerTemplate do
+ subject { described_class.new(template, form, user) }
+
+ let(:organization) { create(:organization) }
+ let(:user) { create(:user, :admin, :confirmed, organization:) }
+ let!(:template) { create(:template, :proposal_answer, organization:) }
+ let!(:component_constraint) { 0 }
+ let(:name) { { "en" => "name" } }
+ let(:description) { { "en" => "description" } }
+ let(:internal_state) { "accepted" }
+
+ let(:form) do
+ instance_double(
+ ProposalAnswerTemplateForm,
+ invalid?: invalid,
+ valid?: !invalid,
+ current_user: user,
+ current_organization: organization,
+ name:,
+ description:,
+ internal_state:,
+ component_constraint:
+ )
+ end
+
+ let(:invalid) { false }
+
+ context "when the form is not valid" do
+ let(:invalid) { true }
+
+ it "is not valid" do
+ expect { subject.call }.to broadcast(:invalid)
+ end
+ end
+
+ context "when the form is valid" do
+ it "broadcasts ok" do
+ expect { subject.call }.to broadcast(:ok)
+ end
+
+ it "updates the template" do
+ subject.call
+ expect(template.name).to eq(name)
+ expect(template.description).to eq(description)
+ end
+
+ it "traces the action", versioning: true do
+ expect(Decidim.traceability)
+ .to receive(:perform_action!)
+ .with(:update, template, user, {})
+ .and_call_original
+
+ expect { subject.call }.to change(Decidim::ActionLog, :count)
+ action_log = Decidim::ActionLog.last
+ expect(action_log.version).to be_present
+ end
+
+ context "when changing the internal state" do
+ context "with rejected" do
+ let(:internal_state) { "rejected" }
+
+ it "saves the internal state" do
+ subject.call
+ expect(template.reload.field_values["internal_state"]).to eq(internal_state)
+ end
+ end
+
+ context "with accepted" do
+ let(:internal_state) { "accepted" }
+
+ it "saves the internal state" do
+ subject.call
+ expect(template.reload.field_values["internal_state"]).to eq(internal_state)
+ end
+ end
+
+ context "with evaluating" do
+ let(:internal_state) { "evaluating" }
+
+ it "saves the internal state" do
+ subject.call
+ expect(template.reload.field_values["internal_state"]).to eq(internal_state)
+ end
+ end
+
+ context "with not_answered" do
+ let(:internal_state) { "not_answered" }
+
+ it "saves the internal state" do
+ subject.call
+ expect(template.reload.field_values["internal_state"]).to eq(internal_state)
+ end
+ end
+ end
+
+ context "when the form has a component constraint" do
+ context "and templatable is Organization" do
+ it "creates the second resource" do
+ expect { subject.call }.to broadcast(:ok)
+ expect(template.reload.templatable).to eq(organization)
+ end
+ end
+
+ context "and templatable is Proposal Component" do
+ let(:component) { create(:component, manifest_name: :proposals, organization:) }
+ let(:component_constraint) { component.id }
+
+ it "creates the second resource" do
+ expect(template.reload.templatable).to eq(organization)
+ expect { subject.call }.to broadcast(:ok)
+ expect(template.reload.templatable).to eq(component)
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/decidim-templates/spec/factories.rb b/decidim-templates/spec/factories.rb
index 02f6849bb118b..c223a6a8c6f68 100644
--- a/decidim-templates/spec/factories.rb
+++ b/decidim-templates/spec/factories.rb
@@ -3,3 +3,4 @@
require "decidim/core/test/factories"
require "decidim/forms/test/factories"
require "decidim/templates/test/factories"
+require "decidim/proposals/test/factories"
diff --git a/decidim-templates/spec/forms/decidim/templates/admin/proposal_answer_template_form_spec.rb b/decidim-templates/spec/forms/decidim/templates/admin/proposal_answer_template_form_spec.rb
new file mode 100644
index 0000000000000..a01b1910ea84d
--- /dev/null
+++ b/decidim-templates/spec/forms/decidim/templates/admin/proposal_answer_template_form_spec.rb
@@ -0,0 +1,63 @@
+# frozen_string_literal: true
+
+require "spec_helper"
+
+module Decidim
+ module Templates
+ module Admin
+ describe ProposalAnswerTemplateForm do
+ subject do
+ described_class.from_params(attributes).with_context(
+ current_organization:
+ )
+ end
+
+ let(:current_organization) { create(:organization) }
+
+ let(:name) do
+ {
+ "en" => name_english,
+ "ca" => "Nom",
+ "es" => "Nombre"
+ }
+ end
+
+ let(:description) do
+ {
+ "en" => "
Content
",
+ "ca" => "
Contingut
",
+ "es" => "
Contenido
"
+ }
+ end
+
+ let(:internal_state) { :accepted }
+
+ let(:name_english) { "Name" }
+
+ let(:attributes) do
+ {
+ "name" => name,
+ "description" => description,
+ "internal_state" => internal_state
+ }
+ end
+
+ context "when everything is OK" do
+ it { is_expected.to be_valid }
+ end
+
+ context "when name is not valid" do
+ let(:name_english) { "" }
+
+ it { is_expected.not_to be_valid }
+ end
+
+ context "when internal_state is not valid" do
+ let(:internal_state) { "" }
+
+ it { is_expected.not_to be_valid }
+ end
+ end
+ end
+ end
+end
diff --git a/decidim-templates/spec/system/admin/admin_manages_proposal_answer_templates_spec.rb b/decidim-templates/spec/system/admin/admin_manages_proposal_answer_templates_spec.rb
new file mode 100644
index 0000000000000..a7fe53be324e9
--- /dev/null
+++ b/decidim-templates/spec/system/admin/admin_manages_proposal_answer_templates_spec.rb
@@ -0,0 +1,310 @@
+# frozen_string_literal: true
+
+require "spec_helper"
+
+describe "Admin manages proposal answer templates" do
+ let!(:organization) { create(:organization) }
+ let!(:user) { create(:user, :admin, :confirmed, organization:) }
+
+ before do
+ switch_to_host(organization.host)
+ login_as user, scope: :user
+ visit decidim_admin_templates.proposal_answer_templates_path
+ end
+
+ describe "listing templates" do
+ let!(:template) { create(:template, :proposal_answer, organization:) }
+
+ before do
+ visit decidim_admin_templates.proposal_answer_templates_path
+ end
+
+ it "shows a table with the templates info" do
+ within ".table-list" do
+ expect(page).to have_i18n_content(template.name)
+ expect(page).to have_i18n_content("Global (available everywhere)")
+ end
+ end
+
+ context "when a template is scoped to an invalid resource" do
+ let!(:template) { create(:template, :proposal_answer, organization:, templatable: create(:dummy_resource)) }
+
+ it "shows a table info about the invalid resource" do
+ within ".table-list" do
+ expect(page).to have_i18n_content(template.name)
+ expect(page).to have_i18n_content("(missing resource)")
+ end
+ end
+ end
+ end
+
+ describe "creating a proposal_answer_template" do
+ let(:participatory_process) { create(:participatory_process, title: { en: "A participatory process" }, organization:) }
+ let!(:proposals_component) { create(:component, manifest_name: :proposals, name: { en: "A component" }, participatory_space: participatory_process) }
+
+ before do
+ within ".layout-content" do
+ click_link("New")
+ end
+ end
+
+ shared_examples "creates a new template with scopes" do |scope_name|
+ it "creates a new template" do
+ within ".new_proposal_answer_template" do
+ fill_in_i18n(
+ :proposal_answer_template_name,
+ "#proposal_answer_template-name-tabs",
+ en: "My template",
+ es: "Mi plantilla",
+ ca: "La meva plantilla"
+ )
+ fill_in_i18n_editor(
+ :proposal_answer_template_description,
+ "#proposal_answer_template-description-tabs",
+ en: "Description",
+ es: "DescripciĆ³n",
+ ca: "DescripciĆ³"
+ )
+
+ choose "Not answered"
+ select scope_name, from: :proposal_answer_template_component_constraint
+
+ page.find("*[type=submit]").click
+ end
+
+ expect(page).to have_admin_callout("successfully")
+ expect(page).to have_current_path decidim_admin_templates.proposal_answer_templates_path
+ within ".table-list" do
+ expect(page).to have_i18n_content(scope_name)
+ expect(page).to have_content("My template")
+ end
+ end
+ end
+
+ it_behaves_like "creates a new template with scopes", "Global (available everywhere)"
+ it_behaves_like "creates a new template with scopes", "Participatory process: A participatory process > A component"
+ end
+
+ describe "updating a template" do
+ let!(:template) { create(:template, :proposal_answer, organization:) }
+ let(:participatory_process) { create(:participatory_process, title: { en: "A participatory process" }, organization:) }
+ let!(:proposals_component) { create(:component, manifest_name: :proposals, name: { en: "A component" }, participatory_space: participatory_process) }
+
+ before do
+ visit decidim_admin_templates.proposal_answer_templates_path
+ click_link translated(template.name)
+ end
+
+ shared_examples "updates a template with scopes" do |scope_name|
+ it "updates a template" do
+ fill_in_i18n(
+ :proposal_answer_template_name,
+ "#proposal_answer_template-name-tabs",
+ en: "My new name",
+ es: "Mi nuevo nombre",
+ ca: "El meu nou nom"
+ )
+
+ select scope_name, from: :proposal_answer_template_component_constraint
+
+ within ".edit_proposal_answer_template" do
+ page.find("*[type=submit]").click
+ end
+
+ expect(page).to have_admin_callout("successfully")
+ expect(page).to have_current_path decidim_admin_templates.proposal_answer_templates_path
+ within ".table-list" do
+ expect(page).to have_i18n_content(scope_name)
+ expect(page).to have_content("My new name")
+ end
+ end
+ end
+
+ it_behaves_like "updates a template with scopes", "Global (available everywhere)"
+ it_behaves_like "updates a template with scopes", "Participatory process: A participatory process > A component"
+ end
+
+ describe "updating a template with invalid values" do
+ let!(:template) { create(:template, :proposal_answer, organization:) }
+
+ before do
+ visit decidim_admin_templates.proposal_answer_templates_path
+ click_link translated(template.name)
+ end
+
+ it "does not update the template" do
+ fill_in_i18n(
+ :proposal_answer_template_name,
+ "#proposal_answer_template-name-tabs",
+ en: "",
+ es: "",
+ ca: ""
+ )
+
+ within ".edit_proposal_answer_template" do
+ find("*[type=submit]").click
+ end
+
+ expect(page).to have_admin_callout("problem")
+ end
+ end
+
+ describe "copying a template" do
+ let!(:template) { create(:template, :proposal_answer, organization:) }
+
+ before do
+ visit decidim_admin_templates.proposal_answer_templates_path
+ end
+
+ it "copies the template" do
+ within find("tr", text: translated(template.name)) do
+ click_link "Duplicate"
+ end
+
+ expect(page).to have_admin_callout("successfully")
+ expect(page).to have_content(template.name["en"], count: 2)
+ end
+ end
+
+ describe "destroying a template" do
+ let!(:template) { create(:template, :proposal_answer, organization:) }
+
+ before do
+ visit decidim_admin_templates.proposal_answer_templates_path
+ end
+
+ it "destroys the template" do
+ within find("tr", text: translated(template.name)) do
+ accept_confirm { click_link "Delete" }
+ end
+
+ expect(page).to have_admin_callout("successfully")
+ expect(page).to have_no_i18n_content(template.name)
+ end
+ end
+
+ describe "using a proposal_answer_template" do
+ let(:participatory_process) { create(:participatory_process, title: { en: "A participatory process" }, organization:) }
+ let!(:component) { create(:component, manifest_name: :proposals, name: { en: "A component" }, participatory_space: participatory_process) }
+
+ let(:description) { "Some meaningful answer" }
+ let(:field_values) { { internal_state: "rejected" } }
+ let!(:template) { create(:template, :proposal_answer, description: { en: description }, field_values:, organization:, templatable: component) }
+ let!(:proposal) { create(:proposal, component:) }
+
+ before do
+ visit Decidim::EngineRouter.admin_proxy(component).root_path
+ find("a", class: "action-icon--show-proposal").click
+ end
+
+ it "uses the template" do
+ expect(proposal.reload.internal_state).to eq("not_answered")
+ within ".edit_proposal_answer" do
+ select template.name["en"], from: :proposal_answer_template_chooser
+ expect(page).to have_content(description)
+ click_button "Answer"
+ end
+
+ expect(page).to have_admin_callout("Proposal successfully answered")
+
+ within find("tr", text: proposal.title["en"]) do
+ expect(page).to have_content("Rejected")
+ end
+ expect(proposal.reload.internal_state).to eq("rejected")
+ end
+
+ context "when there are no templates" do
+ before do
+ template.destroy!
+ visit Decidim::EngineRouter.admin_proxy(component).root_path
+ find("a", class: "action-icon--show-proposal").click
+ end
+
+ it "hides the template selector in the proposal answer page" do
+ expect(page).not_to have_select(:proposal_answer_template_chooser)
+ end
+ end
+
+ context "when displaying current component and organization templates" do
+ let!(:other_component) { create(:component, manifest_name: :proposals, name: { en: "Another component" }, participatory_space: participatory_process) }
+ let!(:other_component_template) { create(:template, :proposal_answer, description: { en: "Foo bar" }, field_values: { internal_state: "evaluating" }, organization:, templatable: other_component) }
+ let!(:proposal) { create(:proposal, component:) }
+
+ before do
+ visit Decidim::EngineRouter.admin_proxy(component).root_path
+ find("a", class: "action-icon--show-proposal").click
+ end
+
+ it "displays the global template in dropdown" do
+ expect(page).to have_select(:proposal_answer_template_chooser, with_options: [translated(template.name)])
+ end
+
+ it "hides templates scoped for other components" do
+ expect(proposal.reload.internal_state).to eq("not_answered")
+ expect(page).not_to have_select(:proposal_answer_template_chooser, with_options: [translated(other_component_template.name)])
+ end
+ end
+
+ context "when selecting a global template" do
+ let!(:global_template) { create(:template, :proposal_answer, description: { en: "Foo bar" }, field_values: { internal_state: "evaluating" }, organization:, templatable: organization) }
+ let!(:proposal) { create(:proposal, component:) }
+
+ before do
+ visit Decidim::EngineRouter.admin_proxy(component).root_path
+ find("a", class: "action-icon--show-proposal").click
+ end
+
+ it "all the fields are properly changed" do
+ expect(proposal.reload.internal_state).to eq("not_answered")
+ expect(page).to have_select(:proposal_answer_template_chooser, with_options: [translated(template.name), translated(global_template.name)])
+ within ".edit_proposal_answer" do
+ select translated(global_template.name), from: :proposal_answer_template_chooser
+ expect(page).to have_content("Foo bar")
+ click_button "Answer"
+ end
+
+ expect(page).to have_admin_callout("Proposal successfully answered")
+
+ within find("tr", text: translated(proposal.title)) do
+ expect(page).to have_content("Evaluating")
+ end
+ expect(proposal.reload.internal_state).to eq("evaluating")
+ end
+ end
+
+ context "when the template uses interpolations" do
+ context "with the organization variable" do
+ let(:description) { "Some meaningful answer with the %{organization}" }
+
+ it "changes it with the organization name" do
+ within ".edit_proposal_answer" do
+ select template.name["en"], from: :proposal_answer_template_chooser
+ expect(page).to have_content("Some meaningful answer with the #{organization.name}")
+ end
+ end
+ end
+
+ context "with the admin variable" do
+ let(:description) { "Some meaningful answer with the %{admin}" }
+
+ it "changes it with the admin's user name" do
+ within ".edit_proposal_answer" do
+ select template.name["en"], from: :proposal_answer_template_chooser
+ expect(page).to have_content("Some meaningful answer with the #{user.name}")
+ end
+ end
+ end
+
+ context "with the user variable" do
+ let(:description) { "Some meaningful answer with the %{name}" }
+
+ it "changes it with the author's user name" do
+ within ".edit_proposal_answer" do
+ select template.name["en"], from: :proposal_answer_template_chooser
+ expect(page).to have_content("Some meaningful answer with the #{proposal.creator_author.name}")
+ end
+ end
+ end
+ end
+ end
+end