0
0
Fork 1
Spiegel von https://github.com/paviliondev/discourse-custom-wizard.git synchronisiert 2024-09-19 23:31:11 +02:00
Dieser Commit ist enthalten in:
Angus McLeod 2020-11-03 11:24:20 +11:00
Ursprung b726d40a0c
Commit fc7c5b9b34
60 geänderte Dateien mit 2138 neuen und 479 gelöschten Zeilen

Datei anzeigen

@ -26,6 +26,11 @@ en:
file_large: "File too large" file_large: "File too large"
invalid_json: "File is not a valid json file" invalid_json: "File is not a valid json file"
no_valid_wizards: "File doesn't contain any valid wizards" no_valid_wizards: "File doesn't contain any valid wizards"
validation:
required: "%{property} is required"
conflict: "Wizard with %{wizard_id} already exists"
after_time: "After time setting is invalid"
site_settings: site_settings:
custom_wizard_enabled: "Enable custom wizards." custom_wizard_enabled: "Enable custom wizards."

Datei anzeigen

@ -33,8 +33,8 @@ Discourse::Application.routes.append do
get 'admin/wizards/logs' => 'admin_logs#index' get 'admin/wizards/logs' => 'admin_logs#index'
get 'admin/wizards/transfer' => 'transfer#index' get 'admin/wizards/transfer' => 'admin_transfer#index'
get 'admin/wizards/transfer/export' => 'transfer#export' get 'admin/wizards/transfer/export' => 'admin_transfer#export'
post 'admin/wizards/transfer/import' => 'transfer#import' post 'admin/wizards/transfer/import' => 'admin_transfer#import'
end end
end end

Datei anzeigen

@ -9,5 +9,6 @@ class CustomWizard::AdminController < ::Admin::AdminController
def find_wizard def find_wizard
params.require(:wizard_id) params.require(:wizard_id)
@wizard = CustomWizard::Wizard.create(params[:wizard_id].underscore) @wizard = CustomWizard::Wizard.create(params[:wizard_id].underscore)
raise Discourse::InvalidParameters.new(:wizard_id) unless @wizard
end end
end end

Datei anzeigen

@ -1,7 +1,7 @@
class CustomWizard::AdminLogsController < CustomWizard::AdminController class CustomWizard::AdminLogsController < CustomWizard::AdminController
def index def index
render_serialized( render_serialized(
CustomWizard::Log.list(params[:page].to_i), CustomWizard::Log.list(params[:page].to_i, params[:limit].to_i),
CustomWizard::LogSerializer CustomWizard::LogSerializer
) )
end end

Datei anzeigen

@ -1,29 +1,23 @@
class CustomWizard::AdminSubmissionsController < CustomWizard::AdminController class CustomWizard::AdminSubmissionsController < CustomWizard::AdminController
skip_before_action :preload_json, :check_xhr, only: [:download] skip_before_action :preload_json, :check_xhr, only: [:download]
before_action :find_wizard, except: [:index]
before_action :find_wizard
def index def index
render json: ActiveModel::ArraySerializer.new( render json: ActiveModel::ArraySerializer.new(
CustomWizard::Wizard.list, CustomWizard::Wizard.list(current_user),
each_serializer: CustomWizard::BasicWizardSerializer each_serializer: CustomWizard::BasicWizardSerializer
) )
end end
def show def show
result = {} render_json_dump(
wizard: CustomWizard::BasicWizardSerializer.new(@wizard, root: false),
if wizard = @wizard submissions: build_submissions.as_json
submissions = build_submissions(wizard.id) )
result[:wizard] = CustomWizard::BasicWizardSerializer.new(wizard, root: false)
result[:submissions] = submissions.as_json
end
render_json_dump(result)
end end
def download def download
send_data build_submissions(@wizard.id).to_json, send_data build_submissions.to_json,
filename: "#{Discourse.current_hostname}-wizard-submissions-#{@wizard.name}.json", filename: "#{Discourse.current_hostname}-wizard-submissions-#{@wizard.name}.json",
content_type: "application/json", content_type: "application/json",
disposition: "attachment" disposition: "attachment"
@ -31,23 +25,21 @@ class CustomWizard::AdminSubmissionsController < CustomWizard::AdminController
private private
def build_submissions(wizard_id) def build_submissions
rows = PluginStoreRow.where(plugin_name: "#{wizard_id}_submissions").order('id DESC') PluginStoreRow.where(plugin_name: "#{@wizard.id}_submissions")
.order('id DESC')
submissions = [*rows].map do |row| .map do |row|
value = ::JSON.parse(row.value) value = ::JSON.parse(row.value)
if user = User.find_by(id: row.key) if user = User.find_by(id: row.key)
username = user.username username = user.username
else else
username = I18n.t('admin.wizard.submissions.no_user', id: row.key) username = I18n.t('admin.wizard.submissions.no_user', id: row.key)
end end
value.map do |submission| value.map do |v|
{ { username: username }.merge!(v.except("redirect_to"))
username: username end
}.merge!(submission.except("redirect_to")) end.flatten
end
end.flatten
end end
end end

Datei anzeigen

@ -1,25 +1,22 @@
class CustomWizard::TransferController < ::ApplicationController class CustomWizard::AdminTransferController < CustomWizard::AdminController
before_action :ensure_logged_in
before_action :ensure_admin
skip_before_action :check_xhr, :only => [:export] skip_before_action :check_xhr, :only => [:export]
def index
end
def export def export
wizards = params['wizards'] wizard_ids = params['wizards']
wizard_objects = [] templates = []
if wizards.nil? if wizard_ids.nil?
render json: { error: I18n.t('wizard.export.error.select_one') } render json: { error: I18n.t('wizard.export.error.select_one') }
return return
end end
wizards.each do |w| wizard_ids.each do |wizard_id|
wizard_objects.push(PluginStore.get('custom_wizard', w.tr('-', '_'))) if template = CustomWizard::Template.find(wizard_id)
templates.push(template)
end
end end
send_data wizard_objects.to_json, send_data templates.to_json,
type: "application/json", type: "application/json",
disposition: 'attachment', disposition: 'attachment',
filename: 'wizards.json' filename: 'wizards.json'
@ -27,45 +24,42 @@ class CustomWizard::TransferController < ::ApplicationController
def import def import
file = File.read(params['file'].tempfile) file = File.read(params['file'].tempfile)
if file.nil? if file.nil?
render json: { error: I18n.t('wizard.import.error.no_file') } render json: { error: I18n.t('wizard.import.error.no_file') }
return return
end end
fileSize = file.size file_size = file.size
maxFileSize = 512 * 1024 max_file_size = 512 * 1024
if maxFileSize < fileSize if max_file_size < file_size
render json: { error: I18n.t('wizard.import.error.file_large') } render json: { error: I18n.t('wizard.import.error.file_large') }
return return
end end
begin begin
jsonObject = JSON.parse file template_json = JSON.parse file
rescue JSON::ParserError rescue JSON::ParserError
render json: { error: I18n.t('wizard.import.error.invalid_json') } render json: { error: I18n.t('wizard.import.error.invalid_json') }
return return
end end
countValid = 0
success_ids = [] success_ids = []
failed_ids = [] failed_ids = []
jsonObject.each do |o| template_json.each do |t_json|
if !CustomWizard::Wizard.new(o) template = CustomWizard::Template.new(t_json)
failed_ids.push o['id'] template.save(skip_jobs: true)
next
if template.errors.any?
failed_ids.push t_json['id']
else
success_ids.push t_json['id']
end end
countValid += 1
pluginStoreEntry = PluginStore.new 'custom_wizard'
saved = pluginStoreEntry.set(o['id'], o) unless pluginStoreEntry.get(o['id'])
success_ids.push o['id'] if !!saved
failed_ids.push o['id'] if !saved
end end
if countValid == 0 if success_ids.length == 0
render json: { error: I18n.t('wizard.import.error.no_valid_wizards') } render json: { error: I18n.t('wizard.import.error.no_valid_wizards') }
else else
render json: { success: success_ids, failed: failed_ids } render json: { success: success_ids, failed: failed_ids }

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::Template.list, CustomWizard::Wizard.list(current_user),
each_serializer: CustomWizard::BasicWizardSerializer each_serializer: CustomWizard::BasicWizardSerializer
), ),
field_types: CustomWizard::Field.types field_types: CustomWizard::Field.types
@ -30,20 +30,13 @@ class CustomWizard::AdminWizardController < CustomWizard::AdminController
end end
def save def save
opts = {} template = CustomWizard::Template.new(save_wizard_params.to_h)
opts[:create] = params[:create] if params[:create] wizard_id = template.save(create: params[:create])
validator = CustomWizard::Validator.new(save_wizard_params.to_h, opts) if template.errors.any?
validation = validator.perform render json: failed_json.merge(errors: result.errors.full_messages)
if validation[:error]
render json: { error: validation[:error] }
else else
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
render json: failed_json
end
end end
end end

Datei anzeigen

@ -15,22 +15,24 @@ class CustomWizard::StepsController < ::ApplicationController
field_ids = step.fields.map(&:id) field_ids = step.fields.map(&:id)
update = update_params.to_h
if params[:fields] if params[:fields]
permitted_fields = params[:fields].select { |k, v| field_ids.include? k } update[:fields] = {}
update_params[:fields] = permitted_fields
update_params.permit! params[:fields].each do |k, v|
update[:fields][k] = v if field_ids.include? k
end
end end
updater = wizard.create_updater( updater = wizard.create_updater(update[:step_id], update[:fields])
update_params[:step_id],
update_params[:fields]
)
updater.update updater.update
if updater.success? if updater.success?
result = success_json result = success_json
result.merge!(updater.result) if updater.result result.merge!(updater.result) if updater.result
result[:refresh_required] = true if updater.refresh_required? result[:refresh_required] = true if updater.refresh_required?
render json: result render json: result
else else
errors = [] errors = []

Datei anzeigen

@ -1,7 +1,8 @@
class CustomWizard::WizardController < ::ApplicationController class CustomWizard::WizardController < ::ApplicationController
prepend_view_path(Rails.root.join('plugins', 'discourse-custom-wizard', 'views')) prepend_view_path(Rails.root.join('plugins', 'discourse-custom-wizard', 'views'))
layout 'wizard' layout 'wizard'
before_action :ensure_plugin_enabled
helper_method :wizard_page_title helper_method :wizard_page_title
helper_method :theme_ids helper_method :theme_ids
@ -38,7 +39,7 @@ class CustomWizard::WizardController < ::ApplicationController
def skip def skip
params.require(:wizard_id) params.require(:wizard_id)
if wizard.required && !wizard.completed? && wizard.permitted? if wizard.required && !wizard.completed? && wizard.permitted?
return render json: { error: I18n.t('wizard.no_skip') } return render json: { error: I18n.t('wizard.no_skip') }
end end
@ -47,16 +48,11 @@ class CustomWizard::WizardController < ::ApplicationController
user = current_user user = current_user
if user if user
submission = wizard.submissions.last submission = wizard.current_submission
if submission && submission['redirect_to'] if submission && submission['redirect_to']
result.merge!(redirect_to: submission['redirect_to']) result.merge!(redirect_to: submission['redirect_to'])
end end
if submission && !wizard.save_submissions
PluginStore.remove("#{wizard.id}_submissions", user.id)
end
if user.custom_fields['redirect_to_wizard'] === wizard.id if user.custom_fields['redirect_to_wizard'] === wizard.id
user.custom_fields.delete('redirect_to_wizard') user.custom_fields.delete('redirect_to_wizard')
user.save_custom_fields(true) user.save_custom_fields(true)
@ -65,4 +61,12 @@ class CustomWizard::WizardController < ::ApplicationController
render json: result render json: result
end end
private
def ensure_plugin_enabled
unless SiteSetting.custom_wizard_enabled
redirect_to path("/")
end
end
end end

Datei anzeigen

@ -3,7 +3,7 @@ module Jobs
def execute(args) def execute(args)
if SiteSetting.custom_wizard_enabled if SiteSetting.custom_wizard_enabled
wizard = CustomWizard::Wizard.create(args[:wizard_id]) wizard = CustomWizard::Wizard.create(args[:wizard_id])
if wizard && wizard.after_time if wizard && wizard.after_time
user_ids = [] user_ids = []

Datei anzeigen

@ -267,7 +267,7 @@ class CustomWizard::Action
url += "&body=#{params[:raw]}" url += "&body=#{params[:raw]}"
if category_id = action_category if category_id = action_category
if category_id && category = Category.find(category_id) if category = Category.find_by(id: category_id)
url += "&category=#{category.full_slug('/')}" url += "&category=#{category.full_slug('/')}"
end end
end end
@ -412,6 +412,8 @@ class CustomWizard::Action
data: data, data: data,
user: user user: user
).perform ).perform
return false unless output.present?
if output.is_a?(Array) if output.is_a?(Array)
output.first output.first
@ -428,6 +430,8 @@ class CustomWizard::Action
data: data, data: data,
user: user, user: user,
).perform ).perform
return false unless output.present?
if output.is_a?(Array) if output.is_a?(Array)
output.flatten output.flatten
@ -483,11 +487,11 @@ class CustomWizard::Action
def public_topic_params def public_topic_params
params = {} params = {}
if (category = action_category) if category = action_category
params[:category] = category params[:category] = category
end end
if (tags = action_tags) if tags = action_tags
params[:tags] = tags params[:tags] = tags
end end

Datei anzeigen

@ -49,7 +49,6 @@ class CustomWizard::Builder
return @wizard if !@wizard.can_access? return @wizard if !@wizard.can_access?
build_opts[:reset] = build_opts[:reset] || @wizard.restart_on_revisit build_opts[:reset] = build_opts[:reset] || @wizard.restart_on_revisit
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|
@ -110,9 +109,7 @@ class CustomWizard::Builder
data = updater.fields data = updater.fields
## if the wizard has data from the previous steps make that accessible to the actions. if submission = @wizard.current_submission
if @submissions && @submissions.last && !@submissions.last.key?("submitted_at")
submission = @submissions.last
data = submission.merge(data) data = submission.merge(data)
end end
@ -133,29 +130,31 @@ class CustomWizard::Builder
end end
end end
end end
if route_to = data['route_to']
data.delete('route_to')
end
if @wizard.save_submissions && updater.errors.empty?
save_submissions(data, final_step)
elsif final_step
PluginStore.remove("#{@wizard.id}_submissions", @wizard.user.id)
end
if final_step && @wizard.id === @wizard.user.custom_fields['redirect_to_wizard']
@wizard.user.custom_fields.delete('redirect_to_wizard');
@wizard.user.save_custom_fields(true)
end
if updater.errors.empty? if updater.errors.empty?
if route_to = data['route_to']
data.delete('route_to')
end
if @wizard.save_submissions
save_submissions(data, final_step)
end
if final_step if final_step
if @wizard.id == @wizard.user.custom_fields['redirect_to_wizard']
@wizard.user.custom_fields.delete('redirect_to_wizard');
@wizard.user.save_custom_fields(true)
end
redirect_url = route_to || data['redirect_on_complete'] || data["redirect_to"] redirect_url = route_to || data['redirect_on_complete'] || data["redirect_to"]
updater.result[:redirect_on_complete] = redirect_url updater.result[:redirect_on_complete] = redirect_url
elsif route_to elsif route_to
updater.result[:redirect_on_next] = route_to updater.result[:redirect_on_next] = route_to
end end
true
else
false
end end
end end
end end
@ -177,10 +176,8 @@ class CustomWizard::Builder
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[:min_length] = field_template['min_length'] if field_template['min_length']
params[:value] = prefill_field(field_template, step_template) params[:value] = prefill_field(field_template, step_template)
## Load previously submitted values if !build_opts[:reset] && (submission = @wizard.current_submission)
if !build_opts[:reset] && @submissions.last && !@submissions.last.key?("submitted_at")
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
@ -359,15 +356,9 @@ class CustomWizard::Builder
if data.present? if data.present?
@submissions.pop(1) if @wizard.unfinished? @submissions.pop(1) if @wizard.unfinished?
@submissions.push(data) @submissions.push(data)
PluginStore.set("#{@wizard.id}_submissions", @wizard.user.id, @submissions) @wizard.set_submissions(@submissions)
end end
end end
def reset_submissions
@submissions.pop(1) if @wizard.unfinished?
PluginStore.set("#{@wizard.id}_submissions", @wizard.user.id, @submissions)
@wizard.reset
end
def save_permitted_params(permitted_params, params) def save_permitted_params(permitted_params, params)
permitted_data = {} permitted_data = {}

Datei anzeigen

@ -57,10 +57,10 @@ class CustomWizard::Field
@require_assets ||= {} @require_assets ||= {}
end end
def self.add_assets(type, plugin = nil, asset_paths = [], opts={}) def self.register(type, plugin = nil, asset_paths = [], opts={})
if type if type
types[type] ||= {} types[type.to_sym] ||= {}
types[type] = opts[:type_opts] if opts[:type_opts].present? types[type.to_sym] = opts[:type_opts] if opts[:type_opts].present?
end end
if plugin && asset_paths if plugin && asset_paths

Datei anzeigen

@ -29,9 +29,12 @@ class CustomWizard::Log
").order("value::json->>'date' DESC") ").order("value::json->>'date' DESC")
end end
def self.list(page = 0) def self.list(page = 0, limit = nil)
self.list_query.limit(PAGE_LIMIT) limit = limit.to_i > 0 ? limit.to_i : PAGE_LIMIT
.offset(page * PAGE_LIMIT) page = page.to_i
self.list_query.limit(limit)
.offset(page * limit)
.map { |r| self.new(JSON.parse(r.value)) } .map { |r| self.new(JSON.parse(r.value)) }
end end
end end

Datei anzeigen

@ -144,7 +144,7 @@ class CustomWizard::Mapper
end end
if operator == '=~' if operator == '=~'
result == 0 ? true : false result.nil? ? false : true
else else
result result
end end

Datei anzeigen

@ -1,53 +1,48 @@
class CustomWizard::Template class CustomWizard::Template
def self.add(obj) include HasErrors
wizard = obj.is_a?(String) ? ::JSON.parse(json) : obj
PluginStore.set('custom_wizard', wizard["id"], wizard) attr_reader :data,
:opts
def initialize(data)
@data = data
end end
def save(opts={})
@opts = opts
normalize_data
validate_data
prepare_data
return false if errors.any?
ActiveRecord::Base.transaction do
schedule_save_jobs unless opts[:skip_jobs]
PluginStore.set('custom_wizard', @data[:id], @data)
end
@data[:id]
end
def self.save(data, opts={})
new(data).save(opts)
end
def self.find(wizard_id) def self.find(wizard_id)
PluginStore.get('custom_wizard', wizard_id) PluginStore.get('custom_wizard', wizard_id)
end 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) def self.remove(wizard_id)
wizard = self.create(wizard_id) wizard = CustomWizard::Wizard.create(wizard_id)
ActiveRecord::Base.transaction do ActiveRecord::Base.transaction do
PluginStore.remove('custom_wizard', wizard.id)
if wizard.after_time if wizard.after_time
Jobs.cancel_scheduled_job(:set_after_time_wizard) Jobs.cancel_scheduled_job(:set_after_time_wizard)
Jobs.enqueue(:clear_after_time_wizard, wizard_id: wizard.id) Jobs.enqueue(:clear_after_time_wizard, wizard_id: wizard_id)
end end
PluginStore.remove('custom_wizard', wizard.id)
end end
end end
@ -55,8 +50,11 @@ class CustomWizard::Template
PluginStoreRow.exists?(plugin_name: 'custom_wizard', key: wizard_id) PluginStoreRow.exists?(plugin_name: 'custom_wizard', key: wizard_id)
end end
def self.list(user=nil) def self.list(setting: nil, order: :id)
PluginStoreRow.where(plugin_name: 'custom_wizard').order(:id) query = "plugin_name = 'custom_wizard'"
query += "AND (value::json ->> '#{setting}')::boolean IS TRUE" if setting
PluginStoreRow.where(query).order(order)
.reduce([]) do |result, record| .reduce([]) do |result, record|
attrs = JSON.parse(record.value) attrs = JSON.parse(record.value)
@ -72,10 +70,46 @@ class CustomWizard::Template
end end
end end
def self.setting_enabled(attr) private
PluginStoreRow.where("
plugin_name = 'custom_wizard' AND def normalize_data
(value::json ->> '#{attr}')::boolean IS TRUE @data = ::JSON.parse(@data) if @data.is_a?(String)
") @data = @data.with_indifferent_access
end
def prepare_data
@data[:steps].each do |step|
if step[:raw_description]
step[:description] = PrettyText.cook(step[:raw_description])
end
end
end
def validate_data
validator = CustomWizard::Validator.new(@data, @opts)
validator.perform
add_errors_from(validator)
end
def schedule_save_jobs
if @data[:after_time] && @data[:after_time_scheduled]
wizard_id = @data[:id]
old_data = CustomWizard::Template.find(data[:id])
begin
enqueue_wizard_at = Time.parse(@data[:after_time_scheduled]).utc
rescue ArgumentError
errors.add :validation, I18n.t("wizard.validation.after_time")
raise ActiveRecord::Rollback.new
end
if enqueue_wizard_at
Jobs.cancel_scheduled_job(:set_after_time_wizard, wizard_id: wizard_id)
Jobs.enqueue_at(enqueue_wizard_at, :set_after_time_wizard, wizard_id: wizard_id)
elsif old_data && old_data[: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
end end
end end

Datei anzeigen

@ -1,52 +1,38 @@
class CustomWizard::Validator class CustomWizard::Validator
include HasErrors
def initialize(params, opts={}) def initialize(data, opts={})
@params = params @data = data
@opts = opts @opts = opts
@error = nil
end end
def perform def perform
params = @params data = @data
check_id(params, :wizard) check_id(data, :wizard)
check_required(params, :wizard) check_required(data, :wizard)
check_depdendent(params, :wizard) validate_after_time
after_time = nil
if !@error && @params[:after_time]
validate_after_time
end
if !@error data[:steps].each do |step|
params[:steps].each do |step| check_required(step, :step)
check_required(step, :step)
check_depdendent(step, :step)
break if @error.present?
if params[:fields].present?
params[:fields].each do |field|
check_required(field, :field)
check_depdendent(field, :field)
break if @error.present?
end
end
end
if params[:actions].present? if data[:fields].present?
params[:actions].each do |action| data[:fields].each do |field|
check_required(action, :action) check_required(field, :field)
check_depdendent(action, :action)
break if @error.present?
end end
end end
end end
if data[:actions].present?
data[:actions].each do |action|
check_required(action, :action)
end
end
if @error if errors.any?
{ error: @error } false
else else
{ wizard: params } true
end end
end end
@ -59,54 +45,28 @@ class CustomWizard::Validator
} }
end end
def self.dependent
{
wizard: {
after_time: 'after_time_scheduled'
},
step: {},
field: {},
action: {}
}
end
private private
def check_required(object, type) def check_required(object, type)
CustomWizard::Validator.required[type].each do |property| CustomWizard::Validator.required[type].each do |property|
if object[property].blank? if object[property].blank?
@error = { errors.add :validation, I18n.t("wizard.validation.required", property: property)
type: 'required',
params: { type: type, property: property }
}
end end
end end
end end
def check_depdendent(object, type)
CustomWizard::Validator.dependent[type].each do |property, dependent|
if object[property] && object[dependent].blank?
@error = {
type: 'dependent',
params: { property: property, dependent: dependent }
}
end
end
end
def check_id(object, type) def check_id(object, type)
if type === :wizard && @opts[:create] && CustomWizard::Template.exists?(object[:id]) if type === :wizard && @opts[:create] && CustomWizard::Template.exists?(object[:id])
@error = { errors.add :validation, I18n.t("wizard.validation.conflict", id: object[:id])
type: 'conflict',
params: { type: type, property: 'id', value: object[:id] }
}
end end
end end
def validate_after_time def validate_after_time
wizard = CustomWizard::Wizard.create(@params[:id]) if !@opts[:create] return unless @data[:after_time]
wizard = CustomWizard::Wizard.create(@data[:id]) if !@opts[:create]
current_time = wizard.present? ? wizard.after_time_scheduled : nil current_time = wizard.present? ? wizard.after_time_scheduled : nil
new_time = @params[:after_time_scheduled] new_time = @data[:after_time_scheduled]
begin begin
active_time = Time.parse(new_time.present? ? new_time : current_time).utc active_time = Time.parse(new_time.present? ? new_time : current_time).utc
@ -115,7 +75,7 @@ class CustomWizard::Validator
end end
if invalid_time || active_time.blank? || active_time < Time.now.utc if invalid_time || active_time.blank? || active_time < Time.now.utc
@error = { type: 'after_time' } errors.add :validation, I18n.t("wizard.validation.after_time")
end end
end end
end end

Datei anzeigen

@ -26,22 +26,24 @@ class CustomWizard::Wizard
:steps, :steps,
:step_ids, :step_ids,
:actions, :actions,
:user :user,
:first_step
def initialize(attrs = {}, user=nil) def initialize(attrs = {}, user=nil)
@user = user @user = user
attrs = attrs.with_indifferent_access
@id = attrs['id'] @id = attrs['id']
@name = attrs['name'] @name = attrs['name']
@background = attrs['background'] @background = attrs['background']
@save_submissions = attrs['save_submissions'] || false @save_submissions = cast_bool(attrs['save_submissions'])
@multiple_submissions = attrs['multiple_submissions'] || false @multiple_submissions = cast_bool(attrs['multiple_submissions'])
@prompt_completion = attrs['prompt_completion'] || false @prompt_completion = cast_bool(attrs['prompt_completion'])
@restart_on_revisit = attrs['restart_on_revisit'] || false @restart_on_revisit = cast_bool(attrs['restart_on_revisit'])
@after_signup = attrs['after_signup'] @after_signup = cast_bool(attrs['after_signup'])
@after_time = attrs['after_time'] @after_time = cast_bool(attrs['after_time'])
@after_time_scheduled = attrs['after_time_scheduled'] @after_time_scheduled = attrs['after_time_scheduled']
@required = attrs['required'] || false @required = cast_bool(attrs['required'])
@permitted = attrs['permitted'] || nil @permitted = attrs['permitted'] || nil
@needs_categories = false @needs_categories = false
@needs_groups = false @needs_groups = false
@ -53,10 +55,17 @@ class CustomWizard::Wizard
end end
@first_step = nil @first_step = nil
@step_ids = attrs['steps'].map { |s| s['id'] }
@steps = [] @steps = []
if attrs['steps'].present?
@step_ids = attrs['steps'].map { |s| s['id'] }
end
@actions = [] @actions = []
end end
def cast_bool(val)
val.nil? ? false : ActiveRecord::Type::Boolean.new.cast(val)
end
def create_step(step_name) def create_step(step_name)
::Wizard::Step.new(step_name) ::Wizard::Step.new(step_name)
@ -69,9 +78,9 @@ class CustomWizard::Wizard
last_step = steps.last last_step = steps.last
steps << step steps << 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
@ -94,14 +103,14 @@ class CustomWizard::Wizard
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, inputs)
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, inputs)
end end
def unfinished? def unfinished?
@ -116,7 +125,7 @@ class CustomWizard::Wizard
if most_recent && most_recent.subject == "reset" if most_recent && most_recent.subject == "reset"
false false
elsif most_recent elsif most_recent
most_recent.subject != steps.last.id most_recent.subject != steps.last.id
else else
true true
end end
@ -134,7 +143,7 @@ class CustomWizard::Wizard
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? (step_ids - completed).empty?
end end
@ -166,6 +175,7 @@ class CustomWizard::Wizard
end end
def can_access? def can_access?
return false unless user
return true if user.admin return true if user.admin
return permitted? && (multiple_submissions || !completed?) return permitted? && (multiple_submissions || !completed?)
end end
@ -191,63 +201,76 @@ class CustomWizard::Wizard
Array.wrap(PluginStore.get("#{id}_submissions", user.id)) Array.wrap(PluginStore.get("#{id}_submissions", user.id))
end end
def current_submission
if submissions.present? && !submissions.last.key?("submitted_at")
submissions.last
else
nil
end
end
def set_submissions(submissions)
PluginStore.set("#{id}_submissions", user.id, Array.wrap(submissions))
end
def self.submissions(wizard_id, user)
new({ id: wizard_id }, user).submissions
end
def self.set_submissions(wizard_id, user, submissions)
new({ id: wizard_id }, user).set_submissions(submissions)
end
def self.create(wizard_id, user = nil) def self.create(wizard_id, user = nil)
if template = CustomWizard::Template.find(wizard_id) if template = CustomWizard::Template.find(wizard_id)
self.new(template.to_h, user) new(template.to_h, user)
else else
false false
end end
end end
def self.list(user=nil) def self.list(user, template_opts: {})
CustomWizard::Template.list.map { |template| self.new(template.to_h, user) } return [] unless user
CustomWizard::Template.list(template_opts).reduce([]) do |result, template|
wizard = new(template, user)
result.push(wizard) if wizard.can_access?
result
end
end end
def self.after_signup(user) def self.after_signup(user)
if (records = CustomWizard::Template.setting_enabled('after_signup')).any? wizards = list(
result = false user,
template_opts: {
records setting: 'after_signup',
.sort_by { |record| record.value['permitted'].present? ? 0 : 1 } order: "(value::json ->> 'permitted') IS NOT NULL DESC"
.each do |record| }
wizard = self.new(JSON.parse(record.value), user) )
wizards.any? ? wizards.first : false
if wizard.permitted?
result = wizard
break
end
end
result
else
false
end
end end
def self.prompt_completion(user) def self.prompt_completion(user)
if (records = CustomWizard::Template.setting_enabled('prompt_completion')).any? wizards = list(
records.reduce([]) do |result, record| user,
wizard = self.new(::JSON.parse(record.value), user) template_opts: {
setting: 'prompt_completion',
if wizard.permitted? && !wizard.completed? order: "(value::json ->> 'permitted') IS NOT NULL DESC"
result.push(id: wizard.id, name: wizard.name) }
end )
result if wizards.any?
wizards.map do |w|
{
id: w.id,
name: w.name
}
end end
else else
false false
end end
end end
def self.restart_on_revisit
if (records = CustomWizard::Template.setting_enabled('restart_on_revisit')).any?
records.first.key
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

Datei anzeigen

@ -42,9 +42,9 @@ after_initialize do
../controllers/custom_wizard/admin/submissions.rb ../controllers/custom_wizard/admin/submissions.rb
../controllers/custom_wizard/admin/api.rb ../controllers/custom_wizard/admin/api.rb
../controllers/custom_wizard/admin/logs.rb ../controllers/custom_wizard/admin/logs.rb
../controllers/custom_wizard/admin/transfer.rb
../controllers/custom_wizard/wizard.rb ../controllers/custom_wizard/wizard.rb
../controllers/custom_wizard/steps.rb ../controllers/custom_wizard/steps.rb
../controllers/custom_wizard/transfer.rb
../jobs/clear_after_time_wizard.rb ../jobs/clear_after_time_wizard.rb
../jobs/refresh_api_access_token.rb ../jobs/refresh_api_access_token.rb
../jobs/set_after_time_wizard.rb ../jobs/set_after_time_wizard.rb
@ -128,8 +128,7 @@ after_initialize do
if request.referer !~ /\/w\// && request.referer !~ /\/invites\// if request.referer !~ /\/w\// && request.referer !~ /\/invites\//
CustomWizard::Wizard.set_submission_redirect(current_user, wizard_id, request.referer) CustomWizard::Wizard.set_submission_redirect(current_user, wizard_id, request.referer)
end end
if CustomWizard::Template.exists?(wizard_id)
if CustomWizard::Wizard.exists?(wizard_id)
redirect_to "/w/#{wizard_id.dasherize}" redirect_to "/w/#{wizard_id.dasherize}"
end end
end end

Datei anzeigen

@ -7,31 +7,62 @@ describe CustomWizard::Action do
before do before do
Group.refresh_automatic_group!(:trust_level_2) Group.refresh_automatic_group!(:trust_level_2)
CustomWizard::Template.add( CustomWizard::Template.save(
JSON.parse(File.open( 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),
) skip_jobs: true)
@template = CustomWizard::Template.find('super_mega_fun_wizard') @template = CustomWizard::Template.find('super_mega_fun_wizard')
end end
it 'creates a topic' do context "creating a topic" do
wizard = CustomWizard::Builder.new(@template[:id], user).build
end
updater = wizard.create_updater(
wizard.steps[0].id, context 'creating a topic' do
step_1_field_1: "Topic Title", it "works" do
step_1_field_2: "topic body" wizard = CustomWizard::Builder.new(@template[:id], user).build
).update wizard.create_updater(
updater2 = wizard.create_updater(wizard.steps[1].id, {}).update wizard.steps.first.id,
step_1_field_1: "Topic Title",
step_1_field_2: "topic body"
).update
wizard.create_updater(wizard.steps.second.id, {}).update
wizard.create_updater(wizard.steps.last.id,
step_3_field_3: category.id
).update
topic = Topic.where(
title: "Topic Title",
category_id: category.id
)
expect(topic.exists?).to eq(true)
expect(Post.where(
topic_id: topic.pluck(:id),
raw: "topic body"
).exists?).to eq(true)
end
topic = Topic.where(title: "Topic Title") it "fails silently without basic topic inputs" do
wizard = CustomWizard::Builder.new(@template[:id], user).build
expect(topic.exists?).to eq(true) wizard.create_updater(
expect(Post.where( wizard.steps.first.id,
topic_id: topic.pluck(:id), step_1_field_2: "topic body"
raw: "topic body" ).update
).exists?).to eq(true) wizard.create_updater(wizard.steps.second.id, {}).update
updater = wizard.create_updater(wizard.steps.last.id, {})
updater.update
expect(updater.success?).to eq(true)
expect(UserHistory.where(
acting_user_id: user.id,
context: "super_mega_fun_wizard",
subject: "step_3"
).exists?).to eq(true)
expect(Post.where(
raw: "topic body"
).exists?).to eq(false)
end
end end
it 'sends a message' do it 'sends a message' do
@ -79,8 +110,7 @@ describe CustomWizard::Action do
updater = wizard.create_updater(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) category = Category.find_by(id: wizard.current_submission['action_8'])
category = Category.find_by(id: submissions.first['action_8'])
expect(updater.result[:redirect_on_next]).to eq( expect(updater.result[:redirect_on_next]).to eq(
"/new-topic?title=Title%20of%20the%20composer%20topic&body=I%20am%20interpolating%20some%20user%20fields%20Angus%20angus%20angus@email.com&category=#{category.slug}/#{category.id}&tags=tag1" "/new-topic?title=Title%20of%20the%20composer%20topic&body=I%20am%20interpolating%20some%20user%20fields%20Angus%20angus%20angus@email.com&category=#{category.slug}/#{category.id}&tags=tag1"
@ -91,24 +121,21 @@ describe CustomWizard::Action do
wizard = CustomWizard::Builder.new(@template[:id], user).build wizard = CustomWizard::Builder.new(@template[:id], user).build
wizard.create_updater(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
wizard.create_updater(wizard.steps[1].id, {}).update wizard.create_updater(wizard.steps[1].id, {}).update
submissions = PluginStore.get("super_mega_fun_wizard_submissions", user.id) expect(Category.where(id: wizard.current_submission['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
wizard = CustomWizard::Builder.new(@template[:id], user).build wizard = CustomWizard::Builder.new(@template[:id], user).build
step_id = wizard.steps[0].id step_id = wizard.steps[0].id
updater = 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) expect(Group.where(name: wizard.current_submission['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
wizard = CustomWizard::Builder.new(@template[:id], user).build wizard = CustomWizard::Builder.new(@template[:id], user).build
step_id = wizard.steps[0].id step_id = wizard.steps[0].id
updater = 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) group = Group.find_by(name: wizard.current_submission['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
@ -116,9 +143,8 @@ describe CustomWizard::Action do
wizard = CustomWizard::Builder.new(@template[:id], user).build wizard = CustomWizard::Builder.new(@template[:id], user).build
wizard.create_updater(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
wizard.create_updater(wizard.steps[1].id, {}).update wizard.create_updater(wizard.steps[1].id, {}).update
submissions = PluginStore.get("super_mega_fun_wizard_submissions", user.id)
expect(CategoryUser.where( expect(CategoryUser.where(
category_id: submissions.first['action_8'], category_id: wizard.current_submission['action_8'],
user_id: user.id user_id: user.id
).first.notification_level).to eq(2) ).first.notification_level).to eq(2)
expect(CategoryUser.where( expect(CategoryUser.where(

Datei anzeigen

@ -18,7 +18,7 @@ describe CustomWizard::Builder do
let(:required_data_json) { let(:required_data_json) {
JSON.parse(File.open( JSON.parse(File.open(
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard/required_data.json" "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/step/required_data.json"
).read) ).read)
} }
@ -36,11 +36,11 @@ describe CustomWizard::Builder do
before do before do
Group.refresh_automatic_group!(:trust_level_3) Group.refresh_automatic_group!(:trust_level_3)
CustomWizard::Template.add( CustomWizard::Template.save(
JSON.parse(File.open( 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),
) skip_jobs: true)
@template = CustomWizard::Template.find('super_mega_fun_wizard') @template = CustomWizard::Template.find('super_mega_fun_wizard')
end end
@ -175,9 +175,8 @@ describe CustomWizard::Builder do
context "user has partially completed" do context "user has partially completed" do
before do before do
PluginStore.set("super_mega_fun_wizard_submissions", user.id, wizard = CustomWizard::Wizard.new(@template, user)
step_1_field_1: 'I am a user submission' wizard.set_submissions(step_1_field_1: 'I am a user submission')
)
end end
it 'returns saved submissions' do it 'returns saved submissions' do
@ -241,7 +240,7 @@ describe CustomWizard::Builder do
end end
it 'is permitted if required data is present' do it 'is permitted if required data is present' do
PluginStore.set('super_mega_fun_wizard_submissions', user.id, CustomWizard::Wizard.set_submissions('super_mega_fun_wizard', user,
required_data: "required_value" required_data: "required_value"
) )
expect( expect(
@ -259,13 +258,10 @@ describe CustomWizard::Builder do
end end
it 'saves permitted params' do it 'saves permitted params' do
CustomWizard::Builder.new(@template[:id], user).build({}, wizard = CustomWizard::Builder.new(@template[:id], user).build({},
param: 'param_value' param: 'param_value'
) )
expect( expect(wizard.current_submission['saved_param']).to eq('param_value')
PluginStore.get("super_mega_fun_wizard_submissions", user.id)
.first['saved_param']
).to eq('param_value')
end end
end end
end end
@ -302,7 +298,7 @@ describe CustomWizard::Builder do
it 'saves submissions' do it 'saves submissions' do
perform_update('step_1', step_1_field_1: 'Text input') perform_update('step_1', step_1_field_1: 'Text input')
expect( expect(
PluginStore.get("super_mega_fun_wizard_submissions", user.id) CustomWizard::Wizard.submissions(@template[:id], user)
.first['step_1_field_1'] .first['step_1_field_1']
).to eq('Text input') ).to eq('Text input')
end end
@ -316,7 +312,7 @@ describe CustomWizard::Builder do
it "does not save submissions" do it "does not save submissions" do
perform_update('step_1', step_1_field_1: 'Text input') perform_update('step_1', step_1_field_1: 'Text input')
expect( expect(
PluginStore.get("super_mega_fun_wizard_submissions", user.id) CustomWizard::Wizard.submissions(@template[:id], user).first
).to eq(nil) ).to eq(nil)
end end
end end
@ -332,7 +328,7 @@ describe CustomWizard::Builder do
it 'standardises boolean entries' do it 'standardises boolean entries' do
perform_update('step_2', step_2_field_5: 'false') perform_update('step_2', step_2_field_5: 'false')
expect( expect(
PluginStore.get("super_mega_fun_wizard_submissions", user.id) CustomWizard::Wizard.submissions(@template[:id], user)
.first['step_2_field_5'] .first['step_2_field_5']
).to eq(false) ).to eq(false)
end end

Datei anzeigen

@ -0,0 +1,30 @@
require 'rails_helper'
describe CustomWizard::Field do
before do
CustomWizard::Field.register(
'location',
'discourse-locations',
['components', 'helpers', 'lib', 'stylesheets', 'templates'],
type_opts: {
prefill: { "coordinates": [35.3082, 149.1244] }
}
)
end
it "registers custom field types" do
expect(CustomWizard::Field.types[:location].present?).to eq(true)
end
it "allows custom field types to set default attributes" do
expect(
CustomWizard::Field.types[:location][:prefill]
).to eq({ "coordinates": [35.3082, 149.1244] })
end
it "registers custom field assets" do
expect(
CustomWizard::Field.require_assets['discourse-locations']
).to eq(['components', 'helpers', 'lib', 'stylesheets', 'templates'])
end
end

Datei anzeigen

@ -0,0 +1,27 @@
require 'rails_helper'
describe CustomWizard::Log do
before do
CustomWizard::Log.create("First log message")
CustomWizard::Log.create("Second log message")
CustomWizard::Log.create("Third log message")
end
it "creates logs" do
expect(
CustomWizard::Log.list.length
).to eq(3)
end
it "lists logs by time created" do
expect(
CustomWizard::Log.list.first.message
).to eq("Third log message")
end
it "paginates logs" do
expect(
CustomWizard::Log.list(0, 2).length
).to eq(2)
end
end

Datei anzeigen

@ -0,0 +1,250 @@
require 'rails_helper'
describe CustomWizard::Mapper do
fab!(:user1) {
Fabricate(:user,
name: "Angus",
username: "angus",
email: "angus@email.com",
trust_level: TrustLevel[3]
)
}
fab!(:user2) {
Fabricate(:user,
name: "Patrick",
username: "patrick",
email: "patrick@email2.com",
trust_level: TrustLevel[1]
)
}
fab!(:user_field) {
field = Fabricate(:user_field,
id: 3,
name: 'dropdown_field',
description: 'field desc',
field_type: 'dropdown',
user_field_options_attributes: [
{ value: "a" },
{ value: "b" },
{ value: "c" }
]
)
}
let(:inputs) {
JSON.parse(File.open(
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/mapper/inputs.json"
).read)
}
let(:data) {
JSON.parse(File.open(
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/mapper/data.json"
).read)
}
it "maps values" do
expect(CustomWizard::Mapper.new(
inputs: inputs['assignment'],
data: data,
user: user1
).perform).to eq([13])
end
it "maps associations" do
association = CustomWizard::Mapper.new(
inputs: inputs['association'],
data: data,
user: user1
).perform
expect(association.length).to eq(3)
expect(association.first[:value]).to eq("Choice 1")
end
context "conditional mapping" do
it "maps when the condition is met" do
expect(CustomWizard::Mapper.new(
inputs: inputs['conditional'],
data: data,
user: user1
).perform).to eq("true")
end
it "does not map when the condition is not met" do
expect(CustomWizard::Mapper.new(
inputs: inputs['conditional'],
data: data,
user: user2
).perform).to eq(nil)
end
it "maps when multiple conditions are met" do
expect(CustomWizard::Mapper.new(
inputs: inputs['conditional_multiple_pairs'],
data: data,
user: user1
).perform).to eq("true")
end
it "does not map when one of multiple conditions are not met" do
user1.email = "angus@other-email.com"
expect(CustomWizard::Mapper.new(
inputs: inputs['conditional_multiple_pairs'],
data: data,
user: user1
).perform).to eq(nil)
end
end
it "validates valid data" do
expect(CustomWizard::Mapper.new(
inputs: inputs['validation'],
data: data,
user: user1
).perform).to eq(true)
end
it "does not validate invalid data" do
data["input_2"] = "value 3"
expect(CustomWizard::Mapper.new(
inputs: inputs['validation'],
data: data,
user: user1
).perform).to eq(false)
end
it "maps text fields" do
expect(CustomWizard::Mapper.new(
inputs: inputs['assignment_text'],
data: data,
user: user1
).perform).to eq("Value")
end
it "maps user fields" do
expect(CustomWizard::Mapper.new(
inputs: inputs['assignment_user_field'],
data: data,
user: user1
).perform).to eq("Angus")
end
it "maps user field options" do
expect(CustomWizard::Mapper.new(
inputs: inputs['assignment_user_field_options'],
data: data,
user: user1
).perform).to eq(["a", "b", "c"])
end
it "maps wizard fields" do
expect(CustomWizard::Mapper.new(
inputs: inputs['assignment_wizard_field'],
data: data,
user: user1
).perform).to eq("value 1")
end
it "maps wizard actions" do
expect(CustomWizard::Mapper.new(
inputs: inputs['assignment_wizard_action'],
data: data,
user: user1
).perform).to eq("value 2")
end
it "interpolates user fields" do
expect(CustomWizard::Mapper.new(
inputs: inputs['interpolate_user_field'],
data: data,
user: user1
).perform).to eq("Name: Angus")
end
it "interpolates wizard fields" do
expect(CustomWizard::Mapper.new(
inputs: inputs['interpolate_wizard_field'],
data: data,
user: user1
).perform).to eq("Input 1: value 1")
end
it "interpolates date" do
expect(CustomWizard::Mapper.new(
inputs: inputs['interpolate_timestamp'],
data: data,
user: user1
).perform).to eq("Time: #{Time.now.strftime("%B %-d, %Y")}")
end
it "handles greater than pairs" do
expect(CustomWizard::Mapper.new(
inputs: inputs['greater_than_pair'],
data: data,
user: user1
).perform).to eq(true)
expect(CustomWizard::Mapper.new(
inputs: inputs['greater_than_pair'],
data: data,
user: user2
).perform).to eq(false)
end
it "handles less than pairs" do
expect(CustomWizard::Mapper.new(
inputs: inputs['less_than_pair'],
data: data,
user: user1
).perform).to eq(false)
expect(CustomWizard::Mapper.new(
inputs: inputs['less_than_pair'],
data: data,
user: user2
).perform).to eq(true)
end
it "handles greater than or equal pairs" do
expect(CustomWizard::Mapper.new(
inputs: inputs['greater_than_or_equal_pair'],
data: data,
user: user1
).perform).to eq(true)
expect(CustomWizard::Mapper.new(
inputs: inputs['greater_than_or_equal_pair'],
data: data,
user: user2
).perform).to eq(true)
end
it "handles less than or equal pairs" do
expect(CustomWizard::Mapper.new(
inputs: inputs['less_than_or_equal_pair'],
data: data,
user: user1
).perform).to eq(true)
expect(CustomWizard::Mapper.new(
inputs: inputs['less_than_or_equal_pair'],
data: data,
user: user2
).perform).to eq(true)
end
it "handles regex pairs" do
expect(CustomWizard::Mapper.new(
inputs: inputs['regex_pair'],
data: data,
user: user1
).perform).to eq(true)
expect(CustomWizard::Mapper.new(
inputs: inputs['regex_pair'],
data: data,
user: user2
).perform).to eq(false)
end
it "handles shorthand pairs" do
expect(CustomWizard::Mapper.new(
inputs: inputs['shorthand_pair'],
data: data,
user: user1
).perform).to eq(false)
end
end

Datei anzeigen

@ -0,0 +1,111 @@
require 'rails_helper'
describe CustomWizard::Template do
fab!(:user) { Fabricate(:user) }
let(:template_json) {
JSON.parse(File.open(
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
).read)
}
let(:permitted_json) {
JSON.parse(File.open(
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard/permitted.json"
).read)
}
let(:after_time) {
JSON.parse(File.open(
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard/after_time.json"
).read).with_indifferent_access
}
before do
CustomWizard::Template.save(template_json, skip_jobs: true)
end
it "saves wizard templates" do
expect(
PluginStoreRow.exists?(
plugin_name: 'custom_wizard',
key: 'super_mega_fun_wizard'
)
).to eq(true)
end
it "finds wizard templates" do
expect(
CustomWizard::Template.find('super_mega_fun_wizard')['id']
).to eq('super_mega_fun_wizard')
end
it "removes wizard templates" do
CustomWizard::Template.remove('super_mega_fun_wizard')
expect(
CustomWizard::Template.find('super_mega_fun_wizard')
).to eq(nil)
end
it "checks for wizard template existence" do
expect(
CustomWizard::Template.exists?('super_mega_fun_wizard')
).to eq(true)
end
context "wizard template list" do
before do
template_json_2 = template_json.dup
template_json_2["id"] = 'super_mega_fun_wizard_2'
template_json_2["permitted"] = permitted_json['permitted']
CustomWizard::Template.save(template_json_2, skip_jobs: true)
template_json_3 = template_json.dup
template_json_3["id"] = 'super_mega_fun_wizard_3'
template_json_3["after_signup"] = true
CustomWizard::Template.save(template_json_3, skip_jobs: true)
end
it "works" do
expect(
CustomWizard::Template.list.length
).to eq(3)
end
it "can be filtered by wizard settings" do
expect(
CustomWizard::Template.list(setting: "after_signup").length
).to eq(1)
end
it "can be ordered" do
expect(
CustomWizard::Template.list(
order: "(value::json ->> 'permitted') IS NOT NULL DESC"
).first['id']
).to eq('super_mega_fun_wizard_2')
end
end
context "after time setting" do
before do
freeze_time Time.now
@after_time_template = template_json.dup
@after_time_template["after_time"] = after_time['after_time']
@after_time_template["after_time_scheduled"] = after_time['after_time_scheduled']
end
it 'if enabled queues jobs after wizard is saved' do
expect_enqueued_with(job: :set_after_time_wizard, at: Time.parse(after_time['after_time_scheduled']).utc) do
CustomWizard::Template.save(@after_time_template)
end
end
it 'if disabled clears jobs after wizard is saved' do
CustomWizard::Template.save(@after_time_template)
@after_time_template['after_time'] = false
expect_not_enqueued_with(job: :set_after_time_wizard) do
CustomWizard::Template.save(@after_time_template)
end
end
end
end

Datei anzeigen

@ -0,0 +1,53 @@
require 'rails_helper'
describe CustomWizard::Validator do
fab!(:user) { Fabricate(:user) }
let(:template) {
JSON.parse(File.open(
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
).read).with_indifferent_access
}
let(:after_time) {
JSON.parse(File.open(
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard/after_time.json"
).read).with_indifferent_access
}
it "validates valid templates" do
expect(
CustomWizard::Validator.new(template).perform
).to eq(true)
end
it "invalidates templates without required attributes" do
template.delete(:id)
expect(
CustomWizard::Validator.new(template).perform
).to eq(false)
end
it "invalidates templates with duplicate ids if creating a new template" do
CustomWizard::Template.save(template)
expect(
CustomWizard::Validator.new(template, create: true).perform
).to eq(false)
end
it "validates after time settings" do
template[:after_time] = after_time[:after_time]
template[:after_time_scheduled] = after_time[:after_time_scheduled]
expect(
CustomWizard::Validator.new(template).perform
).to eq(true)
end
it "invalidates invalid after time settings" do
template[:after_time] = after_time[:after_time]
template[:after_time_scheduled] = "not a time"
expect(
CustomWizard::Validator.new(template).perform
).to eq(false)
end
end

Datei anzeigen

@ -0,0 +1,230 @@
require 'rails_helper'
describe CustomWizard::Wizard do
fab!(:user) { Fabricate(:user) }
fab!(:trusted_user) { Fabricate(:user, trust_level: TrustLevel[3])}
fab!(:admin_user) { Fabricate(:user, admin: true)}
let(:template_json) {
JSON.parse(File.open(
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
).read)
}
let(:after_time) {
JSON.parse(File.open(
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard/after_time.json"
).read)
}
let(:permitted_json) {
JSON.parse(File.open(
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard/permitted.json"
).read)
}
before do
Group.refresh_automatic_group!(:trust_level_3)
@permitted_template = template_json.dup
@permitted_template["permitted"] = permitted_json["permitted"]
@wizard = CustomWizard::Wizard.new(template_json, user)
template_json['steps'].each do |step_template|
@wizard.append_step(step_template['id'])
end
end
def progress_step(step_id, acting_user = user)
UserHistory.create(
action: UserHistory.actions[:custom_wizard_step],
acting_user_id: acting_user.id,
context: @wizard.id,
subject: step_id
)
end
it "appends steps from a template" do
expect(@wizard.steps.length).to eq(3)
end
it "determines the user's current step" do
expect(@wizard.start.id).to eq('step_1')
progress_step('step_1')
expect(@wizard.start.id).to eq('step_2')
end
it "creates a step updater" do
expect(
@wizard.create_updater('step_1', step_1_field_1: "Text input")
.class
).to eq(CustomWizard::StepUpdater)
end
it "determines whether a wizard is unfinished" do
expect(@wizard.unfinished?).to eq(true)
progress_step("step_1")
expect(@wizard.unfinished?).to eq(true)
progress_step("step_2")
expect(@wizard.unfinished?).to eq(true)
progress_step("step_3")
expect(@wizard.unfinished?).to eq(false)
end
it "determines whether a wizard has been completed by a user" do
expect(@wizard.completed?).to eq(false)
progress_step("step_1")
progress_step("step_2")
progress_step("step_3")
expect(@wizard.completed?).to eq(true)
end
it "is not completed if steps submitted before after time" do
progress_step("step_1")
progress_step("step_2")
progress_step("step_3")
template_json['after_time'] = after_time['after_time']
template_json['after_time_scheduled'] = after_time['after_time_scheduled']
wizard = CustomWizard::Wizard.new(template_json, user)
expect(wizard.completed?).to eq(false)
end
it "permits admins" do
expect(
CustomWizard::Wizard.new(@permitted_template, admin_user).permitted?
).to eq(true)
end
it "permits permitted users" do
expect(
CustomWizard::Wizard.new(@permitted_template, trusted_user).permitted?
).to eq(true)
end
it "does not permit unpermitted users" do
expect(
CustomWizard::Wizard.new(@permitted_template, user).permitted?
).to eq(false)
end
it "does not let an unpermitted user access a wizard" do
expect(
CustomWizard::Wizard.new(@permitted_template, user).can_access?
).to eq(false)
end
it "lets a permitted user access an incomplete wizard" do
expect(
CustomWizard::Wizard.new(@permitted_template, trusted_user).can_access?
).to eq(true)
end
it "lets a permitted user access a complete wizard with multiple submissions" do
progress_step("step_1", trusted_user)
progress_step("step_2", trusted_user)
progress_step("step_3", trusted_user)
expect(
CustomWizard::Wizard.new(@permitted_template, trusted_user).can_access?
).to eq(true)
end
it "does not let an unpermitted user access a complete wizard without multiple submissions" do
progress_step("step_1", trusted_user)
progress_step("step_2", trusted_user)
progress_step("step_3", trusted_user)
@permitted_template['multiple_submissions'] = false
expect(
CustomWizard::Wizard.new(@permitted_template, trusted_user).can_access?
).to eq(false)
end
it "lists the site groups" do
expect(@wizard.groups.length).to eq(8)
end
it "lists the site categories" do
expect(@wizard.categories.length).to eq(1)
end
context "submissions" do
before do
@wizard.set_submissions(step_1_field_1: 'I am a user submission')
end
it "sets the user's submission" do
expect(
PluginStore.get("#{template_json['id']}_submissions", user.id)
.first['step_1_field_1']
).to eq('I am a user submission')
end
it "lists the user's submissions" do
expect(@wizard.submissions.length).to eq(1)
end
it "returns the user's current submission" do
expect(@wizard.current_submission['step_1_field_1']).to eq('I am a user submission')
end
end
it "provides class methods to set and list submissions" do
CustomWizard::Wizard.set_submissions(template_json['id'], user,
step_1_field_1: 'I am a user submission'
)
expect(
CustomWizard::Wizard.submissions(template_json['id'], user)
.first['step_1_field_1']
).to eq('I am a user submission')
end
context do
before do
CustomWizard::Template.save(@permitted_template, skip_jobs: true)
template_json_2 = template_json.dup
template_json_2["id"] = 'super_mega_fun_wizard_2'
template_json_2["prompt_completion"] = true
CustomWizard::Template.save(template_json_2, skip_jobs: true)
template_json_3 = template_json.dup
template_json_3["id"] = 'super_mega_fun_wizard_3'
template_json_3["after_signup"] = true
CustomWizard::Template.save(template_json_3, skip_jobs: true)
end
it "lists wizards the user can see" do
expect(CustomWizard::Wizard.list(user).length).to eq(2)
expect(CustomWizard::Wizard.list(trusted_user).length).to eq(3)
end
it "returns the first after signup wizard" do
expect(CustomWizard::Wizard.after_signup(user).id).to eq('super_mega_fun_wizard_3')
end
it "lists prompt completion wizards" do
expect(CustomWizard::Wizard.prompt_completion(user).length).to eq(2)
end
end
it "sets wizard redirects if user is permitted" do
CustomWizard::Template.save(@permitted_template, skip_jobs: true)
CustomWizard::Wizard.set_wizard_redirect('super_mega_fun_wizard', trusted_user)
expect(
trusted_user.custom_fields['redirect_to_wizard']
).to eq("super_mega_fun_wizard")
end
it "does not set a wizard redirect if user is not permitted" do
CustomWizard::Template.save(@permitted_template, skip_jobs: true)
CustomWizard::Wizard.set_wizard_redirect('super_mega_fun_wizard', user)
expect(
trusted_user.custom_fields['redirect_to_wizard']
).to eq(nil)
end
end

Datei anzeigen

@ -0,0 +1,23 @@
require 'rails_helper'
describe ExtraLocalesControllerCustomWizard, type: :request do
before do
CustomWizard::Template.save(
JSON.parse(File.open(
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
).read),
skip_jobs: true)
end
before do
@controller = ExtraLocalesController.new
end
it "returns locales when requested by wizard" do
expect(
ExtraLocalesController.url("wizard")
).to eq(
"#{Discourse.base_path}/extra-locales/wizard?v=#{ExtraLocalesController.bundle_js_hash("wizard")}"
)
end
end

Datei anzeigen

@ -0,0 +1,24 @@
require 'rails_helper'
describe InvitesControllerCustomWizard, type: :request do
fab!(:topic) { Fabricate(:topic) }
let(:invite) do
Invite.invite_by_email("angus@email.com", topic.user, topic)
end
let(:template) do
JSON.parse(File.open(
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
).read)
end
before do
@controller = InvitesController.new
end
it "redirects a user to wizard after invite if after signup is enabled" do
template['after_signup'] = true
CustomWizard::Template.save(template, skip_jobs: true)
put "/invites/show/#{invite.invite_key}.json"
expect(response.parsed_body["redirect_to"]).to eq("/w/super-mega-fun-wizard")
end
end

Datei anzeigen

@ -0,0 +1,21 @@
require 'rails_helper'
describe CustomWizardUsersController, type: :request do
let(:template) do
JSON.parse(File.open(
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
).read)
end
before do
@controller = UsersController.new
end
it "redirects a user to wizard after sign up if after signup is enabled" do
template['after_signup'] = true
CustomWizard::Template.save(template, skip_jobs: true)
sign_in(Fabricate(:user))
get "/u/account-created"
expect(response).to redirect_to("/w/super-mega-fun-wizard")
end
end

Datei anzeigen

@ -0,0 +1,21 @@
require 'rails_helper'
describe CustomWizardFieldExtension do
let(:field_hash) do
JSON.parse(File.open(
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/field/field.json"
).read).with_indifferent_access
end
it "adds custom field attributes" do
field = Wizard::Field.new(field_hash)
expect(field.id).to eq("field_id")
expect(field.label).to eq("<p>Field Label</p>")
expect(field.image).to eq("field_image_url.png")
expect(field.description).to eq("Field description")
expect(field.required).to eq(true)
expect(field.key).to eq("field.locale.key")
expect(field.type).to eq("field_type")
expect(field.content).to eq([])
end
end

Datei anzeigen

@ -0,0 +1,23 @@
require 'rails_helper'
describe CustomWizardStepExtension do
let(:step_hash) do
JSON.parse(File.open(
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/step/step.json"
).read).with_indifferent_access
end
it "adds custom step attributes" do
step = Wizard::Step.new(step_hash[:id])
[
:title,
:description,
:key,
:permitted,
:permitted_message
].each do |attr|
step.send("#{attr.to_s}=", step_hash[attr])
expect(step.send(attr)).to eq(step_hash[attr])
end
end
end

10
spec/fixtures/field/field.json gevendort Normale Datei
Datei anzeigen

@ -0,0 +1,10 @@
{
"id": "field_id",
"label": "Field Label",
"image": "field_image_url.png",
"description": "Field description",
"required": true,
"key": "field.locale.key",
"type": "field_type",
"content": []
}

4
spec/fixtures/mapper/data.json gevendort Normale Datei
Datei anzeigen

@ -0,0 +1,4 @@
{
"input_1": "value 1",
"input_2": "value 2"
}

264
spec/fixtures/mapper/inputs.json gevendort Normale Datei
Datei anzeigen

@ -0,0 +1,264 @@
{
"assignment": [
{
"type": "assignment",
"output_type": "group",
"output_connector": "set",
"output": [
13
]
}
],
"assignment_text": [
{
"type": "assignment",
"output_type": "text",
"output_connector": "set",
"output": "Value"
}
],
"assignment_user_field": [
{
"type": "assignment",
"output_type": "user_field",
"output_connector": "set",
"output": "name"
}
],
"assignment_user_field_options": [
{
"type": "assignment",
"output_type": "user_field_options",
"output_connector": "set",
"output": "user_field_3"
}
],
"assignment_wizard_field": [
{
"type": "assignment",
"output_type": "wizard_field",
"output_connector": "set",
"output": "input_1"
}
],
"assignment_wizard_action": [
{
"type": "assignment",
"output_type": "wizard_action",
"output_connector": "set",
"output": "input_2"
}
],
"interpolate_user_field": [
{
"type": "assignment",
"output_type": "text",
"output_connector": "set",
"output": "Name: u{name}"
}
],
"interpolate_wizard_field": [
{
"type": "assignment",
"output_type": "text",
"output_connector": "set",
"output": "Input 1: w{input_1}"
}
],
"interpolate_timestamp": [
{
"type": "assignment",
"output_type": "text",
"output_connector": "set",
"output": "Time: v{time}"
}
],
"validation": [
{
"type": "validation",
"pairs": [
{
"index": 0,
"key": "input_1",
"key_type": "wizard_field",
"value": "value 1",
"value_type": "text",
"connector": "equal"
},
{
"index": 1,
"key": "input_2",
"key_type": "wizard_field",
"value": "value 2",
"value_type": "text",
"connector": "equal"
}
]
}
],
"association": [
{
"type": "association",
"pairs": [
{
"index": 0,
"key": "choice1",
"key_type": "text",
"value": "Choice 1",
"value_type": "text",
"connector": "equal"
},
{
"index": 1,
"key": "choice2",
"key_type": "text",
"value": "Choice 2",
"value_type": "text",
"connector": "association"
},
{
"index": 2,
"key": "choice3",
"key_type": "text",
"value": "Choice 3",
"value_type": "text",
"connector": "association"
}
]
}
],
"conditional": [
{
"type": "conditional",
"output": "true",
"output_type": "text",
"output_connector": "then",
"pairs": [
{
"index": 0,
"key": "name",
"key_type": "user_field",
"value": "Angus",
"value_type": "text",
"connector": "equal"
}
]
}
],
"conditional_multiple_pairs": [
{
"type": "conditional",
"output": "true",
"output_type": "text",
"output_connector": "then",
"pairs": [
{
"index": 0,
"key": "name",
"key_type": "user_field",
"value": "Angus",
"value_type": "text",
"connector": "equal"
},
{
"index": 1,
"key": "email",
"key_type": "user_field",
"value": "angus@email.com",
"value_type": "text",
"connector": "equal"
}
]
}
],
"greater_than_pair": [
{
"type": "validation",
"pairs": [
{
"index": 0,
"key": "trust_level",
"key_type": "user_field",
"value": "2",
"value_type": "text",
"connector": "greater"
}
]
}
],
"less_than_pair": [
{
"type": "validation",
"pairs": [
{
"index": 0,
"key": "trust_level",
"key_type": "user_field",
"value": "2",
"value_type": "text",
"connector": "less"
}
]
}
],
"greater_than_or_equal_pair": [
{
"type": "validation",
"pairs": [
{
"index": 0,
"key": "trust_level",
"key_type": "user_field",
"value": "1",
"value_type": "text",
"connector": "greater_or_equal"
}
]
}
],
"less_than_or_equal_pair": [
{
"type": "validation",
"pairs": [
{
"index": 0,
"key": "trust_level",
"key_type": "user_field",
"value": "3",
"value_type": "text",
"connector": "less_or_equal"
}
]
}
],
"regex_pair": [
{
"type": "validation",
"pairs": [
{
"index": 0,
"key": "email",
"key_type": "user_field",
"value": "@email.com",
"value_type": "text",
"connector": "regex"
}
]
}
],
"shorthand_pair": [
{
"type": "validation",
"pairs": [
{
"index": 0,
"key": "bio_raw",
"key_type": "user_field",
"value": "present",
"value_type": "text",
"connector": "is"
}
]
}
]
}

10
spec/fixtures/step/step.json gevendort Normale Datei
Datei anzeigen

@ -0,0 +1,10 @@
{
"id": "step_1",
"title": "Text",
"description": "Step description",
"image": "step_image_url.png",
"key": "step.locale.key",
"fields": [],
"required_data": [],
"permitted": []
}

Datei anzeigen

@ -16,6 +16,7 @@
{ {
"id": "step_1_field_1", "id": "step_1_field_1",
"label": "Text", "label": "Text",
"description": "Text field description.",
"type": "text", "type": "text",
"min_length": "3", "min_length": "3",
"prefill": [ "prefill": [
@ -351,7 +352,7 @@
}, },
{ {
"id": "action_1", "id": "action_1",
"run_after": "step_2", "run_after": "step_3",
"type": "create_topic", "type": "create_topic",
"skip_redirect": true, "skip_redirect": true,
"post": "step_1_field_2", "post": "step_1_field_2",

4
spec/fixtures/wizard/after_time.json gevendort Normale Datei
Datei anzeigen

@ -0,0 +1,4 @@
{
"after_time": true,
"after_time_scheduled": "2020-11-20T00:59:00.000Z"
}

Datei anzeigen

@ -0,0 +1,37 @@
# frozen_string_literal: true
require 'rails_helper'
describe Jobs::ClearAfterTimeWizard do
fab!(:user1) { Fabricate(:user) }
fab!(:user2) { Fabricate(:user) }
fab!(:user3) { Fabricate(:user) }
let(:template) {
JSON.parse(File.open(
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
).read).with_indifferent_access
}
let(:after_time) {
JSON.parse(File.open(
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard/after_time.json"
).read).with_indifferent_access
}
it "clears wizard redirect for all users " do
after_time_template = template.dup
after_time_template["after_time"] = after_time['after_time']
after_time_template["after_time_scheduled"] = after_time['after_time_scheduled']
CustomWizard::Template.save(after_time_template)
described_class.new.execute(wizard_id: 'super_mega_fun_wizard')
expect(
UserCustomField.where("
name = 'redirect_to_wizard' AND
value = 'super_mega_fun_wizard'
").exists?
).to eq(false)
end
end

Datei anzeigen

@ -0,0 +1,42 @@
# frozen_string_literal: true
require 'rails_helper'
describe Jobs::SetAfterTimeWizard do
fab!(:user1) { Fabricate(:user) }
fab!(:user2) { Fabricate(:user) }
fab!(:user3) { Fabricate(:user) }
let(:template) {
JSON.parse(File.open(
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
).read).with_indifferent_access
}
let(:after_time) {
JSON.parse(File.open(
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard/after_time.json"
).read).with_indifferent_access
}
it "sets wizard redirect for all users " do
after_time_template = template.dup
after_time_template["after_time"] = after_time['after_time']
after_time_template["after_time_scheduled"] = after_time['after_time_scheduled']
CustomWizard::Template.save(after_time_template)
messages = MessageBus.track_publish("/redirect_to_wizard") do
described_class.new.execute(wizard_id: 'super_mega_fun_wizard')
end
expect(
UserCustomField.where(
name: 'redirect_to_wizard',
value: 'super_mega_fun_wizard'
).length
).to eq(3)
expect(messages.first.data).to eq("super_mega_fun_wizard")
expect(messages.first.user_ids).to match_array([user1.id,user2.id,user3.id])
end
end

Datei anzeigen

@ -3,6 +3,8 @@ require 'simplecov'
SimpleCov.configure do SimpleCov.configure do
add_filter do |src| add_filter do |src|
src.filename !~ /discourse-custom-wizard/ || src.filename !~ /discourse-custom-wizard/ ||
src.filename =~ /spec/ src.filename =~ /spec/ ||
src.filename =~ /db/ ||
src.filename =~ /api/ ## API features are currently experimental
end end
end end

Datei anzeigen

@ -0,0 +1,22 @@
require 'rails_helper'
describe CustomWizard::AdminLogsController do
fab!(:admin_user) { Fabricate(:user, admin: true) }
before do
CustomWizard::Log.create("First log message")
CustomWizard::Log.create("Second log message")
CustomWizard::Log.create("Third log message")
sign_in(admin_user)
end
it "returns a list of logs" do
get "/admin/wizards/logs.json"
expect(response.parsed_body.length).to eq(3)
end
it "paginates" do
get "/admin/wizards/logs.json", params: { page: 1, limit: 2 }
expect(response.parsed_body.length).to eq(1)
end
end

Datei anzeigen

@ -0,0 +1,45 @@
require 'rails_helper'
describe CustomWizard::AdminSubmissionsController do
fab!(:admin_user) {Fabricate(:user, admin: true)}
fab!(:user1) {Fabricate(:user)}
fab!(:user2) {Fabricate(:user)}
let(:template) {
JSON.parse(File.open(
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
).read)
}
before do
CustomWizard::Template.save(template, skip_jobs: true)
CustomWizard::Wizard.set_submissions(template['id'], user1,
step_1_field_1: "I am a user1's submission"
)
CustomWizard::Wizard.set_submissions(template['id'], user2,
step_1_field_1: "I am a user2's submission"
)
sign_in(admin_user)
end
it "returns a basic list of wizards" do
get "/admin/wizards/submissions.json"
expect(response.parsed_body.length).to eq(1)
expect(response.parsed_body.first['id']).to eq(template['id'])
end
it "returns the all user's submissions for a wizard" do
get "/admin/wizards/submissions/#{template['id']}.json"
expect(response.parsed_body['submissions'].length).to eq(2)
end
it "returns the all user's submissions for a wizard" do
get "/admin/wizards/submissions/#{template['id']}.json"
expect(response.parsed_body['submissions'].length).to eq(2)
end
it "downloads all user submissions" do
get "/admin/wizards/submissions/#{template['id']}/download"
expect(response.parsed_body.length).to eq(2)
end
end

Datei anzeigen

@ -0,0 +1,52 @@
require 'rails_helper'
describe CustomWizard::AdminTransferController do
fab!(:admin_user) { Fabricate(:user, admin: true) }
let(:template) {
JSON.parse(File.open(
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
).read)
}
before do
sign_in(admin_user)
CustomWizard::Template.save(template, skip_jobs: true)
template_2 = template.dup
template_2["id"] = 'super_mega_fun_wizard_2'
CustomWizard::Template.save(template_2, skip_jobs: true)
template_3 = template.dup
template_3["id"] = 'super_mega_fun_wizard_3'
template_3["after_signup"] = true
CustomWizard::Template.save(template_3, skip_jobs: true)
@template_array = [template, template_2, template_3]
FileUtils.mkdir_p(file_from_fixtures_tmp_folder) unless Dir.exists?(file_from_fixtures_tmp_folder)
@tmp_file_path = File.join(file_from_fixtures_tmp_folder, SecureRandom.hex << 'wizards.json')
File.write(@tmp_file_path, @template_array.to_json)
end
it 'exports all the wizard templates' do
get '/admin/wizards/transfer/export.json', params: {
wizards: [
'super_mega_fun_wizard',
'super_mega_fun_wizard_2',
'super_mega_fun_wizard_3'
]
}
expect(response.status).to eq(200)
expect(response.parsed_body).to match_array(@template_array)
end
it 'imports wizard a template' do
post '/admin/wizards/transfer/import.json', params: {
file: fixture_file_upload(File.open(@tmp_file_path))
}
expect(response.status).to eq(200)
expect(response.parsed_body['success']).to eq(@template_array.map { |t| t['id'] })
end
end

Datei anzeigen

@ -0,0 +1,66 @@
require 'rails_helper'
describe CustomWizard::AdminWizardController do
fab!(:admin_user) {Fabricate(:user, admin: true)}
fab!(:user1) {Fabricate(:user)}
fab!(:user2) {Fabricate(:user)}
let(:template) {
JSON.parse(File.open(
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
).read)
}
before do
CustomWizard::Template.save(template, skip_jobs: true)
template_2 = template.dup
template_2["id"] = 'super_mega_fun_wizard_2'
template_2["permitted"] = template_2['permitted']
CustomWizard::Template.save(template_2, skip_jobs: true)
template_3 = template.dup
template_3["id"] = 'super_mega_fun_wizard_3'
template_3["after_signup"] = true
CustomWizard::Template.save(template_3, skip_jobs: true)
sign_in(admin_user)
end
it "returns a basic list of wizard templates and wizard field types" do
get "/admin/wizards/wizard.json"
expect(
response.parsed_body['wizard_list'].map { |w| w['id'] }
).to match_array(['super_mega_fun_wizard', 'super_mega_fun_wizard_2', 'super_mega_fun_wizard_3'])
expect(
response.parsed_body['field_types'].keys
).to eq(CustomWizard::Field.types.keys.map(&:to_s))
end
it "returns a wizard template" do
get "/admin/wizards/wizard/#{template['id']}.json"
expect(response.parsed_body['id']).to eq(template['id'])
expect(response.parsed_body['steps'].length).to eq(3)
end
it "removes wizard templates" do
delete "/admin/wizards/wizard/#{template['id']}.json"
expect(response.status).to eq(200)
expect(CustomWizard::Template.exists?(template['id'])).to eq(false)
end
it "saves wizard templates" do
template_updated = template.dup
template_updated['name'] = "Super Mega Fun Wizard 2"
template_updated['multiple_submissions'] = false
template_updated['steps'][0]['fields'][0]['label'] = "Text 2"
put "/admin/wizards/wizard/#{template['id']}.json", params: { wizard: template_updated }
expect(response.status).to eq(200)
updated_template = CustomWizard::Template.find('super_mega_fun_wizard')
expect(updated_template['name']).to eq("Super Mega Fun Wizard 2")
expect(updated_template['multiple_submissions']).to eq("false")
expect(updated_template['steps'][0]['fields'][0]['label']).to eq("Text 2")
end
end

Datei anzeigen

@ -0,0 +1,61 @@
require 'rails_helper'
describe ApplicationController do
fab!(:user) {
Fabricate(
:user,
username: 'angus',
email: "angus@email.com",
trust_level: TrustLevel[3]
)
}
before do
CustomWizard::Template.save(
JSON.parse(File.open(
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
).read),
skip_jobs: true)
@template = CustomWizard::Template.find('super_mega_fun_wizard')
end
context "with signed in user" do
before do
sign_in(user)
end
context "who is required to complete wizard" do
before do
user.custom_fields['redirect_to_wizard'] = 'super_mega_fun_wizard'
user.save_custom_fields(true)
end
it "redirects if user is required to complete a wizard" do
get "/"
expect(response).to redirect_to("/w/super-mega-fun-wizard")
end
it "saves original destination of user" do
get '/', headers: { 'REFERER' => "/t/2" }
expect(
CustomWizard::Wizard.submissions(@template['id'], user)
.first['redirect_to']
).to eq("/t/2")
end
end
context "who is not required to complete wizard" do
it "does nothing" do
get "/"
expect(response.status).to eq(200)
end
end
end
context "with guest" do
it "does nothing" do
get "/"
expect(response.status).to eq(200)
end
end
end

Datei anzeigen

@ -0,0 +1,34 @@
require 'rails_helper'
describe CustomWizard::StepsController do
fab!(:user) {
Fabricate(
:user,
username: 'angus',
email: "angus@email.com",
trust_level: TrustLevel[3]
)
}
before do
CustomWizard::Template.save(
JSON.parse(File.open(
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
).read),
skip_jobs: true)
sign_in(user)
end
it 'performs a step update' do
put '/w/super-mega-fun-wizard/steps/step_1.json', params: {
fields: {
step_1_field_1: "Text input"
}
}
expect(response.status).to eq(200)
wizard = CustomWizard::Builder.new("super_mega_fun_wizard", user).build
expect(wizard.current_submission['step_1_field_1']).to eq("Text input")
expect(wizard.start.id).to eq("step_2")
end
end

Datei anzeigen

@ -0,0 +1,63 @@
require 'rails_helper'
describe CustomWizard::WizardController do
fab!(:user) {
Fabricate(
:user,
username: 'angus',
email: "angus@email.com",
trust_level: TrustLevel[3]
)
}
before do
CustomWizard::Template.save(
JSON.parse(File.open(
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
).read),
skip_jobs: true)
@template = CustomWizard::Template.find("super_mega_fun_wizard")
sign_in(user)
end
context 'plugin disabled' do
before do
SiteSetting.custom_wizard_enabled = false
end
it 'redirects to root' do
get '/w/super-mega-fun-wizard', xhr: true
expect(response).to redirect_to("/")
end
end
it 'returns wizard' do
get '/w/super-mega-fun-wizard.json'
expect(response.parsed_body["id"]).to eq("super_mega_fun_wizard")
end
it 'returns missing message if no wizard exists' do
get '/w/super-mega-fun-wizards.json'
expect(response.parsed_body["error"]).to eq("We couldn't find a wizard at that address.")
end
it 'skips a wizard if user is allowed to skip' do
put '/w/super-mega-fun-wizard/skip.json'
expect(response.status).to eq(200)
end
it 'returns a no skip message if user is not allowed to skip' do
@template['required'] = 'true'
CustomWizard::Template.save(@template)
put '/w/super-mega-fun-wizard/skip.json'
expect(response.parsed_body['error']).to eq("Wizard can't be skipped")
end
it 'skip response contains a redirect_to if in users submissions' do
CustomWizard::Wizard.set_submissions(@template['id'], user,
redirect_to: '/t/2'
)
put '/w/super-mega-fun-wizard/skip.json'
expect(response.parsed_body['redirect_to']).to eq('/t/2')
end
end

Datei anzeigen

@ -0,0 +1,21 @@
# frozen_string_literal: true
require 'rails_helper'
describe CustomWizard::BasicWizardSerializer do
fab!(:user) { Fabricate(:user) }
it 'should return basic wizard attributes' do
CustomWizard::Template.save(
JSON.parse(File.open(
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
).read),
skip_jobs: true)
json = CustomWizard::BasicWizardSerializer.new(
CustomWizard::Builder.new("super_mega_fun_wizard", user).build,
scope: Guardian.new(user)
).as_json
expect(json[:basic_wizard][:id]).to eq("super_mega_fun_wizard")
expect(json[:basic_wizard][:name]).to eq("Super Mega Fun Wizard")
end
end

Datei anzeigen

@ -0,0 +1,19 @@
# frozen_string_literal: true
require 'rails_helper'
describe CustomWizard::LogSerializer do
fab!(:user) { Fabricate(:user) }
it 'should return log attributes' do
CustomWizard::Log.create("First log message")
CustomWizard::Log.create("Second log message")
json_array = ActiveModel::ArraySerializer.new(
CustomWizard::Log.list(0),
each_serializer: CustomWizard::LogSerializer
).as_json
expect(json_array.length).to eq(2)
expect(json_array[0][:message]).to eq("Second log message")
end
end

Datei anzeigen

@ -0,0 +1,37 @@
# frozen_string_literal: true
require 'rails_helper'
describe CustomWizard::FieldSerializer do
fab!(:user) { Fabricate(:user) }
before do
CustomWizard::Template.save(
JSON.parse(File.open(
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
).read),
skip_jobs: true)
@wizard = CustomWizard::Builder.new("super_mega_fun_wizard", user).build
end
it "should return basic field attributes" do
json_array = ActiveModel::ArraySerializer.new(
@wizard.steps.first.fields,
each_serializer: CustomWizard::FieldSerializer,
scope: Guardian.new(user)
).as_json
expect(json_array.length).to eq(4)
expect(json_array[0][:label]).to eq("<p>Text</p>")
expect(json_array[0][:description]).to eq("Text field description.")
end
it "should return optional field attributes" do
json_array = ActiveModel::ArraySerializer.new(
@wizard.steps.second.fields,
each_serializer: CustomWizard::FieldSerializer,
scope: Guardian.new(user)
).as_json
expect(json_array[0][:format]).to eq("YYYY-MM-DD")
expect(json_array[5][:file_types]).to eq(".jpg,.png")
end
end

Datei anzeigen

@ -0,0 +1,86 @@
# frozen_string_literal: true
require 'rails_helper'
describe CustomWizard::WizardSerializer do
fab!(:user) { Fabricate(:user) }
fab!(:category) { Fabricate(:category) }
before do
CustomWizard::Template.save(
JSON.parse(File.open(
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
).read),
skip_jobs: true)
@template = CustomWizard::Template.find('super_mega_fun_wizard')
end
it 'should return the wizard attributes' do
json = CustomWizard::WizardSerializer.new(
CustomWizard::Builder.new(@template[:id], user).build,
scope: Guardian.new(user)
).as_json
expect(json[:wizard][:id]).to eq("super_mega_fun_wizard")
expect(json[:wizard][:name]).to eq("Super Mega Fun Wizard")
expect(json[:wizard][:background]).to eq("#333333")
expect(json[:wizard][:required]).to eq(false)
end
it 'should return the wizard steps' do
json = CustomWizard::WizardSerializer.new(
CustomWizard::Builder.new(@template[:id], user).build,
scope: Guardian.new(user)
).as_json
expect(json[:wizard][:steps].length).to eq(3)
end
it "should return the wizard user attributes" do
json = CustomWizard::WizardSerializer.new(
CustomWizard::Builder.new(@template[:id], user).build,
scope: Guardian.new(user)
).as_json
expect(
json[:wizard][:user]
).to eq(BasicUserSerializer.new(user, root: false).as_json)
end
it "should not return categories if there are no category fields" do
@template[:steps][2][:fields].delete_at(2)
CustomWizard::Template.save(@template)
json = CustomWizard::WizardSerializer.new(
CustomWizard::Builder.new(@template[:id], user).build,
scope: Guardian.new(user)
).as_json
expect(json[:wizard][:categories].present?).to eq(false)
expect(json[:wizard][:uncategorized_category_id].present?).to eq(false)
end
it "should return categories if there is a category selector field" do
json = CustomWizard::WizardSerializer.new(
CustomWizard::Builder.new(@template[:id], user).build,
scope: Guardian.new(user)
).as_json
expect(json[:wizard][:categories].present?).to eq(true)
expect(json[:wizard][:uncategorized_category_id].present?).to eq(true)
end
it 'should return groups if there is a group selector field' do
json = CustomWizard::WizardSerializer.new(
CustomWizard::Builder.new(@template[:id], user).build,
scope: Guardian.new(user)
).as_json
expect(json[:wizard][:groups].length).to eq(8)
end
it 'should not return groups if there is not a group selector field' do
@template[:steps][2][:fields].delete_at(3)
CustomWizard::Template.save(@template)
json = CustomWizard::WizardSerializer.new(
CustomWizard::Builder.new(@template[:id], user).build,
scope: Guardian.new(user)
).as_json
expect(json[:wizard][:groups].present?).to eq(false)
end
end

Datei anzeigen

@ -0,0 +1,59 @@
# frozen_string_literal: true
require 'rails_helper'
describe CustomWizard::StepSerializer do
fab!(:user) { Fabricate(:user) }
let(:required_data_json) {
JSON.parse(File.open(
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/step/required_data.json"
).read)
}
before do
CustomWizard::Template.save(
JSON.parse(File.open(
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
).read),
skip_jobs: true)
@wizard = CustomWizard::Builder.new("super_mega_fun_wizard", user).build
end
it 'should return basic step attributes' do
json_array = ActiveModel::ArraySerializer.new(
@wizard.steps,
each_serializer: CustomWizard::StepSerializer,
scope: Guardian.new(user)
).as_json
expect(json_array[0][:wizard_step][:title]).to eq("Text")
expect(json_array[0][:wizard_step][:description]).to eq("Text inputs!")
end
it 'should return fields' do
json_array = ActiveModel::ArraySerializer.new(
@wizard.steps,
each_serializer: CustomWizard::StepSerializer,
scope: Guardian.new(user)
).as_json
expect(json_array[0][:wizard_step][:fields].length).to eq(4)
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 'should return permitted attributes' do
json_array = ActiveModel::ArraySerializer.new(
@wizard.steps,
each_serializer: CustomWizard::StepSerializer,
scope: Guardian.new(user)
).as_json
expect(json_array[0][:wizard_step][:permitted]).to eq(false)
expect(json_array[0][:wizard_step][:permitted_message]).to eq("Missing required data")
end
end
end

Datei anzeigen

@ -1,21 +0,0 @@
# frozen_string_literal: true
require 'rails_helper'
describe CustomWizard::Api do
context 'authorization' do
it 'authorizes with an oauth2 api' do
end
it 'refreshes the api access token' do
end
end
context 'endpoint' do
it 'requests an api endpoint' do
end
end
end

Datei anzeigen

@ -1,13 +0,0 @@
describe CustomWizard::Mapper do
fab!(:user) { Fabricate(:user, name: 'Angus', username: 'angus', email: "angus@email.com") }
it 'interpolates user data' do
expect(
CustomWizard::Mapper.fill_placeholders(
"My name is u{name}",
user,
{}
)
).to eq('My name is Angus')
end
end

Datei anzeigen

@ -1,5 +0,0 @@
require 'rails_helper'
describe CustomWizard::AdminController do
end

Datei anzeigen

@ -1,5 +0,0 @@
require 'rails_helper'
describe ApplicationController do
end

Datei anzeigen

@ -1,31 +0,0 @@
require 'rails_helper'
describe CustomWizard::WizardController do
it 'returns a wizard if enabled' do
end
it 'returns a disabled message if disabled' do
end
it 'returns a missing message if no wizard exists' do
end
it 'returns a custom wizard theme' do
end
it 'updates the page title' do
end
it 'skips a wizard if user is allowed to skip' do
end
it 'returns a no skip message if user is not allowed to skip' do
end
end

Datei anzeigen

@ -1,45 +0,0 @@
# frozen_string_literal: true
require 'rails_helper'
describe CustomWizard::WizardSerializer do
fab!(:user) { Fabricate(:user) }
fab!(:category) { Fabricate(:category) }
before do
template = JSON.parse(File.open(
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
).read)
CustomWizard::Template.add(template)
@wizard = CustomWizard::Wizard.create('super_mega_fun_wizard', user)
end
it 'should return the wizard attributes' do
built_wizard = CustomWizard::Builder.new(@wizard.id, user).build
json = CustomWizard::WizardSerializer.new(built_wizard, scope: Guardian.new(user)).as_json
expect(json[:custom_wizard][:id]).to eq("super_mega_fun_wizard")
expect(json[:custom_wizard][:name]).to eq("Super Mega Fun Wizard")
expect(json[:custom_wizard][:background]).to eq("#333333")
expect(json[:custom_wizard][:required]).to eq(false)
end
it "should return the wizard user attributes" do
built_wizard = CustomWizard::Builder.new(@wizard.id, user).build
json = CustomWizard::WizardSerializer.new(built_wizard, scope: Guardian.new(user)).as_json
expect(json[:custom_wizard][:user]).to eq(BasicUserSerializer.new(user, root: false).as_json)
end
it "should not return category attributes if there are no category fields" do
built_wizard = CustomWizard::Builder.new(@wizard.id, user).build
json = CustomWizard::WizardSerializer.new(built_wizard, scope: Guardian.new(user)).as_json
expect(json[:custom_wizard][:categories].present?).to eq(false)
expect(json[:custom_wizard][:uncategorized_category_id].present?).to eq(false)
end
it "should return category attributes if there is a category selector field" do
built_wizard = CustomWizard::Builder.new(@wizard.id, user).build
json = CustomWizard::WizardSerializer.new(built_wizard, scope: Guardian.new(user)).as_json
expect(json[:custom_wizard][:categories].present?).to eq(true)
expect(json[:custom_wizard][:uncategorized_category_id].present?).to eq(true)
end
end