1
0
Fork 0

Merge pull request #58 from paviliondev/tests_improvements

Rspec Suite
Dieser Commit ist enthalten in:
Angus McLeod 2020-11-03 12:10:24 +11:00 committet von GitHub
Commit 017ac37743
Es konnte kein GPG-Schlüssel zu dieser Signatur gefunden werden
GPG-Schlüssel-ID: 4AEE18F83AFDEB23
60 geänderte Dateien mit 3245 neuen und 805 gelöschten Zeilen

Datei anzeigen

@ -1,12 +1,10 @@
# We want to use the KVM-based system, so require sudo
sudo: required
services: services:
- docker - docker
before_install: before_install:
- git clone --depth=1 https://github.com/discourse/discourse-plugin-ci - git clone --depth=1 https://github.com/discourse/discourse-plugin-ci
install: true # Prevent travis doing bundle install install: true
script: script:
- discourse-plugin-ci/script.sh - discourse-plugin-ci/script.sh

Datei anzeigen

@ -113,6 +113,7 @@
wizardFieldSelection=true wizardFieldSelection=true
userFieldSelection='key,value' userFieldSelection='key,value'
categorySelection='output' categorySelection='output'
wizardActionSelection='output'
outputDefaultSelection='category' outputDefaultSelection='category'
context='action' context='action'
)}} )}}
@ -198,6 +199,7 @@
textSelection='value' textSelection='value'
userFieldSelection='key' userFieldSelection='key'
wizardFieldSelection='value' wizardFieldSelection='value'
wizardActionSelection='value'
keyDefaultSelection='userField' keyDefaultSelection='userField'
context='action' context='action'
)}} )}}
@ -270,6 +272,7 @@
textSelection='value,output' textSelection='value,output'
wizardFieldSelection='key,value,assignment' wizardFieldSelection='key,value,assignment'
userFieldSelection='key,value,assignment' userFieldSelection='key,value,assignment'
wizardActionSelection=true
groupSelection='value,output' groupSelection='value,output'
outputDefaultSelection='group' outputDefaultSelection='group'
context='action' context='action'

Datei anzeigen

@ -27,6 +27,11 @@ en:
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."
wizard_redirect_exclude_paths: "Routes excluded from wizard redirects." wizard_redirect_exclude_paths: "Routes excluded from wizard redirects."

Datei anzeigen

@ -36,8 +36,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,6 +9,7 @@ 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
def custom_field_list def custom_field_list

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')
.map do |row|
value = ::JSON.parse(row.value)
submissions = [*rows].map do |row| if user = User.find_by(id: row.key)
value = ::JSON.parse(row.value) username = user.username
else
username = I18n.t('admin.wizard.submissions.no_user', id: row.key)
end
if user = User.find_by(id: row.key) value.map do |v|
username = user.username { username: username }.merge!(v.except("redirect_to"))
else end
username = I18n.t('admin.wizard.submissions.no_user', id: row.key) end.flatten
end
value.map do |submission|
{
username: username
}.merge!(submission.except("redirect_to"))
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'
@ -33,39 +30,36 @@ class CustomWizard::TransferController < ::ApplicationController
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
end
countValid += 1 if template.errors.any?
pluginStoreEntry = PluginStore.new 'custom_wizard' failed_ids.push t_json['id']
saved = pluginStoreEntry.set(o['id'], o) unless pluginStoreEntry.get(o['id']) else
success_ids.push o['id'] if !!saved success_ids.push t_json['id']
failed_ids.push o['id'] if !saved end
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::Wizard.list, CustomWizard::Wizard.list(current_user),
each_serializer: CustomWizard::BasicWizardSerializer each_serializer: CustomWizard::BasicWizardSerializer
), ),
field_types: CustomWizard::Field.types, field_types: CustomWizard::Field.types,
@ -15,7 +15,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 }
@ -23,25 +23,21 @@ 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
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::Wizard.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

@ -1,25 +1,38 @@
class CustomWizard::StepsController < ::ApplicationController class CustomWizard::StepsController < ::ApplicationController
before_action :ensure_logged_in before_action :ensure_logged_in
before_action :ensure_can_update
def update def update
params.require(:step_id) params.require(:step_id)
params.require(:wizard_id) params.require(:wizard_id)
field_ids = CustomWizard::Wizard.field_ids(params[:wizard_id], params[:step_id])
permitted = params.permit(:wizard_id, :step_id) wizard = @builder.build
if params[:fields] step = wizard.steps.select { |s| s.id == update_params[:step_id] }.first
permitted[:fields] = params[:fields].select { |k, v| field_ids.include? k }
permitted.permit! if !step || step.fields.blank?
raise Discourse::InvalidParameters.new(:step_id)
end end
wizard = CustomWizard::Builder.new(permitted[:wizard_id].underscore, current_user).build field_ids = step.fields.map(&:id)
updater = wizard.create_updater(permitted[:step_id], permitted[:fields])
update = update_params.to_h
if params[:fields]
update[:fields] = {}
params[:fields].each do |k, v|
update[:fields][k] = v if field_ids.include? k
end
end
updater = wizard.create_updater(update[:step_id], update[: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 = []
@ -29,4 +42,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

@ -2,6 +2,7 @@ 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
@ -24,7 +25,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)
@ -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

@ -83,6 +83,11 @@ class CustomWizard::Action
multiple: true multiple: true
).perform ).perform
if targets.blank?
log_error("no recipients", "send_message has no recipients")
return
end
targets.each do |target| targets.each do |target|
if Group.find_by(name: target) if Group.find_by(name: target)
params[:target_group_names] = target params[:target_group_names] = target
@ -130,7 +135,13 @@ class CustomWizard::Action
profile_updates.first[:pairs].each do |pair| profile_updates.first[:pairs].each do |pair|
if allowed_profile_field?(pair['key']) if allowed_profile_field?(pair['key'])
key = cast_profile_key(pair['key']) key = cast_profile_key(pair['key'])
value = cast_profile_value(mapper.map_field(pair['value'], pair['value_type']), pair['key']) value = cast_profile_value(
mapper.map_field(
pair['value'],
pair['value_type']
),
pair['key']
)
if user_field?(pair['key']) if user_field?(pair['key'])
params[:custom_fields] ||= {} params[:custom_fields] ||= {}
@ -262,7 +273,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
@ -272,7 +283,7 @@ class CustomWizard::Action
end end
route_to = Discourse.base_uri + URI.encode(url) route_to = Discourse.base_uri + URI.encode(url)
data['redirect_on_complete'] = route_to data['route_to'] = route_to
log_info("route: #{route_to}") log_info("route: #{route_to}")
else else
@ -290,7 +301,14 @@ class CustomWizard::Action
} }
).perform ).perform
groups = group_map.flatten.reduce([]) do |groups, g| group_map = group_map.flatten.compact
unless group_map.present?
log_error("invalid group map")
return
end
groups = group_map.reduce([]) do |groups, g|
begin begin
groups.push(Integer(g)) groups.push(Integer(g))
rescue ArgumentError rescue ArgumentError
@ -345,17 +363,33 @@ class CustomWizard::Action
def create_group def create_group
group = group =
begin begin
Group.new(new_group_params) Group.new(new_group_params.except(:usernames, :owner_usernames))
rescue ArgumentError => e rescue ArgumentError => e
raise Discourse::InvalidParameters, "Invalid group params" raise Discourse::InvalidParameters, "Invalid group params"
end end
if group.save if group.save
GroupActionLogger.new(user, group).log_change_group_settings def get_user_ids(username_string)
User.where(username: username_string.split(",")).pluck(:id)
end
if new_group_params[:owner_usernames].present?
owner_ids = get_user_ids(new_group_params[:owner_usernames])
owner_ids.each { |user_id| group.group_users.build(user_id: user_id, owner: true) }
end
if new_group_params[:usernames].present?
user_ids = get_user_ids(new_group_params[:usernames])
user_ids -= owner_ids if owner_ids
user_ids.each { |user_id| group.group_users.build(user_id: user_id) }
end
GroupActionLogger.new(user, group, skip_guardian: true).log_change_group_settings
log_success("Group created", group.name) log_success("Group created", group.name)
result.output = group.name result.output = group.name
else else
log_error("Group creation failed") log_error("Group creation failed", group.errors.messages)
end end
end end
@ -372,7 +406,7 @@ class CustomWizard::Action
log_success("Category created", category.name) log_success("Category created", category.name)
result.output = category.id result.output = category.id
else else
log_error("Category creation failed") log_error("Category creation failed", category.errors.messages)
end end
end end
@ -385,6 +419,8 @@ class CustomWizard::Action
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
elsif output.is_a?(Integer) elsif output.is_a?(Integer)
@ -401,6 +437,8 @@ class CustomWizard::Action
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
else output.is_a?(String) else output.is_a?(String)
@ -472,11 +510,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
@ -528,10 +566,12 @@ class CustomWizard::Action
user: user user: user
).perform ).perform
value = value.parameterize(separator: '_') if attr === "name" if value
value = value.to_i if attr.include?("_level") value = value.parameterize(separator: '_') if attr === "name"
value = value.to_i if attr.include?("_level")
params[attr.to_sym] = value params[attr.to_sym] = value
end
end end
end end
@ -556,30 +596,36 @@ class CustomWizard::Action
user: user user: user
).perform ).perform
if attr === "parent_category_id" && value.is_a?(Array) if value
value = value[0] if attr === "parent_category_id" && value.is_a?(Array)
end value = value[0]
if attr === "permissions" && value.is_a?(Array)
permissions = value
value = {}
permissions.each do |p|
k = p[:key]
v = p[:value].to_i
if k.is_a?(Array)
group = Group.find_by(id: k[0])
k = group.name
else
k = k.parameterize(separator: '_')
end
value[k] = v
end end
end
params[attr.to_sym] = value if attr === "permissions" && value.is_a?(Array)
permissions = value
value = {}
permissions.each do |p|
k = p[:key]
v = p[:value].to_i
if k.is_a?(Array)
group = Group.find_by(id: k[0])
k = group.name
else
k = k.parameterize(separator: '_')
end
value[k] = v
end
end
if attr === 'slug'
value = value.parameterize(separator: '-')
end
params[attr.to_sym] = value
end
end end
end end
@ -607,6 +653,8 @@ class CustomWizard::Action
end end
def cast_profile_value(value, key) def cast_profile_value(value, key)
return value if value.nil?
if profile_url_fields.include?(key) if profile_url_fields.include?(key)
value['url'] value['url']
elsif key === 'avatar' elsif key === 'avatar'

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,26 @@ 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?
reset_submissions if build_opts[:reset] build_opts[:reset] = build_opts[:reset] || @wizard.restart_on_revisit
@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 +77,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
@ -146,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
@ -170,28 +131,30 @@ class CustomWizard::Builder
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
@ -211,15 +174,13 @@ 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 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
params[:value] = prefill_field(field_template, step_template) || params[:value]
if field_template['type'] === 'group' && params[:value].present? if field_template['type'] === 'group' && params[:value].present?
params[:value] = params[:value].first params[:value] = params[:value].first
end end
@ -395,13 +356,47 @@ 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 def save_permitted_params(permitted_params, params)
@submissions.pop(1) if @wizard.unfinished? permitted_data = {}
PluginStore.set("#{@wizard.id}_submissions", @wizard.user.id, @submissions)
@wizard.reset 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 end

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

115
lib/custom_wizard/template.rb Normale Datei
Datei anzeigen

@ -0,0 +1,115 @@
class CustomWizard::Template
include HasErrors
attr_reader :data,
:opts
def initialize(data)
@data = data
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)
PluginStore.get('custom_wizard', wizard_id)
end
def self.remove(wizard_id)
wizard = CustomWizard::Wizard.create(wizard_id)
ActiveRecord::Base.transaction do
PluginStore.remove('custom_wizard', wizard.id)
if wizard.after_time
Jobs.cancel_scheduled_job(:set_after_time_wizard)
Jobs.enqueue(:clear_after_time_wizard, wizard_id: wizard_id)
end
end
end
def self.exists?(wizard_id)
PluginStoreRow.exists?(plugin_name: 'custom_wizard', key: wizard_id)
end
def self.list(setting: nil, 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|
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
private
def normalize_data
@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

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 data[:steps].each do |step|
check_required(step, :step)
if !@error && @params[:after_time] if data[:fields].present?
validate_after_time data[:fields].each do |field|
end check_required(field, :field)
if !@error
params[:steps].each do |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?
params[:actions].each do |action|
check_required(action, :action)
check_depdendent(action, :action)
break if @error.present?
end end
end end
end end
if @error if data[:actions].present?
{ error: @error } data[:actions].each do |action|
check_required(action, :action)
end
end
if errors.any?
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
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 end
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 = { 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

@ -24,23 +24,26 @@ class CustomWizard::Wizard
:needs_categories, :needs_categories,
:needs_groups, :needs_groups,
:steps, :steps,
: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,9 +56,17 @@ class CustomWizard::Wizard
@first_step = nil @first_step = nil
@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)
end end
@ -65,12 +76,10 @@ class CustomWizard::Wizard
yield step if block_given? yield step if block_given?
last_step = @steps.last last_step = steps.last
steps << step
@steps << step if steps.size == 1
# If it's the first step
if @steps.size == 1
@first_step = step @first_step = step
step.index = 0 step.index = 0
elsif last_step.present? elsif last_step.present?
@ -81,67 +90,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, 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?
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?
@ -171,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
@ -178,173 +183,89 @@ 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 current_submission
PluginStoreRow.where(" if submissions.present? && !submissions.last.key?("submitted_at")
plugin_name = 'custom_wizard' AND submissions.last
(value::json ->> '#{filter}')::boolean IS TRUE
")
end
def self.after_signup(user)
if (records = filter_records('after_signup')).any?
result = false
records
.sort_by { |record| record.value['permitted'].present? ? 0 : 1 }
.each do |record|
wizard = self.new(JSON.parse(record.value), user)
if wizard.permitted?
result = wizard
break
end
end
result
else else
false nil
end end
end end
def self.prompt_completion(user) def set_submissions(submissions)
if (records = filter_records('prompt_completion')).any? PluginStore.set("#{id}_submissions", user.id, Array.wrap(submissions))
records.reduce([]) do |result, record|
wizard = CustomWizard::Wizard.new(::JSON.parse(record.value), user)
if wizard.permitted? && !wizard.completed?
result.push(id: wizard.id, name: wizard.name)
end
result
end
else
false
end
end end
def self.restart_on_revisit def self.submissions(wizard_id, user)
if (records = filter_records('restart_on_revisit')).any? new({ id: wizard_id }, user).submissions
records.first.key
else
false
end
end end
def self.steps(wizard_id) def self.set_submissions(wizard_id, user, submissions)
wizard = PluginStore.get('custom_wizard', wizard_id) new({ id: wizard_id }, user).set_submissions(submissions)
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 end
def self.create(wizard_id, user = nil) def self.create(wizard_id, user = nil)
if wizard = self.find(wizard_id) if template = CustomWizard::Template.find(wizard_id)
self.new(wizard.to_h, user) new(template.to_h, user)
else
false
end
end
def self.list(user, template_opts: {})
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
def self.after_signup(user)
wizards = list(
user,
template_opts: {
setting: 'after_signup',
order: "(value::json ->> 'permitted') IS NOT NULL DESC"
}
)
wizards.any? ? wizards.first : false
end
def self.prompt_completion(user)
wizards = list(
user,
template_opts: {
setting: 'prompt_completion',
order: "(value::json ->> 'permitted') IS NOT NULL DESC"
}
)
if wizards.any?
wizards.map do |w|
{
id: w.id,
name: w.name
}
end
else else
false false
end end
@ -364,12 +285,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

@ -42,10 +42,10 @@ 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/admin/custom_fields.rb ../controllers/custom_wizard/admin/custom_fields.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
@ -57,6 +57,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
@ -130,8 +131,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
@ -161,7 +161,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
CustomWizard::CustomField::CLASSES.each do |klass| CustomWizard::CustomField::CLASSES.each do |klass|
add_model_callback(klass.to_sym, :after_initialize) do add_model_callback(klass.to_sym, :after_initialize) do

Datei anzeigen

@ -1,92 +1,162 @@
require 'rails_helper'
describe CustomWizard::Action do describe CustomWizard::Action do
let(:create_topic_action) {{"id":"create_topic","type":"create_topic","title":"text","post":"textarea"}} fab!(:user) { Fabricate(:user, name: "Angus", username: 'angus', email: "angus@email.com", trust_level: TrustLevel[2]) }
let(:send_message_action) {{"id":"send_message","type":"send_message","title":"text","post":"textarea","username":"angus"}} fab!(:category) { Fabricate(:category, name: 'cat1', slug: 'cat-slug') }
let(:route_to_action) {{"id":"route_to","type":"route_to","url":"https://google.com"}} fab!(:group) { Fabricate(:group) }
let(:open_composer_action) {{"id":"open_composer","type":"open_composer","title":"text","post":"textarea"}}
let(:add_to_group_action) {{"id":"add_to_group","type":"add_to_group","group_id":"dropdown_groups"}}
it 'creates a topic' do before do
template['steps'][0]['fields'] = [text_field, textarea_field] Group.refresh_automatic_group!(:trust_level_2)
template['steps'][0]["actions"] = [create_topic_action] CustomWizard::Template.save(
updater = run_update(template, nil, JSON.parse(File.open(
text: "Topic Title", "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
textarea: "topic body" ).read),
) skip_jobs: true)
topic = Topic.where(title: "Topic Title") @template = CustomWizard::Template.find('super_mega_fun_wizard')
end
expect(topic.exists?).to eq(true) context "creating a topic" do
expect(Post.where(
topic_id: topic.pluck(:id), end
raw: "topic body"
).exists?).to eq(true) context 'creating a topic' do
it "works" do
wizard = CustomWizard::Builder.new(@template[:id], user).build
wizard.create_updater(
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
it "fails silently without basic topic inputs" do
wizard = CustomWizard::Builder.new(@template[:id], user).build
wizard.create_updater(
wizard.steps.first.id,
step_1_field_2: "topic body"
).update
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
fields = [text_field, textarea_field] User.create(username: 'angus1', email: "angus1@email.com")
if extra_field wizard = CustomWizard::Builder.new(@template[:id], user).build
fields.push(extra_field) wizard.create_updater(wizard.steps[0].id, {}).update
end wizard.create_updater(wizard.steps[1].id, {}).update
template['steps'][0]['fields'] = fields
template['steps'][0]["actions"] = [send_message_action.merge(extra_action_opts)]
run_update(template, nil,
text: "Message Title",
textarea: "message body"
)
topic = Topic.where( topic = Topic.where(
archetype: Archetype.private_message, archetype: Archetype.private_message,
title: "Message Title" title: "Message title"
)
post = Post.where(
topic_id: topic.pluck(:id),
raw: "I will interpolate some wizard fields"
) )
expect(topic.exists?).to eq(true) expect(topic.exists?).to eq(true)
expect( expect(topic.first.topic_allowed_users.first.user.username).to eq('angus1')
topic.first.topic_allowed_users.first.user.username expect(post.exists?).to eq(true)
).to eq('angus')
expect(Post.where(
topic_id: topic.pluck(:id),
raw: "message body"
).exists?).to eq(true)
end end
it 'updates a profile' do it 'updates a profile' do
run_update(template, template['steps'][1]['id'], name: "Sally") wizard = CustomWizard::Builder.new(@template[:id], user).build
expect(user.name).to eq('Sally') upload = Upload.create!(
url: '/images/image.png',
original_filename: 'image.png',
filesize: 100,
user_id: -1,
)
steps = wizard.steps
wizard.create_updater(steps[0].id, {}).update
wizard.create_updater(steps[1].id,
step_2_field_7: upload.as_json
).update
expect(user.profile_background_upload.id).to eq(upload.id)
end end
it 'opens a composer' do it 'opens a composer' do
template['steps'][0]['fields'] = [text_field, textarea_field] wizard = CustomWizard::Builder.new(@template[:id], user).build
template['steps'][0]["actions"] = [open_composer_action] wizard.create_updater(wizard.steps[0].id, step_1_field_1: "Text input").update
updater = run_update(template, nil, updater = wizard.create_updater(wizard.steps[1].id, {})
text: "Topic Title", updater.update
textarea: "topic body"
category = Category.find_by(id: wizard.current_submission['action_8'])
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"
) )
end
expect(updater.result.blank?).to eq(true) it 'creates a category' do
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[1].id, {}).update
expect(Category.where(id: wizard.current_submission['action_8']).exists?).to eq(true)
end
updater = run_update(template, template['steps'][1]['id']) it 'creates a group' do
wizard = CustomWizard::Builder.new(@template[:id], user).build
expect(updater.result[:redirect_on_complete]).to eq( step_id = wizard.steps[0].id
"/new-topic?title=Topic%20Title&body=topic%20body" updater = wizard.create_updater(step_id, step_1_field_1: "Text input").update
) expect(Group.where(name: wizard.current_submission['action_9']).exists?).to eq(true)
end end
it 'adds a user to a group' do it 'adds a user to a group' do
template['steps'][0]['fields'] = [dropdown_groups_field] wizard = CustomWizard::Builder.new(@template[:id], user).build
template['steps'][0]["actions"] = [add_to_group_action] step_id = wizard.steps[0].id
updater = wizard.create_updater(step_id, step_1_field_1: "Text input").update
updater = run_update(template, nil, dropdown_groups: group.id) group = Group.find_by(name: wizard.current_submission['action_9'])
expect(group.users.first.username).to eq('angus') expect(group.users.first.username).to eq('angus')
end end
it 'watches categories' do
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[1].id, {}).update
expect(CategoryUser.where(
category_id: wizard.current_submission['action_8'],
user_id: user.id
).first.notification_level).to eq(2)
expect(CategoryUser.where(
category_id: category.id,
user_id: user.id
).first.notification_level).to eq(0)
end
it 're-routes a user' do it 're-routes a user' do
template['steps'][0]["actions"] = [route_to_action] wizard = CustomWizard::Builder.new(@template[:id], user).build
updater = run_update(template, nil, {}) updater = wizard.create_updater(wizard.steps.last.id, {})
expect(updater.result[:redirect_on_next]).to eq( updater.update
"https://google.com" expect(updater.result[:redirect_on_complete]).to eq("https://google.com")
)
end 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

@ -3,39 +3,45 @@
require 'rails_helper' require 'rails_helper'
describe CustomWizard::Builder do describe CustomWizard::Builder do
fab!(:user) { Fabricate(:user, username: 'angus') } fab!(:trusted_user) {
fab!(:trusted_user) { Fabricate(:user, trust_level: 3) } Fabricate(
:user,
username: 'angus',
email: "angus@email.com",
trust_level: TrustLevel[3]
)
}
fab!(:user) { Fabricate(:user) }
fab!(:category1) { Fabricate(:category, name: 'cat1') } fab!(:category1) { Fabricate(:category, name: 'cat1') }
fab!(:category2) { Fabricate(:category, name: 'cat2') } fab!(:category2) { Fabricate(:category, name: 'cat2') }
fab!(:group) { Fabricate(:group) } fab!(:group) { Fabricate(:group) }
let!(:template) do let(:required_data_json) {
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/step/required_data.json"
).read) ).read)
end }
def build_wizard(t = template, u = user, build_opts = {}, params = {}) let(:permitted_json) {
CustomWizard::Wizard.add_wizard(t) JSON.parse(File.open(
CustomWizard::Builder.new('welcome', u).build(build_opts, params) "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard/permitted.json"
end ).read)
}
def add_submission_data(data = {}) let(:permitted_param_json) {
PluginStore.set("welcome_submissions", user.id, { JSON.parse(File.open(
name: 'Angus', "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/step/permitted_params.json"
website: 'https://thepavilion.io' ).read)
}.merge(data)) }
end
def get_submission_data before do
PluginStore.get("welcome_submissions", user.id) Group.refresh_automatic_group!(:trust_level_3)
end CustomWizard::Template.save(
JSON.parse(File.open(
def run_update(t = template, step_id = nil, data = {}) "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/wizard.json"
wizard = build_wizard(t) ).read),
updater = wizard.create_updater(step_id || t['steps'][0]['id'], data) skip_jobs: true)
updater.update @template = CustomWizard::Template.find('super_mega_fun_wizard')
updater
end end
context 'disabled' do context 'disabled' do
@ -43,15 +49,10 @@ describe CustomWizard::Builder do
SiteSetting.custom_wizard_enabled = false SiteSetting.custom_wizard_enabled = false
end end
it "returns no steps" do it "returns nil" do
wizard = build_wizard expect(
expect(wizard.steps.length).to eq(0) CustomWizard::Builder.new(@template[:id], user).build
expect(wizard.name).to eq('Welcome') ).to eq(nil)
end
it "doesn't save submissions" do
run_update(template, nil, name: 'Angus')
expect(get_submission_data.blank?).to eq(true)
end end
end end
@ -60,123 +61,287 @@ 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(build_wizard.steps.length).to eq(2) expect(
CustomWizard::Builder.new(@template[:id], user).build
.steps.length
).to eq(3)
end end
it 'returns no steps if multiple submissions are disabled and user has completed' do context "with multiple submissions disabled" do
history_params = { before do
action: UserHistory.actions[:custom_wizard_step], @template[:multiple_submissions] = false
acting_user_id: user.id, CustomWizard::Template.save(@template.as_json)
context: template['id'] end
}
UserHistory.create!(history_params.merge(subject: template['steps'][0]['id']))
UserHistory.create!(history_params.merge(subject: template['steps'][1]['id']))
template["multiple_submissions"] = false it 'returns steps if user has not completed it' do
expect(build_wizard(template).steps.length).to eq(0) expect(
CustomWizard::Builder.new(@template[:id], user).build
.steps.length
).to eq(3)
end
it 'returns no steps if user has completed it' do
@template[:steps].each do |step|
UserHistory.create!(
{
action: UserHistory.actions[:custom_wizard_step],
acting_user_id: user.id,
context: @template[:id]
}.merge(
subject: step[:id]
)
)
end
expect(
CustomWizard::Builder.new(@template[:id], user).build
.steps.length
).to eq(0)
end
end end
it 'returns no steps if user is not permitted' do context "with restricted permissions" do
template["min_trust"] = 3 before do
expect(build_wizard(template).steps.length).to eq(0) @template[:permitted] = permitted_json["permitted"]
CustomWizard::Template.save(@template.as_json)
end
it 'is not permitted if user is not in permitted group' do
expect(
CustomWizard::Builder.new(@template[:id], user).build
.permitted?
).to eq(false)
end
it 'user cannot access if not permitted' do
expect(
CustomWizard::Builder.new(@template[:id], user).build
.can_access?
).to eq(false)
end
it 'returns wizard metadata if user is not permitted' do
expect(
CustomWizard::Builder.new(@template[:id], user).build
.name
).to eq("Super Mega Fun Wizard")
end
it 'returns no steps if user is not permitted' do
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)
end
it 'user can access if permitted' do
expect(
CustomWizard::Builder.new(@template[:id], trusted_user).build
.can_access?
).to eq(true)
end
it 'returns steps if user is permitted' do
expect(
CustomWizard::Builder.new(@template[:id], trusted_user).build
.steps.length
).to eq(3)
end
end end
it 'returns steps if user is permitted' do it 'returns prefilled data' do
template["min_trust"] = 3 expect(
expect(build_wizard(template, trusted_user).steps.length).to eq(2) CustomWizard::Builder.new(@template[:id], user).build
.steps.first
.fields.first
.value
).to eq('I am prefilled')
end end
it 'returns a wizard with prefilled data if user has partially completed it' do context "user has partially completed" do
add_submission_data before do
wizard = build_wizard wizard = CustomWizard::Wizard.new(@template, user)
expect(wizard.steps[0].fields.first.value).to eq('Angus') wizard.set_submissions(step_1_field_1: 'I am a user submission')
expect(wizard.steps[1].fields.first.value).to eq('https://thepavilion.io') 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 end
it 'returns a wizard with no prefilled data if options include reset' do context 'building step' do
add_submission_data
wizard = build_wizard(template, user, reset: true)
expect(wizard.steps[0].fields.first.value).to eq(nil)
expect(wizard.steps[1].fields.first.value).to eq(nil)
end
context 'building steps' do
it 'returns step metadata' do it 'returns step metadata' do
expect(build_wizard.steps[0].title).to eq('Welcome to Pavilion') first_step = CustomWizard::Builder.new(@template[:id], user)
expect(build_wizard.steps[1].title).to eq('Tell us about you') .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 end
it 'saves permitted params' do context 'with required data' do
template['steps'][0]['permitted_params'] = permitted_params before do
wizard = build_wizard(template, user, {}, param_key: 'param_value') @template[:steps][0][:required_data] = required_data_json['required_data']
submissions = get_submission_data @template[:steps][0][:required_data_message] = required_data_json['required_data_message']
expect(submissions.first['submission_param_key']).to eq('param_value') 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
CustomWizard::Wizard.set_submissions('super_mega_fun_wizard', user,
required_data: "required_value"
)
expect(
CustomWizard::Builder.new(@template[:id], user).build
.steps.first
.permitted
).to eq(true)
end
end end
it 'is not permitted if required data is not present' do context "with permitted params" do
template['steps'][0]['required_data'] = required_data before do
expect(build_wizard(template, user).steps[0].permitted).to eq(false) @template[:steps][0][:permitted_params] = permitted_param_json['permitted_params']
end CustomWizard::Template.save(@template.as_json)
end
it 'it shows required data message if required data has message' do it 'saves permitted params' do
template['steps'][0]['required_data'] = required_data wizard = CustomWizard::Builder.new(@template[:id], user).build({},
template['steps'][0]['required_data_message'] = required_data_message param: 'param_value'
add_submission_data(nickname: "John") )
wizard = build_wizard(template, user) expect(wizard.current_submission['saved_param']).to eq('param_value')
expect(wizard.steps[0].permitted).to eq(false) end
expect(wizard.steps[0].permitted_message).to eq(required_data_message)
end
it 'is permitted if required data is present' do
template['steps'][0]['required_data'] = required_data
PluginStore.set('welcome_submissions', user.id, nickname: "Angus", name: "Angus")
expect(build_wizard(template, user).steps[0].permitted).to eq(true)
end end
end
context 'building field' do
it 'returns field metadata' do it 'returns field metadata' do
expect(build_wizard(template, user).steps[0].fields[0].label).to eq("<p>Name</p>") wizard = CustomWizard::Builder.new(@template[:id], user).build
expect(build_wizard(template, user).steps[0].fields[0].type).to eq("text") 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 end
it 'returns fields' do it 'returns all step fields' do
template['steps'][0]['fields'][1] = checkbox_field expect(
expect(build_wizard(template, user).steps[0].fields.length).to eq(2) 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
run_update(template, nil, name: 'Angus') perform_update('step_1', step_1_field_1: 'Text input')
expect(get_submission_data.first['name']).to eq('Angus') expect(
CustomWizard::Wizard.submissions(@template[:id], user)
.first['step_1_field_1']
).to eq('Text input')
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(
CustomWizard::Wizard.submissions(@template[:id], user).first
).to eq(nil)
end
end end
context 'validation' do context 'validation' do
it 'applies min length' do it 'applies min length' do
template['steps'][0]['fields'][0]['min_length'] = 10 expect(
updater = run_update(template, nil, name: 'short') perform_update('step_1', step_1_field_1: 'Te')
expect(updater.errors.messages[:name].first).to eq( .errors.messages[:step_1_field_1].first
I18n.t('wizard.field.too_short', label: 'Name', min: 10) ).to eq(I18n.t('wizard.field.too_short', label: 'Text', min: 3))
)
end end
it 'standardises boolean entries' do it 'standardises boolean entries' do
template['steps'][0]['fields'][0] = checkbox_field perform_update('step_2', step_2_field_5: 'false')
run_update(template, nil, checkbox: 'false') expect(
expect(get_submission_data.first['checkbox']).to eq(false) CustomWizard::Wizard.submissions(@template[:id], user)
.first['step_2_field_5']
).to eq(false)
end end
it 'requires required fields' do it 'requires required fields' do
template['steps'][0]['fields'][0]['required'] = true @template[:steps][0][:fields][1][:required] = true
expect(run_update(template).errors.messages[:name].first).to eq( CustomWizard::Template.save(@template.as_json)
I18n.t('wizard.field.required', label: 'Name')
)
end
end
it 'runs actions attached to a step' do expect(
run_update(template, template['steps'][1]['id'], name: "Gus") perform_update('step_1', step_1_field_2: nil)
expect(user.name).to eq('Gus') .errors.messages[:step_1_field_2].first
).to eq(I18n.t('wizard.field.required', label: 'Textarea'))
end
end end
end end
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

@ -1,14 +1,250 @@
require 'rails_helper'
describe CustomWizard::Mapper do describe CustomWizard::Mapper do
fab!(:user1) {
it 'interpolates user data' do Fabricate(:user,
user.name = "Angus" name: "Angus",
user.save! username: "angus",
email: "angus@email.com",
expect( trust_level: TrustLevel[3]
CustomWizard::Builder.fill_placeholders(
"My name is u{name}",
user,
{}
) )
).to eq('My name is Angus') }
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 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"
}
]
}
]
}

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"
}
]
}
]
}

18
spec/fixtures/step/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"
}
]
}
]
}

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

@ -1,49 +1,516 @@
{ {
"id": "welcome", "id": "super_mega_fun_wizard",
"name": "Welcome", "name": "Super Mega Fun Wizard",
"background": "#006da3", "background": "#333333",
"save_submissions": true, "save_submissions": true,
"multiple_submissions": true, "multiple_submissions": true,
"after_signup": true, "after_signup": false,
"min_trust": 1, "prompt_completion": true,
"theme_id": 4, "theme_id": 2,
"steps": [ "steps": [
{ {
"id": "welcome", "id": "step_1",
"title": "Welcome to Pavilion", "title": "Text",
"raw_description": "Hey there, thanks for signing up.\n\nWe're Pavilion, an international freelancer cooperative that specialises in online communities.\n\nThis site is our own community, where we work with our clients, users of our open source work and our broader community.\n\n", "raw_description": "Text inputs!",
"description": "<p>Hey there, thanks for signing up.</p>\n<p>Were Pavilion, an international freelancer cooperative that specialises in online communities.</p>\n<p>This site is our own community, where we work with our clients, users of our open source work and our broader community.</p>",
"fields": [ "fields": [
{ {
"id": "name", "id": "step_1_field_1",
"label": "Text",
"description": "Text field description.",
"type": "text", "type": "text",
"label": "Name" "min_length": "3",
"prefill": [
{
"type": "assignment",
"output": "I am prefilled",
"output_type": "text",
"output_connector": "set"
}
]
},
{
"id": "step_1_field_2",
"label": "Textarea",
"type": "textarea",
"min_length": ""
},
{
"id": "step_1_field_3",
"label": "Composer",
"type": "composer"
},
{
"id": "step_1_field_4",
"label": "I'm only text",
"description": "",
"type": "text_only"
}
],
"description": "<p>Text inputs!</p>"
},
{
"id": "step_2",
"title": "Values",
"raw_description": "Because I couldn't think of another name for this step :)",
"fields": [
{
"id": "step_2_field_1",
"label": "Date",
"type": "date",
"format": "YYYY-MM-DD"
},
{
"id": "step_2_field_2",
"label": "Time",
"type": "time",
"format": "HH:mm"
},
{
"id": "step_2_field_3",
"label": "Date & Time",
"type": "date_time",
"format": ""
},
{
"id": "step_2_field_4",
"label": "Number",
"type": "number"
},
{
"id": "step_2_field_5",
"label": "Checkbox",
"type": "checkbox"
},
{
"id": "step_2_field_7",
"label": "Upload",
"type": "upload",
"file_types": ".jpg,.png"
}
],
"description": "<p>Because I couldnt think of another name for this step <img src=\"/images/emoji/twitter/slight_smile.png?v=9\" title=\":slight_smile:\" class=\"emoji\" alt=\":slight_smile:\"></p>"
},
{
"id": "step_3",
"title": "Combo-boxes",
"raw_description": "Unfortunately not the edible type :sushi: ",
"fields": [
{
"id": "step_3_field_1",
"label": "Custom Dropdown",
"type": "dropdown",
"content": [
{
"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"
}
]
}
]
},
{
"id": "step_3_field_2",
"label": "Tag",
"type": "tag"
},
{
"id": "step_3_field_3",
"label": "Category",
"type": "category",
"limit": 1,
"property": "id"
},
{
"id": "step_3_field_4",
"label": "Group",
"type": "group"
},
{
"id": "step_3_field_5",
"label": "User Selector",
"description": "",
"type": "user_selector"
}
],
"description": "<p>Unfortunately not the edible type <img src=\"/images/emoji/twitter/sushi.png?v=9\" title=\":sushi:\" class=\"emoji\" alt=\":sushi:\"></p>"
}
],
"actions": [
{
"id": "action_9",
"run_after": "step_1",
"type": "create_group",
"title": [
{
"type": "assignment",
"output": "New Group Member",
"output_type": "text",
"output_connector": "set"
}
],
"custom_fields": [
{
"type": "association",
"pairs": [
{
"index": 0,
"key": "group_custom_field",
"key_type": "text",
"value": "step_3_field_1",
"value_type": "wizard_field",
"connector": "association"
}
]
}
],
"name": [
{
"type": "assignment",
"output": "step_1_field_1",
"output_type": "wizard_field",
"output_connector": "set"
}
],
"full_name": [
{
"type": "assignment",
"output": "step_1_field_1",
"output_type": "wizard_field",
"output_connector": "set"
}
],
"usernames": [
{
"type": "assignment",
"output_type": "user",
"output_connector": "set",
"output": [
"angus1"
]
}
],
"owner_usernames": [
{
"type": "assignment",
"output_type": "user",
"output_connector": "set",
"output": [
"angus"
]
}
],
"grant_trust_level": [
{
"type": "assignment",
"output": "3",
"output_type": "text",
"output_connector": "set"
}
],
"mentionable_level": [
{
"type": "assignment",
"output": "1",
"output_type": "text",
"output_connector": "set"
}
],
"messageable_level": [
{
"type": "assignment",
"output": "2",
"output_type": "text",
"output_connector": "set"
}
],
"visibility_level": [
{
"type": "assignment",
"output": "3",
"output_type": "text",
"output_connector": "set"
}
],
"members_visibility_level": [
{
"type": "assignment",
"output": "99",
"output_type": "text",
"output_connector": "set"
} }
] ]
}, },
{ {
"id": "about_you", "id": "action_6",
"title": "Tell us about you", "run_after": "step_1",
"raw_description": "We'd like to know a little more about you. Add a your name and your website below. This will update your user profile.", "type": "add_to_group",
"description": "<p>Wed like to know a little more about you. Add a your name and your website below. This will update your user profile.</p>", "group": [
"fields": [
{ {
"id": "website", "type": "assignment",
"label": "Website", "output": "action_9",
"type": "text" "output_type": "wizard_action",
"output_connector": "set"
} }
], ]
"actions": [ },
{
"id": "action_8",
"run_after": "step_1",
"type": "create_category",
"custom_fields": [
{ {
"id": "update_profile", "type": "association",
"type": "update_profile", "pairs": [
"profile_updates": [
{ {
"key": "name", "index": 0,
"value": "name" "key": "category_custom_field",
"key_type": "text",
"value": "CC Val",
"value_type": "text",
"connector": "association"
} }
] ]
} }
],
"name": [
{
"type": "assignment",
"output": "step_1_field_1",
"output_type": "wizard_field",
"output_connector": "set"
}
],
"slug": [
{
"type": "assignment",
"output": "step_1_field_1",
"output_type": "wizard_field",
"output_connector": "set"
}
],
"permissions": [
{
"type": "association",
"pairs": [
{
"index": 0,
"key": "action_9",
"key_type": "wizard_action",
"value": "2",
"value_type": "text",
"connector": "association"
}
]
}
]
},
{
"id": "action_5",
"run_after": "step_1",
"type": "watch_categories",
"notification_level": "tracking",
"wizard_user": true,
"categories": [
{
"type": "assignment",
"output": "action_8",
"output_type": "wizard_action",
"output_connector": "set"
}
],
"mute_remainder": [
{
"type": "assignment",
"output": "true",
"output_type": "text",
"output_connector": "set"
}
]
},
{
"id": "action_1",
"run_after": "step_3",
"type": "create_topic",
"skip_redirect": true,
"post": "step_1_field_2",
"title": [
{
"type": "assignment",
"output": "step_1_field_1",
"output_type": "wizard_field",
"output_connector": "set"
}
],
"category": [
{
"type": "assignment",
"output": "step_3_field_3",
"output_type": "wizard_field",
"output_connector": "set"
}
],
"tags": [
{
"type": "assignment",
"output": "step_3_field_2",
"output_type": "wizard_field",
"output_connector": "set"
}
],
"custom_fields": [
{
"type": "association",
"pairs": [
{
"index": 0,
"key": "custom_field_1",
"key_type": "text",
"value": "title",
"value_type": "user_field",
"connector": "association"
}
]
}
],
"visible": [
{
"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"
}
]
}
]
},
{
"id": "action_4",
"run_after": "step_2",
"type": "update_profile",
"profile_updates": [
{
"type": "association",
"pairs": [
{
"index": 0,
"key": "profile_background",
"key_type": "user_field",
"value": "step_2_field_7",
"value_type": "wizard_field",
"connector": "association"
}
]
}
]
},
{
"id": "action_2",
"run_after": "step_2",
"type": "send_message",
"post_builder": true,
"post_template": "I will interpolate some wizard fields w{step_1_field_1} w{step_1_field_2}",
"title": [
{
"type": "assignment",
"output": "Message title",
"output_type": "text",
"output_connector": "set"
}
],
"recipient": [
{
"type": "assignment",
"output_type": "user",
"output_connector": "set",
"output": [
"angus1"
]
}
]
},
{
"id": "action_3",
"run_after": "step_2",
"type": "open_composer",
"post_builder": true,
"post_template": "I am interpolating some user fields u{name} u{username} u{email}",
"title": [
{
"type": "assignment",
"output": "Title of the composer topic",
"output_type": "text",
"output_connector": "set"
}
],
"category": [
{
"type": "assignment",
"output": "action_8",
"output_type": "wizard_action",
"output_connector": "set",
"pairs": [
{
"index": 0,
"key": "step_2_field_5",
"key_type": "wizard_field",
"value": "true",
"value_type": "text",
"connector": "is"
}
]
}
],
"tags": [
{
"type": "assignment",
"output": "tag1",
"output_type": "text",
"output_connector": "set"
}
]
},
{
"id": "action_10",
"run_after": "wizard_completion",
"type": "route_to",
"url": [
{
"type": "assignment",
"output": "https://google.com",
"output_type": "text",
"output_connector": "set"
}
] ]
} }
] ]

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"
}

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
]
}
]
}

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

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

Datei anzeigen

@ -1,5 +1,61 @@
require 'rails_helper' require 'rails_helper'
describe ApplicationController do 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 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

@ -1,31 +1,63 @@
require 'rails_helper' require 'rails_helper'
describe CustomWizard::WizardController do describe CustomWizard::WizardController do
it 'returns a wizard if enabled' 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 end
it 'returns a disabled message if disabled' do 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 end
it 'returns a missing message if no wizard exists' do it 'returns wizard' do
get '/w/super-mega-fun-wizard.json'
expect(response.parsed_body["id"]).to eq("super_mega_fun_wizard")
end end
it 'returns a custom wizard theme' do it 'returns missing message if no wizard exists' do
get '/w/super-mega-fun-wizards.json'
end expect(response.parsed_body["error"]).to eq("We couldn't find a wizard at that address.")
it 'updates the page title' do
end end
it 'skips a wizard if user is allowed to skip' do 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 end
it 'returns a no skip message if user is not allowed to skip' do 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
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

@ -2,48 +2,85 @@
require 'rails_helper' require 'rails_helper'
describe CustomWizardSerializer do describe CustomWizard::WizardSerializer do
fab!(:user) { Fabricate(:user) } fab!(:user) { Fabricate(:user) }
fab!(:category) { Fabricate(:category) } fab!(:category) { Fabricate(:category) }
let!(:template) do before do
JSON.parse(File.open( CustomWizard::Template.save(
"#{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"
end ).read),
skip_jobs: true)
let(:category_field) {{"id": "category","type": "category","limit": "1","label": "Category"}} @template = CustomWizard::Template.find('super_mega_fun_wizard')
def build_wizard(t = template, u = user, build_opts = {}, params = {})
CustomWizard::Wizard.add_wizard(t)
CustomWizard::Builder.new('welcome', u).build(build_opts, params)
end end
it 'should return the wizard attributes' do it 'should return the wizard attributes' do
json = CustomWizardSerializer.new(build_wizard, scope: Guardian.new(user)).as_json json = CustomWizard::WizardSerializer.new(
expect(json[:custom_wizard][:id]).to eq("welcome") CustomWizard::Builder.new(@template[:id], user).build,
expect(json[:custom_wizard][:name]).to eq("Welcome") scope: Guardian.new(user)
expect(json[:custom_wizard][:background]).to eq("#006da3") ).as_json
expect(json[:custom_wizard][:required]).to eq(false) expect(json[:wizard][:id]).to eq("super_mega_fun_wizard")
expect(json[:custom_wizard][:min_trust]).to eq(1) 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 end
it "should return the wizard user attributes" do it "should return the wizard user attributes" do
json = CustomWizardSerializer.new(build_wizard, scope: Guardian.new(user)).as_json json = CustomWizard::WizardSerializer.new(
expect(json[:custom_wizard][:permitted]).to eq(true) CustomWizard::Builder.new(@template[:id], user).build,
expect(json[:custom_wizard][:user]).to eq(BasicUserSerializer.new(user, root: false).as_json) scope: Guardian.new(user)
).as_json
expect(
json[:wizard][:user]
).to eq(BasicUserSerializer.new(user, root: false).as_json)
end end
it "should not return category attributes if there are no category fields" do it "should not return categories if there are no category fields" do
json = CustomWizardSerializer.new(build_wizard, scope: Guardian.new(user)).as_json @template[:steps][2][:fields].delete_at(2)
expect(json[:custom_wizard][:categories].present?).to eq(false) CustomWizard::Template.save(@template)
expect(json[:custom_wizard][:uncategorized_category_id].present?).to eq(false)
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 end
it "should return category attributes if there is a category selector field" do it "should return categories if there is a category selector field" do
template['steps'][0]['fields'][0] = category_field json = CustomWizard::WizardSerializer.new(
json = CustomWizardSerializer.new(build_wizard(template), scope: Guardian.new(user)).as_json CustomWizard::Builder.new(@template[:id], user).build,
expect(json[:custom_wizard][:categories].present?).to eq(true) scope: Guardian.new(user)
expect(json[:custom_wizard][:uncategorized_category_id].present?).to eq(true) ).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
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