1
0
Fork 0

Update subscription, tests and type handling

Dieser Commit ist enthalten in:
Angus McLeod 2022-02-15 12:33:04 +11:00
Ursprung 7de00ca040
Commit d351577ea3
33 geänderte Dateien mit 467 neuen und 449 gelöschten Zeilen

Datei anzeigen

@ -2,7 +2,7 @@ import Component from "@ember/component";
import discourseComputed, { observes } from "discourse-common/utils/decorators";
import { alias, equal, or } from "@ember/object/computed";
import I18n from "I18n";
import { generateSubscriptionContent } from "../lib/wizard";
import { buildSubscriptionContent } from "../lib/wizard";
export default Component.extend({
tagName: "tr",
@ -38,12 +38,12 @@ export default Component.extend({
@discourseComputed("subscription")
customFieldTypes(subscription) {
return generateSubscriptionContent("custom_fields", "type", subscription);
return buildSubscriptionContent("custom_fields", "types", subscription);
},
@discourseComputed("subscription")
customFieldKlasses(subscription) {
return generateSubscriptionContent("custom_fields", "klass", subscription);
return buildSubscriptionContent("custom_fields", "klass", subscription);
},
@observes("field.klass")

Datei anzeigen

@ -1,9 +1,9 @@
import { default as discourseComputed } from "discourse-common/utils/decorators";
import { empty, equal, or } from "@ember/object/computed";
import {
buildSubscriptionContent,
notificationLevels,
selectKitContent,
generateSubscriptionContent
} from "../lib/wizard";
import { computed } from "@ember/object";
import UndoChanges from "../mixins/undo-changes";
@ -99,6 +99,6 @@ export default Component.extend(UndoChanges, {
@discourseComputed("subscription")
actionTypes(subscription) {
return generateSubscriptionContent("action", "types", subscription);
return buildSubscriptionContent("action", "type", subscription);
},
});

Datei anzeigen

@ -64,7 +64,7 @@ export default {
this.set("customWizardCriticalNotices", criticalNotices);
}
});
}
},
});
api.modifyClass("component:d-navigation", {

Datei anzeigen

@ -71,7 +71,7 @@ const field = {
type: null,
condition: null,
},
types: {},
type: {},
mapped: ["prefill", "content", "condition", "index"],
required: ["id", "type"],
dependent: {},
@ -84,7 +84,7 @@ const action = {
run_after: "wizard_completion",
type: null,
},
types: {
type: {
create_topic: {
title: null,
post: null,
@ -208,19 +208,25 @@ const wizardSchema = {
step,
field,
custom_field,
action
action,
};
export function hasRequiredSubscription(currentSubscriptionType, featureSubscriptionType) {
export function hasRequiredSubscription(
currentSubscriptionType,
featureSubscriptionType
) {
const types = wizardSchema.subscription.types;
return types.indexOf(currentSubscriptionType) >= types.indexOf(featureSubscriptionType);
return (
types.indexOf(currentSubscriptionType) >=
types.indexOf(featureSubscriptionType)
);
}
export function subscriptionType(feature, attribute, value) {
let attributes = wizardSchema.subscription.features[feature];
export function subscriptionType(klass, attribute, value) {
let attributes = wizardSchema.subscription.features[klass];
if (!attributes || !attributes[attribute] || !attributes[attribute][value]) {
return wizardSchema.subscription_types[0];
return wizardSchema.subscription.types[0];
} else {
return attributes[attribute][value];
}
@ -228,7 +234,12 @@ export function subscriptionType(feature, attribute, value) {
export function buildSchema(model) {
wizardSchema.subscription = {};
wizardSchema.subscription.features = model.subscription_features;
let features = model.subscription_features;
features["field"]["types"] = features["field"]["type"];
features["action"]["types"] = features["action"]["type"];
wizardSchema.subscription.features = features;
wizardSchema.subscription.types = model.subscription_types;
wizardSchema.field.types = model.field_types;
wizardSchema.field.validations = model.realtime_validations;

Datei anzeigen

@ -1,8 +1,9 @@
import EmberObject from "@ember/object";
import wizardSchema, {
hasRequiredSubscription,
subscriptionType
subscriptionType,
} from "./wizard-schema";
import I18n from "I18n";
function selectKitContent(content) {
return content.map((i) => ({ id: i, name: i }));
@ -113,23 +114,22 @@ function wizardFieldList(steps = [], opts = {}) {
}, []);
}
function buildSubscriptionContent(feature, attribute, currentSubscription) {
let attributes = wizardSchema[feature];
function buildSubscriptionContent(klass, attribute, currentSubscription) {
let attributes = wizardSchema[klass];
let values = attributes[attribute];
if (typeof values === 'object') {
values = Object.keys(values):
if (typeof values === "object") {
values = Object.keys(values);
}
return values.map((value) => {
let subscriptionType = subscriptionType(feature, attribute, value);
let type = subscriptionType(klass, attribute, value);
return {
id: value,
name: I18n.t(`admin.wizard.${feature}.${attribute}.${value}`),
subscription: subscriptionType,
disabled: hasRequiredSubscription(currentSubscription, subscriptionType)
}
name: I18n.t(`admin.wizard.${klass}.${attribute}.${value}.label`),
subscription: type,
disabled: hasRequiredSubscription(currentSubscription, type),
};
});
}
@ -144,5 +144,5 @@ export {
notificationLevels,
wizardFieldList,
sentenceCase,
buildSubscriptionContent
buildSubscriptionContent,
};

Datei anzeigen

@ -11,7 +11,7 @@ export default DiscourseRoute.extend({
},
afterModel(model) {
buildSchema(model)
buildSchema(model);
return all([
this._getThemes(model),

Datei anzeigen

@ -53,16 +53,6 @@
</div>
<div class="wizard-settings">
<div class="setting">
<div class="setting-label">
<label>{{i18n "admin.wizard.required"}}</label>
</div>
<div class="setting-value">
{{input type="checkbox" checked=wizard.required}}
<span>{{i18n "admin.wizard.required_label"}}</span>
</div>
</div>
<div class="setting">
<div class="setting-label">
<label>{{i18n "admin.wizard.after_signup"}}</label>
@ -83,6 +73,26 @@
</div>
</div>
<div class="setting">
<div class="setting-label">
<label>{{i18n "admin.wizard.save_submissions"}}</label>
</div>
<div class="setting-value">
{{input type="checkbox" checked=wizard.save_submissions}}
<span>{{i18n "admin.wizard.save_submissions_label"}}</span>
</div>
</div>
<div class="setting">
<div class="setting-label">
<label>{{i18n "admin.wizard.restart_on_revisit"}}</label>
</div>
<div class="setting-value">
{{input type="checkbox" checked=wizard.restart_on_revisit}}
<span>{{i18n "admin.wizard.restart_on_revisit_label"}}</span>
</div>
</div>
<div class="setting">
<div class="setting-label">
<label>{{i18n "admin.wizard.prompt_completion"}}</label>
@ -93,7 +103,7 @@
</div>
</div>
<div class="setting full-inline">
<div class="setting">
<div class="setting-label">
<label>{{i18n "admin.wizard.after_time"}}</label>
</div>
@ -108,42 +118,32 @@
</div>
</div>
<div class="setting full field-mapper-setting">
<div class="setting-label">
<label>{{i18n "admin.wizard.permitted"}}</label>
</div>
<div class="setting-value">
{{wizard-mapper
inputs=wizard.permitted
options=(hash
context="wizard"
inputTypes="assignment,validation"
groupSelection="output"
userFieldSelection="key"
textSelection="value"
inputConnector="and"
)}}
</div>
</div>
{{#subscription-container subscribed=subscribed}}
<div class="setting">
<div class="setting-label">
<label>{{i18n "admin.wizard.save_submissions"}}</label>
<label>{{i18n "admin.wizard.required"}}</label>
</div>
<div class="setting-value">
{{input type="checkbox" checked=wizard.save_submissions}}
<span>{{i18n "admin.wizard.save_submissions_label"}}</span>
{{input type="checkbox" checked=wizard.required}}
<span>{{i18n "admin.wizard.required_label"}}</span>
</div>
</div>
<div class="setting">
<div class="setting full field-mapper-setting">
<div class="setting-label">
<label>{{i18n "admin.wizard.restart_on_revisit"}}</label>
<label>{{i18n "admin.wizard.permitted"}}</label>
</div>
<div class="setting-value">
{{input type="checkbox" checked=wizard.restart_on_revisit}}
<span>{{i18n "admin.wizard.restart_on_revisit_label"}}</span>
{{wizard-mapper
inputs=wizard.permitted
options=(hash
context="wizard"
inputTypes="assignment,validation"
groupSelection="output"
userFieldSelection="key"
textSelection="value"
inputConnector="and"
)}}
</div>
</div>
{{/subscription-container}}

Datei anzeigen

@ -19,7 +19,7 @@
<div class="setting-label">
<label>{{i18n "admin.wizard.field.required"}}</label>
</div>
<div class="setting-value">
<span>{{i18n "admin.wizard.field.required_label"}}</span>
{{input type="checkbox" checked=field.required}}
@ -54,7 +54,7 @@
<div class="setting-label">
<label>{{i18n "admin.wizard.type"}}</label>
</div>
<div class="setting-value">
{{combo-box
value=field.type
@ -220,10 +220,10 @@
options=fieldConditionOptions}}
</div>
</div>
<div class="setting full field-mapper-setting">
<div class="setting-label">
<label>{{i18n "admin.wizard.index"}}</label>>
<label>{{i18n "admin.wizard.index"}}</label>
</div>
<div class="setting-value">
@ -234,12 +234,11 @@
</div>
{{#if isCategory}}
<div class="setting pro">
<div class="setting">
<div class="setting-label">
<label>{{i18n "admin.wizard.field.property"}}</label>
<span class="pro-label">{{i18n "admin.wizard.pro.label"}}</span>
</div>
<div class="setting-value">
{{combo-box
value=field.property

Datei anzeigen

@ -56,10 +56,9 @@
</div>
</div>
<div class="setting full field-mapper-setting pro">
<div class="setting full field-mapper-setting">
<div class="setting-label">
<label>{{i18n "admin.wizard.step.required_data.label"}}</label>
<span class="pro-label">{{i18n "admin.wizard.pro.label"}}</span>
</div>
<div class="setting-value">
{{wizard-mapper

Datei anzeigen

@ -920,6 +920,7 @@ $error: #ef1700;
&:not(.subscribed) .subscription-settings {
filter: blur(1px);
pointer-events: none;
}
}

Datei anzeigen

@ -47,7 +47,7 @@ en:
value: "Value"
profile: "profile"
type: "Type"
none: "Make a selection"
none: "Make a selection"
submission_key: 'submission key'
param_key: 'param'
group: "Group"
@ -126,9 +126,9 @@ en:
hide: "Hide"
preview: "{{action}} Preview"
popover: "{{action}} Fields"
input:
conditional:
conditional:
name: 'if'
output: 'then'
assignment:
@ -137,7 +137,7 @@ en:
name: 'map'
validation:
name: 'ensure'
selector:
label:
text: "text"
@ -175,7 +175,7 @@ en:
dependent: "{{property}} is dependent on {{dependent}}"
conflict: "{{type}} with {{property}} '{{value}}' already exists"
after_time: "After time invalid"
step:
header: "Steps"
title: "Title"
@ -189,7 +189,7 @@ en:
force_final:
label: "Conditional Final Step"
description: "Display this step as the final step if conditions on later steps have not passed when the user reaches this step."
field:
header: "Fields"
label: "Label"
@ -211,7 +211,7 @@ en:
property: "Property"
prefill: "Prefill"
content: "Content"
date_time_format:
date_time_format:
label: "Format"
instructions: "<a href='https://momentjs.com/docs/#/displaying/format/' target='_blank'>Moment.js format</a>"
validations:
@ -228,7 +228,7 @@ en:
weeks: "Weeks"
months: "Months"
years: "Years"
type:
text: "Text"
textarea: Textarea
@ -247,7 +247,7 @@ en:
date: Date
time: Time
date_time: Date & Time
connector:
and: "and"
or: "or"
@ -261,7 +261,7 @@ en:
regex: '=~'
association: '→'
is: 'is'
action:
header: "Actions"
include: "Include Fields"
@ -269,8 +269,7 @@ en:
post: "Post"
topic_attr: "Topic Attribute"
interpolate_fields: "Insert wizard fields using the field_id in w{}. Insert user fields using field key in u{}."
run_after:
run_after:
label: "Run After"
wizard_completion: "Wizard Completion"
custom_fields:
@ -282,81 +281,83 @@ en:
suppress_notifications:
label: "Suppress Notifications"
description: "Suppress normal notifications triggered by post creation"
send_message:
label: "Send Message"
recipient: "Recipient"
create_topic:
label: "Create Topic"
category: "Category"
tags: "Tags"
visible: "Visible"
open_composer:
label: "Open Composer"
update_profile:
label: "Update Profile"
setting: "Fields"
key: "field"
watch_categories:
label: "Watch Categories"
categories: "Categories"
mute_remainder: "Mute Remainder"
notification_level:
label: "Notification Level"
regular: "Normal"
watching: "Watching"
tracking: "Tracking"
watching_first_post: "Watching First Post"
muted: "Muted"
select_a_notification_level: "Select level"
wizard_user: "Wizard User"
usernames: "Users"
post_builder:
checkbox: "Post Builder"
label: "Builder"
user_properties: "User Properties"
wizard_fields: "Wizard Fields"
wizard_actions: "Wizard Actions"
placeholder: "Insert wizard fields using the field_id in w{}. Insert user properties using property in u{}."
add_to_group:
label: "Add to Group"
route_to:
label: "Route To"
url: "Url"
code: "Code"
send_to_api:
label: "Send to API"
api: "API"
endpoint: "Endpoint"
select_an_api: "Select an API"
select_an_endpoint: "Select an endpoint"
body: "Body"
body_placeholder: "JSON"
create_category:
label: "Create Category"
name: Name
slug: Slug
color: Color
text_color: Text color
parent_category: Parent Category
permissions: Permissions
create_group:
label: Create Group
name: Name
full_name: Full Name
title: Title
bio_raw: About
owner_usernames: Owners
usernames: Members
grant_trust_level: Automatic Trust Level
mentionable_level: Mentionable Level
messageable_level: Messageable Level
visibility_level: Visibility Level
members_visibility_level: Members Visibility Level
type:
send_message:
label: "Send Message"
recipient: "Recipient"
create_topic:
label: "Create Topic"
category: "Category"
tags: "Tags"
visible: "Visible"
open_composer:
label: "Open Composer"
update_profile:
label: "Update Profile"
setting: "Fields"
key: "field"
watch_categories:
label: "Watch Categories"
categories: "Categories"
mute_remainder: "Mute Remainder"
notification_level:
label: "Notification Level"
regular: "Normal"
watching: "Watching"
tracking: "Tracking"
watching_first_post: "Watching First Post"
muted: "Muted"
select_a_notification_level: "Select level"
wizard_user: "Wizard User"
usernames: "Users"
post_builder:
checkbox: "Post Builder"
label: "Builder"
user_properties: "User Properties"
wizard_fields: "Wizard Fields"
wizard_actions: "Wizard Actions"
placeholder: "Insert wizard fields using the field_id in w{}. Insert user properties using property in u{}."
add_to_group:
label: "Add to Group"
route_to:
label: "Route To"
url: "Url"
code: "Code"
send_to_api:
label: "Send to API"
api: "API"
endpoint: "Endpoint"
select_an_api: "Select an API"
select_an_endpoint: "Select an endpoint"
body: "Body"
body_placeholder: "JSON"
create_category:
label: "Create Category"
name: Name
slug: Slug
color: Color
text_color: Text color
parent_category: Parent Category
permissions: Permissions
create_group:
label: Create Group
name: Name
full_name: Full Name
title: Title
bio_raw: About
owner_usernames: Owners
usernames: Members
grant_trust_level: Automatic Trust Level
mentionable_level: Mentionable Level
messageable_level: Messageable Level
visibility_level: Visibility Level
members_visibility_level: Members Visibility Level
custom_field:
nav_label: "Custom Fields"
add: "Add"
external:
external:
label: "from another plugin"
title: "This custom field has been added by another plugin. You can use it in your wizards but you can't edit the field here."
name:
@ -385,7 +386,7 @@ en:
basic_category: "Category"
basic_group: "Group"
post: "Post"
submissions:
nav_label: "Submissions"
title: "{{name}} Submissions"
@ -467,7 +468,7 @@ en:
subscription_container:
title: Subscriber Features
subscribed:
subscribed:
label: Subscribed
title: You're subscribed and can use these features
not_subscribed:
@ -635,7 +636,7 @@ en:
yourself_confirm:
title: "Did you forget to add recipients?"
body: "Right now this message is only being sent to yourself!"
realtime_validations:
similar_topics:
insufficient_characters: "Type a minimum 5 characters to start looking for similar topics"

Datei anzeigen

@ -6,7 +6,7 @@ en:
wizard:
custom_title: "Wizard"
custom_field:
error:
required_attribute: "'%{attr}' is a required attribute"
@ -18,7 +18,7 @@ en:
name_already_taken: "'%{name}' is already taken as a custom field name"
save_default: "Failed to save custom field '%{name}'"
subscription_type: "%{type} custom fields require a subscription"
field:
too_short: "%{label} must be at least %{min} characters"
too_long: "%{label} must not be more than %{max} characters"
@ -27,30 +27,30 @@ en:
invalid_file: "%{label} must be a %{types}"
invalid_date: "Invalid date"
invalid_time: "Invalid time"
none: "We couldn't find a wizard at that address."
no_skip: "Wizard can't be skipped"
export:
error:
select_one: "Please select at least one valid wizard"
invalid_wizards: "No valid wizards selected"
import:
error:
no_file: "No file selected"
file_large: "File too large"
invalid_json: "File is not a valid json file"
destroy:
error:
no_template: No template found
default: Error destroying wizard
validation:
required: "%{property} is required"
required: "%{attribute} is required"
conflict: "Wizard with id '%{wizard_id}' already exists"
after_time: "After time setting is invalid"
subscription: "%{type} %{property} is subscription only"
subscription: "%{class} %{attribute} is subscription only"
notice:
connection_error: "Failed to connect to http://%{domain}"
@ -62,7 +62,7 @@ en:
title: Unable to connect to the Custom Wizard Plugin status server
message: Please check the Custom Wizard Plugin status on [%{domain}](http://%{domain}) before updating Discourse. If this issue persists contact <a href="mailto:support@thepavilion.io">support@thepavilion.io</a> for further assistance.
subscription_message:
connection_error:
connection_error:
title: Unable to connect to the Custom Wizard Plugin subscription server
message: If this issue persists contact <a href="mailto:support@thepavilion.io">support@thepavilion.io</a> for further assistance.

Datei anzeigen

@ -84,7 +84,7 @@ class ::CustomWizard::CustomField
next
end
if attr == 'klass' && @subscription.subscription.can_use_feature?("custom_fields", "klass", value)
if attr == 'klass' && !@subscription.can_use_feature?("custom_field", "klass", value)
add_error(I18n.t("wizard.custom_field.error.subscription_type", type: value))
end
@ -99,7 +99,7 @@ class ::CustomWizard::CustomField
add_error(I18n.t("#{i18n_key}.unsupported_type", type: value))
end
if attr == 'type' && @subscription.subscription.can_use_feature?("custom_fields", "type", value)
if attr == 'type' && !@subscription.can_use_feature?("custom_field", "type", value)
add_error(I18n.t("wizard.custom_field.error.subscription_type", type: value))
end

Datei anzeigen

@ -19,6 +19,11 @@ class CustomWizard::Subscription
@subscription.active?
end
def can_use_feature?(klass, attribute, value)
t = @subscription.determine_feature_subscription_type(klass, attribute, value)
!t || @subscription.has_required_type?(t)
end
def server
"test.thepavilion.io"
end

Datei anzeigen

@ -5,16 +5,34 @@ class CustomWizard::Subscription::Subscription
attr_reader :type,
:updated_at
NONE ||= "none"
STANDARD ||= "standard"
BUSINESS ||= "business"
FEATURES ||= {
actions: {
wizard: {
permitted: STANDARD
},
step: {
index: STANDARD,
condition: STANDARD,
required_data: BUSINESS,
permitted_params: BUSINESS
},
field: {
index: STANDARD,
condition: STANDARD,
prefill: STANDARD,
content: STANDARD,
validations: STANDARD,
type: {
tag: STANDARD,
category: STANDARD,
group: STANDARD,
composer: STANDARD,
composer_preview: STANDARD
}
},
action: {
type: {
create_topic: NONE,
update_profile: NONE,
open_composer: NONE,
route_to: NONE,
send_message: STANDARD,
watch_categories: STANDARD,
add_to_group: STANDARD,
@ -23,20 +41,16 @@ class CustomWizard::Subscription::Subscription
create_group: BUSINESS
}
},
custom_fields: {
custom_field: {
klass: {
topic: NONE,
post: NONE,
group: BUSINESS,
category: BUSINESS
},
type: {
string: NONE,
boolean: NONE,
integer: NONE,
json: STANDARD
}
}
},
api: {}
}
def initialize(subscription)
@ -47,23 +61,33 @@ class CustomWizard::Subscription::Subscription
end
def active?
types.include?(type) && updated_at.to_datetime > (Time.zone.now - 2.hours).to_datetime
self.class.types.include?(type) && updated_recently
end
def can_use_feature?(feature, attribute, value)
feature_type = FEATURES.dig(*[feature.to_sym, attribute.to_sym, value.to_sym])
!feature_type || has_required_type?(feature_type)
def updated_recently
updated_at.to_datetime > (Time.zone.now - 2.hours).to_datetime
end
def has_required_type?(t)
t && type_index(t) >= type_index(type)
t && type && type_index(type) >= type_index(t)
end
def type_index(t)
self.class.types.index(t)
end
def determine_feature_subscription_type(klass, attribute, value)
return BUSINESS if klass.to_sym === :api
type = FEATURES.dig(*[klass.to_sym, attribute.to_sym])
if type.is_a?(Hash) && value.present?
type = type[value.to_sym]
else
type
end
end
def self.types
[NONE, STANDARD, BUSINESS]
[STANDARD, BUSINESS]
end
end

Datei anzeigen

@ -13,34 +13,26 @@ class CustomWizard::TemplateValidator
data = @data
check_id(data, :wizard)
check_required(data, :wizard)
validate_after_time
validate_subscription(data, :wizard)
validate_class(data, :wizard)
data[:steps].each do |step|
check_required(step, :step)
validate_subscription(step, :step)
validate_class(step, :step)
if step[:fields].present?
step[:fields].each do |field|
validate_subscription(field, :field)
check_required(field, :field)
validate_class(field, :field)
end
end
end
if data[:actions].present?
data[:actions].each do |action|
validate_subscription(action, :action)
check_required(action, :action)
validate_class(action, :action)
end
end
if errors.any?
false
else
true
end
!errors.any?
end
def self.required
@ -52,56 +44,25 @@ class CustomWizard::TemplateValidator
}
end
def self.subscription
{
wizard: {
save_submissions: 'false',
restart_on_revisit: 'true',
},
step: {
condition: 'present',
index: 'conditional',
required_data: 'present',
permitted_params: 'present'
},
field: {
condition: 'present',
index: 'conditional'
},
action: {
type: %w[
send_message
add_to_group
create_category
create_group
send_to_api
]
}
}
end
private
def check_required(object, type)
self.class.required[type].each do |property|
if object[property].blank?
errors.add :base, I18n.t("wizard.validation.required", property: property)
def validate_class(object, klass)
check_required(object, klass)
validate_subscription(object, klass)
end
def check_required(object, klass)
self.class.required[klass].each do |attribute|
if object[attribute].blank?
errors.add :base, I18n.t("wizard.validation.required", attribute: attribute)
end
end
end
def validate_subscription(object, type)
self.class.subscription[type].each do |property, subscription_type|
val = object[property.to_s]
is_subscription = (val != nil) && (
subscription_type === 'present' && val.present? ||
(['true', 'false'].include?(subscription_type) && cast_bool(val) == cast_bool(subscription_type)) ||
(subscription_type === 'conditional' && val.is_a?(Hash)) ||
(subscription_type.is_a?(Array) && subscription_type.include?(val))
)
if is_subscription && !@subscription.subscribed?
errors.add :base, I18n.t("wizard.validation.subscription", type: type.to_s, property: property)
def validate_subscription(object, klass)
object.keys.each do |attribute|
if !@subscription.can_use_feature?(klass, attribute, object[attribute])
errors.add :base, I18n.t("wizard.validation.subscription", class: klass.to_s, attribute: attribute)
end
end
end

Datei anzeigen

@ -12,6 +12,7 @@ describe CustomWizard::Action do
let(:create_group) { get_wizard_fixture("actions/create_group") }
let(:add_to_group) { get_wizard_fixture("actions/add_to_group") }
let(:send_message) { get_wizard_fixture("actions/send_message") }
let(:watch_categories) { get_wizard_fixture("actions/watch_categories") }
let(:send_message_multi) { get_wizard_fixture("actions/send_message_multi") }
let(:api_test_endpoint) { get_wizard_fixture("endpoints/test_endpoint") }
let(:api_test_endpoint_body) { get_wizard_fixture("endpoints/test_endpoint_body") }
@ -159,17 +160,6 @@ describe CustomWizard::Action do
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: category.id,
user_id: user.id
).first.notification_level).to eq(0)
end
it 're-routes a user' do
wizard = CustomWizard::Builder.new(@template[:id], user).build
updater = wizard.create_updater(wizard.steps.last.id, {})
@ -177,7 +167,7 @@ describe CustomWizard::Action do
expect(updater.result[:redirect_on_next]).to eq("https://google.com")
end
context "subscription actions" do
context "standard subscription actions" do
before do
enable_subscription("standard")
end
@ -235,6 +225,38 @@ describe CustomWizard::Action do
expect(post.exists?).to eq(true)
end
it '#add_to_group' do
add_to_group["group"][0]["output"] = group.name
wizard_template['actions'] << add_to_group
update_template(wizard_template)
wizard = CustomWizard::Builder.new(@template[:id], user).build
step_id = wizard.steps[0].id
updater = wizard.create_updater(step_id, step_1_field_1: "Text input").update
expect(group.users.first.username).to eq('angus')
end
it '#watch_categories' do
wizard_template['actions'] << watch_categories
update_template(wizard_template)
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: category.id,
user_id: user.id
).first.notification_level).to eq(0)
end
end
context "business subscription actions" do
before do
enable_subscription("business")
end
it '#create_category' do
wizard_template['actions'] << create_category
update_template(wizard_template)
@ -256,19 +278,6 @@ describe CustomWizard::Action do
expect(Group.where(name: wizard.current_submission.fields['action_9']).exists?).to eq(true)
end
it '#add_to_group' do
wizard_template['actions'] << create_group
wizard_template['actions'] << add_to_group
update_template(wizard_template)
wizard = CustomWizard::Builder.new(@template[:id], user).build
step_id = wizard.steps[0].id
updater = wizard.create_updater(step_id, step_1_field_1: "Text input").update
group = Group.find_by(name: wizard.current_submission.fields['action_9'])
expect(group.users.first.username).to eq('angus')
end
it '#send_to_api successful' do
stub_request(:put, "https://myexternalapi.com/update").
with(

Datei anzeigen

@ -20,6 +20,7 @@ describe CustomWizard::Builder do
let(:permitted_json) { get_wizard_fixture("wizard/permitted") }
let(:permitted_param_json) { get_wizard_fixture("step/permitted_params") }
let(:user_condition_json) { get_wizard_fixture("condition/user_condition") }
let(:prefill_json) { get_wizard_fixture("field/prefill") }
let(:boolean_field_condition_json) {
JSON.parse(
@ -35,6 +36,12 @@ describe CustomWizard::Builder do
@template = CustomWizard::Template.find('super_mega_fun_wizard')
end
def perform_update(step_id, submission)
updater = @wizard.create_updater(step_id, submission)
updater.update
updater
end
context 'disabled' do
before do
SiteSetting.custom_wizard_enabled = false
@ -101,6 +108,7 @@ describe CustomWizard::Builder do
context "with restricted permissions" do
before do
enable_subscription("standard")
@template[:permitted] = permitted_json["permitted"]
CustomWizard::Template.save(@template.as_json)
end
@ -155,13 +163,21 @@ describe CustomWizard::Builder do
end
end
it 'returns prefilled data' do
expect(
CustomWizard::Builder.new(@template[:id], user).build
.steps.first
.fields.first
.value
).to eq('I am prefilled')
context "prefilled data" do
before do
enable_subscription("standard")
@template[:steps][0][:fields][0][:prefill] = prefill_json["prefill"]
CustomWizard::Template.save(@template.as_json)
end
it "works" do
expect(
CustomWizard::Builder.new(@template[:id], user).build
.steps.first
.fields.first
.value
).to eq('I am prefilled')
end
end
context "user has partially completed" do
@ -190,12 +206,14 @@ describe CustomWizard::Builder do
end
it 'does not return saved submissions' do
@wizard = CustomWizard::Builder.new(@template[:id], user).build
perform_update('step_1', step_1_field_1: 'Text input')
expect(
CustomWizard::Builder.new(@template[:id], user).build
.steps.first
.fields.first
.value
).to eq('I am prefilled')
).to eq(nil)
end
end
end
@ -213,7 +231,7 @@ describe CustomWizard::Builder do
context 'with required data' do
before do
enable_subscription("standard")
enable_subscription("business")
@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)
@ -249,7 +267,7 @@ describe CustomWizard::Builder do
context "with permitted params" do
before do
enable_subscription("standard")
enable_subscription("business")
@template[:steps][0][:permitted_params] = permitted_param_json['permitted_params']
CustomWizard::Template.save(@template.as_json)
end
@ -298,14 +316,14 @@ describe CustomWizard::Builder do
.build
.steps.first
.fields.length
).to eq(4)
).to eq(3)
end
context "with condition" do
before do
enable_subscription("standard")
@template[:steps][0][:fields][0][:condition] = user_condition_json['condition']
@template[:steps][2][:fields][5][:condition] = boolean_field_condition_json['condition']
@template[:steps][2][:fields][2][:condition] = boolean_field_condition_json['condition']
CustomWizard::Template.save(@template.as_json)
end
@ -326,18 +344,12 @@ describe CustomWizard::Builder do
builder = CustomWizard::Builder.new(@template[:id], user)
wizard = builder.build
expect(wizard.steps.last.fields.last.id).to eq(@template[:steps][2][:fields][5]['id'])
expect(wizard.steps.last.fields.last.id).to eq(@template[:steps][2][:fields][2]['id'])
end
end
end
context 'on update' do
def perform_update(step_id, submission)
updater = @wizard.create_updater(step_id, submission)
updater.update
updater
end
it 'saves submissions' do
@wizard = CustomWizard::Builder.new(@template[:id], user).build
perform_update('step_1', step_1_field_1: 'Text input')

Datei anzeigen

@ -48,6 +48,8 @@ describe CustomWizard::Template do
context "wizard template list" do
before do
enable_subscription("standard")
template_json_2 = template_json.dup
template_json_2["id"] = 'super_mega_fun_wizard_2'
template_json_2["permitted"] = permitted_json['permitted']

Datei anzeigen

@ -6,6 +6,7 @@ describe CustomWizard::TemplateValidator do
let(:template) { get_wizard_fixture("wizard") }
let(:create_category) { get_wizard_fixture("actions/create_category") }
let(:user_condition) { get_wizard_fixture("condition/user_condition") }
let(:permitted_json) { get_wizard_fixture("wizard/permitted") }
it "validates valid templates" do
expect(
@ -45,7 +46,7 @@ describe CustomWizard::TemplateValidator do
context "without subscription" do
it "invalidates subscription wizard attributes" do
template[:save_submissions] = false
template[:permitted] = permitted_json["permitted"]
expect(
CustomWizard::TemplateValidator.new(template).perform
).to eq(false)
@ -73,13 +74,13 @@ describe CustomWizard::TemplateValidator do
end
end
context "with subscription" do
context "with standard subscription" do
before do
enable_subscription("standard")
end
it "validates wizard attributes" do
template[:save_submissions] = false
template[:permitted] = permitted_json["permitted"]
expect(
CustomWizard::TemplateValidator.new(template).perform
).to eq(true)
@ -98,6 +99,12 @@ describe CustomWizard::TemplateValidator do
CustomWizard::TemplateValidator.new(template).perform
).to eq(true)
end
end
context "with business subscription" do
before do
enable_subscription("business")
end
it "validates actions" do
template[:actions] << create_category

Datei anzeigen

@ -22,7 +22,6 @@ describe CustomWizard::UpdateValidator do
@template[:steps][0][:fields][0][:min_length] = min_length
@template[:steps][0][:fields][1][:min_length] = min_length
@template[:steps][0][:fields][2][:min_length] = min_length
CustomWizard::Template.save(@template)
@ -35,11 +34,6 @@ describe CustomWizard::UpdateValidator do
expect(
updater.errors.messages[:step_1_field_2].first
).to eq(I18n.t('wizard.field.too_short', label: 'Textarea', min: min_length))
updater = perform_validation('step_1', step_1_field_3: 'Te')
expect(
updater.errors.messages[:step_1_field_3].first
).to eq(I18n.t('wizard.field.too_short', label: 'Composer', min: min_length))
end
it 'prevents submission if the length is over the max length' do
@ -47,7 +41,6 @@ describe CustomWizard::UpdateValidator do
@template[:steps][0][:fields][0][:max_length] = max_length
@template[:steps][0][:fields][1][:max_length] = max_length
@template[:steps][0][:fields][2][:max_length] = max_length
CustomWizard::Template.save(@template)
long_string = "Our Competitive Capability solution offers platforms a suite of wholesale offerings. In the future, will you be able to effectively revolutionize synergies in your business? In the emerging market space, industry is ethically investing its mission critical executive searches. Key players will take ownership of their capabilities by iteratively right-sizing world-class visibilities. "
@ -60,11 +53,6 @@ describe CustomWizard::UpdateValidator do
expect(
updater.errors.messages[:step_1_field_2].first
).to eq(I18n.t('wizard.field.too_long', label: 'Textarea', max: max_length))
updater = perform_validation('step_1', step_1_field_3: long_string)
expect(
updater.errors.messages[:step_1_field_3].first
).to eq(I18n.t('wizard.field.too_long', label: 'Composer', max: max_length))
end
it "allows submission if the length is under or equal to the max length" do
@ -72,7 +60,6 @@ describe CustomWizard::UpdateValidator do
@template[:steps][0][:fields][0][:max_length] = max_length
@template[:steps][0][:fields][1][:max_length] = max_length
@template[:steps][0][:fields][2][:max_length] = max_length
CustomWizard::Template.save(@template)
hundred_chars_string = "This is a line, exactly hundred characters long and not more even a single character more than that."
@ -85,11 +72,6 @@ describe CustomWizard::UpdateValidator do
expect(
updater.errors.messages[:step_1_field_2].first
).to eq(nil)
updater = perform_validation('step_1', step_1_field_3: hundred_chars_string)
expect(
updater.errors.messages[:step_1_field_3].first
).to eq(nil)
end
it "applies min length only if the input is non-empty" do

Datei anzeigen

@ -14,6 +14,7 @@ describe CustomWizard::Wizard do
@permitted_template = template_json.dup
@permitted_template["permitted"] = permitted_json["permitted"]
@wizard = CustomWizard::Wizard.new(template_json, user)
enable_subscription("standard")
end
def append_steps

Datei anzeigen

@ -5,9 +5,9 @@
"group": [
{
"type": "assignment",
"output": "action_9",
"output_type": "wizard_action",
"output": "",
"output_type": "text",
"output_connector": "set"
}
]
}
}

23
spec/fixtures/actions/watch_categories.json gevendort Normale Datei
Datei anzeigen

@ -0,0 +1,23 @@
{
"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"
}
]
}

33
spec/fixtures/field/content.json gevendort Normale Datei
Datei anzeigen

@ -0,0 +1,33 @@
{
"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"
}
]
}
]
}

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

@ -0,0 +1,10 @@
{
"prefill": [
{
"type": "assignment",
"output": "I am prefilled",
"output_type": "text",
"output_connector": "set"
}
]
}

Datei anzeigen

@ -17,15 +17,7 @@
"label": "Text",
"description": "Text field description.",
"type": "text",
"min_length": "3",
"prefill": [
{
"type": "assignment",
"output": "I am prefilled",
"output_type": "text",
"output_connector": "set"
}
]
"min_length": "3"
},
{
"id": "step_1_field_2",
@ -33,11 +25,6 @@
"type": "textarea",
"min_length": ""
},
{
"id": "step_1_field_3",
"label": "Composer",
"type": "composer"
},
{
"id": "step_1_field_4",
"label": "I'm only text",
@ -102,55 +89,7 @@
{
"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"
"type": "dropdown"
},
{
"id": "step_3_field_5",
@ -257,29 +196,6 @@
}
]
},
{
"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_4",
"run_after": "step_2",
@ -337,4 +253,4 @@
]
}
]
}
}

Datei anzeigen

@ -33,6 +33,7 @@ def enable_subscription(type)
# CustomWizard::Subscription.new
CustomWizard::Subscription.any_instance.stubs(:subscribed?).returns(true)
CustomWizard::Subscription.any_instance.stubs(:type).returns(type)
CustomWizard::Subscription::Subscription.any_instance.stubs(:type).returns(type)
end
def disable_subscription

Datei anzeigen

@ -8,6 +8,7 @@ describe CustomWizard::AdminWizardController do
let(:template) { get_wizard_fixture("wizard") }
before do
enable_subscription("standard")
CustomWizard::Template.save(template, skip_jobs: true)
template_2 = template.dup

Datei anzeigen

@ -35,6 +35,8 @@ describe CustomWizard::StepsController do
end
it "when the user cant access the wizard" do
enable_subscription("standard")
new_template = wizard_template.dup
new_template["permitted"] = permitted_json["permitted"]
CustomWizard::Template.save(new_template, skip_jobs: true)

Datei anzeigen

@ -18,10 +18,10 @@ describe CustomWizard::FieldSerializer do
scope: Guardian.new(user)
).as_json
expect(json_array.size).to eq(4)
expect(json_array.size).to eq(3)
expect(json_array[0][:label]).to eq("<p>Text</p>")
expect(json_array[0][:description]).to eq("Text field description.")
expect(json_array[3][:index]).to eq(3)
expect(json_array[2][:index]).to eq(2)
end
it "should return optional field attributes" do

Datei anzeigen

@ -42,55 +42,73 @@ describe CustomWizard::WizardSerializer do
).to eq(BasicUserSerializer.new(user, root: false).as_json)
end
it "should not return categories if there are no category fields" do
@template[:steps][2][:fields].delete_at(2)
CustomWizard::Template.save(@template)
context "with subscription" do
before do
enable_subscription("standard")
end
json = CustomWizard::WizardSerializer.new(
CustomWizard::Builder.new(@template[:id], user).build,
scope: Guardian.new(user)
).as_json
expect(json[:wizard][:categories].present?).to eq(false)
expect(json[:wizard][:uncategorized_category_id].present?).to eq(false)
end
it "should not return categories if there are no category fields" do
@template[:steps][2][:fields].delete_at(2)
CustomWizard::Template.save(@template)
it "should return categories if there is a category selector field" do
json = CustomWizard::WizardSerializer.new(
CustomWizard::Builder.new(@template[:id], user).build,
scope: Guardian.new(user)
).as_json
expect(json[:wizard][:categories].present?).to eq(true)
expect(json[:wizard][:uncategorized_category_id].present?).to eq(true)
end
json = CustomWizard::WizardSerializer.new(
CustomWizard::Builder.new(@template[:id], user).build,
scope: Guardian.new(user)
).as_json
it "should return categories if there is a similar topics validation scoped to category(s)" do
@template[:steps][0][:fields][0][:validations] = similar_topics_validation[:validations]
CustomWizard::Template.save(@template)
expect(json[:wizard][:categories].present?).to eq(false)
expect(json[:wizard][:uncategorized_category_id].present?).to eq(false)
end
json = CustomWizard::WizardSerializer.new(
CustomWizard::Builder.new(@template[:id], user).build,
scope: Guardian.new(user)
).as_json
expect(json[:wizard][:categories].present?).to eq(true)
expect(json[:wizard][:uncategorized_category_id].present?).to eq(true)
end
it "should return categories if there is a category selector field" do
@template[:steps][0][:fields] << { "id": "step_1_field_5", "label": "Category", "type": "category" }.as_json
CustomWizard::Template.save(@template)
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
json = CustomWizard::WizardSerializer.new(
CustomWizard::Builder.new(@template[:id], user).build,
scope: Guardian.new(user)
).as_json
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)
expect(json[:wizard][:categories].present?).to eq(true)
expect(json[:wizard][:uncategorized_category_id].present?).to eq(true)
end
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)
it "should return categories if there is a similar topics validation scoped to category(s)" do
@template[:steps][0][:fields][0][:validations] = similar_topics_validation
CustomWizard::Template.save(@template)
json = CustomWizard::WizardSerializer.new(
CustomWizard::Builder.new(@template[:id], user).build,
scope: Guardian.new(user)
).as_json
expect(json[:wizard][:categories].present?).to eq(true)
expect(json[:wizard][:uncategorized_category_id].present?).to eq(true)
end
it 'should return groups if there is a group selector field' do
@template[:steps][0][:fields] << { "id": "step_1_field_5", "label": "Group", "type": "group" }.as_json
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(true)
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

Datei anzeigen

@ -29,12 +29,12 @@ describe CustomWizard::StepSerializer do
each_serializer: described_class,
scope: Guardian.new(user)
).as_json
expect(json_array[0][:fields].length).to eq(4)
expect(json_array[0][:fields].length).to eq(3)
end
context 'with required data' do
before do
enable_subscription("standard")
enable_subscription("business")
wizard_template['steps'][0]['required_data'] = required_data_json['required_data']
wizard_template['steps'][0]['required_data_message'] = required_data_json['required_data_message']
CustomWizard::Template.save(wizard_template)