0
0
Fork 1
Spiegel von https://github.com/paviliondev/discourse-custom-wizard.git synchronisiert 2024-11-22 09:20:29 +01:00

working builder and action specs

Dieser Commit ist enthalten in:
Angus McLeod 2020-10-31 18:05:50 +11:00
Ursprung 8fdd263d8e
Commit b726d40a0c
15 geänderte Dateien mit 603 neuen und 393 gelöschten Zeilen

Datei anzeigen

@ -4,7 +4,7 @@ class CustomWizard::AdminWizardController < CustomWizard::AdminController
def index def index
render_json_dump( render_json_dump(
wizard_list: ActiveModel::ArraySerializer.new( wizard_list: ActiveModel::ArraySerializer.new(
CustomWizard::Wizard.list, CustomWizard::Template.list,
each_serializer: CustomWizard::BasicWizardSerializer each_serializer: CustomWizard::BasicWizardSerializer
), ),
field_types: CustomWizard::Field.types field_types: CustomWizard::Field.types
@ -14,7 +14,7 @@ class CustomWizard::AdminWizardController < CustomWizard::AdminController
def show def show
params.require(:wizard_id) params.require(:wizard_id)
if data = CustomWizard::Wizard.find(params[:wizard_id].underscore) if data = CustomWizard::Template.find(params[:wizard_id].underscore)
render json: data.as_json render json: data.as_json
else else
render json: { none: true } render json: { none: true }
@ -22,8 +22,11 @@ class CustomWizard::AdminWizardController < CustomWizard::AdminController
end end
def remove def remove
CustomWizard::Wizard.remove(@wizard.id) if CustomWizard::Template.remove(@wizard.id)
render json: success_json render json: success_json
else
render json: failed_json
end
end end
def save def save
@ -36,7 +39,7 @@ class CustomWizard::AdminWizardController < CustomWizard::AdminController
if validation[:error] if validation[:error]
render json: { error: validation[:error] } render json: { error: validation[:error] }
else else
if wizard_id = CustomWizard::Wizard.save(validation[:wizard]) if wizard_id = CustomWizard::Template.save(validation[:wizard])
render json: success_json.merge(wizard_id: wizard_id) render json: success_json.merge(wizard_id: wizard_id)
else else
render json: failed_json render json: failed_json

Datei anzeigen

@ -1,19 +1,30 @@
class CustomWizard::StepsController < ::ApplicationController class CustomWizard::StepsController < ::ApplicationController
before_action :ensure_logged_in before_action :ensure_logged_in
before_action :ensure_can_update
def update def update
params.require(:step_id) params.require(:step_id)
params.require(:wizard_id) params.require(:wizard_id)
field_ids = CustomWizard::Wizard.field_ids(params[:wizard_id], params[:step_id])
permitted = params.permit(:wizard_id, :step_id) wizard = @builder.build
if params[:fields] step = wizard.steps.select { |s| s.id == update_params[:step_id] }.first
permitted[:fields] = params[:fields].select { |k, v| field_ids.include? k }
permitted.permit! if !step || step.fields.blank?
raise Discourse::InvalidParameters.new(:step_id)
end end
wizard = CustomWizard::Builder.new(permitted[:wizard_id].underscore, current_user).build field_ids = step.fields.map(&:id)
updater = wizard.create_updater(permitted[:step_id], permitted[:fields])
if params[:fields]
permitted_fields = params[:fields].select { |k, v| field_ids.include? k }
update_params[:fields] = permitted_fields
update_params.permit!
end
updater = wizard.create_updater(
update_params[:step_id],
update_params[:fields]
)
updater.update updater.update
if updater.success? if updater.success?
@ -29,4 +40,25 @@ class CustomWizard::StepsController < ::ApplicationController
render json: { errors: errors }, status: 422 render json: { errors: errors }, status: 422
end end
end end
private
def ensure_can_update
@builder = CustomWizard::Builder.new(
update_params[:wizard_id].underscore,
current_user
)
if @builder.nil?
raise Discourse::InvalidParameters.new(:wizard_id)
end
if !@builder.wizard || !@builder.wizard.can_access?
raise Discourse::InvalidAccess.new
end
end
def update_params
params.permit(:wizard_id, :step_id)
end
end end

Datei anzeigen

@ -24,7 +24,7 @@ class CustomWizard::WizardController < ::ApplicationController
if builder.wizard.present? if builder.wizard.present?
builder_opts = {} builder_opts = {}
builder_opts[:reset] = params[:reset] || builder.wizard.restart_on_revisit builder_opts[:reset] = params[:reset]
built_wizard = builder.build(builder_opts, params) built_wizard = builder.build(builder_opts, params)
render_serialized(built_wizard, ::CustomWizard::WizardSerializer, root: false) render_serialized(built_wizard, ::CustomWizard::WizardSerializer, root: false)

Datei anzeigen

@ -2,13 +2,13 @@ class CustomWizard::Builder
attr_accessor :wizard, :updater, :submissions attr_accessor :wizard, :updater, :submissions
def initialize(wizard_id, user=nil) def initialize(wizard_id, user=nil)
params = CustomWizard::Wizard.find(wizard_id) template = CustomWizard::Template.find(wizard_id)
return nil if params.blank? return nil if template.blank?
@wizard = CustomWizard::Wizard.new(params, user) @wizard = CustomWizard::Wizard.new(template, user)
@steps = params['steps'] || [] @steps = template['steps'] || []
@actions = params['actions'] || [] @actions = template['actions'] || []
@submissions = @wizard.submissions if user && @wizard @submissions = @wizard.submissions
end end
def self.sorted_handlers def self.sorted_handlers
@ -48,12 +48,27 @@ class CustomWizard::Builder
return nil if !SiteSetting.custom_wizard_enabled || !@wizard return nil if !SiteSetting.custom_wizard_enabled || !@wizard
return @wizard if !@wizard.can_access? return @wizard if !@wizard.can_access?
build_opts[:reset] = build_opts[:reset] || @wizard.restart_on_revisit
reset_submissions if build_opts[:reset] reset_submissions if build_opts[:reset]
@steps.each do |step_template| @steps.each do |step_template|
@wizard.append_step(step_template['id']) do |step| @wizard.append_step(step_template['id']) do |step|
step.permitted = true
if step_template['required_data']
step = ensure_required_data(step, step_template)
end
if !step.permitted
if step_template['required_data_message']
step.permitted_message = step_template['required_data_message']
end
next
end
step.title = step_template['title'] if step_template['title'] step.title = step_template['title'] if step_template['title']
step.banner = step_template['banner'] if step_template['banner'] step.banner = step_template['banner'] if step_template['banner']
step.key = step_template['key'] if step_template['key']
if step_template['description'] if step_template['description']
step.description = mapper.interpolate( step.description = mapper.interpolate(
@ -63,59 +78,8 @@ class CustomWizard::Builder
) )
end end
step.key = step_template['key'] if step_template['key']
step.permitted = true
if permitted_params = step_template['permitted_params'] if permitted_params = step_template['permitted_params']
permitted_data = {} save_permitted_params(permitted_params, params)
permitted_params.each do |p|
pair = p['pairs'].first
params_key = pair['key'].to_sym
submission_key = pair['value'].to_sym
permitted_data[submission_key] = params[params_key] if params[params_key]
end
if permitted_data.present?
current_data = @submissions.last || {}
save_submissions(current_data.merge(permitted_data), false)
end
end
if (required_data = step_template['required_data']).present?
has_required_data = true
required_data.each do |required|
required['pairs'].each do |pair|
if pair['key'].blank? || pair['value'].blank?
has_required_data = false
end
end
end
if has_required_data
if !@submissions.last
step.permitted = false
else
required_data.each do |required|
pairs = required['pairs'].map do |p|
p['key'] = @submissions.last[p['key']]
end
unless mapper.validate_pairs(pairs)
step.permitted = false
end
end
end
if !step.permitted
if step_template['required_data_message']
step.permitted_message = step_template['required_data_message']
end
next
end
end
end end
if step_template['fields'] && step_template['fields'].length if step_template['fields'] && step_template['fields'].length
@ -211,6 +175,8 @@ class CustomWizard::Builder
params[:description] = field_template['description'] if field_template['description'] params[:description] = field_template['description'] if field_template['description']
params[:image] = field_template['image'] if field_template['image'] params[:image] = field_template['image'] if field_template['image']
params[:key] = field_template['key'] if field_template['key'] params[:key] = field_template['key'] if field_template['key']
params[:min_length] = field_template['min_length'] if field_template['min_length']
params[:value] = prefill_field(field_template, step_template)
## Load previously submitted values ## Load previously submitted values
if !build_opts[:reset] && @submissions.last && !@submissions.last.key?("submitted_at") if !build_opts[:reset] && @submissions.last && !@submissions.last.key?("submitted_at")
@ -218,8 +184,6 @@ class CustomWizard::Builder
params[:value] = submission[field_template['id']] if submission[field_template['id']] params[:value] = submission[field_template['id']] if submission[field_template['id']]
end end
params[:value] = prefill_field(field_template, step_template) || params[:value]
if field_template['type'] === 'group' && params[:value].present? if field_template['type'] === 'group' && params[:value].present?
params[:value] = params[:value].first params[:value] = params[:value].first
end end
@ -404,4 +368,44 @@ class CustomWizard::Builder
PluginStore.set("#{@wizard.id}_submissions", @wizard.user.id, @submissions) PluginStore.set("#{@wizard.id}_submissions", @wizard.user.id, @submissions)
@wizard.reset @wizard.reset
end end
def save_permitted_params(permitted_params, params)
permitted_data = {}
permitted_params.each do |pp|
pair = pp['pairs'].first
params_key = pair['key'].to_sym
submission_key = pair['value'].to_sym
permitted_data[submission_key] = params[params_key] if params[params_key]
end
if permitted_data.present?
current_data = @submissions.last || {}
save_submissions(current_data.merge(permitted_data), false)
end
end
def ensure_required_data(step, step_template)
step_template['required_data'].each do |required|
pairs = required['pairs'].select do |pair|
pair['key'].present? && pair['value'].present?
end
if pairs.any? && !@submissions.last
step.permitted = false
break
end
pairs.each do |pair|
pair['key'] = @submissions.last[pair['key']]
end
if !mapper.validate_pairs(pairs)
step.permitted = false
break
end
end
step
end
end end

Datei anzeigen

@ -0,0 +1,81 @@
class CustomWizard::Template
def self.add(obj)
wizard = obj.is_a?(String) ? ::JSON.parse(json) : obj
PluginStore.set('custom_wizard', wizard["id"], wizard)
end
def self.find(wizard_id)
PluginStore.get('custom_wizard', wizard_id)
end
def self.save(data)
data = data.with_indifferent_access
existing = self.find(data[:id])
data[:steps].each do |step|
if step[:raw_description]
step[:description] = PrettyText.cook(step[:raw_description])
end
end
data = data.slice!(:create)
ActiveRecord::Base.transaction do
PluginStore.set('custom_wizard', data[:id], data)
if data[:after_time]
Jobs.cancel_scheduled_job(:set_after_time_wizard, wizard_id: data[:id])
enqueue_at = Time.parse(data[:after_time_scheduled]).utc
Jobs.enqueue_at(enqueue_at, :set_after_time_wizard, wizard_id: data[:id])
end
if existing && existing[:after_time] && !data[:after_time]
Jobs.cancel_scheduled_job(:set_after_time_wizard, wizard_id: data[:id])
Jobs.enqueue(:clear_after_time_wizard, wizard_id: data[:id])
end
end
data[:id]
end
def self.remove(wizard_id)
wizard = self.create(wizard_id)
ActiveRecord::Base.transaction do
if wizard.after_time
Jobs.cancel_scheduled_job(:set_after_time_wizard)
Jobs.enqueue(:clear_after_time_wizard, wizard_id: wizard.id)
end
PluginStore.remove('custom_wizard', wizard.id)
end
end
def self.exists?(wizard_id)
PluginStoreRow.exists?(plugin_name: 'custom_wizard', key: wizard_id)
end
def self.list(user=nil)
PluginStoreRow.where(plugin_name: 'custom_wizard').order(:id)
.reduce([]) do |result, record|
attrs = JSON.parse(record.value)
if attrs.present? &&
attrs.is_a?(Hash) &&
attrs['id'].present? &&
attrs['name'].present?
result.push(attrs)
end
result
end
end
def self.setting_enabled(attr)
PluginStoreRow.where("
plugin_name = 'custom_wizard' AND
(value::json ->> '#{attr}')::boolean IS TRUE
")
end
end

Datei anzeigen

@ -95,7 +95,7 @@ class CustomWizard::Validator
end end
def check_id(object, type) def check_id(object, type)
if type === :wizard && @opts[:create] && CustomWizard::Wizard.exists?(object[:id]) if type === :wizard && @opts[:create] && CustomWizard::Template.exists?(object[:id])
@error = { @error = {
type: 'conflict', type: 'conflict',
params: { type: type, property: 'id', value: object[:id] } params: { type: type, property: 'id', value: object[:id] }

Datei anzeigen

@ -24,6 +24,7 @@ class CustomWizard::Wizard
:needs_categories, :needs_categories,
:needs_groups, :needs_groups,
:steps, :steps,
:step_ids,
:actions, :actions,
:user :user
@ -52,6 +53,7 @@ class CustomWizard::Wizard
end end
@first_step = nil @first_step = nil
@step_ids = attrs['steps'].map { |s| s['id'] }
@steps = [] @steps = []
@actions = [] @actions = []
end end
@ -65,13 +67,11 @@ class CustomWizard::Wizard
yield step if block_given? yield step if block_given?
last_step = @steps.last last_step = steps.last
steps << step
@steps << step if steps.size == 1
first_step = step
# If it's the first step
if @steps.size == 1
@first_step = step
step.index = 0 step.index = 0
elsif last_step.present? elsif last_step.present?
last_step.next = step last_step.next = step
@ -81,67 +81,62 @@ class CustomWizard::Wizard
end end
def start def start
return nil if !@user return nil if !user
if unfinished? && last_completed_step = ::UserHistory.where( if unfinished? && last_completed_step = ::UserHistory.where(
acting_user_id: @user.id, acting_user_id: user.id,
action: ::UserHistory.actions[:custom_wizard_step], action: ::UserHistory.actions[:custom_wizard_step],
context: @id, context: id,
subject: @steps.map(&:id) subject: steps.map(&:id)
).order("created_at").last ).order("created_at").last
step_id = last_completed_step.subject step_id = last_completed_step.subject
last_index = @steps.index { |s| s.id == step_id } last_index = steps.index { |s| s.id == step_id }
@steps[last_index + 1] steps[last_index + 1]
else else
@first_step first_step
end end
end end
def create_updater(step_id, fields) def create_updater(step_id, fields)
step = @steps.find { |s| s.id == step_id } step = @steps.find { |s| s.id == step_id }
wizard = self wizard = self
CustomWizard::StepUpdater.new(@user, wizard, step, fields) CustomWizard::StepUpdater.new(user, wizard, step, fields)
end end
def unfinished? def unfinished?
return nil if !@user return nil if !user
most_recent = ::UserHistory.where( most_recent = ::UserHistory.where(
acting_user_id: @user.id, acting_user_id: user.id,
action: ::UserHistory.actions[:custom_wizard_step], action: ::UserHistory.actions[:custom_wizard_step],
context: @id, context: id,
).distinct.order('updated_at DESC').first ).distinct.order('updated_at DESC').first
if most_recent && most_recent.subject == "reset" if most_recent && most_recent.subject == "reset"
false false
elsif most_recent elsif most_recent
last_finished_step = most_recent.subject most_recent.subject != steps.last.id
last_step = CustomWizard::Wizard.step_ids(@id).last
last_finished_step != last_step
else else
true true
end end
end end
def completed? def completed?
return nil if !@user return nil if !user
steps = CustomWizard::Wizard.step_ids(@id)
history = ::UserHistory.where( history = ::UserHistory.where(
acting_user_id: @user.id, acting_user_id: user.id,
action: ::UserHistory.actions[:custom_wizard_step], action: ::UserHistory.actions[:custom_wizard_step],
context: @id context: id
) )
if @after_time if after_time
history = history.where("updated_at > ?", @after_time_scheduled) history = history.where("updated_at > ?", after_time_scheduled)
end end
completed = history.distinct.order(:subject).pluck(:subject) completed = history.distinct.order(:subject).pluck(:subject)
(step_ids - completed).empty?
(steps - completed).empty?
end end
def permitted? def permitted?
@ -178,33 +173,38 @@ class CustomWizard::Wizard
def reset def reset
::UserHistory.create( ::UserHistory.create(
action: ::UserHistory.actions[:custom_wizard_step], action: ::UserHistory.actions[:custom_wizard_step],
acting_user_id: @user.id, acting_user_id: user.id,
context: @id, context: id,
subject: "reset" subject: "reset"
) )
end end
def categories def categories
@categories ||= ::Site.new(Guardian.new(@user)).categories @categories ||= ::Site.new(Guardian.new(user)).categories
end end
def groups def groups
@groups ||= ::Site.new(Guardian.new(@user)).groups @groups ||= ::Site.new(Guardian.new(user)).groups
end end
def submissions def submissions
Array.wrap(PluginStore.get("#{id}_submissions", @user.id)) Array.wrap(PluginStore.get("#{id}_submissions", user.id))
end end
def self.filter_records(filter) def self.create(wizard_id, user = nil)
PluginStoreRow.where(" if template = CustomWizard::Template.find(wizard_id)
plugin_name = 'custom_wizard' AND self.new(template.to_h, user)
(value::json ->> '#{filter}')::boolean IS TRUE else
") false
end
end
def self.list(user=nil)
CustomWizard::Template.list.map { |template| self.new(template.to_h, user) }
end end
def self.after_signup(user) def self.after_signup(user)
if (records = filter_records('after_signup')).any? if (records = CustomWizard::Template.setting_enabled('after_signup')).any?
result = false result = false
records records
@ -225,9 +225,9 @@ class CustomWizard::Wizard
end end
def self.prompt_completion(user) def self.prompt_completion(user)
if (records = filter_records('prompt_completion')).any? if (records = CustomWizard::Template.setting_enabled('prompt_completion')).any?
records.reduce([]) do |result, record| records.reduce([]) do |result, record|
wizard = CustomWizard::Wizard.new(::JSON.parse(record.value), user) wizard = self.new(::JSON.parse(record.value), user)
if wizard.permitted? && !wizard.completed? if wizard.permitted? && !wizard.completed?
result.push(id: wizard.id, name: wizard.name) result.push(id: wizard.id, name: wizard.name)
@ -241,115 +241,13 @@ class CustomWizard::Wizard
end end
def self.restart_on_revisit def self.restart_on_revisit
if (records = filter_records('restart_on_revisit')).any? if (records = CustomWizard::Template.setting_enabled('restart_on_revisit')).any?
records.first.key records.first.key
else else
false false
end end
end end
def self.steps(wizard_id)
wizard = PluginStore.get('custom_wizard', wizard_id)
wizard ? wizard['steps'] : nil
end
def self.step_ids(wizard_id)
steps = self.steps(wizard_id)
return [] if !steps
steps.map { |s| s['id'] }.flatten.uniq
end
def self.field_ids(wizard_id, step_id)
steps = self.steps(wizard_id)
return [] if !steps
step = steps.select { |s| s['id'] === step_id }.first
if step && fields = step['fields']
fields.map { |f| f['id'] }
else
[]
end
end
def self.add_wizard(obj)
wizard = obj.is_a?(String) ? ::JSON.parse(json) : obj
PluginStore.set('custom_wizard', wizard["id"], wizard)
end
def self.find(wizard_id)
PluginStore.get('custom_wizard', wizard_id)
end
def self.list(user=nil)
PluginStoreRow.where(plugin_name: 'custom_wizard').order(:id)
.reduce([]) do |result, record|
attrs = JSON.parse(record.value)
if attrs.present? &&
attrs.is_a?(Hash) &&
attrs['id'].present? &&
attrs['name'].present?
result.push(self.new(attrs, user))
end
result
end
end
def self.save(wizard)
existing_wizard = self.create(wizard[:id])
wizard[:steps].each do |step|
if step[:raw_description]
step[:description] = PrettyText.cook(step[:raw_description])
end
end
wizard = wizard.slice!(:create)
ActiveRecord::Base.transaction do
PluginStore.set('custom_wizard', wizard[:id], wizard)
if wizard[:after_time]
Jobs.cancel_scheduled_job(:set_after_time_wizard, wizard_id: wizard[:id])
enqueue_at = Time.parse(wizard[:after_time_scheduled]).utc
Jobs.enqueue_at(enqueue_at, :set_after_time_wizard, wizard_id: wizard[:id])
end
if existing_wizard && existing_wizard.after_time && !wizard[:after_time]
Jobs.cancel_scheduled_job(:set_after_time_wizard, wizard_id: wizard[:id])
Jobs.enqueue(:clear_after_time_wizard, wizard_id: wizard[:id])
end
end
wizard[:id]
end
def self.remove(wizard_id)
wizard = self.create(wizard_id)
ActiveRecord::Base.transaction do
if wizard.after_time
Jobs.cancel_scheduled_job(:set_after_time_wizard)
Jobs.enqueue(:clear_after_time_wizard, wizard_id: wizard.id)
end
PluginStore.remove('custom_wizard', wizard.id)
end
end
def self.exists?(wizard_id)
PluginStoreRow.exists?(plugin_name: 'custom_wizard', key: wizard_id)
end
def self.create(wizard_id, user = nil)
if wizard = self.find(wizard_id)
self.new(wizard.to_h, user)
else
false
end
end
def self.set_submission_redirect(user, wizard_id, url) def self.set_submission_redirect(user, wizard_id, url)
PluginStore.set("#{wizard_id.underscore}_submissions", user.id, [{ redirect_to: url }]) PluginStore.set("#{wizard_id.underscore}_submissions", user.id, [{ redirect_to: url }])
end end
@ -364,12 +262,4 @@ class CustomWizard::Wizard
false false
end end
end end
def self.register_styles
full_path = "#{Rails.root}/plugins/discourse-custom-wizard/assets/stylesheets/wizard/wizard_custom.scss"
DiscoursePluginRegistry.register_asset(full_path, {}, "wizard_custom")
Stylesheet::Importer.register_import("wizard_custom") do
import_files(DiscoursePluginRegistry.stylesheets["wizard_custom"])
end
end
end end

Datei anzeigen

@ -55,6 +55,7 @@ after_initialize do
../lib/custom_wizard/mapper.rb ../lib/custom_wizard/mapper.rb
../lib/custom_wizard/log.rb ../lib/custom_wizard/log.rb
../lib/custom_wizard/step_updater.rb ../lib/custom_wizard/step_updater.rb
../lib/custom_wizard/template.rb
../lib/custom_wizard/validator.rb ../lib/custom_wizard/validator.rb
../lib/custom_wizard/wizard.rb ../lib/custom_wizard/wizard.rb
../lib/custom_wizard/api/api.rb ../lib/custom_wizard/api/api.rb
@ -158,7 +159,11 @@ after_initialize do
::Wizard::Field.prepend CustomWizardFieldExtension ::Wizard::Field.prepend CustomWizardFieldExtension
::Wizard::Step.prepend CustomWizardStepExtension ::Wizard::Step.prepend CustomWizardStepExtension
CustomWizard::Wizard.register_styles full_path = "#{Rails.root}/plugins/discourse-custom-wizard/assets/stylesheets/wizard/wizard_custom.scss"
DiscoursePluginRegistry.register_asset(full_path, {}, "wizard_custom")
Stylesheet::Importer.register_import("wizard_custom") do
import_files(DiscoursePluginRegistry.stylesheets["wizard_custom"])
end
DiscourseEvent.trigger(:custom_wizard_ready) DiscourseEvent.trigger(:custom_wizard_ready)
end end

Datei anzeigen

@ -7,20 +7,23 @@ describe CustomWizard::Action do
before do before do
Group.refresh_automatic_group!(:trust_level_2) Group.refresh_automatic_group!(:trust_level_2)
template = JSON.parse(File.open( CustomWizard::Template.add(
JSON.parse(File.open(
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json" "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
).read) ).read)
CustomWizard::Wizard.add_wizard(template) )
@wizard = CustomWizard::Wizard.create('super_mega_fun_wizard', user) @template = CustomWizard::Template.find('super_mega_fun_wizard')
end end
it 'creates a topic' do it 'creates a topic' do
built_wizard = CustomWizard::Builder.new(@wizard.id, user).build wizard = CustomWizard::Builder.new(@template[:id], user).build
updater = built_wizard.create_updater(built_wizard.steps[0].id,
updater = wizard.create_updater(
wizard.steps[0].id,
step_1_field_1: "Topic Title", step_1_field_1: "Topic Title",
step_1_field_2: "topic body" step_1_field_2: "topic body"
).update ).update
updater2 = built_wizard.create_updater(built_wizard.steps[1].id, {}).update updater2 = wizard.create_updater(wizard.steps[1].id, {}).update
topic = Topic.where(title: "Topic Title") topic = Topic.where(title: "Topic Title")
@ -34,9 +37,9 @@ describe CustomWizard::Action do
it 'sends a message' do it 'sends a message' do
User.create(username: 'angus1', email: "angus1@email.com") User.create(username: 'angus1', email: "angus1@email.com")
built_wizard = CustomWizard::Builder.new(@wizard.id, user).build wizard = CustomWizard::Builder.new(@template[:id], user).build
built_wizard.create_updater(built_wizard.steps[0].id, {}).update wizard.create_updater(wizard.steps[0].id, {}).update
built_wizard.create_updater(built_wizard.steps[1].id, {}).update wizard.create_updater(wizard.steps[1].id, {}).update
topic = Topic.where( topic = Topic.where(
archetype: Archetype.private_message, archetype: Archetype.private_message,
@ -54,26 +57,26 @@ describe CustomWizard::Action do
end end
it 'updates a profile' do it 'updates a profile' do
built_wizard = CustomWizard::Builder.new(@wizard.id, user).build wizard = CustomWizard::Builder.new(@template[:id], user).build
upload = Upload.create!( upload = Upload.create!(
url: '/images/image.png', url: '/images/image.png',
original_filename: 'image.png', original_filename: 'image.png',
filesize: 100, filesize: 100,
user_id: -1, user_id: -1,
) )
steps = built_wizard.steps steps = wizard.steps
built_wizard.create_updater(steps[0].id, {}).update wizard.create_updater(steps[0].id, {}).update
built_wizard.create_updater(steps[1].id, wizard.create_updater(steps[1].id,
step_2_field_7: upload.as_json, step_2_field_7: upload.as_json
).update ).update
expect(user.profile_background_upload.id).to eq(upload.id) expect(user.profile_background_upload.id).to eq(upload.id)
end end
it 'opens a composer' do it 'opens a composer' do
built_wizard = CustomWizard::Builder.new(@wizard.id, user).build wizard = CustomWizard::Builder.new(@template[:id], user).build
built_wizard.create_updater(built_wizard.steps[0].id, step_1_field_1: "Text input").update wizard.create_updater(wizard.steps[0].id, step_1_field_1: "Text input").update
updater = built_wizard.create_updater(built_wizard.steps[1].id, {}) updater = wizard.create_updater(wizard.steps[1].id, {})
updater.update updater.update
submissions = PluginStore.get("super_mega_fun_wizard_submissions", user.id) submissions = PluginStore.get("super_mega_fun_wizard_submissions", user.id)
@ -85,34 +88,34 @@ describe CustomWizard::Action do
end end
it 'creates a category' do it 'creates a category' do
built_wizard = CustomWizard::Builder.new(@wizard.id, user).build wizard = CustomWizard::Builder.new(@template[:id], user).build
built_wizard.create_updater(built_wizard.steps[0].id, step_1_field_1: "Text input").update wizard.create_updater(wizard.steps[0].id, step_1_field_1: "Text input").update
built_wizard.create_updater(built_wizard.steps[1].id, {}).update wizard.create_updater(wizard.steps[1].id, {}).update
submissions = PluginStore.get("super_mega_fun_wizard_submissions", user.id) submissions = PluginStore.get("super_mega_fun_wizard_submissions", user.id)
expect(Category.where(id: submissions.first['action_8']).exists?).to eq(true) expect(Category.where(id: submissions.first['action_8']).exists?).to eq(true)
end end
it 'creates a group' do it 'creates a group' do
built_wizard = CustomWizard::Builder.new(@wizard.id, user).build wizard = CustomWizard::Builder.new(@template[:id], user).build
step_id = built_wizard.steps[0].id step_id = wizard.steps[0].id
updater = built_wizard.create_updater(step_id, step_1_field_1: "Text input").update updater = wizard.create_updater(step_id, step_1_field_1: "Text input").update
submissions = PluginStore.get("super_mega_fun_wizard_submissions", user.id) submissions = PluginStore.get("super_mega_fun_wizard_submissions", user.id)
expect(Group.where(name: submissions.first['action_9']).exists?).to eq(true) expect(Group.where(name: submissions.first['action_9']).exists?).to eq(true)
end end
it 'adds a user to a group' do it 'adds a user to a group' do
built_wizard = CustomWizard::Builder.new(@wizard.id, user).build wizard = CustomWizard::Builder.new(@template[:id], user).build
step_id = built_wizard.steps[0].id step_id = wizard.steps[0].id
updater = built_wizard.create_updater(step_id, step_1_field_1: "Text input").update updater = wizard.create_updater(step_id, step_1_field_1: "Text input").update
submissions = PluginStore.get("super_mega_fun_wizard_submissions", user.id) submissions = PluginStore.get("super_mega_fun_wizard_submissions", user.id)
group = Group.find_by(name: submissions.first['action_9']) group = Group.find_by(name: submissions.first['action_9'])
expect(group.users.first.username).to eq('angus') expect(group.users.first.username).to eq('angus')
end end
it 'watches categories' do it 'watches categories' do
built_wizard = CustomWizard::Builder.new(@wizard.id, user).build wizard = CustomWizard::Builder.new(@template[:id], user).build
built_wizard.create_updater(built_wizard.steps[0].id, step_1_field_1: "Text input").update wizard.create_updater(wizard.steps[0].id, step_1_field_1: "Text input").update
built_wizard.create_updater(built_wizard.steps[1].id, {}).update wizard.create_updater(wizard.steps[1].id, {}).update
submissions = PluginStore.get("super_mega_fun_wizard_submissions", user.id) submissions = PluginStore.get("super_mega_fun_wizard_submissions", user.id)
expect(CategoryUser.where( expect(CategoryUser.where(
category_id: submissions.first['action_8'], category_id: submissions.first['action_8'],
@ -125,8 +128,8 @@ describe CustomWizard::Action do
end end
it 're-routes a user' do it 're-routes a user' do
built_wizard = CustomWizard::Builder.new(@wizard.id, user).build wizard = CustomWizard::Builder.new(@template[:id], user).build
updater = built_wizard.create_updater(built_wizard.steps.last.id, {}) updater = wizard.create_updater(wizard.steps.last.id, {})
updater.update updater.update
expect(updater.result[:redirect_on_complete]).to eq("https://google.com") expect(updater.result[:redirect_on_complete]).to eq("https://google.com")
end end

Datei anzeigen

@ -3,19 +3,45 @@
require 'rails_helper' require 'rails_helper'
describe CustomWizard::Builder do describe CustomWizard::Builder do
fab!(:user) { Fabricate(:user, username: 'angus', email: "angus@email.com", trust_level: TrustLevel[2]) } fab!(:trusted_user) {
fab!(:new_user) { Fabricate(:user, trust_level: 0) } Fabricate(
:user,
username: 'angus',
email: "angus@email.com",
trust_level: TrustLevel[3]
)
}
fab!(:user) { Fabricate(:user) }
fab!(:category1) { Fabricate(:category, name: 'cat1') } fab!(:category1) { Fabricate(:category, name: 'cat1') }
fab!(:category2) { Fabricate(:category, name: 'cat2') } fab!(:category2) { Fabricate(:category, name: 'cat2') }
fab!(:group) { Fabricate(:group) } fab!(:group) { Fabricate(:group) }
let(:required_data_json) {
JSON.parse(File.open(
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard/required_data.json"
).read)
}
let(:permitted_json) {
JSON.parse(File.open(
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard/permitted.json"
).read)
}
let(:permitted_param_json) {
JSON.parse(File.open(
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/step/permitted_params.json"
).read)
}
before do before do
Group.refresh_automatic_group!(:trust_level_2) Group.refresh_automatic_group!(:trust_level_3)
template = JSON.parse(File.open( CustomWizard::Template.add(
JSON.parse(File.open(
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json" "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
).read) ).read)
CustomWizard::Wizard.add_wizard(template) )
@wizard = CustomWizard::Wizard.create('super_mega_fun_wizard', user) @template = CustomWizard::Template.find('super_mega_fun_wizard')
end end
context 'disabled' do context 'disabled' do
@ -24,7 +50,9 @@ describe CustomWizard::Builder do
end end
it "returns nil" do it "returns nil" do
expect(CustomWizard::Builder.new(@wizard.id, user).build).to eq(nil) expect(
CustomWizard::Builder.new(@template[:id], user).build
).to eq(nil)
end end
end end
@ -33,150 +61,276 @@ describe CustomWizard::Builder do
SiteSetting.custom_wizard_enabled = true SiteSetting.custom_wizard_enabled = true
end end
it "returns wizard metadata" do
wizard = CustomWizard::Builder.new(@template[:id], user).build
expect(wizard.id).to eq("super_mega_fun_wizard")
expect(wizard.name).to eq("Super Mega Fun Wizard")
expect(wizard.background).to eq("#333333")
end
it "returns steps" do it "returns steps" do
expect( expect(
CustomWizard::Builder.new(@wizard.id, user).build.steps.length CustomWizard::Builder.new(@template[:id], user).build
).to eq(2) .steps.length
).to eq(3)
end end
it 'returns no steps if multiple submissions are disabled and user has completed' do context "with multiple submissions disabled" do
wizard_template = CustomWizard::Wizard.find(@wizard.id) before do
wizard_template[:multiple_submissions] = false @template[:multiple_submissions] = false
CustomWizard::Wizard.save(wizard_template) CustomWizard::Template.save(@template.as_json)
end
history_params = { it 'returns steps if user has not completed it' do
expect(
CustomWizard::Builder.new(@template[:id], user).build
.steps.length
).to eq(3)
end
it 'returns no steps if user has completed it' do
@template[:steps].each do |step|
UserHistory.create!(
{
action: UserHistory.actions[:custom_wizard_step], action: UserHistory.actions[:custom_wizard_step],
acting_user_id: user.id, acting_user_id: user.id,
context: @wizard.id context: @template[:id]
} }.merge(
@wizard.steps.each do |step| subject: step[:id]
UserHistory.create!(history_params.merge(subject: step.id)) )
)
end end
built_wizard = CustomWizard::Builder.new(@wizard.id, user).build
expect( expect(
CustomWizard::Builder.new(@wizard.id, user).build.steps.length CustomWizard::Builder.new(@template[:id], user).build
.steps.length
).to eq(0) ).to eq(0)
end end
end
context "with restricted permissions" do
before do
@template[:permitted] = permitted_json["permitted"]
CustomWizard::Template.save(@template.as_json)
end
it 'is not permitted if user is not in permitted group' do
expect(
CustomWizard::Builder.new(@template[:id], user).build
.permitted?
).to eq(false)
end
it 'user cannot access if not permitted' do
expect(
CustomWizard::Builder.new(@template[:id], user).build
.can_access?
).to eq(false)
end
it 'returns wizard metadata if user is not permitted' do
expect(
CustomWizard::Builder.new(@template[:id], user).build
.name
).to eq("Super Mega Fun Wizard")
end
it 'returns no steps if user is not permitted' do it 'returns no steps if user is not permitted' do
expect( expect(
CustomWizard::Builder.new(@wizard.id, new_user).build.steps.length CustomWizard::Builder.new(@template[:id], user).build
.steps.length
).to eq(0) ).to eq(0)
end end
it 'is permitted if user is in permitted group' do
expect(
CustomWizard::Builder.new(@template[:id], trusted_user).build
.permitted?
).to eq(true)
end
it 'user can access if permitted' do
expect(
CustomWizard::Builder.new(@template[:id], trusted_user).build
.can_access?
).to eq(true)
end
it 'returns steps if user is permitted' do it 'returns steps if user is permitted' do
expect( expect(
CustomWizard::Builder.new(@wizard.id, user).build.steps.length CustomWizard::Builder.new(@template[:id], trusted_user).build
.steps.length
).to eq(3) ).to eq(3)
end end
end
it 'returns a wizard with prefilled data if user has partially completed it' do it 'returns prefilled data' do
expect( expect(
CustomWizard::Builder.new(@wizard.id, user) CustomWizard::Builder.new(@template[:id], user).build
.build .steps.first
.steps[0].fields[0].value .fields.first
.value
).to eq('I am prefilled') ).to eq('I am prefilled')
end end
it 'returns a wizard with no prefilled data if options include reset' do context "user has partially completed" do
PluginStore.set("super_mega_fun_wizard_submissions", user.id, { before do
text: 'Input into text', PluginStore.set("super_mega_fun_wizard_submissions", user.id,
}) step_1_field_1: 'I am a user submission'
expect( )
CustomWizard::Builder.new(@wizard.id, user)
.build(reset: true)
.steps[0].fields[0].value
).to eq(nil)
end end
context 'building steps' do it 'returns saved submissions' do
expect(
CustomWizard::Builder.new(@template[:id], user).build
.steps.first
.fields.first
.value
).to eq('I am a user submission')
end
context "restart is enabled" do
before do
@template[:restart_on_revisit] = true
CustomWizard::Template.save(@template.as_json)
end
it 'does not return saved submissions' do
expect(
CustomWizard::Builder.new(@template[:id], user).build
.steps.first
.fields.first
.value
).to eq('I am prefilled')
end
end
end
context 'building step' do
it 'returns step metadata' do it 'returns step metadata' do
expect( first_step = CustomWizard::Builder.new(@template[:id], user)
CustomWizard::Builder.new(@wizard.id, user)
.build(reset: true) .build(reset: true)
.steps[0] .steps.first
).to eq('Super Mega Fun Wizard')
expect(first_step.id).to eq("step_1")
expect(first_step.title).to eq("Text")
expect(first_step.description).to eq("<p>Text inputs!</p>")
end end
it 'saves permitted params' do context 'with required data' do
@wizard.steps[0].permitted_params = permitted_params before do
built_wizard = CustomWizard::Builder.new(@wizard.id, user).build({}, param_key: 'param_value') @template[:steps][0][:required_data] = required_data_json['required_data']
submissions = PluginStore.get("super_mega_fun_wizard_submissions", user.id) @template[:steps][0][:required_data_message] = required_data_json['required_data_message']
expect(submissions[0]['submission_param_key']).to eq('param_value') CustomWizard::Template.save(@template.as_json)
end end
it 'is not permitted if required data is not present' do it 'is not permitted if required data is not present' do
@wizard.steps[0].required_data = required_data
expect( expect(
CustomWizard::Builder.new(@wizard.id, user).build.steps[0].permitted CustomWizard::Builder.new(@template[:id], user).build
.steps.first
.permitted
).to eq(false) ).to eq(false)
end end
it 'it shows required data message if required data has message' do it 'it shows required data message' do
@wizard.steps[0].required_data = required_data expect(
@wizard.steps[0].required_data_message = "Data is required" CustomWizard::Builder.new(@template[:id], user).build
PluginStore.set("super_mega_fun_wizard_submissions", user.id, .steps.first
text: 'Input into text', .permitted_message
) ).to eq("Missing required data")
built_wizard = CustomWizard::Builder.new(@wizard.id, user).build
expect(built_wizard.steps[0].permitted).to eq(false)
expect(built_wizard.steps[0].permitted_message).to eq("Data is required")
end end
it 'is permitted if required data is present' do it 'is permitted if required data is present' do
@wizard.steps[0].required_data = required_data
PluginStore.set('super_mega_fun_wizard_submissions', user.id, PluginStore.set('super_mega_fun_wizard_submissions', user.id,
text: "Input into text" required_data: "required_value"
) )
expect( expect(
CustomWizard::Builder.new(@wizard.id, user).build.steps[0].permitted CustomWizard::Builder.new(@template[:id], user).build
.steps.first
.permitted
).to eq(true) ).to eq(true)
end end
it 'returns field metadata' do
built_wizard = CustomWizard::Builder.new(@wizard.id, user).build
expect(built_wizard.steps[0].fields[0].label).to eq("<p>Name</p>")
expect(built_wizard.steps[0].fields[0].type).to eq("text")
end end
it 'returns fields' do context "with permitted params" do
@wizard.steps[0].fields[1] = checkbox_field before do
built_wizard = CustomWizard::Builder.new(@wizard.id, user).build @template[:steps][0][:permitted_params] = permitted_param_json['permitted_params']
expect(built_wizard.steps[0].fields.length).to eq(2) CustomWizard::Template.save(@template.as_json)
end
it 'saves permitted params' do
CustomWizard::Builder.new(@template[:id], user).build({},
param: 'param_value'
)
expect(
PluginStore.get("super_mega_fun_wizard_submissions", user.id)
.first['saved_param']
).to eq('param_value')
end
end
end
context 'building field' do
it 'returns field metadata' do
wizard = CustomWizard::Builder.new(@template[:id], user).build
field = wizard.steps.first.fields.first
expect(field.label).to eq("<p>Text</p>")
expect(field.type).to eq("text")
expect(field.id).to eq("step_1_field_1")
expect(field.min_length).to eq("3")
end
it 'returns all step fields' do
expect(
CustomWizard::Builder.new(@template[:id], user)
.build
.steps.first
.fields.length
).to eq(4)
end end
end end
context 'on update' do context 'on update' do
def perform_update(step_id, submission)
wizard = CustomWizard::Builder.new(@template[:id], user).build
updater = wizard.create_updater(step_id, submission)
updater.update
updater
end
it 'saves submissions' do it 'saves submissions' do
built_wizard = CustomWizard::Builder.new(@wizard.id, user).build perform_update('step_1', step_1_field_1: 'Text input')
built_wizard.create_updater(built_wizard.steps[0].id,
step_1_field_1: 'Text input'
).update
expect( expect(
PluginStore.get("super_mega_fun_wizard_submissions", user.id) PluginStore.get("super_mega_fun_wizard_submissions", user.id)
.first['step_1_field_1'] .first['step_1_field_1']
).to eq('Text input') ).to eq('Text input')
end end
context 'save submissions disabled' do
before do
@template[:save_submissions] = false
CustomWizard::Template.save(@template.as_json)
end
it "does not save submissions" do
perform_update('step_1', step_1_field_1: 'Text input')
expect(
PluginStore.get("super_mega_fun_wizard_submissions", user.id)
).to eq(nil)
end
end
context 'validation' do context 'validation' do
it 'applies min length' do it 'applies min length' do
@wizard.steps[0].fields[0].min_length = 10 expect(
built_wizard = CustomWizard::Builder.new(@wizard.id, user).build perform_update('step_1', step_1_field_1: 'Te')
updater = built_wizard.create_updater(built_wizard.steps[0].id, .errors.messages[:step_1_field_1].first
step_1_field_1: 'Te' ).to eq(I18n.t('wizard.field.too_short', label: 'Text', min: 3))
).update
expect(updater.errors.messages[:text].first).to eq(
I18n.t('wizard.field.too_short', label: 'Text', min: 3)
)
end end
it 'standardises boolean entries' do it 'standardises boolean entries' do
@wizard.steps[0].fields[0] = checkbox_field perform_update('step_2', step_2_field_5: 'false')
built_wizard = CustomWizard::Builder.new(@wizard.id, user).build
updater = built_wizard.create_updater(built_wizard.steps[1].id,
step_2_field_5: 'false'
).update
expect( expect(
PluginStore.get("super_mega_fun_wizard_submissions", user.id) PluginStore.get("super_mega_fun_wizard_submissions", user.id)
.first['step_2_field_5'] .first['step_2_field_5']
@ -184,12 +338,13 @@ describe CustomWizard::Builder do
end end
it 'requires required fields' do it 'requires required fields' do
@wizard.steps[0].fields[0]['required'] = true @template[:steps][0][:fields][1][:required] = true
built_wizard = CustomWizard::Builder.new(@wizard.id, user).build CustomWizard::Template.save(@template.as_json)
updater = built_wizard.create_updater(built_wizard.steps.second.id).update
expect( expect(
updater.errors.messages[:step_1_field_1].first perform_update('step_1', step_1_field_2: nil)
).to eq(I18n.t('wizard.field.required', label: 'Text')) .errors.messages[:step_1_field_2].first
).to eq(I18n.t('wizard.field.required', label: 'Textarea'))
end end
end end
end end

17
spec/fixtures/step/permitted_params.json gevendort Normale Datei
Datei anzeigen

@ -0,0 +1,17 @@
{
"permitted_params": [
{
"type": "association",
"pairs": [
{
"index": 0,
"key": "param",
"key_type": "text",
"value": "saved_param",
"value_type": "text",
"connector": "association"
}
]
}
]
}

Datei anzeigen

@ -7,16 +7,6 @@
"after_signup": false, "after_signup": false,
"prompt_completion": true, "prompt_completion": true,
"theme_id": 2, "theme_id": 2,
"permitted": [
{
"type": "assignment",
"output_type": "group",
"output_connector": "set",
"output": [
12
]
}
],
"steps": [ "steps": [
{ {
"id": "step_1", "id": "step_1",

12
spec/fixtures/wizard/permitted.json gevendort Normale Datei
Datei anzeigen

@ -0,0 +1,12 @@
{
"permitted": [
{
"type": "assignment",
"output_type": "group",
"output_connector": "set",
"output": [
13
]
}
]
}

18
spec/fixtures/wizard/required_data.json gevendort Normale Datei
Datei anzeigen

@ -0,0 +1,18 @@
{
"required_data_message": "Missing required data",
"required_data": [
{
"type": "validation",
"pairs": [
{
"index": 0,
"key": "required_data",
"key_type": "text",
"value": "required_value",
"value_type": "text",
"connector": "equal"
}
]
}
]
}

Datei anzeigen

@ -10,7 +10,7 @@ describe CustomWizard::WizardSerializer do
template = JSON.parse(File.open( template = JSON.parse(File.open(
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json" "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
).read) ).read)
CustomWizard::Wizard.add_wizard(template) CustomWizard::Template.add(template)
@wizard = CustomWizard::Wizard.create('super_mega_fun_wizard', user) @wizard = CustomWizard::Wizard.create('super_mega_fun_wizard', user)
end end