decidim-toggle 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.erb-lint.yml +2134 -0
- data/.github/workflows/website.yml +57 -0
- data/.gitignore +13 -0
- data/.gitlab-ci.yml +165 -0
- data/.node-version +1 -0
- data/.rspec +1 -0
- data/.rubocop.yml +2 -0
- data/.ruby-version +1 -0
- data/.simplecov +18 -0
- data/CONTRIBUTING.md +17 -0
- data/Gemfile +33 -0
- data/Gemfile.lock +843 -0
- data/LICENSE.md +661 -0
- data/README.md +90 -0
- data/Rakefile +38 -0
- data/app/commands/decidim/toggle/update_authorizations_command.rb +31 -0
- data/app/commands/decidim/toggle/update_emails_command.rb +30 -0
- data/app/commands/decidim/toggle/update_file_upload_settings_command.rb +31 -0
- data/app/commands/decidim/toggle/update_locale_command.rb +47 -0
- data/app/commands/decidim/toggle/update_module_config_command.rb +31 -0
- data/app/commands/decidim/toggle/update_name_command.rb +31 -0
- data/app/commands/decidim/toggle/update_omniauth_command.rb +30 -0
- data/app/commands/decidim/toggle/update_security_command.rb +31 -0
- data/app/controllers/decidim_toggle/system/settings_tab_controller.rb +64 -0
- data/app/forms/decidim/toggle/update_authorizations_form.rb +54 -0
- data/app/forms/decidim/toggle/update_emails_form.rb +28 -0
- data/app/forms/decidim/toggle/update_file_upload_settings_form.rb +26 -0
- data/app/forms/decidim/toggle/update_locale_form.rb +116 -0
- data/app/forms/decidim/toggle/update_name_form.rb +63 -0
- data/app/forms/decidim/toggle/update_omniauth_form.rb +37 -0
- data/app/forms/decidim/toggle/update_security_form.rb +65 -0
- data/app/helpers/decidim/toggle/javascript_config_helper.rb +11 -0
- data/app/helpers/decidim/toggle/system_settings_tab_helper.rb +59 -0
- data/app/models/decidim/toggle/organization_module_config.rb +15 -0
- data/app/overrides/add_toggle_javascript_admin.rb +11 -0
- data/app/overrides/add_toggle_javascript_public.rb +11 -0
- data/app/packs/entrypoints/decidim_toggle.js +3 -0
- data/app/packs/src/decidim/toggle/organization_settings_tabs.js +114 -0
- data/app/packs/stylesheets/decidim/toggle/organization_settings.scss +160 -0
- data/app/views/decidim/system/organizations/edit.html.erb +20 -0
- data/app/views/decidim_toggle/system/organizations/_default_form_tab.html.erb +5 -0
- data/app/views/decidim_toggle/system/organizations/_encryption_not_configured_callout.html.erb +6 -0
- data/app/views/decidim_toggle/system/organizations/_settings_tab_active_tab_field.html.erb +1 -0
- data/app/views/decidim_toggle/system/organizations/_settings_tab_submit.html.erb +4 -0
- data/app/views/decidim_toggle/system/organizations/_settings_tabs.html.erb +31 -0
- data/app/views/decidim_toggle/system/organizations/tabs/_authorizations_tab.html.erb +9 -0
- data/app/views/decidim_toggle/system/organizations/tabs/_emails_tab.html.erb +5 -0
- data/app/views/decidim_toggle/system/organizations/tabs/_file_upload_tab.html.erb +5 -0
- data/app/views/decidim_toggle/system/organizations/tabs/_language_tab.html.erb +35 -0
- data/app/views/decidim_toggle/system/organizations/tabs/_omniauth_tab.html.erb +5 -0
- data/app/views/decidim_toggle/system/organizations/tabs/_security_tab.html.erb +20 -0
- data/app/views/layouts/decidim/toggle/_javascript_config.html.erb +3 -0
- data/bin/check +10 -0
- data/bin/i18n-tasks +27 -0
- data/bin/postversion +14 -0
- data/config/assets.rb +8 -0
- data/config/locales/decidim_toggle_en.yml +58 -0
- data/crowdin.yml +15 -0
- data/db/migrate/20260321120000_create_decidim_toggle_organization_module_configs.rb +20 -0
- data/decidim-toggle.gemspec +35 -0
- data/docker-compose.yml +41 -0
- data/lib/decidim/toggle/engine.rb +62 -0
- data/lib/decidim/toggle/expose_attributes_to_js.rb +26 -0
- data/lib/decidim/toggle/expose_attributes_to_js_validator.rb +32 -0
- data/lib/decidim/toggle/gem_registry.rb +15 -0
- data/lib/decidim/toggle/informative_callouts.rb +76 -0
- data/lib/decidim/toggle/javascript_config.rb +87 -0
- data/lib/decidim/toggle/module_config.rb +64 -0
- data/lib/decidim/toggle/module_config_form.rb +41 -0
- data/lib/decidim/toggle/module_config_i18n.rb +44 -0
- data/lib/decidim/toggle/module_configuration_presenter.rb +55 -0
- data/lib/decidim/toggle/organization_settings_tabs.rb +69 -0
- data/lib/decidim/toggle/settings_form_builder.rb +200 -0
- data/lib/decidim/toggle/settings_tab_item.rb +37 -0
- data/lib/decidim/toggle/settings_tab_registry.rb +109 -0
- data/lib/decidim/toggle/settings_tabs.rb +56 -0
- data/lib/decidim/toggle/tab_form.rb +20 -0
- data/lib/decidim/toggle/version.rb +14 -0
- data/lib/decidim/toggle.rb +36 -0
- data/lib/tasks/decidim/toggle/toggle_upgrade.rake +13 -0
- data/lib/tasks/decidim/toggle/toggle_webpacker.rake +60 -0
- data/log/.gitignore +2 -0
- data/package.json +18 -0
- data/prettier.config.js +15 -0
- data/spec/commands/decidim/toggle/update_authorizations_command_spec.rb +41 -0
- data/spec/commands/decidim/toggle/update_emails_command_spec.rb +84 -0
- data/spec/commands/decidim/toggle/update_file_upload_settings_command_spec.rb +28 -0
- data/spec/commands/decidim/toggle/update_locale_command_spec.rb +53 -0
- data/spec/commands/decidim/toggle/update_module_config_command_spec.rb +38 -0
- data/spec/commands/decidim/toggle/update_name_command_spec.rb +49 -0
- data/spec/commands/decidim/toggle/update_omniauth_command_spec.rb +80 -0
- data/spec/commands/decidim/toggle/update_security_command_spec.rb +25 -0
- data/spec/decidim/toggle/settings_tab_item_spec.rb +34 -0
- data/spec/decidim/toggle/settings_tab_registry_spec.rb +66 -0
- data/spec/decidim/toggle/settings_tabs_spec.rb +60 -0
- data/spec/forms/concerns/decidim/toggle/informative_callouts_spec.rb +48 -0
- data/spec/forms/decidim/toggle/update_authorizations_form_spec.rb +40 -0
- data/spec/forms/decidim/toggle/update_emails_form_spec.rb +35 -0
- data/spec/forms/decidim/toggle/update_file_upload_settings_form_spec.rb +20 -0
- data/spec/forms/decidim/toggle/update_locale_form_spec.rb +64 -0
- data/spec/forms/decidim/toggle/update_name_form_spec.rb +57 -0
- data/spec/forms/decidim/toggle/update_omniauth_form_spec.rb +56 -0
- data/spec/forms/decidim/toggle/update_security_form_spec.rb +32 -0
- data/spec/helpers/decidim/toggle/system_settings_tab_helper_spec.rb +99 -0
- data/spec/lib/decidim/toggle/expose_attributes_to_js_spec.rb +31 -0
- data/spec/lib/decidim/toggle/expose_attributes_to_js_validator_spec.rb +30 -0
- data/spec/lib/decidim/toggle/gem_registry_spec.rb +30 -0
- data/spec/lib/decidim/toggle/javascript_config_spec.rb +169 -0
- data/spec/lib/decidim/toggle/module_config_form_spec.rb +45 -0
- data/spec/lib/decidim/toggle/module_config_spec.rb +74 -0
- data/spec/lib/decidim/toggle/module_configuration_presenter_spec.rb +53 -0
- data/spec/lib/decidim/toggle/settings_form_builder_spec.rb +115 -0
- data/spec/requests/decidim_toggle/system/settings_tab_spec.rb +144 -0
- data/spec/spec_helper.rb +12 -0
- data/spec/support/decidim_factories.rb +3 -0
- data/spec/support/devise.rb +5 -0
- data/spec/system/decidim_toggle/javascript_config_spec.rb +56 -0
- data/website/.gitignore +4 -0
- data/website/docs/developer/_category_.json +8 -0
- data/website/docs/developer/code-of-conduct.md +99 -0
- data/website/docs/developer/contribute.md +50 -0
- data/website/docs/developer/deface-usage.md +37 -0
- data/website/docs/developer/documentation.md +58 -0
- data/website/docs/developer/error-handling.md +51 -0
- data/website/docs/developer/tab-registry.md +51 -0
- data/website/docs/developer/view-customization.md +49 -0
- data/website/docs/index.md +80 -0
- data/website/docs/integrate/_category_.json +8 -0
- data/website/docs/integrate/attributes.md +121 -0
- data/website/docs/integrate/customize-views.md +62 -0
- data/website/docs/integrate/index.md +38 -0
- data/website/docs/integrate/informative_callout.md +80 -0
- data/website/docs/integrate/javascript.md +84 -0
- data/website/docs/integrate/labels.md +91 -0
- data/website/docs/integrate/quickstart.md +77 -0
- data/website/docusaurus.config.ts +100 -0
- data/website/package.json +31 -0
- data/website/sidebars.ts +7 -0
- data/website/src/css/custom.css +37 -0
- data/website/static/img/logo.svg +1 -0
- data/website/static/img/schema_overview.png +0 -0
- data/website/static/img/screenshot_informative_callouts.png +0 -0
- data/website/static/img/screenshot_saved_flash.png +0 -0
- data/website/static/img/screenshots_locale_tab.png +0 -0
- data/website/static/img/screenshots_name_tab.png +0 -0
- data/website/static/img/screenshots_security_tab.png +0 -0
- data/website/tsconfig.json +6 -0
- data/website/yarn.lock +8336 -0
- data/yarn.lock +13 -0
- metadata +249 -0
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
module Decidim
|
|
6
|
+
module Toggle
|
|
7
|
+
describe JavascriptConfig do
|
|
8
|
+
let(:organization) { create(:organization) }
|
|
9
|
+
let(:registry_name) { :javascript_config_spec }
|
|
10
|
+
let(:registry) { SettingsTabRegistry.find(registry_name) }
|
|
11
|
+
|
|
12
|
+
def self.ensure_registry!
|
|
13
|
+
@registry_ready ||= begin
|
|
14
|
+
geo_form_class = Class.new(Decidim::Form) do
|
|
15
|
+
include ExposeAttributesToJs
|
|
16
|
+
include ModuleConfigForm
|
|
17
|
+
|
|
18
|
+
self.module_config_name = "decidim_geo"
|
|
19
|
+
|
|
20
|
+
mimic :organization
|
|
21
|
+
attribute :enabled, :boolean
|
|
22
|
+
attribute :search_bar, :boolean
|
|
23
|
+
attribute :tags, [String]
|
|
24
|
+
attribute :secret, :string
|
|
25
|
+
|
|
26
|
+
expose_to_javascript :enabled, :search_bar, :tags
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
other_form_class = Class.new(Decidim::Form) do
|
|
30
|
+
include ExposeAttributesToJs
|
|
31
|
+
include ModuleConfigForm
|
|
32
|
+
|
|
33
|
+
self.module_config_name = "decidim_other"
|
|
34
|
+
|
|
35
|
+
mimic :organization
|
|
36
|
+
attribute :mode, :string
|
|
37
|
+
expose_to_javascript :mode
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
test_registry = SettingsTabRegistry.create(:javascript_config_spec)
|
|
41
|
+
test_registry.register_form_tab(:geo, geo_form_class, Integer, module_name: :decidim_geo)
|
|
42
|
+
test_registry.register_form_tab(:other, other_form_class, Integer, module_name: :decidim_other)
|
|
43
|
+
test_registry.mark_configurations_applied!
|
|
44
|
+
true
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def self.reset_registry!
|
|
49
|
+
SettingsTabRegistry.destroy(:javascript_config_spec) if SettingsTabRegistry.find(:javascript_config_spec)
|
|
50
|
+
remove_instance_variable(:@registry_ready) if instance_variable_defined?(:@registry_ready)
|
|
51
|
+
ensure_registry!
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
before do
|
|
55
|
+
self.class.reset_registry!
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# rubocop:disable RSpec/BeforeAfterAll -- isolated registry for this file
|
|
59
|
+
after(:context) do
|
|
60
|
+
SettingsTabRegistry.destroy(:javascript_config_spec)
|
|
61
|
+
end
|
|
62
|
+
# rubocop:enable RSpec/BeforeAfterAll
|
|
63
|
+
|
|
64
|
+
it "returns an empty hash without organization" do
|
|
65
|
+
expect(described_class.for(nil, registry_name:)).to eq({})
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
it "builds flat dot-notation keys from exposed attributes" do
|
|
69
|
+
Decidim::Toggle.save_config!(
|
|
70
|
+
organization,
|
|
71
|
+
:decidim_geo,
|
|
72
|
+
{ "enabled" => true, "search_bar" => false, "tags" => %w(a b), "secret" => "hidden" }
|
|
73
|
+
)
|
|
74
|
+
Decidim::Toggle.save_config!(organization, :decidim_other, { "mode" => "live" })
|
|
75
|
+
|
|
76
|
+
expect(described_class.for(organization, registry_name:)).to eq(
|
|
77
|
+
"decidim_geo.enabled" => true,
|
|
78
|
+
"decidim_geo.search_bar" => false,
|
|
79
|
+
"decidim_geo.tags" => %w(a b),
|
|
80
|
+
"decidim_other.mode" => "live"
|
|
81
|
+
)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
it "skips forms without ExposeAttributesToJs" do
|
|
85
|
+
plain_form = Class.new(Decidim::Form) do
|
|
86
|
+
include ModuleConfigForm
|
|
87
|
+
|
|
88
|
+
self.module_config_name = "decidim_plain"
|
|
89
|
+
mimic :organization
|
|
90
|
+
attribute :enabled, :boolean
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
registry.register_form_tab(:plain, plain_form, Integer, module_name: :decidim_plain)
|
|
94
|
+
Decidim::Toggle.save_config!(organization, :decidim_plain, { "enabled" => true })
|
|
95
|
+
|
|
96
|
+
expect(described_class.for(organization, registry_name:)).not_to have_key("decidim_plain.enabled")
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
it "skips unsupported scalar types with a warning in test" do
|
|
100
|
+
expect(Rails.logger).to receive(:warn).with(/Skipping unsupported JavaScript config value/)
|
|
101
|
+
|
|
102
|
+
expect(described_class.send(:serialize_value, Object.new, nil)).to be_nil
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
it "stringifies nested hashes and arrays inside hash values" do
|
|
106
|
+
nested_form_class = Class.new(Decidim::Form) do
|
|
107
|
+
include ExposeAttributesToJs
|
|
108
|
+
include ModuleConfigForm
|
|
109
|
+
|
|
110
|
+
self.module_config_name = "decidim_nested"
|
|
111
|
+
|
|
112
|
+
mimic :organization
|
|
113
|
+
attribute :settings, Object
|
|
114
|
+
expose_to_javascript :settings
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
registry.register_form_tab(:nested, nested_form_class, Integer, module_name: :decidim_nested)
|
|
118
|
+
Decidim::Toggle.save_config!(
|
|
119
|
+
organization,
|
|
120
|
+
:decidim_nested,
|
|
121
|
+
{ "settings" => { labels: %w(a b), meta: { "count" => 2 } } }
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
expect(described_class.for(organization, registry_name:)).to include(
|
|
125
|
+
"decidim_nested.settings" => { "labels" => %w(a b), "meta" => { "count" => 2 } }
|
|
126
|
+
)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
context "with translatable attributes" do
|
|
130
|
+
def self.ensure_translatable_form!
|
|
131
|
+
@translatable_ready ||= begin
|
|
132
|
+
translatable_form_class = Class.new(Decidim::Form) do
|
|
133
|
+
include Decidim::TranslatableAttributes
|
|
134
|
+
include ExposeAttributesToJs
|
|
135
|
+
include ModuleConfigForm
|
|
136
|
+
|
|
137
|
+
self.module_config_name = "decidim_i18n"
|
|
138
|
+
|
|
139
|
+
mimic :organization
|
|
140
|
+
translatable_attribute :title, String
|
|
141
|
+
expose_to_javascript :title
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
SettingsTabRegistry.find(:javascript_config_spec).register_form_tab(
|
|
145
|
+
:i18n, translatable_form_class, Integer, module_name: :decidim_i18n
|
|
146
|
+
)
|
|
147
|
+
true
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
before do
|
|
152
|
+
self.class.ensure_translatable_form!
|
|
153
|
+
Decidim::Toggle.save_config!(
|
|
154
|
+
organization,
|
|
155
|
+
:decidim_i18n,
|
|
156
|
+
{ "title" => { "en" => "Hello", "ca" => "Hola" } }
|
|
157
|
+
)
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
it "exposes the raw locale hash" do
|
|
161
|
+
expect(described_class.for(organization, registry_name:)["decidim_i18n.title"]).to eq(
|
|
162
|
+
"en" => "Hello",
|
|
163
|
+
"ca" => "Hola"
|
|
164
|
+
)
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
module Decidim
|
|
6
|
+
module Toggle
|
|
7
|
+
describe ModuleConfigForm do
|
|
8
|
+
let(:form_class) do
|
|
9
|
+
Class.new(Decidim::Form) do
|
|
10
|
+
include ModuleConfigForm
|
|
11
|
+
|
|
12
|
+
self.module_config_name = "decidim_demo"
|
|
13
|
+
|
|
14
|
+
mimic :organization
|
|
15
|
+
|
|
16
|
+
attribute :enabled, :boolean
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
describe ".human_attribute_name" do
|
|
21
|
+
it "uses decidim_toggle.system.<module_config_name> when present" do
|
|
22
|
+
I18n.with_locale(:en) do
|
|
23
|
+
I18n.backend.store_translations(:en, {
|
|
24
|
+
decidim_toggle: {
|
|
25
|
+
system: {
|
|
26
|
+
decidim_demo: {
|
|
27
|
+
enabled: "Demo enabled label"
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
expect(form_class.human_attribute_name(:enabled)).to eq("Demo enabled label")
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it "falls back when the module key is missing" do
|
|
38
|
+
I18n.with_locale(:en) do
|
|
39
|
+
expect(form_class.human_attribute_name(:missing_attr)).to eq("Missing attr")
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
module Decidim
|
|
6
|
+
module Toggle
|
|
7
|
+
describe Decidim::Toggle do
|
|
8
|
+
let(:organization) { create(:organization) }
|
|
9
|
+
|
|
10
|
+
after do
|
|
11
|
+
SettingsTabRegistry.destroy(:module_config_spec)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
describe ".save_config!" do
|
|
15
|
+
it "creates a row and shallow-merges by default" do
|
|
16
|
+
described_class.save_config!(organization, :decidim_geo, { "enabled" => true })
|
|
17
|
+
described_class.save_config!(organization, :decidim_geo, { "foo" => "bar" })
|
|
18
|
+
|
|
19
|
+
row = OrganizationModuleConfig.find_by!(decidim_organization_id: organization.id, module_name: "decidim_geo")
|
|
20
|
+
expect(row.config).to eq("enabled" => true, "foo" => "bar")
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it "replaces config when merge: false" do
|
|
24
|
+
described_class.save_config!(organization, :decidim_geo, { "a" => 1 })
|
|
25
|
+
described_class.save_config!(organization, :decidim_geo, { "b" => 2 }, merge: false)
|
|
26
|
+
|
|
27
|
+
row = OrganizationModuleConfig.find_by!(decidim_organization_id: organization.id, module_name: "decidim_geo")
|
|
28
|
+
expect(row.config).to eq("b" => 2)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
describe ".config_for" do
|
|
33
|
+
let(:demo_form_class) do
|
|
34
|
+
Class.new(Decidim::Form) do
|
|
35
|
+
mimic :organization
|
|
36
|
+
attribute :enabled, :boolean
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it "returns indifferent hash when no form is registered for the module" do
|
|
41
|
+
described_class.save_config!(organization, :unknown_mod, { "x" => 1 })
|
|
42
|
+
result = described_class.config_for(organization, :unknown_mod, registry_name: :module_config_spec)
|
|
43
|
+
expect(result[:x]).to eq(1)
|
|
44
|
+
expect(result["x"]).to eq(1)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it "returns a normalized hash when a tab registered module_name with a form" do
|
|
48
|
+
form_class = demo_form_class
|
|
49
|
+
SettingsTabRegistry.register(:module_config_spec) do |tabs|
|
|
50
|
+
tabs.add_tab :geo, "Geo", form: form_class, command: Integer, module_name: :decidim_geo
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
described_class.save_config!(organization, :decidim_geo, { "enabled" => true })
|
|
54
|
+
|
|
55
|
+
result = described_class.config_for(organization, :decidim_geo, registry_name: :module_config_spec)
|
|
56
|
+
expect(result).to be_a(ActiveSupport::HashWithIndifferentAccess)
|
|
57
|
+
expect(result[:enabled]).to be(true)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
it "normalizes nil boolean to false when a form is registered" do
|
|
61
|
+
form_class = demo_form_class
|
|
62
|
+
SettingsTabRegistry.register(:module_config_spec) do |tabs|
|
|
63
|
+
tabs.add_tab :geo, "Geo", form: form_class, command: Integer, module_name: :decidim_geo
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
described_class.save_config!(organization, :decidim_geo, {})
|
|
67
|
+
|
|
68
|
+
result = described_class.config_for(organization, :decidim_geo, registry_name: :module_config_spec)
|
|
69
|
+
expect(result[:enabled]).to be(false)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
module Decidim
|
|
6
|
+
module Toggle
|
|
7
|
+
describe ModuleConfigurationPresenter do
|
|
8
|
+
let(:organization) { create(:organization) }
|
|
9
|
+
|
|
10
|
+
let(:demo_form_class) do
|
|
11
|
+
Class.new(Decidim::Form) do
|
|
12
|
+
mimic :organization
|
|
13
|
+
|
|
14
|
+
attribute :enabled, :boolean
|
|
15
|
+
attribute :tags, :array
|
|
16
|
+
attribute :meta, :hash
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def build_presenter(attrs)
|
|
21
|
+
form = demo_form_class.from_params(organization: attrs).with_context(current_organization: organization)
|
|
22
|
+
described_class.new(form)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it "normalizes nil array to empty array" do
|
|
26
|
+
p = build_presenter(tags: nil)
|
|
27
|
+
expect(p.tags).to eq([])
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it "normalizes nil hash to empty hash" do
|
|
31
|
+
p = build_presenter(meta: nil)
|
|
32
|
+
expect(p.meta).to eq({})
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it "normalizes nil boolean to false and exposes predicate" do
|
|
36
|
+
p = build_presenter(enabled: nil)
|
|
37
|
+
expect(p.enabled).to be(false)
|
|
38
|
+
expect(p.enabled?).to be(false)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it "preserves true boolean" do
|
|
42
|
+
p = build_presenter(enabled: true)
|
|
43
|
+
expect(p.enabled).to be(true)
|
|
44
|
+
expect(p.enabled?).to be(true)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it "exports normalized attributes as a hash" do
|
|
48
|
+
p = build_presenter(enabled: nil, tags: nil, meta: nil)
|
|
49
|
+
expect(p.to_config_hash).to eq("enabled" => false, "tags" => [], "meta" => {})
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
module Decidim
|
|
6
|
+
module Toggle
|
|
7
|
+
describe SettingsFormBuilder do
|
|
8
|
+
include ActionView::TestCase::Behavior
|
|
9
|
+
|
|
10
|
+
it "renders fields_for_names subset" do
|
|
11
|
+
organization = create(:organization)
|
|
12
|
+
form = UpdateLocaleForm.from_model(organization)
|
|
13
|
+
template = ActionView::Base.with_empty_template_cache.new(ActionView::LookupContext.new([]), {}, nil)
|
|
14
|
+
builder = described_class.new(:organization, form, template, {})
|
|
15
|
+
|
|
16
|
+
html = builder.fields_for_names(:enable_machine_translations)
|
|
17
|
+
expect(html).to be_present
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it "renders informative callouts with decidim announcement cell markup" do
|
|
21
|
+
form_class = Class.new(UpdateSecurityForm) do
|
|
22
|
+
include InformativeCallouts
|
|
23
|
+
|
|
24
|
+
info :spec_info_callout
|
|
25
|
+
|
|
26
|
+
def spec_info_callout
|
|
27
|
+
"Spec info callout"
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
form = form_class.from_params(organization: {})
|
|
31
|
+
template = ActionView::Base.with_empty_template_cache.new(ActionView::LookupContext.new([]), {}, nil)
|
|
32
|
+
allow(template).to receive(:cell) do |_name, message, options|
|
|
33
|
+
%(<div class="flash flex-col #{options[:callout_class]}">#{message}</div>).html_safe
|
|
34
|
+
end
|
|
35
|
+
builder = described_class.new(:organization, form, template, {})
|
|
36
|
+
|
|
37
|
+
html = builder.informative_callouts
|
|
38
|
+
expect(html).to include("Spec info callout")
|
|
39
|
+
expect(html).to include('class="flash flex-col info"')
|
|
40
|
+
expect(html).to include('class="decidim_toggle_informative_callout"')
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it "renders i18n helptext under the field when present" do
|
|
44
|
+
organization = create(:organization)
|
|
45
|
+
form = UpdateSecurityForm.from_model(organization)
|
|
46
|
+
template = ActionView::Base.with_empty_template_cache.new(ActionView::LookupContext.new([]), {}, nil)
|
|
47
|
+
builder = described_class.new(:organization, form, template, {})
|
|
48
|
+
|
|
49
|
+
helptext = "Spec helptext for users_registration_mode"
|
|
50
|
+
model_key = form.class.model_name.i18n_key.to_s
|
|
51
|
+
|
|
52
|
+
I18n.with_locale(:en) do
|
|
53
|
+
I18n.backend.store_translations(:en, {
|
|
54
|
+
activemodel: {
|
|
55
|
+
attributes: {
|
|
56
|
+
"organization" => {
|
|
57
|
+
helptext: {
|
|
58
|
+
"users_registration_mode" => helptext
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
model_key => {
|
|
62
|
+
helptext: {
|
|
63
|
+
"users_registration_mode" => helptext
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
html = builder.fields_for_names(:users_registration_mode)
|
|
71
|
+
expect(html).to include(helptext)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
it "disables fields when the form implements attribute_disabled?" do
|
|
76
|
+
form_class = Class.new(Decidim::Form) do
|
|
77
|
+
attribute :enabled, :boolean
|
|
78
|
+
attribute :locked, :boolean
|
|
79
|
+
|
|
80
|
+
def attribute_disabled?(attribute)
|
|
81
|
+
attribute == :locked
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
form = form_class.from_params(organization: { enabled: true, locked: false })
|
|
85
|
+
template = ActionView::Base.with_empty_template_cache.new(ActionView::LookupContext.new([]), {}, nil)
|
|
86
|
+
builder = described_class.new(:organization, form, template, {})
|
|
87
|
+
|
|
88
|
+
html = builder.all_fields
|
|
89
|
+
expect(html).to include('name="organization[enabled]"')
|
|
90
|
+
expect(html).not_to include('organization[enabled]" disabled')
|
|
91
|
+
expect(html).to include('name="organization[locked]" disabled="disabled"')
|
|
92
|
+
expect(html).to include('class="field is-disabled"')
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
it "renders boolean fields, text areas, and collection inputs" do
|
|
96
|
+
organization = create(:organization, secondary_hosts: %w(extra.example.org))
|
|
97
|
+
template = ActionView::Base.with_empty_template_cache.new(ActionView::LookupContext.new([]), {}, nil)
|
|
98
|
+
|
|
99
|
+
security_form = UpdateSecurityForm.from_model(organization)
|
|
100
|
+
security_builder = described_class.new(:organization, security_form, template, {})
|
|
101
|
+
security_html = security_builder.all_fields
|
|
102
|
+
expect(security_html).to include('type="checkbox"')
|
|
103
|
+
expect(security_builder.fields_for_names(:users_registration_mode)).to include('type="radio"')
|
|
104
|
+
|
|
105
|
+
name_form = UpdateNameForm.from_model(organization)
|
|
106
|
+
name_builder = described_class.new(:organization, name_form, template, {})
|
|
107
|
+
expect(name_builder.all_fields).to include("extra.example.org")
|
|
108
|
+
|
|
109
|
+
authorizations_form = UpdateAuthorizationsForm.from_model(organization)
|
|
110
|
+
authorizations_builder = described_class.new(:organization, authorizations_form, template, {})
|
|
111
|
+
expect(authorizations_builder.all_fields).to include('type="checkbox"')
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
describe "DecidimToggle::System::SettingsTabController" do
|
|
6
|
+
let(:organization) { create(:organization) }
|
|
7
|
+
let(:admin) { create(:admin) }
|
|
8
|
+
|
|
9
|
+
before { login_as admin, scope: :admin }
|
|
10
|
+
|
|
11
|
+
describe "PATCH /decidim_toggle/system/organizations/:organization_id/settings_tab/:tab_id" do
|
|
12
|
+
let(:path) { "/decidim_toggle/system/organizations/#{organization.id}/settings_tab/security" }
|
|
13
|
+
let(:valid_params) do
|
|
14
|
+
{
|
|
15
|
+
organization: {
|
|
16
|
+
force_users_to_authenticate_before_access_organization: "1",
|
|
17
|
+
users_registration_mode: "enabled"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
it "updates the tab and redirects with notice" do
|
|
23
|
+
patch path, params: valid_params
|
|
24
|
+
|
|
25
|
+
expect(response).to redirect_to(decidim_system.edit_organization_path(organization))
|
|
26
|
+
expect(flash[:notice]).to eq(I18n.t("decidim_toggle.system.organizations.form_tab.success"))
|
|
27
|
+
expect(organization.reload.force_users_to_authenticate_before_access_organization).to be(true)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it "redirects with anchor when active tab param matches a registered tab" do
|
|
31
|
+
patch path, params: valid_params.merge(decidim_toggle_active_tab: "security")
|
|
32
|
+
|
|
33
|
+
expect(response).to redirect_to(
|
|
34
|
+
decidim_system.edit_organization_path(organization, anchor: "panel-toggle-security")
|
|
35
|
+
)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
it "ignores unknown active tab ids when building the redirect anchor" do
|
|
39
|
+
patch path, params: valid_params.merge(decidim_toggle_active_tab: "not-a-tab")
|
|
40
|
+
|
|
41
|
+
expect(response).to redirect_to(decidim_system.edit_organization_path(organization))
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it "returns not found for unknown tab ids" do
|
|
45
|
+
patch "/decidim_toggle/system/organizations/#{organization.id}/settings_tab/unknown"
|
|
46
|
+
|
|
47
|
+
expect(response).to have_http_status(:not_found)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it "stores invalid tab data in flash when the form is invalid" do
|
|
51
|
+
patch path, params: { organization: { users_registration_mode: "not_a_mode" } }
|
|
52
|
+
|
|
53
|
+
expect(response).to redirect_to(decidim_system.edit_organization_path(organization))
|
|
54
|
+
expect(flash[:decidim_toggle_invalid_settings_tab]).to include(
|
|
55
|
+
organization_id: organization.id,
|
|
56
|
+
tab_id: "security"
|
|
57
|
+
)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
it "re-renders field errors after redirect" do
|
|
61
|
+
patch path, params: { organization: { users_registration_mode: "not_a_mode" } }
|
|
62
|
+
follow_redirect!
|
|
63
|
+
|
|
64
|
+
expect(response.body).to include("form-error")
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
describe "PATCH name tab" do
|
|
69
|
+
let(:path) { "/decidim_toggle/system/organizations/#{organization.id}/settings_tab/name" }
|
|
70
|
+
let(:valid_params) do
|
|
71
|
+
{
|
|
72
|
+
organization: {
|
|
73
|
+
name_en: "Renamed organization",
|
|
74
|
+
host: "renamed.example.org",
|
|
75
|
+
secondary_hosts: ""
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
it "updates host and redirects with notice" do
|
|
81
|
+
patch path, params: valid_params
|
|
82
|
+
|
|
83
|
+
expect(response).to redirect_to(decidim_system.edit_organization_path(organization))
|
|
84
|
+
expect(flash[:notice]).to eq(I18n.t("decidim_toggle.system.organizations.form_tab.success"))
|
|
85
|
+
organization.reload
|
|
86
|
+
expect(organization.host).to eq("renamed.example.org")
|
|
87
|
+
expect(organization.name["en"]).to eq("Renamed organization")
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
it "stores invalid tab data in flash when host is blank" do
|
|
91
|
+
patch path, params: { organization: { name_en: "", host: "", secondary_hosts: "" } }
|
|
92
|
+
|
|
93
|
+
expect(response).to redirect_to(decidim_system.edit_organization_path(organization))
|
|
94
|
+
expect(flash[:decidim_toggle_invalid_settings_tab]).to include(
|
|
95
|
+
organization_id: organization.id,
|
|
96
|
+
tab_id: "name"
|
|
97
|
+
)
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
describe "PATCH language tab" do
|
|
102
|
+
let(:path) { "/decidim_toggle/system/organizations/#{organization.id}/settings_tab/language" }
|
|
103
|
+
let(:organization) { create(:organization, available_locales: %w(en ca), default_locale: "ca") }
|
|
104
|
+
|
|
105
|
+
before do
|
|
106
|
+
allow(Rails.application).to receive(:load_tasks)
|
|
107
|
+
allow(Rake::Task).to receive(:[]).and_call_original
|
|
108
|
+
allow(Rake::Task).to receive(:[]).with("decidim:locales:rebuild_search").and_return(
|
|
109
|
+
instance_double(Rake::Task, invoke: nil, reenable: nil)
|
|
110
|
+
)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
it "updates available locales and default locale" do
|
|
114
|
+
patch path, params: {
|
|
115
|
+
organization: {
|
|
116
|
+
available_locales: { "en" => "1" },
|
|
117
|
+
default_locale: "en",
|
|
118
|
+
enable_machine_translations: false,
|
|
119
|
+
machine_translation_display_priority: "original"
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
expect(response).to redirect_to(decidim_system.edit_organization_path(organization))
|
|
124
|
+
organization.reload
|
|
125
|
+
expect(organization.available_locales).to eq(%w(en))
|
|
126
|
+
expect(organization.default_locale).to eq("en")
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
it "stores invalid tab data when default locale is not enabled" do
|
|
130
|
+
patch path, params: {
|
|
131
|
+
organization: {
|
|
132
|
+
available_locales: { "en" => "1" },
|
|
133
|
+
default_locale: "ca"
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
expect(response).to redirect_to(decidim_system.edit_organization_path(organization))
|
|
138
|
+
expect(flash[:decidim_toggle_invalid_settings_tab]).to include(
|
|
139
|
+
organization_id: organization.id,
|
|
140
|
+
tab_id: "language"
|
|
141
|
+
)
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
data/spec/spec_helper.rb
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "decidim/dev"
|
|
4
|
+
|
|
5
|
+
ENV["ENGINE_ROOT"] = File.dirname(__dir__)
|
|
6
|
+
|
|
7
|
+
Decidim::Dev.dummy_app_path = File.expand_path(File.join(__dir__, "decidim_dummy_app"))
|
|
8
|
+
|
|
9
|
+
require "decidim/dev/test/map_server"
|
|
10
|
+
require "decidim/dev/test/base_spec_helper"
|
|
11
|
+
|
|
12
|
+
Dir[File.expand_path("support/**/*.rb", __dir__)].each { |f| require f }
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
module Decidim
|
|
6
|
+
module Toggle
|
|
7
|
+
describe "JavaScript config injection" do
|
|
8
|
+
let(:organization) { create(:organization) }
|
|
9
|
+
let(:admin) { create(:user, :confirmed, :admin, organization:) }
|
|
10
|
+
|
|
11
|
+
def self.ensure_demo_tab!
|
|
12
|
+
@demo_tab_ready ||= begin
|
|
13
|
+
demo_form_class = Class.new(Decidim::Form) do
|
|
14
|
+
include ExposeAttributesToJs
|
|
15
|
+
include ModuleConfigForm
|
|
16
|
+
|
|
17
|
+
self.module_config_name = "decidim_toggle_demo"
|
|
18
|
+
|
|
19
|
+
mimic :organization
|
|
20
|
+
attribute :enabled, :boolean
|
|
21
|
+
expose_to_javascript :enabled
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
SettingsTabRegistry.find(:organization_settings).register_form_tab(
|
|
25
|
+
:toggle_demo_js,
|
|
26
|
+
demo_form_class,
|
|
27
|
+
UpdateModuleConfigCommand,
|
|
28
|
+
module_name: :decidim_toggle_demo
|
|
29
|
+
)
|
|
30
|
+
true
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
before do
|
|
35
|
+
self.class.ensure_demo_tab!
|
|
36
|
+
Decidim::Toggle.save_config!(organization, :decidim_toggle_demo, { "enabled" => true })
|
|
37
|
+
switch_to_host(organization.host)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it "exposes window.DecidimToggle on the public site" do
|
|
41
|
+
visit decidim.root_path
|
|
42
|
+
|
|
43
|
+
expect(page.body).to include("window.DecidimToggle")
|
|
44
|
+
expect(page.body).to include('"decidim_toggle_demo.enabled":true')
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it "exposes window.DecidimToggle in the admin layout" do
|
|
48
|
+
login_as admin, scope: :user
|
|
49
|
+
visit decidim_admin.root_path
|
|
50
|
+
|
|
51
|
+
expect(page.body).to include("window.DecidimToggle")
|
|
52
|
+
expect(page.body).to include('"decidim_toggle_demo.enabled":true')
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
data/website/.gitignore
ADDED