0
0
Fork 0
mirror of https://github.com/discourse/discourse.git synced 2026-08-06 13:08:40 +08:00
discourse/spec/lib/theme_settings_parser_spec.rb
Martin Brennan fc8df41adb
DEV: Add disallowed_groups to theme/component settings (#41792)
Further following on from bee1be8599,
this brings greater parity between theme settings and site settings.

This commit adds a new `disallowed_groups` field to theme and component
settings, which allows theme authors to specify groups that cannot be
selected
by admins for a particular list type or object group type setting.

This is useful for cases where the group-based setting isn't usable
for a group like e.g. anonymous_users, no point allowing them for
a setting that requires a user to be logged in to take effect.

c.f.
https://meta.discourse.org/t/granular-group-based-permissions-for-anonymous-and-logged-in-users/402273/18?u=martin

### List groups

With `disallowed_groups: "4|5"` specified (`logged_in_users` &
`anonymous_users`)

<img width="734" height="450" alt="image"
src="https://github.com/user-attachments/assets/ed8d7338-65fa-44e6-8dce-7073a87076e6"
/>

### Object groups

With `disallowed_groups: "4|5"` specified (`logged_in_users` &
`anonymous_users`)

<img width="696" height="725" alt="image"
src="https://github.com/user-attachments/assets/1a9103d4-cf44-4038-9d62-3838cb779b49"
/>
2026-07-20 09:28:52 +10:00

149 lines
4.7 KiB
Ruby
Vendored

# frozen_string_literal: true
require "theme_settings_parser"
RSpec.describe ThemeSettingsParser do
after { ThemeField.destroy_all }
def types
ThemeSetting.types
end
class Loader
def initialize
@settings = []
load_settings
end
def load_settings
yaml = File.read("#{Rails.root.join("spec/fixtures/theme_settings/valid_settings.yaml")}")
field = ThemeField.create!(theme_id: -1, target_id: 3, name: "yaml", value: yaml)
ThemeSettingsParser
.new(field)
.load { |name, default, type, opts| @settings << setting(name, default, type, opts) }
end
def setting(name, default, type, opts = {})
{ name: name, default: default, type: type, opts: opts }
end
def find_by_name(name)
@settings.find { |setting| setting[:name] == name }
end
end
let(:loader) { Loader.new }
it "guesses types correctly" do
expect(loader.find_by_name(:boolean_setting)[:type]).to eq(types[:bool])
expect(loader.find_by_name(:boolean_setting_02)[:type]).to eq(types[:bool])
expect(loader.find_by_name(:string_setting)[:type]).to eq(types[:string])
expect(loader.find_by_name(:integer_setting)[:type]).to eq(types[:integer])
expect(loader.find_by_name(:integer_setting_03)[:type]).to eq(types[:integer])
expect(loader.find_by_name(:float_setting)[:type]).to eq(types[:float])
expect(loader.find_by_name(:list_setting)[:type]).to eq(types[:list])
expect(loader.find_by_name(:enum_setting)[:type]).to eq(types[:enum])
end
describe "description locale" do
it "favors I18n.locale" do
I18n.locale = :ar
SiteSetting.default_locale = "en"
expect(loader.find_by_name(:enum_setting_02)[:opts][:description]).to eq("Arabic text")
end
it "uses SiteSetting.default_locale if I18n.locale isn't supported" do
I18n.locale = :en
SiteSetting.default_locale = "es"
expect(loader.find_by_name(:integer_setting_02)[:opts][:description]).to eq("Spanish text")
end
it "finds the first supported locale and uses it as a last resort" do
I18n.locale = :de
SiteSetting.default_locale = "it"
expect(loader.find_by_name(:integer_setting_02)[:opts][:description]).to eq("French text")
end
it "doesn't set locale if no supported locale is provided" do
expect(loader.find_by_name(:integer_setting_03)[:opts][:description]).to be_nil
end
end
describe "enum setting" do
it "should never have less than 1 choices" do
choices = loader.find_by_name(:enum_setting)[:opts][:choices]
expect(choices.class).to eq(Array)
expect(choices.length).to eq(3)
choices = loader.find_by_name(:enum_setting_02)[:opts][:choices]
expect(choices.class).to eq(Array)
expect(choices.length).to eq(1)
end
end
describe "list setting" do
it "supports list type" do
list_type = loader.find_by_name(:compact_list_setting)[:opts][:list_type]
expect(list_type).to eq("compact")
end
it "supports disallowed groups metadata" do
yaml = <<~YAML
groups_setting:
type: list
list_type: group
disallowed_groups: "0|1"
default: "2|3"
YAML
field = ThemeField.create!(theme_id: -1, target_id: 3, name: "yaml", value: yaml)
parsed = []
ThemeSettingsParser
.new(field)
.load { |name, default, type, opts| parsed << { name: name, opts: opts } }
setting = parsed.find { |parsed_setting| parsed_setting[:name] == :groups_setting }
expect(setting[:opts][:disallowed_groups]).to eq("0|1")
end
end
describe "resolve_group_membership" do
it "parses resolve_group_membership option" do
yaml = <<~YAML
groups_setting:
type: list
list_type: group
resolve_group_membership: true
default: "1|2"
YAML
field = ThemeField.create!(theme_id: -1, target_id: 3, name: "yaml", value: yaml)
parsed = []
ThemeSettingsParser
.new(field)
.load { |name, default, type, opts| parsed << { name: name, opts: opts } }
setting = parsed.find { |s| s[:name] == :groups_setting }
expect(setting[:opts][:resolve_group_membership]).to eq(true)
end
it "defaults to false when not specified" do
yaml = <<~YAML
groups_setting:
type: list
list_type: group
default: "1|2"
YAML
field = ThemeField.create!(theme_id: -1, target_id: 3, name: "yaml", value: yaml)
parsed = []
ThemeSettingsParser
.new(field)
.load { |name, default, type, opts| parsed << { name: name, opts: opts } }
setting = parsed.find { |s| s[:name] == :groups_setting }
expect(setting[:opts][:resolve_group_membership]).to eq(false)
end
end
end