1
0
Fork 0

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])
wizard = @builder.build
step = wizard.steps.select { |s| s.id == update_params[:step_id] }.first
permitted = params.permit(:wizard_id, :step_id) if !step || step.fields.blank?
if params[:fields] raise Discourse::InvalidParameters.new(:step_id)
permitted[:fields] = params[:fields].select { |k, v| field_ids.include? k }
permitted.permit!
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,14 +175,14 @@ 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")
submission = @submissions.last submission = @submissions.last
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
@ -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 it's the first step if steps.size == 1
if @steps.size == 1 first_step = step
@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(
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json" JSON.parse(File.open(
).read) "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
CustomWizard::Wizard.add_wizard(template) ).read)
@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) }
before do let(:required_data_json) {
Group.refresh_automatic_group!(:trust_level_2) JSON.parse(File.open(
template = JSON.parse(File.open( "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard/required_data.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)
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
Group.refresh_automatic_group!(:trust_level_3)
CustomWizard::Template.add(
JSON.parse(File.open(
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
).read)
)
@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
end
it 'returns no steps if multiple submissions are disabled and user has completed' do
wizard_template = CustomWizard::Wizard.find(@wizard.id)
wizard_template[:multiple_submissions] = false
CustomWizard::Wizard.save(wizard_template)
history_params = {
action: UserHistory.actions[:custom_wizard_step],
acting_user_id: user.id,
context: @wizard.id
}
@wizard.steps.each do |step|
UserHistory.create!(history_params.merge(subject: step.id))
end
built_wizard = CustomWizard::Builder.new(@wizard.id, user).build
expect(
CustomWizard::Builder.new(@wizard.id, user).build.steps.length
).to eq(0)
end
it 'returns no steps if user is not permitted' do
expect(
CustomWizard::Builder.new(@wizard.id, new_user).build.steps.length
).to eq(0)
end
it 'returns steps if user is permitted' do
expect(
CustomWizard::Builder.new(@wizard.id, user).build.steps.length
).to eq(3) ).to eq(3)
end end
it 'returns a wizard with prefilled data if user has partially completed it' do context "with multiple submissions disabled" do
expect( before do
CustomWizard::Builder.new(@wizard.id, user) @template[:multiple_submissions] = false
.build CustomWizard::Template.save(@template.as_json)
.steps[0].fields[0].value
).to eq('I am prefilled')
end
it 'returns a wizard with no prefilled data if options include reset' do
PluginStore.set("super_mega_fun_wizard_submissions", user.id, {
text: 'Input into text',
})
expect(
CustomWizard::Builder.new(@wizard.id, user)
.build(reset: true)
.steps[0].fields[0].value
).to eq(nil)
end
context 'building steps' do
it 'returns step metadata' do
expect(
CustomWizard::Builder.new(@wizard.id, user)
.build(reset: true)
.steps[0]
).to eq('Super Mega Fun Wizard')
end end
it 'saves permitted params' do it 'returns steps if user has not completed it' do
@wizard.steps[0].permitted_params = permitted_params expect(
built_wizard = CustomWizard::Builder.new(@wizard.id, user).build({}, param_key: 'param_value') CustomWizard::Builder.new(@template[:id], user).build
submissions = PluginStore.get("super_mega_fun_wizard_submissions", user.id) .steps.length
expect(submissions[0]['submission_param_key']).to eq('param_value') ).to eq(3)
end end
it 'is not permitted if required data is not present' do it 'returns no steps if user has completed it' do
@wizard.steps[0].required_data = required_data @template[:steps].each do |step|
UserHistory.create!(
{
action: UserHistory.actions[:custom_wizard_step],
acting_user_id: user.id,
context: @template[:id]
}.merge(
subject: step[:id]
)
)
end
expect( expect(
CustomWizard::Builder.new(@wizard.id, user).build.steps[0].permitted CustomWizard::Builder.new(@template[:id], user).build
.steps.length
).to eq(0)
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) ).to eq(false)
end end
it 'it shows required data message if required data has message' do it 'user cannot access if not permitted' 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, .can_access?
text: 'Input into text', ).to eq(false)
)
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 'returns wizard metadata if user is not permitted' do
@wizard.steps[0].required_data = required_data
PluginStore.set('super_mega_fun_wizard_submissions', user.id,
text: "Input into text"
)
expect( expect(
CustomWizard::Builder.new(@wizard.id, user).build.steps[0].permitted 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
expect(
CustomWizard::Builder.new(@template[:id], user).build
.steps.length
).to eq(0)
end
it 'is permitted if user is in permitted group' do
expect(
CustomWizard::Builder.new(@template[:id], trusted_user).build
.permitted?
).to eq(true) ).to eq(true)
end end
it 'returns field metadata' do it 'user can access if permitted' do
built_wizard = CustomWizard::Builder.new(@wizard.id, user).build expect(
expect(built_wizard.steps[0].fields[0].label).to eq("<p>Name</p>") CustomWizard::Builder.new(@template[:id], trusted_user).build
expect(built_wizard.steps[0].fields[0].type).to eq("text") .can_access?
).to eq(true)
end end
it 'returns fields' do it 'returns steps if user is permitted' do
@wizard.steps[0].fields[1] = checkbox_field expect(
built_wizard = CustomWizard::Builder.new(@wizard.id, user).build CustomWizard::Builder.new(@template[:id], trusted_user).build
expect(built_wizard.steps[0].fields.length).to eq(2) .steps.length
).to eq(3)
end
end
it 'returns prefilled data' do
expect(
CustomWizard::Builder.new(@template[:id], user).build
.steps.first
.fields.first
.value
).to eq('I am prefilled')
end
context "user has partially completed" do
before do
PluginStore.set("super_mega_fun_wizard_submissions", user.id,
step_1_field_1: 'I am a user submission'
)
end
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
first_step = CustomWizard::Builder.new(@template[:id], user)
.build(reset: true)
.steps.first
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
context 'with required data' do
before do
@template[:steps][0][:required_data] = required_data_json['required_data']
@template[:steps][0][:required_data_message] = required_data_json['required_data_message']
CustomWizard::Template.save(@template.as_json)
end
it 'is not permitted if required data is not present' do
expect(
CustomWizard::Builder.new(@template[:id], user).build
.steps.first
.permitted
).to eq(false)
end
it 'it shows required data message' do
expect(
CustomWizard::Builder.new(@template[:id], user).build
.steps.first
.permitted_message
).to eq("Missing required data")
end
it 'is permitted if required data is present' do
PluginStore.set('super_mega_fun_wizard_submissions', user.id,
required_data: "required_value"
)
expect(
CustomWizard::Builder.new(@template[:id], user).build
.steps.first
.permitted
).to eq(true)
end
end
context "with permitted params" do
before do
@template[:steps][0][:permitted_params] = permitted_param_json['permitted_params']
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